同构SQL语句:渐进式发现SQL注入漏洞的巧妙方法

本文探讨了如何利用同构SQL语句渐进式检测SQL注入漏洞,通过对比不同但理论上应返回相同结果的SQL查询,识别未正确转义的特殊字符,从而绕过WAF并高效发现注入点。

同构但不同:用同构SQL语句渐进式发现SQL注入

2020年4月5日 · 1219字 · 6分钟阅读

动机 🔗

尽管对象关系映射(ORM)库和预编译SQL语句的应用日益广泛,SQL注入仍然在现代应用中频繁出现。甚至ORM库在将对象映射转换为原始SQL语句时,由于错误也引入了SQL注入。当然,遗留应用程序和危险的开发实践同样导致了SQL注入漏洞。

最初,我在识别SQL注入时遇到了困难。与另一种常见漏洞类别——跨站脚本(XSS)不同,易受SQL注入攻击的端点通常不会提供关于注入位置和方式的反馈。对于XSS,情况很简单:除了盲XSS(XSS最终出现在管理员面板或您无法访问的地方),您总是能在HTML响应中看到有效载荷的位置。

对于SQL注入,最好的情况是您得到一个详细的堆栈跟踪,准确告诉您需要的信息:

1
2
3
4
5
6
7
8
9
HTTP/1.1 500 Internal Server Error
Content-Type: text/html; charset=utf-8

<div id="error">
    <h1>Database Error</h1>
    <div class='message'>
        SQL syntax error near '''' where id=123' at line 1: update common_member SET name=''' where id=123
    </div>
</div>

如果您看到这个,那您很幸运。然而,更多时候,您要么会收到一个通用错误消息,要么更糟,根本没有错误——只有一个空响应。

1
2
3
4
5
6
HTTP/1.1 200 OK
Content-Type: application/json

{
    "users": []
}

因此,寻找SQL注入可能既费力又耗时。许多研究人员更喜欢使用像sqlmap这样的自动化工具进行一次扫描,然后就此打住。然而,在没有特定配置的情况下运行这些工具是一种钝器,容易被Web应用防火墙(WAF)检测和阻止。此外,SQL注入发生在独特的上下文中;您可能在WHERE、LIKE或ORDER BY之后注入,每个上下文需要不同类型的注入。这甚至是在各种清理步骤应用之前。

多态有效载荷帮助研究人员采用更有针对性的方法。然而,多态有效载荷,顾名思义,试图在多个上下文中同时执行,通常牺牲了隐蔽性和简洁性。以Seclists的SQLi多态有效载荷为例:

1
2
3
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),​SLEEP(1)))OR"*/

任何半吊子的WAF都会检测到这些有效载荷并阻止它们。

在现实场景中,研究人员在寻找SQL注入时需要平衡两个关注点:

  • 能够在多个上下文中执行并因此识别注入
  • 能够绕过WAF和清理步骤

研究人员可以通过我称之为同构SQL语句的东西高效解决这个问题(尽管我确信其他研究人员有不同的名称)。

发现漏洞的渐进方法 🔗

回到XSS的类比,虽然XSS扫描器和模糊测试列表比比皆是,但由于上述WAF阻止和独特上下文,它们通常效果不佳。最近,出现了更先进的自动化漏洞发现方法,试图解决暴力扫描的缺点,如James Kettle的反斜杠驱动扫描。正如Kettle所写:

与其扫描漏洞,我们需要扫描有趣的行为。

反过来,像Ameen Mali的qsfuzz和Project Discovery的nuclei这样的自动化管道工具针对定义的启发式规则(“有趣的行为”)进行测试,而不是盲目地暴力破解有效载荷。随着更多组织采用WAF和更好的开发实践,这是大规模漏洞扫描的前进方向。

例如,在测试XSS时,与其问“当我放入这个有效载荷时,是否会弹出警报框?”,我更喜欢问“这个应用程序是否清理单引号?尖括号呢?”这样做的好处是,您可以轻松地在大型规模上自动化这一点,而不会触发除最敏感的WAF之外的所有WAF。然后,您可以为每个独特上下文进行手动利用。

