Astaruf/CVE-2026-41653

GitHub: Astaruf/CVE-2026-41653

针对 BentoPDF ≤ 2.8.2 的存储型 XSS 漏洞 PoC,演示了从 Markdown 注入到静默文件窃取的完整攻击链。

Stars: 0 | Forks: 0

# CVE-2026-41653: BentoPDF ≤ 2.8.2 — Markdown 转 PDF 存储型 XSS → 静默文件窃取

**发现与报告者:** [Astaruf](https://www.linkedin.com/in/lorenzoanastasi/) **完整报告:** [https://nstsec.com/](https://nstsec.com/) **上游公告:** [alam00000/bentopdf GHSA advisory](https://github.com/alam00000/bentopdf/security/advisories/GHSA-6vh8-4frx-647f) **NVD 条目:** [https://nvd.nist.gov/vuln/detail/CVE-2026-41653](https://nvd.nist.gov/vuln/detail/CVE-2026-41653) ## 摘要 [BentoPDF](https://github.com/alam00000/bentopdf) 是一个自托管的、基于浏览器的 PDF 工具箱(压缩、合并、拆分、旋转、转换、Markdown 转 PDF 等)。 **Markdown 转 PDF** 工具使用 `markdown-it` 并配置 `html: true` 处理用户提供的 Markdown,然后通过 `innerHTML` 将渲染后的输出直接注入到 DOM 中,且未进行任何过滤。攻击者通过投递精心构造的 `.md` 文件,即可在 BentoPDF 的源中实现任意 JavaScript 执行。 由于 BentoPDF 是一个完全的客户端应用程序,每个工具(压缩 PDF、合并 PDF、拆分 PDF 等)都会直接在受害者的浏览器中加载和处理文件。因此,单次 XSS 攻击就足以静默窃取受害者在当前会话中通过任何工具打开的每一个文档。 ``` Attacker sends report.md (containing ) -> Victim opens it in Markdown-to-PDF -> markdown-it renders raw HTML (html: true) -> preview.innerHTML = html (no DOMPurify) -> poc_payload.js loaded from attacker server (no CSP) -> FileReader + hooked app-wide -> hidden popup re-injects hooks on every tool navigation -> every file the victim opens is silently exfiltrated ``` ## 影响 - **静默文件窃取。** 受害者在当前会话中通过任何 BentoPDF 工具打开的每个文件(PDF、图像、文档)都会被 POST 到攻击者的服务器。 - **WASM 供应链劫持。** 载荷会覆盖 `localStorage['bentopdf:wasm-providers']`,将 PyMuPDF、Ghostscript 和 cpdf WASM 模块的下载重定向到受攻击者控制的主机。 - **跨页面持久化。** 一个隐藏的 1×1 弹窗会轮询 `window.opener`,并在受害者每次导航到不同的工具时重新注入文件挂钩,从而在整个会话期间保持访问权限。 - **Service Worker 缓存投毒(仅限 HTTPS)。** 在 HTTPS 部署环境下,载荷会注册一个 Service Worker 并对 `bentopdf-*` 缓存进行投毒,向浏览器提供的每个 `/assets/*.js` 文件中附加窃取挂钩,其影响可超越当前会话。 ## 漏洞详情 ### 1. `markdown-it` 中的原生 HTML 透传 (CWE-79) `src/js/utils/markdown-editor.ts` (第 271–272 行): ``` private mdOptions: MarkdownItOptions = { html: true, // raw HTML tags pass through the markdown parser breaks: false, linkify: true, typographer: true, }; ``` 像 ``、``、`
` 这样的标签以及任何事件处理属性(`onerror`、`onload`、`ontoggle`)都会被原封不动地转发到 DOM 中。 ### 2. 未经净化的 `innerHTML` 注入 (CWE-116) `src/js/utils/markdown-editor.ts` (第 689–694 行): ``` private updatePreview(): void { if (!this.editor || !this.preview) return; const markdown = this.editor.value; const html = this.md.render(markdown); this.preview.innerHTML = html; // attacker-controlled HTML injected into DOM this.renderMermaidDiagrams(); } ``` 在 `markdown-it` 和 `innerHTML` 之间没有应用任何过滤库。浏览器解析注入的字符串时遇到内联事件处理程序,会立即执行它。 ### 3. 缺少 Content-Security-Policy `nginx.conf` 默认不附带 `Content-Security-Policy` 头。注入的 JavaScript 可以自由加载外部脚本,向任何主机发起 `fetch()` 请求,以及打开弹窗。即使存在注入口,严格的 CSP 也能阻止外部脚本加载阶段。 ## 概念验证 ### 要求 ``` Python 3.8+ BentoPDF instance ≤ v2.8.2 (Docker: ghcr.io/alam00000/bentopdf:v2.8.2) ``` ### 此仓库中的文件 | 文件 | 作用 | |---|---| | `poc.py` | 用于数据窃取的独立攻击者服务器。内部嵌入了载荷,在启动时生成恶意的 `poc_report.md`,并接收窃取的文件和遥测数据。 | | `docs/` | 本 README 中引用的演示截图。 | | `loot/` | 由 `poc.py` 在运行时创建。被窃取的文件保存在此处。 | ### 快速开始 ``` # 启动 attacker server 进行 exfiltration python3 poc.py --lhost --lport 9999 # poc.py 将会: # - 在当前目录生成 poc_report.md # - 开始监听 callbacks 和 exfiltrated files ``` 将 `poc_report.md` 发送给受害者,并要求他们在 **BentoPDF → Markdown 转 PDF** 中打开它。载荷会在预览渲染时触发。 ### 命令行选项 | 选项 | 默认值 | 描述 | |---|---|---| | `--lhost` | 必填 | 受害者浏览器可访问的 IP | | `--lport` | `9999` | 监听端口 | | `--loot-dir` | `./loot/` | 保存被窃取文件的目录 | | `--log-file` | 无 | 将所有事件追加到文件中(去除 ANSI 代码) | | `--no-color` | 关闭 | 禁用终端输出中的 ANSI 颜色 | ### 攻击流程 ``` Attacker BentoPDF (victim browser) Attacker server | | | |-- 1. Sends poc_report.md ------> Victim | | |-- 2. Opens Markdown-to-PDF | | |-- 3. Loads poc_report.md | | |-- 4.
fires ->|-- poc_payload.js | |<-- poc_payload.js loaded --| | |-- 5. Hooks FileReader | | | opens monitor popup | | |-- 6. Victim switches tool | | |-- 7. Victim opens a PDF -->| | |------- file bytes --------->| /file ``` ### 演示 **1. 使用 `--lhost` 和 `--lport` 启动攻击者服务器。恶意的 `poc_report.md` 会自动生成。** ![攻击者服务器启动](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/1.png) **2. 受害者在 BentoPDF 中打开 Markdown 转 PDF 工具。** ![Markdown 转 PDF 工具](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/2.png) **3. 受害者加载 `poc_report.md`。预览开始渲染,`` 加载失败,`onerror` 触发。由于没有 CSP 阻止,`poc_payload.js` 从攻击者服务器被拉取。** ![报告.md 已渲染](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/3.png) **4. 载荷执行其四个阶段:通过 localStorage 劫持 WASM 提供程序,生成弹窗监视器,在当前页面上安装 `FileReader` 和文件输入挂钩。** ![挂钩已安装及弹窗监视器](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/4.png) **5. 受害者导航至压缩 PDF。弹窗检测到导航并将挂钩重新注入到新页面中。** ![受害者在压缩 PDF 页面](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/5.png) **6. 受害者加载 PDF。该工具正常对其进行压缩。文件字节已经被 POST 到了攻击者服务器。** ![窃取进行中](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/6.png) **7. 经过验证的运行中的攻击者服务器输出:** ``` [17:11:11] WASM HIJACK { stage: 'wasm_hijack', victim: '.../markdown-to-pdf.html' } [17:11:12] BEACON { page: '.../markdown-to-pdf.html' } [17:11:55] BEACON { page: '.../index.html' } [17:12:00] BEACON { page: '.../compress-pdf.html' } [17:12:01] FILE EXFILTRATED Lorem_ipsum.pdf (23.7 KB) -> loot/171201_Lorem_ipsum.pdf [17:13:57] BEACON { page: '.../merge-pdf.html' } [17:13:58] FILE EXFILTRATED Lorem_ipsum.pdf (23.7 KB) -> loot/171358_Lorem_ipsum.pdf ``` ![攻击者服务器日志](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/7.png) **8. 被窃取的文件作为完整、有效的 PDF 打开,与原文件完全一致。** ![被窃取的 PDF](https://raw.githubusercontent.com/Astaruf/CVE-2026-41653/main/docs/8.png) ## 载荷阶段 嵌入在 `poc.py` 中的载荷按顺序执行四个阶段: | 阶段 | 功能 | 持久化范围 | |---|---|---| | 1 | 覆盖 `localStorage['bentopdf:wasm-providers']`,将所有 WASM 模块下载重定向到攻击者 | 跨浏览器会话 | | 2 | 注册 `/sw.js`,枚举所有工具页面中的 `/assets/*.js`,使用窃取挂钩投毒 `bentopdf-*` Service Worker 缓存 | 超出浏览器会话(仅限 HTTPS) | | 3 | 生成一个隐藏的 1×1 弹窗,轮询 `window.opener.location.href` 并在每次工具导航后重新注入文件挂钩 | 在 BentoPDF 标签页打开期间 | | 4 | 挂钩 `FileReader.prototype.readAsArrayBuffer` 和文档级别的 `change` 监听器:受害者接触的每个文件都会被 POST 到攻击者服务器的 `/file?name=` | 当前页面 | ## 修复 已在 **BentoPDF v2.8.3** 中发布。开发者在最初报告的注入口之外对代码库进行了审计,并修复了多个相关的攻击向量: 1. **DOMPurify** 应用于 `markdown-editor.ts` 中所有三个 `innerHTML` 注入口,包括使用 `securityLevel: 'loose'` 并被发现能绕过第一个净化器的 Mermaid SVG 路径。 2. **Mermaid** 现在使用 `securityLevel: 'strict'` 运行,并且 SVG 输出会使用 DOMPurify 的 SVG 配置文件重新净化。 3. **`file.name` XSS** 在大约 8 个工具页面(纠偏、表单填充、移除注释等)中进行了转义,这些页面中的文件名以前未经净化就被拼接到 HTML 中。 4. 引入了 **WASM 提供程序白名单**:加载时会丢弃 `localStorage['bentopdf:wasm-providers']` 中不受信任的 URL。 5. **Service Worker 缓存** 版本升级,并附带具有完整性校验的可信主机列表。 6. 在 `nginx.conf` 中强制执行了 **完整的安全头集**,包括 `Content-Security-Policy`。 7. **包含 17 项测试的回归套件**,断言原始载荷及所有额外的绕过向量均保持无效。 最初报告注入口的最小化修复: ``` import DOMPurify from 'dompurify'; private updatePreview(): void { if (!this.editor || !this.preview) return; const markdown = this.editor.value; const html = this.md.render(markdown); this.preview.innerHTML = DOMPurify.sanitize(html); } ``` 在 `nginx.conf` 中添加的 CSP 头: ``` add_header Content-Security-Policy "default-src 'self'; script-src 'self' blob:; connect-src 'self' https://cdn.jsdelivr.net; object-src 'none';" always; ``` ## 时间线 | 日期 | 事件 | |---|---| | 2026-03-XX | 在对 BentoPDF v2.8.2 进行源代码审查期间发现漏洞 | | 2026-03-XX | 私下向维护者报告并提供完整 PoC | | 2026-03-XX | 维护者确认。广泛的内部审计发现了 Mermaid、文件名、WASM 和 Service Worker 等攻击向量。 | | 2026-04-XX | 修复程序登陆 `edge` 构建版本。重新测试:原始载荷和 20 个绕过变体全部无效。 | | 2026-04-XX | 发布 **v2.8.3** 并公开致谢 | | 2026-04-XX | 发布 GHSA 公告(应维护者要求缩短了封禁期,补丁差异已公开) | | 2026-04-XX | 公开披露 | ## 参考 - [BentoPDF 官方仓库](https://github.com/alam00000/bentopdf) - [BentoPDF v2.8.3 发布说明](https://github.com/alam00000/bentopdf/releases/tag/v2.8.3) - [CWE-79:网页生成期间输入的不当中和](https://cwe.mitre.org/data/definitions/79.html) - [CWE-116:输出编码或转义不当](https://cwe.mitre.org/data/definitions/116.html) - [`markdown-it` 关于 `html: true` 的安全说明](https://github.com/markdown-it/markdown-it/blob/master/docs/security.md) - [DOMPurify](https://github.com/cure53/DOMPurify) - [MDN:内容安全策略](https://developer.mozilla.org/docs/Web/HTTP/CSP) ## 免责声明 本材料仅用于**授权的安全测试和教育目的**。请仅针对您拥有或已获得明确书面测试许可的 BentoPDF 实例使用它。未经授权访问计算机系统是违法行为。作者对滥用行为不承担任何责任。 ## 许可证 [MIT](LICENSE)
标签:BentoPDF, CVE-2026-41653, CWE-79, DOM注入, Go语言工具, innerHTML, markdown-it, Markdown-to-PDF, PoC, Web安全, XSS, 代码执行, 前端安全, 存储型XSS, 数据可视化, 文件窃取, 暴力破解, 漏洞分析, 漏洞情报, 网络安全, 蓝队分析, 请求拦截, 跨站脚本攻击, 路径探测, 输入验证缺失, 逆向工具, 隐私保护