同构但不同:用同构SQL语句渐进式发现SQL注入
2020年4月5日 · 1219字 · 6分钟阅读
动机 🔗
尽管对象关系映射(ORM)库和预编译SQL语句的应用日益广泛,SQL注入仍然在现代应用中频繁出现。甚至ORM库在将对象映射转换为原始SQL语句时,由于错误也引入了SQL注入。当然,遗留应用程序和危险的开发实践同样导致了SQL注入漏洞。
最初,我在识别SQL注入时遇到了困难。与另一种常见漏洞类别——跨站脚本(XSS)不同,易受SQL注入攻击的端点通常不会提供关于注入位置和方式的反馈。对于XSS,情况很简单:除了盲XSS(XSS最终出现在管理员面板或您无法访问的地方),您总是能在HTML响应中看到有效载荷的位置。
对于SQL注入,最好的情况是您得到一个详细的堆栈跟踪,准确告诉您需要的信息:
|
|
如果您看到这个,那您很幸运。然而,更多时候,您要么会收到一个通用错误消息,要么更糟,根本没有错误——只有一个空响应。
|
|
因此,寻找SQL注入可能既费力又耗时。许多研究人员更喜欢使用像sqlmap这样的自动化工具进行一次扫描,然后就此打住。然而,在没有特定配置的情况下运行这些工具是一种钝器,容易被Web应用防火墙(WAF)检测和阻止。此外,SQL注入发生在独特的上下文中;您可能在WHERE、LIKE或ORDER BY之后注入,每个上下文需要不同类型的注入。这甚至是在各种清理步骤应用之前。
多态有效载荷帮助研究人员采用更有针对性的方法。然而,多态有效载荷,顾名思义,试图在多个上下文中同时执行,通常牺牲了隐蔽性和简洁性。以Seclists的SQLi多态有效载荷为例:
|
|
任何半吊子的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注入来说明:
|
|
如果您使用大型SQL多态有效载荷列表进行模糊测试,检测注入将相对简单,但在现实中,情况将因WAF、清理和更复杂的语句而复杂化。
接下来,考虑以下语句:
|
|
如果最后两个语句中的特殊字符未经清理注入,它们都应评估为相同的结果。如果它们没有评估为相同的结果,服务器正在以某种方式清理它们。
现在考虑一个常见的搜索查询版本,SELECT Address FROM Users WHERE FirstName LIKE '%<USER INPUT>%' ORDER BY Address DESC;:
|
|
仅仅通过在第二个语句中两次注入相同的特殊字符%,如果收到相同的响应,我们就得到了关于实际注入的SQL语句的线索(它在LIKE操作符之后)。
更好的是,正如Arne Swinnen早在2013年指出的(一位先驱!):
字符串:将一个有效参数字符串值分成两部分,并在中间添加一个SQL字符串连接指令。两个请求的相同响应将再次让您有理由相信您刚刚遇到了SQL注入。
我们可以通过简单地在第三个语句的注入中添加’ ‘来为字符串实现相同的同构效果。这被解释为将原始字符串与空白字符串连接,这也应返回相同的响应,同时表明’没有被正确转义。
从这里开始,逐步实验就很简单了。因此,您实现了两个目标:
- 发现哪些可注入字符未经清理进入最终SQL语句
- 发现您正在注入的原始SQL语句
大规模自动化和注意事项 🔗
这样做的目标不仅是发现单个SQL注入,而且能够自动化并将其应用于大量URL和输入。传统的SQL注入有效载荷列表或扫描器使大规模扫描变得嘈杂且资源密集。通过渐进式同构方法,您应用一个启发式规则,如:
|
|
这更轻量、更快。当然,虽然您在减少误报方面获益(例如,有效但被WAF阻止的多态有效载荷),但您在增加误报方面有所损失。例如,有些情况下后端在进入SQL语句之前简单地修剪所有非数字字符,在这种情况下,上述同构语句仍然会成功。因此,与其依赖单个同构语句(二进制信号),您将希望观察多个同构语句成功(频谱信号)。
尽管SQL注入变得越来越罕见,我仍然在手动测试中偶尔遇到它们。大规模扫描方法将产生更好的结果。