SQL注入UNION攻击
当一个应用程序存在SQL注入漏洞,并且查询结果在应用程序的响应中返回时,可以使用UNION
关键字从数据库的其他表中检索数据。这就是SQL注入UNION攻击。
UNION
关键字允许你执行一个或多个额外的SELECT
查询,并将结果追加到原始查询中。例如:
此SQL查询将返回一个包含两列的单结果集,其中包含table1
中a
和b
列的值以及table2
中c
和d
列的值。
要使UNION
查询起作用,必须满足两个关键要求:
各个查询必须返回相同数量的列。
每列中的数据类型在各个查询之间必须兼容。
要实施SQL注入UNION攻击,需要确保你的攻击满足这两个要求。通常涉及弄清楚:
原始查询返回了多少列?
从原始查询返回的哪些列适合容纳注入查询的结果?
确定SQL注入UNION攻击所需的列数
在进行SQL注入UNION攻击时,有两种有效的方法可以确定原始查询返回了多少列。
第一种方法是注入一系列ORDER BY
子句,并递增指定的列索引,直到发生错误。例如,假设注入点是原始查询的WHERE
子句中的带引号字符串,你可以提交:
这一系列的有效载荷修改了原始查询,按结果集中的不同列对结果进行排序。ORDER BY
子句中的列可以由其索引指定,因此你不需要知道任何列的名称。当指定的列索引超过结果集中的实际列数时,数据库会返回错误,例如:
应用程序实际上可能会在其HTTP响应中返回数据库错误,或者可能返回一个通用错误,或者根本不返回结果。只要你能检测到应用程序响应中的某些差异,就可以推断出查询返回多少列。
第二种方法是提交一系列UNION SELECT
有效载荷,指定不同数量的空值:
如果空值的数量与列数不匹配,数据库会返回一个错误,例如:
同样,应用程序实际上可能会返回此错误消息,或者可能只返回一个通用错误或没有结果。当空值的数量与列数匹配时,数据库将在结果集中返回一个额外的行,其中每列都包含空值。对产生的HTTP响应的影响取决于应用程序的代码。如果运气好的话,你将在响应中看到一些额外的内容,比如HTML表上的额外行。否则,空值可能会触发其他错误,例如NullPointerException
。最糟糕的情况是,响应可能与由不正确的空值数引起的响应没有区别,使得这种确定列数的方法无效。
LAB
注意
使用
NULL
作为注入的SELECT
查询返回的值的原因是,原始查询和注入查询之间的每个列的数据类型必须兼容。由于NULL
可以转换为常用的每种数据类型,使用NULL
可以最大程度地提高在列数正确时有效载荷成功的机会。在Oracle上,每个
SELECT
查询必须使用FROM
关键字并指定一个有效的表。在Oracle上有一个名为dual
的内置表,可以用于此目的。因此,在Oracle上注入的查询将需要如下所示:
' UNION SELECT NULL FROM DUAL--
所描述的有效载荷使用双破折号注释序列
--
来注释掉注入点后的原始查询的其余部分。在MySQL上,双破折号序列必须跟随一个空格。或者,哈希字符#
可用于标识注释。有关数据库特定语法的更多详细信息,请参阅SQL注入速查表。
在SQL注入UNION攻击中查找具有有用数据类型的列
进行SQL注入UNION攻击的原因是能够从注入的查询中检索结果。通常,希望检索的感兴趣的数据是以字符串形式存在,因此需要在原始查询结果中找到一个或多个数据类型为字符串数据或与之兼容的列。
在已确定所需列数后,你可以通过提交一系列UNION SELECT
有效载荷,依次将字符串值放入每一列中,来测试它是否可以容纳字符串数据。例如,如果查询返回四列,你将提交:
如果某列的数据类型与字符串数据不兼容,注入的查询将导致数据库错误,例如:
如果没有发生错误,并且应用程序的响应包含一些额外的内容,包括注入的字符串值,则相关列适合检索字符串数据。
LAB
使用SQL注入UNION攻击检索感兴趣的数据
当确定了原始查询返回的列数,并找到可以容纳字符串数据的列时,你就可以检索感兴趣的数据。
假设:
原始查询返回两列,两列都可以容纳字符串数据。
注入点是原始查询
WHERE
子句中的引号字符串。数据库中包含一个名为
users
的表,具有username
和password
列。
在这种情况下,你可以通过提交以下输入来检索users
表的内容:
当然,实施此攻击所需的关键信息是存在一个名为users
的表,该表具有名为username
和password
的两列。如果没有这些信息,你将尝试猜测表和列的名称。实际上,所有现代数据库都提供了检查数据库结构的方法,以确定其中包含的表和列。
LAB
阅读更多
在单列中检索多个值
在前面的示例中,假设查询仅返回单个列。
你可以通过将多个值拼接在单个列中,来轻松检索这些值,最好包括一个适当的分隔符,以便可以区分组合值。例如,在Oracle上,可以提交以下输入:
这里使用了双管道序列||
,它是Oracle上的字符串拼接运算符。注入的查询将username
和password
字段的值用~
字符分隔合并在一起。
查询的结果将让你读取所有的用户名和密码,例如:
请注意,不同的数据库使用不同的语法来进行字符串拼接。有关详细信息,请参阅SQL注入速查表。
LAB
Last updated