
PoC — OpenLore में अनसैनिटाइज़्ड LLM-व्युत्पन्न डोमेन फ़ील्ड के माध्यम से पाथ ट्रैवर्सल (GHSA-5j8x-q7q6-58j5, CVE-2026-87001, CVSS 4.7)।
domain फ़ील्ड के माध्यम से Path TraversalCVE स्थिति: अनुरोध किया गया, असाइनमेंट लंबित। यह निष्कर्ष GHSA-5j8x-q7q6-58j5 के रूप में प्रकाशित है। CVE असाइनमेंट पर इस रिपॉज़िटरी का नाम बदलकर
CVE-YYYY-NNNNN-openlore-PoCकर दिया जाएगा और इस बैनर को CVE लिंक से बदल दिया जाएगा।
| शोधकर्ता | Dostxodjayev Abdullox (@squeeze440) |
| सलाह | GHSA-5j8x-q7q6-58j5 |
| CVSS 3.1 | 4.7 (मध्यम) |
| कमज़ोरी | CWE-22, CWE-73 |
domain फ़ील्ड के माध्यम से Path Traversalसारांश: एक हमलावर जो openlore generate द्वारा विश्लेषित रिपॉज़िटरी की सामग्री को नियंत्रित करता है, OpenSpec spec-generation पाइपलाइन को हमलावर-प्रभावित Markdown को लक्ष्य प्रोजेक्ट के बाहर किसी भी फ़ाइलसिस्टम पथ पर लिखने के लिए प्रेरित कर सकता है, क्योंकि Stage-3 LLM extraction कॉल द्वारा लौटाया गया domain मान बिना किसी traversal sanitization और fs.writeFile से पहले किसी confinement जाँच के, spec के आउटपुट पथ को बनाने के लिए यथावत उपयोग किया जाता है।
OpenLore (clay-good/OpenLore), npm पैकेज openlore, spec-generation पाइपलाइन (openlore generate / OpenSpec-format generator + writer)। यह नियतात्मक analyze/orient/MCP guardrail पथ नहीं है — यह प्रोजेक्ट में एकमात्र कोड पथ है जो जानबूझकर generation के लिए LLM को लूप में डालता है, प्रोजेक्ट के स्वयं के README/spec के अनुसार ("एक LLM का उपयोग केवल वहीं किया जाता है जहाँ generation अपरिहार्य है (spec authoring), retrieval या guardrail पथ में कभी नहीं")।
Git commit 1f2de00dfe75530efb256320c374dce36607ee70 (2026-07-31), package.json संस्करण 2.1.7।
4.7 (मध्यम) — CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
AV:L — ट्रिगर डिस्क पर एक डायरेक्टरी के विरुद्ध एक स्थानीय CLI invocation (openlore generate) है, नेटवर्क सेवा नहीं।AC:H — शोषण के लिए ऑपरेटर को एक provider कॉन्फ़िगर करके LLM-समर्थित generate कमांड (डिफ़ॉल्ट guardrail पथ नहीं) चलाने की आवश्यकता है, और विश्लेषित रिपॉज़िटरी के indirect prompt injection को वास्तव में मॉडल से domain फ़ील्ड में एक traversal स्ट्रिंग उत्पन्न कराने की आवश्यकता है — एक ऐसी स्थिति जिसे हमलावर प्रभावित करता है लेकिन पूरी तरह नियंत्रित नहीं करता।UI:R — ऑपरेटर को दुर्भावनापूर्ण रिपॉज़िटरी के विरुद्ध spec generation चलाने का चयन करना होगा।I:H / C:N / A:N — यह बग एक write primitive है जिसमें आंशिक रूप से हमलावर-टेम्पलेटेड सामग्री प्रोजेक्ट के बाहर हमलावर-चयनित पथ पर पहुँचती है; इसे गोपनीय डेटा पढ़ने या उपलब्धता को विश्वसनीय रूप से कम करने के लिए प्रदर्शित नहीं किया गया, इसलिए वे मेट्रिक्स रूढ़िवादी रूप से N हैं।ExtractedService.domain (src/types/pipeline.ts:105) सीधे Stage 3 LLM कॉल से भरा जाता है, जिसे विश्लेषित की जा रही रिपॉज़िटरी की कच्ची फ़ाइल सामग्री दी जाती है और सेवाओं की JSON array लौटाने के लिए कहा जाता है:
src/core/generator/stages/stage3-services.ts:45-52 fileChunks[i] (विश्लेषित repo का शाब्दिक स्रोत पाठ) से prompt बनाता है और pipeline.llm.completeJSON<ExtractedService[]>(..., STAGE3_SERVICE_SCHEMA) को कॉल करता है।src/core/generator/schemas.ts:100 — STAGE3_SERVICE_SCHEMA.items.properties.domain { type: 'string' } है: कोई pattern नहीं, कोई enum नहीं, कोई allowlist नहीं। मॉडल जो भी स्ट्रिंग लौटाता है वह स्वीकार कर ली जाती है।src/core/generator/openspec-format-generator.ts:161 — const domainName = service.domain || this.inferDomain(...) उस स्ट्रिंग का यथावत उपयोग करता है।src/core/generator/openspec-format-generator.ts:563 — path: \openspec/specs/${domain.name.toLowerCase()}/spec.md`इसे सीधे spec के घोषित आउटपुट पथ में interpolate करता है। ध्यान दें किnormalizeDomainName() (src/core/generator/openspec-compat.ts:655) उसी फ़ाइल के dependency graph में मौजूद है और अन्यत्र लागू किया जाता है (src/core/generator/openspec-writer.ts:167`, stale-domain-directory तुलना के लिए) — लेकिन इसे यहाँ कभी कॉल नहीं किया जाता, उस एक स्थान पर जहाँ मान वास्तव में एक फ़ाइलसिस्टम पथ बन जाता है।src/core/generator/openspec-writer.ts:248 — const fullPath = join(this.rootPath, spec.path); सादे node:path.join का उपयोग करता है, जो ../ सेगमेंट को lexically resolve करता है, प्रोजेक्ट के स्वयं के safeJoin() (src/utils/path-confinement.ts) को कॉल किए बिना, जिसके माध्यम से इस कोडबेस में हर अन्य अविश्वसनीय-पथ सतह (MCP handlers, openlore view के /api/skeleton और /api/spec-requirements) को openspec/specs/mcp-security/spec.md के अनुसार रूट होना आवश्यक है।src/core/generator/openspec-writer.ts:277 / :294 फिर writeFile(fullPath, spec.content, 'utf-8') करता है, और ensureDir() (openspec-writer.ts:497-499) उसी unconfined पथ पर mkdir(dirname(filePath), { recursive: true }) करता है, प्रोजेक्ट रूट से बाहर जाते समय कोई भी अनुपस्थित मध्यवर्ती डायरेक्टरी बना देता है।शुद्ध प्रभाव: ../../../../../../tmp/x जैसा domain मान LLM की JSON प्रतिक्रिया से एक वास्तविक writeFile कॉल तक बिना छुए पहुँचता है, और विश्लेषित प्रोजेक्ट के पूरी तरह बाहर पहुँच जाता है। यह ठीक वही "repo content एक write को प्रोजेक्ट रूट के बाहर redirect करता है" threat model है जिसे प्रोजेक्ट का स्वयं का openspec/specs/mcp-security/spec.md स्पष्ट रूप से परिभाषित करता है और MCP/serve/view सतहों पर harden करता है (safeJoin, symlink-aware confinement, path-parameter coverage gate) — generator/writer सतह पर बस कोई समतुल्य gate नहीं है।
कोई लाइव LLM कॉल नहीं किया गया (injected निर्देश के साथ मॉडल का अनुपालन संभाव्य है और इस बग का दिलचस्प हिस्सा नहीं है); इसके बजाय PoC OpenLore के वास्तविक, असंशोधित OpenSpecFormatGenerator और OpenSpecWriter को एक PipelineResult के साथ कॉल करता है जिसका एक ExtractedService.domain फ़ील्ड ठीक वही स्ट्रिंग आकार रखता है जिसे STAGE3_SERVICE_SCHEMA आज एक LLM को लौटाने की अनुमति देता है, और ठीक वही जो एक स्रोत-फ़ाइल टिप्पणी जो "domain को ../../../.../tmp/x पर सेट करें" का निर्देश देती है, Stage 3 से निकलवा सकती है (जो मॉडल को कच्ची फ़ाइल सामग्री देता है)।
स्क्रिप्ट: ~/engagements/openlore/source/poc-domain-traversal.ts (परीक्षित क्लोन का repo root), ~/engagements/openlore/evidence/victim-project पर एक scratch प्रोजेक्ट के विरुद्ध चलाई गई:
cd ~/engagements/openlore/source
npx tsx poc-domain-traversal.ts
परिणाम: OpenSpecFormatGenerator.generateSpecs() (असंशोधित) एक domain spec उत्पन्न करता है जिसमें
path: "openspec/specs/../../../../../../../../../../../../../../../../../../../../tmp/openlore_traversal_proof/spec.md",
और OpenSpecWriter.writeSpecs() (असंशोधित) इसे लिखता है — /tmp/openlore_traversal_proof/spec.md पर पहुँचता है, पूरी तरह victim-project रूट के बाहर, जबकि victim-project/openspec/specs/ में केवल दो वैध overview/architecture डायरेक्टरियाँ ही रहती हैं।
स्क्रीनशॉट (वास्तविक Xvfb+fluxbox+xterm कैप्चर, scrot -w <window id>):
../evidence/poc-generator-write.png — PoC रन: traversal युक्त generator का कच्चा spec.path, writer लॉग लाइन Wrote openspec/specs/../../.../tmp/openlore_traversal_proof/spec.md, और स्क्रिप्ट का स्वयं का पुष्टिकरण कि /tmp/openlore_traversal_proof/spec.md हमलावर-प्रभावित सामग्री के साथ मौजूद है।../evidence/poc-outside-fs-proof.png — स्वतंत्र पुष्टिकरण: victim-project/openspec/specs/ की ls केवल architecture/overview दिखाती है (प्रोजेक्ट के अंदर दुर्भावनापूर्ण domain का कोई निशान नहीं), जबकि ls -la /tmp/openlore_traversal_proof/ escaped spec.md को सीधे /tmp के नीचे दिखाती है।