SQL注入也是如此。但是,如何在没有任何反馈机制的情况下制定测试?请记住,SQL注入与XSS不同,通常没有(积极)响应。然而,我从像Ian Bouchard这样的研究人员那里学到的一件事是,即使没有消息也是好消息。

这就是同构SQL语句发挥作用的地方。在这里应用,同构仅仅意味着写法不同但理论上应返回相同输出的SQL语句。然而,区别在于您将测试包含特殊字符(如’或-)的SQL语句。如果字符被正确转义,注入的SQL语句将无法评估为与原始语句相同的结果。如果它们没有被正确转义,您将得到相同的结果,这表明可能存在SQL注入。

让我们用一个简单的玩具SQL注入来说明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CREATE TABLE Users (
    ID int key auto_increment,
    LastName varchar(255),
    FirstName varchar(255),
    Address varchar(255),
    City varchar(255)
);

INSERT INTO Users (LastName, FirstName, Address, City) VALUES ('Bird', 'Big', '123 Sesame Street', 'New York City'); 

INSERT INTO Users (LastName, FirstName, Address, City) VALUES ('Monster', 'Cookie', '123 Sesame Street', 'New York City'); 

SELECT FirstName FROM Users WHERE ID = <USER INPUT>;

如果您使用大型SQL多态有效载荷列表进行模糊测试,检测注入将相对简单,但在现实中,情况将因WAF、清理和更复杂的语句而复杂化。

接下来,考虑以下语句:

1
2
3
SELECT FirstName FROM Users WHERE ID = 1;
SELECT FirstName FROM Users WHERE ID = 2-1;
SELECT FirstName FROM Users WHERE ID = 1+'';

如果最后两个语句中的特殊字符未经清理注入,它们都应评估为相同的结果。如果它们没有评估为相同的结果,服务器正在以某种方式清理它们。

现在考虑一个常见的搜索查询版本,SELECT Address FROM Users WHERE FirstName LIKE '%<USER INPUT>%' ORDER BY Address DESC;

1
2
3
SELECT Address FROM Users WHERE FirstName LIKE '%Big%' ORDER BY Address DESC;
SELECT Address FROM Users WHERE FirstName LIKE '%Big%%%' ORDER BY Address DESC;
SELECT Address FROM Users WHERE FirstName LIKE '%Big%' '' ORDER BY Address DESC;

仅仅通过在第二个语句中两次注入相同的特殊字符%,如果收到相同的响应,我们就得到了关于实际注入的SQL语句的线索(它在LIKE操作符之后)。

更好的是,正如Arne Swinnen早在2013年指出的(一位先驱!):

字符串:将一个有效参数字符串值分成两部分,并在中间添加一个SQL字符串连接指令。两个请求的相同响应将再次让您有理由相信您刚刚遇到了SQL注入。

我们可以通过简单地在第三个语句的注入中添加’ ‘来为字符串实现相同的同构效果。这被解释为将原始字符串与空白字符串连接,这也应返回相同的响应,同时表明’没有被正确转义。

从这里开始,逐步实验就很简单了。因此,您实现了两个目标:

  • 发现哪些可注入字符未经清理进入最终SQL语句
  • 发现您正在注入的原始SQL语句

大规模自动化和注意事项 🔗

这样做的目标不仅是发现单个SQL注入,而且能够自动化并将其应用于大量URL和输入。传统的SQL注入有效载荷列表或扫描器使大规模扫描变得嘈杂且资源密集。通过渐进式同构方法,您应用一个启发式规则,如:

1
2
3
4
if (id_input_response) == (id_input_response + "+''"):
    return true
else:
    return false

这更轻量、更快。当然,虽然您在减少误报方面获益(例如,有效但被WAF阻止的多态有效载荷),但您在增加误报方面有所损失。例如,有些情况下后端在进入SQL语句之前简单地修剪所有非数字字符,在这种情况下,上述同构语句仍然会成功。因此,与其依赖单个同构语句(二进制信号),您将希望观察多个同构语句成功(频谱信号)。

尽管SQL注入变得越来越罕见,我仍然在手动测试中偶尔遇到它们。大规模扫描方法将产生更好的结果。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计