复现 PortSwigger 2025 Top 1:SSTI 新纪元——利用“报错”侧信道偷数据
字数 3102
更新时间 2026-03-07 12:00:57

SSTI利用新纪元:基于“报错”侧信道的盲注攻击技术深度解析

0x00 引言

本文旨在深入解读并复现 PortSwigger 发布的“2025年十大Web黑客技术”榜首议题——《Successful Errors: New Code Injection and SSTI Techniques》。该研究由 Vladislav Korchagin 提出,为服务端模板注入(SSTI)攻击提供了一套颠覆性的新思路,解决了传统盲注效率低下、依赖外带等核心痛点。

0x01 背景:传统SSTI盲注的局限性

SSTI(服务端模板注入)作为一种已存在十年的漏洞,传统的利用方式在“盲注”场景下面临巨大挑战,主要体现在以下两个方面:

  1. 时间盲注:通过注入如 sleep(5) 之类的语句,根据服务器响应时间来判断条件真假。其缺点是速度极慢,且结果极易受网络延迟、服务器负载等外界因素干扰,稳定性差。
  2. 外带(OOB)数据:利用如 curldnslog 等技术将数据外传到攻击者控制的服务器。这种方法受制于目标服务器的网络策略,如果目标处于严格的内网隔离环境或禁止出站连接,该技术将完全失效。

因此,该研究的核心目标是:在目标服务器不出网、且无直接回显的条件下,实现快速、稳定地窃取数据

0x02 核心技术一:报错回显(Error-Based Exfiltration)

1. 基本原理

该技术利用了编程语言解释器或运行时的一个常见特性:当处理错误的数据类型或执行非法操作时,为了方便调试,报错信息中往往会包含触发错误的具体值

攻击思路的核心转换在于:

  • 传统思路:试图让模板引擎直接渲染并输出Payload的执行结果。
  • 新思路:将Payload的执行结果(即我们想窃取的数据)作为参数,传递给一个“必然会引发异常”的函数或操作。当服务器抛出异常时,我们希望窃取的数据就会包含在错误的堆栈信息或错误消息中被“回显”出来。

2. Python (Jinja2) 场景下的复现与利用

假设存在一个存在SSTI漏洞的Flask应用,但它不直接回显用户输入,仅在出错时显示错误页面。

研究者发现 Python 的 getattr(obj, name) 函数是实现此攻击的完美载体。该函数用于获取对象的属性,当尝试获取一个对象不存在的属性时,会抛出 AttributeError 异常,并且异常信息中会包含我们试图获取的那个不存在的属性名

Payload构造逻辑

# 伪代码逻辑
getattr("任意对象", "命令执行结果")

我们将命令执行的结果(例如读取 /etc/passwd 文件的内容)作为 getattr 的第二个参数(即属性名name)。由于目标对象(例如空字符串 "")没有名为该文件内容的属性,Python 解释器会报错,并在错误信息中泄露该“属性名”。

实战Payload示例 (Jinja2)

{{ "".__class__.__mro__[1].__subclasses__()[138].__init__.__globals__['getattr']("", __import__("os").popen("id").read()) }}

攻击效果:服务器返回 500 内部服务器错误,但在错误页面的堆栈跟踪信息中,我们可以直接看到 id 命令的执行结果(例如:AttributeError: 'str' object has no attribute 'uid=1000(user) gid=1000(user) groups=1000(user)...')。

3. 多语言通用触发点

该研究总结了多种流行模板引擎/语言中可用于触发“报错回显”的构造:

  • Pythongetattr("", OUTPUT)。报错信息会包含 OUTPUT 的内容。
  • PHP (Twig/Smarty)call_user_func(OUTPUT)。报错信息会提示 OUTPUT 不是有效的可调用函数。
  • Java (FreeMarker)${OUTPUT?new()}。报错信息会提示找不到名为 OUTPUT 的类。
  • Ruby (ERB)File.read(OUTPUT)。报错信息会提示找不到名为 OUTPUT 的文件。
  • NodeJSrequire(OUTPUT)。报错信息会提示找不到名为 OUTPUT 的模块。

0x03 核心技术二:布尔报错盲注 (Boolean Error-Based Blind)

1. 基本原理

当目标服务器配置了统一的错误处理页面,屏蔽了详细的报错信息时,“报错回显”技术将失效。此时,可以利用“是否引发服务器错误”这一状态(HTTP 状态码 200 vs 500)来进行布尔盲注。

核心逻辑:构造一个条件表达式,其结果为布尔值(True/False),并将该结果转换为会触发异常的数学运算。

例如,构造表达式:1 / (条件判断)

  • 如果条件为真 (True):在多数语言中,True 在数值上下文中等于 1。表达式变为 1 / 1,正常执行,页面返回 200 OK
  • 如果条件为假 (False)False 在数值上下文中等于 0。表达式变为 1 / 0,触发“除零异常”,导致服务器处理错误,页面返回 500 Internal Server Error

通过观察HTTP响应状态码是200还是500,攻击者就能判断出注入的条件是否为真。

2. 实战Payload示例

假设我们要盲注读取 /etc/passwd 文件,并判断其第一个字符是否为字母 'r'

构造判断条件(读取的文件内容[0] == 'r')

完整Payload示例 (Jinja2)

{{ 1 / (config.__class__.__init__.__globals__['__builtins__']['eval']("open('/etc/passwd').read()[0]=='r'")) }}
  • 如果第一个字符是 'r':条件为真 -> 1 / 1 -> 正常渲染 -> 服务器返回 200
  • 如果第一个字符不是 'r':条件为假 -> 1 / 0 -> 触发异常 -> 服务器返回 500

相比于传统的时间盲注(sleep),这种基于HTTP状态码的布尔盲注速度极快,不受网络延迟影响,检测效率提升了一个数量级。

0x04 技术优势与影响(为何是Top 1)

  1. 思维模式的降维打击:将SSTI盲注这一复杂问题,简化为类似于“报错型SQL注入”的成熟玩法,极大降低了利用门槛。
  2. 通用性与自动化潜力:研究者提供了覆盖 Python、Java、PHP、Ruby、NodeJS 等主流语言和模板引擎的通用Payload。特别是像 (1/0) 这类基础数学错误,几乎所有语言都会触发异常,这使得编写能够大规模、自动化检测各种环境下的Blind SSTI漏洞的扫描器成为可能。
  3. 解决核心痛点:有效解决了内网无回显、无外带场景下的SSTI利用难题,提供了一种稳定、高效的本地数据窃取方案。
  4. 工具集成:作者开发的 SSTImap 工具已集成这些新技术,建议安全测试人员及时更新工具以利用此技术。

0x05 防御建议

对于开发者和安全运维人员:

  1. 输入处理:绝对不要将任何未经严格验证和净化的用户输入直接拼接到模板语句中。使用安全的模板API,明确区分代码和数据。
  2. 错误处理:在生产环境中,务必关闭调试(Debug)模式。配置统一的、友好的自定义错误页面,避免向用户返回任何包含堆栈跟踪、代码片段或内部变量值的详细错误信息。这虽然无法防御布尔报错盲注,但能有效防止通过报错信息直接泄露敏感数据。
  3. 沙箱与限制:在允许使用模板引擎的动态执行环境中,应实施严格的沙箱策略,禁用危险的内置函数和模块访问。

参考资料

  1. Successful Errors Whitepaper by Vladislav Korchagin
相似文章
相似文章
 全屏