由于对于普通的SQL注入利用,已有很多其他论文进行了周详的讨论,故本文只会在下一节介绍一种
UNION SELECT注入。
4 UNION SELECT注入
尽管通过篡改SELECT…WHERE语句来注入对于很多应用程式很有效,但在盲注情况下,攻击者仍
然愿意使用UNION SELECT语句,这是因为和WHERE语句所进行的操作不同,使用UNION SELECT能够
让攻击者在没有错误信息的情况下依然能访问数据库中任何表。
进行UNION SELECT注入需要预先获知数据库的表中的字段个数和类型,而这些信息一般被认为在没
有周详错误信息的提示下是不可能获得的,但本文下面就将给出解决该问题的方法。
另外需要注意的是,进行UNION SELECT的前提是攻击者已确定了正确的注入句法,本文的前面一
节已阐明了这在盲注条件下是能够实现的,而且在使用UNION SELECT语句之前,SQL语句中任何的插
入语符号都应该已完成配对,从而能够自由地使用UNION或其他指令进行注入。UNION SELECT还要
求当前语句和最初的语句查询的信息必须具备相同的数和相同的数据类型,不然就会出错。
4.1 统计列数
当错误信息没有被屏蔽时,要获取列数只需要在进行UNION SELECT注入时每次尝试使用不同的字段
数即可,当错误信息由“列数不匹配”变成“列的类型不匹配”时,当前尝试的列数就是正确的。但在盲注
条件下,由于我们对无法获悉错误信息究竟是哪个,所以该方法也就失去了作用。
新的办法是利用ORDER BY语句,在SELECT语句最后加上ORDER BY能够改变返回的记录集的次序,
一般是按一个指定的列名的值进行排序。例如,当通过产品号查询产品时,一个有效的注入语句如下:
SELECT ProdNum FROM Products WHERE (ProdID=1234) ORDER BY ProdNum --
AND ProdName=’Computer’) AND UserName=’john’
人们往往会忽略的是ORDER BY语句后还能够使用数字指代列名,在上例中假如ProdNum是查询请求返回
的记录中的第一列,则注入1234) ORDER BY 1--返回的结果是相同的。由于上例查询请求只返回一个字段,
注入1234) ORDER BY 2 --就会出错,即返回的记录无法按指定的第二个字段排序。这样,ORDER BY就可
以被利用来对列数进行统计了。由于每个SELECT语句都至少返回一个字段,故攻击者能够先在注入句法中
添加ORDER BY 1来确定语句是否能被正确执行,有时对字段的排序也可能会产生错误,这时添加关键字
ASC或DESC能够解决该问题。一旦确定ORDER BY句法是有效的,攻击者就会对排序列号从列1到列100
进行遍历(或到列1000,直到列号被确定为无效),理论上当出现第一个错误时,前一个列号就是要统计
的列数,但在实际情况中,有些字段可能不允许排序,那么在出现第一次错误时能够再多尝试一到两个数字,
以确认列号已遍历完。
4.2 判断列的数据类型
在统计完列数后,攻击者需要再判断列的数据类型,在盲注情况下判断类型也是有技巧的,由于UNION
SELECT需要前后查询语句查询的字段类型相同,故假如字段数有限,能够简单地利用UNION SELECT语句
对字段类型进行暴力穷举(brute force),但假如字段数较多,判断就会出现问题。根据前文,字段的类型只
10
有数字、字符串和日期三种可能的类型,一旦字段数有10个,那么就意味着有3(约60,000)种可能的
组合,假设每一秒能够自动进行20次尝试,穷举一遍也需要近一个小时,假如字段数更多,那么测试所需
时间就会令人难以忍受。
一种简单的办法是利用SQL的关键字NULL,和静态字段的注入需要区分是数字类型还是字符类型不同,
NULL能够匹配任何一种数据类型。因此能够注入一个任何查询字段都为NULL的UNION SELECT语句,
那么就不会出现任何类型不匹配的错误。让我们再举一个和前面类似的例子:
SELECT ProdNum,ProdType,ProdPrice,ProdProvider FROM Products
WHERE (ProdID=1234 AND ProdName=’ Computer’) AND UserName=’john’
假设攻击者已获得了列数(在该例中为4),那么就能够很简单地构造一个UNION SELECT语句,其中所
有查询字段都为NULL,还需要构造一个不会产生权限问题的FROM语句。对于MS SQL Server,即使忽略
FROM语句也不会出错,但对于Oracle,则能够使用一个名叫dual的表。最后,还需要一个值一定为FALSE
的WHERE语句(比如WHERE 1=2),这是为了确保查询不会返回只包含null值的记录集,以杜绝产生其他
可能的错误。那么针对MS SQL Server的注入语句如下:
SELECT ProdNum,ProdType,ProdPrice,ProdProvider FROM Products
WHERE (ProdID=1234) UNION SELECT NULL,NULL,NULL,NULL
WHERE 1=2 -- AND ProdName=’ Computer’) AND UserName=’john’
这个NULL注入语句有两个目的,主要目的是构造一个不会产生任何错误的UNION SELECT语句以测试
UNION语句是否能够被执行,另一个目的是为了对数据库类型的判断进行100%确认(能够通过在FROM语
句里添加一个数据库研发商预置的表名进行测试)。
假如NULL注入语句被顺利执行,那么就能够快速地对每个列的类型进行判断。在每一轮尝试中,只对
一个字段类型进行测试,由于类型只有三类,所以每个字段最多被测试三次就会有结果,这样尝试的次数最
多是列数的三倍,而不是以3为底数以列数为指数的次数。假设ProdNum属于数字类型,其他三个字段都属
于字符串类型,那么以下顺序的注入语句就能够判断出正确的类型:
* 1234) UNION SELECT NULL,NULL,NULL,NULL WHERE 1=2 --
无错 – 句法正确,使用的是MS SQL Server数据库
* 1234) UNION SELECT 1,NULL,NULL,NULL WHERE 1=2 --
无错 – 第一个字段是数字类型
* 1234) UNION SELECT 1,2,NULL,NULL WHERE 1=2 --
出错 – 第二个字段不是数字类型
* 1234) UNION SELECT 1,’2’,NULL,NULL WHERE 1=2 --
无错 – 第二个字段是字符串类型
* 1234) UNION SELECT 1,’2’,3,NULL WHERE 1=2 --
出错 – 第三个字段不是数字类型
* 1234) UNION SELECT 1,’2’,’3’,NULL WHERE 1=2 --
无错 – 第三个字段是字符串类型
* 1234) UNION SELECT 1,’2’,’3’,4 WHERE 1=2 --
出错 – 第四个字段不是数字类型
* 1234) UNION SELECT 1,’2’,’3’,’4’ WHERE 1=2 --
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




