
Bloomberg Memray 的存储型跨站脚本漏洞:通过未转义的命令行元数据
Bloomberg Memray 通过未转义的命令行元数据实现存储型 XSS
我在审查 Memray(Bloomberg 的 Python 内存分析器)时发现了这个问题,脑海中带着一个简单的问题:
攻击者控制的运行时元数据能否不安全地进入浏览器渲染的报告输出?
在这个案例中,答案是肯定的。
该缺陷存在于 Memray 的 HTML 报告生成路径中,命令行元数据在未经转义的情况下被渲染到浏览器打开的报告里。这将一个操作字段变成了可执行的 HTML 汇点,最终成为 CVE-2026-32722。
项目: GitHub 上的 Memray
公告: GHSA-r5pr-887v-m2w9 /CVE-2026-32722
攻击者控制的 argv → metadata.command_line → 未经转义的 HTML 模板汇点 → 生成报告中的原始标记 → 浏览器端 JavaScript 执行
Memray 是一个 Python 内存分析器。
它检测 Python 进程,记录分配行为,并生成报告,帮助开发者理解:
其中一些报告以 HTML 格式生成并在浏览器中打开。
这使得报告生成成为一个真正的安全边界。
相关的问题并非 Memray 是否是一个“本地工具”。
相关的问题是 攻击者控制的数据能否不安全地进入浏览器渲染的输出。
在这个案例中,答案是肯定的。
很多人低估了开发者工具。
这是一个错误。
一旦工具:
它就继承了与 Web 应用程序相同的输出编码风险。
这就是这里的核心问题。
这个缺陷并非出在分析逻辑中。 也非出在分配跟踪中。 更非出在原生内存处理中。
这是一个典型的 信任边界失败:
这足以构成一个真正的漏洞。
我并非通过随机模糊测试 CLI 选项或追逐崩溃来接近 Memray。
更强的方法首先是识别最高概率的安全面。
对于 Memray 来说,那就是 HTML 报告生成。
为什么?
因为 HTML 输出引入了浏览器汇点,而浏览器汇点会将普通的元数据缺陷变成安全问题,如果:
这正是这里发生的情况。
这个缺陷归结为两行代码。
在:
def get_render_environment() -> jinja2.Environment:
loader = jinja2.PackageLoader("memray.reporters")
env = jinja2.Environment(loader=loader)
Jinja 环境在创建时没有启用自动转义。
然后在:
Command line: <code>{{ metadata.command_line }}</code><br>
metadata.command_line 被直接渲染到 HTML 中。
这就是整个漏洞。
因为 metadata.command_line 受攻击者影响。
Memray 记录用于运行被分析程序的命令行。
这意味着用户控制的 argv 值被保存为元数据,随后插入到报告中。
因此,利用链非常简单:
metadata.command_line 中这就将元数据变成了可执行的浏览器内容。
重要的区别在于执行。
很多缺陷都会产生畸形的 HTML。 但仅此而已是不够的。
在这里,攻击者控制的内容不仅仅在页面源码中可见。 它被浏览器解释为活跃的 HTML 并作为 JavaScript 执行。
这就是以下两者之间的区别:
所以问题不在于:
“HTML 能否出现在报告中?”
真正的问题在于:
“当报告打开时,攻击者控制的 HTML 能否变成可执行的?”
答案是肯定的。
我最初的复现器使用了一个显式脚本加上攻击者控制的参数:
cat > victim.py <<'PY'
x = [b"A" * 1024 for _ in range(1000)]
print("done")
PY
python -m memray run -o poc.bin victim.py ''
python -m memray flamegraph -o poc.html poc.bin
生成的 HTML 包含原始的受攻击者控制的标记:
Command line: <code>~/memray/src/memray/__main__.py run -o poc.bin victim.py </code><br>
打开或刷新生成的报告会触发 JavaScript 执行。
这确立了核心主张:
后来,在协调披露期间,维护者进一步简化了复现器:
python -m memray run -o poc.bin -c '# '
那个版本更好,因为它更直接地隔离了易受攻击的边界:
载荷被有意保持简单:
这并非关于花哨的载荷。
这是一个清晰的执行探针,因为:
对于这类缺陷,这就足够了。
一种报告类型本已足以证明该问题。
但我想知道这是孤立的还是结构性的。
我在以下报告类型中确认了相同的行为:
--no-web 生成的火焰图报告这很重要,原因有二。
它表明易受攻击的汇点在多个 HTML 输出中被重用。
它证明该缺陷不依赖于外部 CDN 托管的资源。
--no-web 仍然复现了问题,这意味着问题存在于 Memray 自身生成的 HTML 和模板处理中,而非远程 JS 行为。
这使得情况更加有力。
维护者的主要担忧是实际攻击者控制能力。
这很合理。
这不是那种未经认证的远程攻击者命中暴露的 HTTP 端点并立即产生影响的类型。
利用条件更为狭窄:
因此,实际分类为低严重性。
但这并不意味着它不是一个严重的缺陷。
严重性关乎利用条件和可能的影响。 有效性关乎问题是否真实。
这个问题显然是真实的:
这就是它仍然成为一个 CVE 的原因。
修复方案非常简洁且正确。
维护者将:
{{ metadata.command_line }}
改为:
{{ metadata.command_line|e }}
这是正确的修复,因为它直接解决了易受攻击的汇点。
现在模板不再发出原始标记,例如:
而是发出转义后的文本:
<img src=x onerror=alert(1)>
这保留了命令行字段的信息价值,同时消除了浏览器执行路径。
维护者还审查了模板上下文的其余部分,并得出结论:
|tojson 渲染
因此问题被正确缩小到 metadata.command_line。这正是在真实披露中你想要的修复审查。
这是通过 GitHub 安全公告私下报告的。
维护者:
该问题被分配: CVE-2026-32722
关键教训很简单:
元数据不会仅仅因为它看起来是操作性的就被自动信任。
一旦攻击者控制的内容在未经转义的情况下进入浏览器渲染的输出,这些都不重要了。
当工具生成 HTML 时,它就必须像生成 HTML 的应用程序一样被对待。
这才是真正的收获。
这个漏洞并非关于巧妙的载荷。
而是关于识别正确的边界。
Memray 获取了受攻击者影响的命令行元数据,并将其渲染到生成的 HTML 中而未转义。 浏览器完成了剩余的工作。
这就是它成为 CVE-2026-32722 的原因。
在 Memray 1.19.2 中已修复。
