深入浅出sql注入(2)——union query injection

在上一篇最后,我们使用了『-1 UNION SELECT username, password FROM account』来获取用户的用户名和密码,在这一篇中,我们将来更详细地了解一下这种注入方式。

要了解union query injection,首先得了解union查询,union用于合并两个或更多个select的结果集。比如说

SELECT username, password FROM account

的结果是

admin 123456
SELECT id, title FROM article

的结果是

1 Hello, World

那么,

SELECT username, password FROM account
UNION 
SELECT id, title FROM article

的结果就是

admin 123456
1 Hello, World

这应该并不难理解,但是有两个注意点,第一个是union只会选取不同的值,换言之,如果两条sql的结果集中有一模一样的记录,那么它并不会被列出来两次(如果你想保留重复记录可以使用UNION ALL);第二个则是union前后的sql必须保证字段数一致,在上面的例子中,username和password是两个字段,id和title也是两个,所以没有任何问题。

关于union的科普到此为止,回到我们的注入话题。注入的本质其实就是拼接各种sql,来达到我们自己想要的结果。union注入一般用在有信息显示的页面,如上个例子中的文章详情页面,或者列表页,以及其他任何一个会将数据库查询结果展示出来的页面。要进行注入,首先得找到注入点,最暴力有效的方法当然就是对各个链接的每一个可能有注入的地方(参数)尝试进行注入,看看能不能成功。当然这对于手注(手工注入,指不借助sqlmap之类的工具进行注入)来说太累了。只不过本系列文章主要是介绍sql注入的原理相关的东西,所以对于寻找注入点,就不做过多的介绍了。。。

现在假设我们已经成功的找到了某个注入点 http://redtiger.labs.overthewire.org/level1.php?cat=1 (这是某个渗透演练平台的第一关),那么接下来该干什么呢?我们可以猜测出在这个程序背后,一定会有一条类似于

$sql = "SELECT * FROM article WHERE id={$_GET['cat']}"

这样子的语句,可是要用union的话,上面提到字段数必须一致,我们怎么判断呢?穷举字段数当然是一个可行的方法。尝试提交『1 union select 1– 』(在自己的sql后面加上注释符是个好习惯,因为你不知道程序里的sql原本是怎么写的,注释符可以让他们不存在),这样子sql就变成了

SELECT * FROM article WHERE id=1
UNION
SELECT 1--

如果字段数不一致,那就会出现错误(这个错误怎么体现取决于程序,有可能是把错误信息输出、有可能是白屏、有可能是页面显示不全)。如果一个字段不对的话,那我们就试试看两个,一个个穷举,直到页面显示正确为止。当然,这样子效率太低太累了,有没有更快的方法?当然有。在数据库中想要根据文章id进行排序怎么办?ORDER BY id吗?这当然是对的,但其实还可以用字段的列数来进行排序,比如ORDER BY 1。当我们使用一个不存在的列(比如总共只有10个,但我们使用100)来进行排序的时候,同样会出错。利用这个特性,再结合二分查找,我们就可以快速定位字段数了。现在,假设我们使用『1 ORDER BY 4– 』是正常的,而『1 ORDER BY 5– 』就出错了,那我们可以断定原来的sql应该有4个字段。

union order by检测,正常情况
union order by检测,正常情况
union order by检测,异常情况
union order by检测,异常情况

接下来我们就该确定那些字段被输出了,为了达到这一步,我们得做到两点:1. 让原本查询的结果集为空,比如在上一篇中使用的,把传入的id从123改为-1(因为-1显然不是一个有效的文章id,因此原本的sql查询应该会返回空集);2. 对于每个字段输出的内容应该不同(不然区分不了哪些字段被输出了)。因此,我们现在提交『-1 UNION SELECT 1,2,3,4』,然后看见页面上原本该输出标题、内容之类的地方,出现了我们填写的这些数字,这些显示出来的数字,就是我们接下来可以利用的点。

3

在Oracle等数据库中,union数字的话可能会提示类型不匹配,这是因为它除了要求前后字段数一致,还要求前后的字段类型也保持一致。这时候可以先用NULL来替换数字,然后逐个字段尝试数字、字母来判断字段类型。

一般来说,我们在一开始是不知道网站里有哪些库/表/字段的,因此接下来我们应该逐步找出他们,但这样子对于这篇文章来说就未免太长了。于是我们假设我们神奇的得知了我们要找的用户信息的表在 level1_users 里(其实在我们这个例子中,页面上已经写了,当然实际的渗透中这种好事基本上不会发生),而用户名和密码分别在username和password字段中(我猜的,并且一下子猜对了)。那么正好我们刚才有两个可以用于显示的字段3和4,我们把它替换掉『-1 UNION SELECT 1,2,username,password FROM level1_users』

4

可以直接看到用户名和密码就这么显示在页面上了~大功告成~

发表评论

电子邮件地址不会被公开。 必填项已用*标注