
CVE-2026-15718 के लिए PoC शोषण श्रृंखला: SpiderMonkey wasm बेसलाइन कंपाइलर array.fill missing-sync -> अमान्य पॉइंटर -> addrOf/fakeobj -> मनमाना R/W -> RCE
CVE-2026-15718 के लिए proof-of-concept exploit श्रृंखला — Mozilla SpiderMonkey के WebAssembly baseline कंपाइलर ("JavaScript: WebAssembly" घटक) में missing-sync() miscompilation। मात्र एक zero-trip array.fill एक fakeobj primitive उत्पन्न करता है; श्रृंखला होस्ट प्रोसेस की मनमानी read/write और मनमानी native code execution (posix_spawnp("/bin/sh", ...)) तक बढ़ती है।
4441102e62e8 ("Bug 2045443", एक पंक्ति)BaseCompiler::emitArrayFill() (js/src/wasm/WasmBaselineCompile.cpp) पहले कॉल किए बिना फिल लूप उत्सर्जित करता है:
sync()array.fill if (elementType.isRefRepr()) {
freePtr(RegPtr(PreBarrierReg));
}
+ sync();
+
// Perform the fill loop using `numElements` as the loop variable ...
sync() baseline कंपाइलर के value stack (stk_) को रजिस्टरों से machine stack पर flush करता है। फिल लूप का per-iteration imprecise post-barrier (emitPostBarrierEdgeImprecise) आंतरिक रूप से sync() कॉल करता है — लेकिन केवल तभी जब लूप चलता है। लूप का exit label (done) दो पथों का join है:
stk_ synced है — हर operand एक machine-stack slot में रहता है;numElements == 0): stk_ अछूता है — operands अभी भी केवल रजिस्टरों में रहते हैं।Join के बाद कंपाइलर का static model हमेशा synced path को दर्शाता है, इसलिए array.fill के बाद उत्सर्जित कोड operands को उन stack slots से पढ़ता है जिन्हें zero-trip execution ने कभी नहीं लिखा। इस तरह वापस पढ़ा गया reftype operand एक अमान्य (stale) पॉइंटर है (CWE-763)।
desync दो frame-identical wasm फ़ंक्शनों में द्विदिशात्मक है:
n = 1, लूप चलता है): in-loop sync() स्लॉट में एक मान लिखता है — एक वास्तविक ref (addrOf) या एक नियंत्रित i64 (fakeobj सेटअप);n = 0, zero-trip): स्लॉट कभी नहीं लिखा जाता; twin फ़ंक्शन stale स्लॉट को वापस पढ़ता है — i64 के रूप में (leak) या (ref $t) के रूप में (fakeobj)।| फ़ाइल | चरण | परिणाम |
|---|---|---|
poc-min.js (+ gen_trigger.py) | desync डेमो | control path सही मान लौटाता है; zero-trip path कभी-न-लिखे गए स्लॉट से पढ़ा गया garbage पॉइंटर लौटाता है |
chain.js (+ gen_chain.py, chain.tpl.js, build.sh) | पूर्ण श्रृंखला | addrOf + fakeobj → fake WasmArrayObject → arbitrary R/W → XUL base leak → runtime Mach-O import walk → forged funcref → posix_spawnp("/bin/sh", ["-c", "touch /tmp/CVE-2026-15718-PWNED"]) |
chain.js में चरण विवरण:
plantSpray/plantCmd/plantFun/plantExt + leak — wasm arrays, funcrefs और JS objects (externref) के लिए addrOf।sprayV + fakeRd/fakeWr — fakeobj; fake WasmArrayObject (numElements_ @+16, data_ @+24, inline elements @+40) जिसमें fake window को out-of-line (malloc'd) array में रखा जाता है ताकि GC स्कैन के बीच में उसे स्थानांतरित न कर सके।0xfeedfacf) के लिए page-wise पीछे स्कैन करें → XUL base।_posix_spawnp के __stubs entry का पता लगाने के लिए arbitrary read (LC_SEGMENT_64/LC_SYMTAB/LC_DYSYMTAB + indirect symbol table) के ज़रिए XUL के Mach-O load commands को walk करें। कोई hardcoded offsets नहीं; stub पहले कॉल पर lazy binding करता है।typeDef @+0x40 एक वास्तविक ref.func से कॉपी किया गया, call target @+0x38 = stub address) और उसे call_ref के माध्यम से posix_spawnp(pid, "/bin/sh", 0, 0, [sh, -c, cmd], 0) के साथ invoke करें।pre-152.0.6 tree से निर्मित xpcshell (या js shell / browser) के साथ परीक्षण किया गया (यहाँ reference build Firefox 149.0a1, 2026-01-14 है):
# minimal desync demo
xpcshell poc/poc-min.js
# full chain (regenerates chain.js from the template + generator)
(cd poc && sh build.sh)
rm -f /tmp/CVE-2026-15718-PWNED
xpcshell poc/chain.js # creates /tmp/CVE-2026-15718-PWNED
ls -l /tmp/CVE-2026-15718-PWNED
Harness लंबे Mach-O स्कैन के दौरान baseline tier को स्थिर रखने के लिए javascript.options.wasm_optimizingjit=false सेट करता है (बग केवल baseline में है; browser में trigger tier-up थ्रेशोल्ड से काफी नीचे चलता है इसलिए वहाँ इसकी आवश्यकता नहीं है)। tools/xul_slots.py एक offline validator है जो XUL के named stub/GOT slots की सूची देता है (runtime walker की cross-check के लिए उपयोग किया जाता है)।
Offsets macOS arm64, इसी tree/build के लिए हैं (WasmArrayObject { +16 numElements, +24 data_, +40 inline }, JSFunction native @ +0x20, WasmFuncRef { +0x38/+0x48 call target, +0x40 typeDef })। ये सभी श्रृंखला के self-tests द्वारा runtime पर पुनः व्युत्पन्न किए जाते हैं; केवल structure layouts मान लिए गए हैं। कोई PAC चिंता नहीं (XUL एक arm64, non-arm64e बाइनरी है); lazy-binding stub पहले कॉल पर target को resolve करता है, इसलिए कोई dyld-shared-cache offsets आवश्यक नहीं हैं।
यह PoC एक unsandboxed shell प्रोसेस में चलता है। एक वास्तविक Firefox हमले में श्रृंखला content process के अंदर पहुँचती है, जहाँ macOS सैंडबॉक्स posix_spawnp को अस्वीकार करता है — पूर्ण compromise के लिए एक अलग OS-sandbox-escape बग की आवश्यकता होती है (देखें, उदाहरण के लिए, CVE-2026-2796-and-CVE-2026-2768-escape-the-wasm-box में stage-2 विश्लेषण)।
javascript.options.wasm=false trigger वेक्टर को ब्लॉक करता है।केवल सुरक्षा अनुसंधान, शिक्षा और रक्षात्मक परीक्षण के लिए। यह भेद्यता वर्तमान Firefox/Thunderbird रिलीज़ों में पैच की जा चुकी है। उन सिस्टमों के विरुद्ध उपयोग न करें जिनके स्वामी आप नहीं हैं या जिनके परीक्षण के लिए आपके पास स्पष्ट प्राधिकरण नहीं है।