PoC — Note Toolbar for Obsidian में frontmatter-संचालित मनमाना JavaScript निष्पादन (GHSA-q8cw-3m8c-5pf2, CVE-2026-87002, CVSS 7.0)।
{{prop_NAME}} → {{js:}} वेरिएबल चेन के माध्यम से Frontmatter-संचालित मनमाना कोड निष्पादनCVE स्थिति: अनुरोध किया गया, असाइनमेंट लंबित। यह निष्कर्ष GHSA-q8cw-3m8c-5pf2 के रूप में प्रकाशित है। CVE असाइनमेंट पर इस रिपॉज़िटरी का नाम बदलकर
CVE-YYYY-NNNNN-obsidian-note-toolbar-PoCकर दिया जाएगा और इस बैनर को CVE लिंक से बदल दिया जाएगा।
| शोधकर्ता | Dostxodjayev Abdullox (@squeeze440) |
| सलाह | GHSA-q8cw-3m8c-5pf2 |
| CVSS 3.1 | 7.0 (उच्च) |
| कमज़ोरी | CWE-94, CWE-1336 |
{{prop_NAME}} → {{js:}} वेरिएबल चेन के माध्यम से Frontmatter-संचालित मनमाना कोड निष्पादनNote Toolbar (Obsidian प्लगइन) के वेरिएबल-प्रतिस्थापन इंजन में अनुचित आउटपुट न्यूट्रलाइज़ेशन एक हमलावर को, जो किसी नोट के YAML frontmatter को नियंत्रित करता है (जैसे किसी सिंक/साझा किए गए वॉल्ट पर सहयोगी), पीड़ित की मशीन पर मनमाना कोड निष्पादन प्राप्त करने की अनुमति देता है, जब पीड़ित केवल नोट खोलता है, एक frontmatter प्रॉपर्टी में {{js: ...}} पेलोड इंजेक्ट करके जिसे कोई पूर्व-मौजूद, गैर-स्क्रिप्ट टूलबार आइटम लेबल/टूलटिप/लिंक {{prop_NAME}} के माध्यम से संदर्भित करता है।
Note Toolbar (note-toolbar) — Obsidian कम्युनिटी प्लगइन
रिपॉज़िटरी: https://github.com/chrisgurney/obsidian-note-toolbar
v1.34.12 (कमिट 520271c3027eb6da38bab10a686b21e14c664c13, 2026-07-29), Linux पर Obsidian डेस्कटॉप 1.13.4 के विरुद्ध।
7.0 (उच्च) — CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
AV:L — कमज़ोर घटक (प्लगइन का JS मूल्यांकनकर्ता, जो Obsidian के Electron रेंडरर के अंदर चलता है) नेटवर्क-फेसिंग नहीं है; पेलोड स्थानीय वॉल्ट/फ़ाइल सामग्री के रूप में पहुँचता है जिसे पीड़ित जिस भी माध्यम से उपयोग करता है (Git, Syncthing, Obsidian Sync, साझा फ़ोल्डर) उसके द्वारा सिंक किया जाता है, जो इस परिवार के पूर्व निष्कर्षों (जैसे obsidian-syncthing-integration, obsidian-codescript-toolkit) के अनुरूप है।AC:H — शोषण उन स्थितियों पर निर्भर करता है जो हमलावर के नियंत्रण से बाहर हैं और पीड़ित के इंस्टॉल पर पहले से मौजूद होनी चाहिए: (1) प्लगइन की "Scripting" सेटिंग पहले से सक्षम होनी चाहिए (डिफ़ॉल्ट रूप से बंद), और (2) पीड़ित ने पहले से ही एक टूलबार आइटम कॉन्फ़िगर किया होना चाहिए जिसका लेबल, टूलटिप, या लिंक किसी frontmatter कुंजी के लिए एक बेयर {{prop_NAME}} संदर्भ हो। दोनों यथार्थवादी, प्रलेखित उपयोग पैटर्न हैं, लेकिन कोई भी डिफ़ॉल्ट नहीं है।PR:N — हमलावर को पीड़ित के सिस्टम तक पहुँच की आवश्यकता नहीं है, केवल हमलावर-नियंत्रित frontmatter के साथ पीड़ित के वॉल्ट में एक नोट डालने की क्षमता चाहिए।UI:R — पीड़ित को विशिष्ट नोट खोलना/देखना चाहिए; टूलबार आइटम पर क्लिक की आवश्यकता नहीं है, टूलबार स्वचालित रूप से रेंडर होता है।S:U, C:H/I:H/A:H — कोड पूर्ण Node.js पहुँच (require('child_process'), फ़ाइलसिस्टम, आदि) के साथ Obsidian चलाने वाले स्थानीय उपयोगकर्ता खाते के रूप में निष्पादित होता है।Note Toolbar एक प्रलेखित {{prop_NAME}} वेरिएबल का समर्थन करता है जो किसी नोट के frontmatter मान को टूलबार आइटम के लेबल, टूलटिप, या लिंक में प्रतिस्थापित करता है (skills/note-toolbar-variables/SKILL.md, विकी Variables.md)। यह एक {{js: <expr>}} वेरिएबल का भी समर्थन करता है जो "Scripting" प्लगइन सेटिंग सक्षम होने पर अभिव्यक्ति को लाइव JavaScript के रूप में मूल्यांकित करता है।
बग src/Toolbar/VariableResolver.ts::replaceVars() में संचालन के क्रम में है:
{{prop_KEY}} प्लेसहोल्डर को वर्तमान नोट के frontmatter[KEY] के कच्चे मान से प्रतिस्थापित किया जाता है — बिना शर्त, scriptingEnabled सेटिंग की परवाह किए बिना, और उस frontmatter मान के स्रोत की परवाह किए बिना।if (this.ntb.settings.scriptingEnabled) के अंदर): फ़ंक्शन तब पहले से प्रतिस्थापित स्ट्रिंग s पर s.trim().startsWith('{{js:') की जाँच करता है, और यदि सत्य है, तो {{js:/}} रैपर को हटा देता है और शेष को मूल्यांकन के लिए JavaScriptAdapter.use() को पास कर देता है।चूँकि चरण 1 चरण 2 से पहले चलता है और s को यथास्थान फिर से लिखता है, एक frontmatter मान जो स्वयं {{js: से शुरू होता है और }} पर समाप्त होता है, "प्रदर्शित पाठ" से "निष्पादित स्क्रिप्ट" में पदोन्नत हो जाता है — भले ही टूलबार आइटम कभी स्क्रिप्ट आइटम के रूप में लिखा ही नहीं गया था। टूलबार आइटम के स्वामी ने केवल {{prop_status}} टाइप किया था; निष्पादन योग्य पेलोड पूरी तरह से नोट के अपने frontmatter से आता है।
JavaScriptAdapter.evaluate() (src/Adapters/JavaScriptAdapter.ts:143-205) अभिव्यक्ति को एक वास्तविक, अनसैंडबॉक्स्ड AsyncFunction के साथ चलाता है:
src/Adapters/Adapter.ts:11
protected static readonly AsyncFunction = (Object.getPrototypeOf(async function(){}) as { constructor: typeof Function }).constructor;
src/Adapters/JavaScriptAdapter.ts:164
const func = new JavaScriptAdapter.AsyncFunction("input", expression);
...
result = await Promise.resolve((func as (...args: unknown[]) => unknown)(args));
यह JS इंजन का वास्तविक AsyncFunction कंस्ट्रक्टर है, कोई इंटरप्रेटर/सैंडबॉक्स नहीं — Obsidian के Electron रेंडरर के अंदर इसके पास पूर्ण require()/Node.js पहुँच है, जिसकी पुष्टि नीचे PoC में require('child_process').execSync(...) के माध्यम से की गई है।
महत्वपूर्ण रूप से, JS एडाप्टर बिल्ट-इन है और इसके लिए किसी साथी प्लगइन की आवश्यकता नहीं है (src/Adapters/AdapterManager.ts:59: adapter = this.js; // built-in, doesn't rely on plugin), Dataview/Templater/JS-Engine वेरिएबल प्रीफ़िक्स के विपरीत जिनके लिए उन प्लगइन्स को इंस्टॉल करना आवश्यक है। केवल प्लगइन का अपना "Scripting" टॉगल (scriptingEnabled, डिफ़ॉल्ट false, src/Settings/NoteToolbarSettings.ts:300) इसे नियंत्रित करता है।
अंत में, प्रतिस्थापन को ट्रिगर करने वाला रेंडर पथ स्वचालित है, क्लिक-गेटेड नहीं: ToolbarRenderer.ts::renderLItems() हर बार जब किसी नोट के लिए टूलबार रेंडर होता है — यानी नोट खोलने/देखने पर — this.ntb.vars.resolveText(toolbar, file) (src/Toolbar/ToolbarRenderer.ts:418) को कॉल करता है, जो replaceVars() के माध्यम से प्रत्येक आइटम के लेबल और टूलटिप को हल करता है। प्रभावित टूलबार आइटम पर क्लिक की आवश्यकता नहीं है।
टूलबार-से-नोट असाइनमेंट स्वयं frontmatter-संचालित है (toolbarProp सेटिंग, डिफ़ॉल्ट कुंजी notetoolbar, src/Settings/NoteToolbarSettings.ts:317), इसलिए एक सिंक किया गया नोट चुन सकता है कि उस पर कौन सा टूलबार रेंडर होगा — लेकिन यदि पीड़ित पहले से ही एक डिफ़ॉल्ट टूलबार या फ़ोल्डर मैपिंग का उपयोग करता है जिसमें कमज़ोर आइटम शामिल है तो शोषण के लिए यह आवश्यक नहीं है।
यह उस जोखिम से अलग है जिसे मेंटेनर का अपना SECURITY.md प्रलेखित करता है ("User scripts... executes JavaScript provided by the user... intentional and by design") — वह वर्णन करता है कि एक उपयोगकर्ता जानबूझकर एक {{js:}} टूलबार आइटम लिखता है, या जानबूझकर एक साझा टूलबार कॉन्फ़िग आयात करता है जिसमें एक शामिल है। यहाँ, कॉन्फ़िगरर के दृष्टिकोण से टूलबार आइटम बिल्कुल भी स्क्रिप्ट आइटम नहीं है (यह एक सादा प्रॉपर्टी-डिस्प्ले लेबल है), और निष्पादन योग्य पेलोड सामान्य नोट सामग्री/frontmatter के माध्यम से आता है — अन्य Obsidian सिंक/कॉन्फ़िग कोड-निष्पादन निष्कर्षों (obsidian-syncthing-integration, obsidian-codescript-toolkit) के समान अविश्वसनीय-वॉल्ट-सामग्री खतरा मॉडल।
वास्तविक Obsidian 1.13.4 डेस्कटॉप इंस्टेंस (Xvfb + fluxbox) के विरुद्ध गतिशील रूप से सत्यापित, वास्तविक बिल्ट प्लगइन (main.js जो ऑडिट किए गए स्रोत से बनाया गया) लोड करके, न कि बेयर-फ़ंक्शन निष्कर्षण।