前言
本文主要记录之前在portswigger官方网站学习时的一些细节;
在不同的情况下会出现各种各样的SQL注入漏洞、攻击和技术。一些常见的SQL注入示例包括:
- 检索隐藏的数据,可以在其中修改SQL查询以返回其他结果。(就是能够查到其他的正常情况下你没有权限的看到的结果)
- 颠覆应用程序逻辑,你可以改变一个查询来干扰应用程序的逻辑。
- 联合攻击,可以从不同的数据库表检索数据。
- 检查数据库,从中可以提取关于数据库版本和结构的信息。
- 盲SQL注入,应用程序的响应中不返回你控制的查询结果。
检索隐藏的数据(搜索功能)
考虑一个显示不同类别产品的购物应用程序。 当用户单击“礼物”类别时,其浏览器将请求URL: https://insecure-website.com/products?category=Gifts
这将导致应用程序执行SQL查询,以从数据库中检索相关产品的详细信息:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1
此SQL查询要求数据库返回:
产品表中类别是礼物的所有详细信息(*)
,并且released=1(猜测为:发布版本)的;
约束条件:released = 1
表示隐藏未发布的产品,对于未发布的产品,假定released= 0
。
这将导致SQL查询:
SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1
这里的关键是双破折号序列--
是SQL中的注释指示符,意味着查询的其余部分将被解释为注释。 这有效地删除了查询的其余部分,因此不再包含AND Release =1
。这意味着将显示所有产品,包括未发布的产品。
更进一步,攻击者可以使应用程序显示任何类别的所有产品,包括他们不知道的类别:
https://insecure-website.com/products?category=Gifts'+OR+1=1--
这将导致SQL查询:
SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1
修改后的查询将返回所有类别为礼品或1等于1的物品。因为1=1总是为真,所以查询将返回所有项。
颠覆应用程序逻辑(登陆功能)
考虑一个允许用户使用用户名和密码登录的应用程序。 如果用户提交用户名wiener和密码bluecheese,则应用程序将通过执行以下SQL查询来检查凭据:
SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'
如果查询返回用户的详细信息,则登录成功。 否则,将被拒绝。
在这里,攻击者可以作为任何用户登录,不需要密码,只需使用SQL注释序列——从查询的WHERE子句中删除密码检查。例如,提交用户名administrator'和空白密码将导致以下查询
SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
该查询返回用户名为administrator的用户,并将攻击者作为该用户成功登录。
union攻击(从其他的数据库表里面接受数据)
如果在应用程序的响应中返回了SQL查询的结果,则攻击者可以利用SQL注入漏洞从数据库中的其他表中检索数据。 这是通过使用UNION关键字完成的,该关键字使您可以执行附加的SELECT查询并将结果附加到原始查询中。
例如,如果应用程序执行以下包含用户输入“ Gifts”的查询:
SELECT name, description FROM products WHERE category = 'Gifts'
然后攻击者可以提交输入
'UNION SELECT username, password FROM users--
这将导致应用程序返回所有用户名和密码以及产品的名称和描述。
union攻击1
当应用程序容易受到SQL注入的攻击,并且在应用程序的响应中返回查询结果时,可以使用UNION关键字从数据库中的其他表检索数据。这会导致SQL注入union攻击。 UNION关键字使您可以执行一个或多个其他SELECT查询,并将结果附加到原始查询中。 例如:
SELECT a, b FROM table1 UNION SELECT c, d FROM table2
这个SQL查询将返回一个包含两列的结果集,其中包含来自表1中的列a和b以及表2中的列c和d的值。
为了使UNION查询正常工作,必须满足两个关键要求:
1.各个查询必须返回相同数量的列;
2.每列中的数据类型在各个查询之间必须兼容。
要执行SQL注入联合攻击,您需要确保您的攻击满足这两个要求。这通常需要弄清楚下面的问题:
1.原始查询返回了多少列?
2.从原始查询返回的哪些列具有合适的数据类型来保存注入的查询的结果?
确定SQL注入UNION攻击所需的列数
当执行SQL注入UNION攻击时,有两种有效的方法可以确定从原始查询返回多少列。
第一种方法涉及注入一系列ORDER BY子句,并递增指定的列索引,直到发生错误为止。 例如,假设注入点是原始查询的WHERE子句中带引号的字符串,则可以提交:
' ORDER BY 1-- ' ORDER BY 2-- ' ORDER BY 3-- etc.
这一系列有效负载修改了原始查询,以按照结果集中的不同列对结果进行排序。order by子句中的列可以通过其索引指定,因此您不需要知道任何列的名称。当指定的列索引超过结果集中实际列的数量时,数据库返回一个错误,例如:
The ORDER BY position number 3 is out of range of the number of items in the select list.
应用程序可能在其HTTP响应中实际返回数据库错误,或者它可能返回一个通用错误,或者只是不返回任何结果。如果您可以检测到应用程序响应中的一些差异,您就可以推断出查询返回了多少列。
第二种方法涉及提交一系列UNION SELECT有效负载,指定不同数量的空值:
' UNION SELECT NULL-- ' UNION SELECT NULL,NULL-- ' UNION SELECT NULL,NULL,NULL-- etc.
如果null的数目与列的数目不匹配,则数据库返回一个错误,例如:
使用UNION,INTERSECT或EXCEPT运算符组合的所有查询在其目标列表中必须具有相等数量的表达式。
同样,应用程序实际上可能返回此错误消息,或者可能仅返回一般错误或没有结果。 当空数与列数匹配时,数据库将在结果集中返回另一行,其中每一列均包含空值。 对产生的HTTP响应的影响取决于应用程序的代码。
如果幸运的话,您将在响应中看到一些其他内容,例如HTML表格上的额外行。 否则,空值可能会触发其他错误,例如NullPointerException。 最坏的情况是,响应可能与由不正确的null数引起的响应无法区分,这使确定列数的此方法无效。
上面这个null的方法可以看下面这个示例来理解:
1.正常情况下:
2.不正常(此时null数目不对):
3.null数目与列数相等的情况下:
4.其他情况:
示例(搜索功能)
注意参数大小写
使用NULL作为从注入的SELECT查询返回的值的原因是,每个列中的数据类型必须在原始查询和注入的查询之间兼容。由于NULL可以转换为每一种常用的数据类型,因此在列计数正确时,使用NULL可以最大限度地提高有效负载成功的几率。
在Oracle上,每个选择查询都必须使用FROM关键字并指定一个有效的表。Oracle上有一个名为DUAL的内置表可以用于此目的。所以在Oracle上注入的查询需要看起来像:' UNION SELECT NULL FROM DUAL
。
所描述的有效负载使用双破折号注释序列——
注释掉注入点之后的原始查询的其余部分。在MySQL中,双破折号序列后面必须跟一个空格。或者,可以使用散列字符#
来标识注释。
有关数据库特定语法的更多细节,请参见SQL注入备忘单:https://portswigger.net/web-security/sql-injection/cheat-sheet。
在SQL注入UNION攻击中查找具有有用数据类型的列
https://portswigger.net/web-security/sql-injection/union-attacks
执行SQL注入联合攻击的原因是为了能够从注入的查询检索结果。通常,您希望检索的感兴趣数据将是字符串形式的,因此您需要在原始查询结果中找到数据类型为字符串数据或与之兼容的一列或多列。
已经确定了所需的列数之后,您可以通过提交一系列将字符串值依次放入各列的UNION SELECT有效负载,来探查各列以测试其是否可以容纳字符串数据。 例如,如果查询返回四列,则您将提交:
' UNION SELECT 'a',NULL,NULL,NULL-- ' UNION SELECT NULL,'a',NULL,NULL-- ' UNION SELECT NULL,NULL,'a',NULL-- ' UNION SELECT NULL,NULL,NULL,'a'--
如果列的数据类型与字符串数据不兼容,则注入的查询将导致数据库错误,例如:
Conversion failed when converting the varchar value 'a' to data type int.
如果没有发生错误,并且应用程序的响应包含一些其他内容,包括注入的字符串值,则相关列适用于检索字符串数据。
示例
上面说明没有找到有效列;
使用SQL注入UNION攻击来检索有趣的数据
确定原始查询返回的列数并确定哪些列可以容纳字符串数据后,就可以检索有趣的数据了。(这个实验是在上面两个的基础之上的)
假设:
原始查询返回两个列,这两个列都可以保存字符串数据。
注入点是WHERE子句中的一个带引号的字符串。
该数据库包含一个名为users的表,列有用户名和密码。
在这种情况下,您可以通过提交输入来检索users表的内容:
' UNION SELECT username, password FROM users--
当然,执行此攻击所需的关键信息是有一个名为users的表,该表有两列,分别为username和password。 没有这些信息,您将不得不尝试猜测表和列的名称。 实际上,所有现代数据库都提供了检查数据库结构,确定数据库包含哪些表和列的方法。
现在已经知道了哪两列是有用的;
将用户名和密码溢出来了:
未完待续......