
Google Chrome CVE-2026-6307 PoC
Riproduzione locale e armamento della confusione di tipo nella deottimizzazione V8 JS-to-Wasm descritta da Nebula, portata a un xcalc aperto dal renderer di Chrome for Testing 146.0.7680.165 (il bug è corretto in 147.0.7727.101).
Tutti i payload sono benigni (una calcolatrice / exit(0x42) / jmp $).
Tutto viene eseguito contro un browser deliberatamente obsoleto in un laboratorio locale, non puntarlo contro nessun altro.
https://github.com/user-attachments/assets/d450f064-ba52-4f39-a88b-367f31217143
JSToWasmFrameStateFunctionInfo's equality operator omette il confronto della firma Wasm, quindi CSE/GVN unisce stati di frame che differiscono solo per la firma. Una deopt pigra quindi materializza un Wasm i64 come externref taggato (e viceversa), dando un addrof/fakeobj completo a 64 bit i cui puntatori non sono compressi nella gabbia (i riferimenti Wasm usano la rappresentazione a registro taggato, restituita tramite kReturnRegister0).
Tutto quanto segue è stato verificato dal vivo su 146.0.7680.165. I numeri per caricamento sono tassi di successo misurati su esecuzioni ripetute (la confusione è sensibile al GC; ogni pagina viene riavviata su una build sporca). Nessuna esecuzione ha mai prodotto un falso positivo.
--allow-natives-syntaxLa maggior parte dei PoC per bug di V8 forza l'ottimizzazione con %OptimizeFunctionOnNextCall, un'intrinseca solo per test protetta da --allow-natives-syntax che nessuna vittima reale abilita. Questi sfruttano il bug senza alcun flag, modellando il feedback di riscaldamento in modo che la confusione della deopt si attivi naturalmente. Ogni indirizzo viene trapelato a runtime — niente è hardcoded. Eseguili headless senza flag.
rce-no-aslr/ lo porta all'esecuzione di codice: una funzione JS ottimizzata g prepara shellcode execve come doppie letterali JIT (costanti movabs, ciascuna terminante con eb rel8 per concatenare la successiva), la scrittura confusa applica una patch nop; jmp nel codice di g, e chiamare g() fa scivolare RIP nello shellcode.
Questa build usa --allow-natives-syntax (%Optimize fissa il payload JIT di g in modo che il deopt-GC non possa rimuoverlo) e --no-memory-protection-keys (così la scrittura confusa può applicare una patch alla pagina JIT — altrimenti PKU di V8 la segna come sola lettura). ASLR è disattivato (randomize_va_space=0) quindi gli indirizzi sono deterministici (A_MAP = 0x5555bbc0128a). Il renderer necessita anche di --disable-seccomp-filter-sandbox --no-zygote, perché altrimenti seccomp blocca l'execve dello shellcode.
Linux nativo x86_64, ASLR disattivato, Chrome 146 in PATH, un display X su :0 (Xvfb o X reale). rce-no-aslr/run.sh imposta tutti i flag e serve su http.
cd rce-no-aslr
./run.sh calc # apre un vero xcalc su http://127.0.0.1:8080, su :0
./run.sh exit # strace conferma l'exit(66) del renderer
./run.sh jmp # gdb conferma RIP all'interno dello shellcode iniettato
./demo.sh calc # slate pulito + auto-riprova, browser headful a schermo intero (per registrazione)
./demo.sh msgbox # stesso, ma il pop è una finestra marchiata "0xSha :: pwned"
pocmode.sh calc|msgbox cambia ciò che l'execve("/usr/bin/xcalc") della RCE lancia (una vera calcolatrice vs la finestra marchiata). Le primitive senza flag necessitano solo di un browser:
chrome --headless=new --no-sandbox http://127.0.0.1:8080/flag-free/01-primitives-flagfree.html
flag-free/ il contributo senza flag (NO --allow-natives-syntax)
00-reachability trigger + differenza di patch (vuln 146 vs patchato 147)
01-primitives addrof / fakeobj
02-store scrittura fuori gabbia
03-arw read64 / write64 in gabbia
rce-no-aslr/ il RCE visibile (deterministico, build di laboratorio senza ASLR)
exploit-calc.html / exploit-exit.html
run.sh, demo.sh, pocmode.sh, xcalc-msgbox.sh
Scrittura del bug e causa principale: Nebula Security (link sopra). Il trigger senza flag, le primitive e i lavori visibili in questo repository sono riproduzioni/estensioni verificate in un laboratorio locale.
| File | Proves | Verified |
|---|
flag-free/00-reachability-flagfree.html | attiva lo stato confuso; si attiva su 146 vulnerabile, non su 147 patchato | 6/15 per caricamento |
flag-free/01-primitives-flagfree.html | addrof / fakeobj (fakeobj(addrof(o))===o) | 8/12 |
flag-free/02-store-flagfree.html | scrittura fuori gabbia inserisce un valore scelto in un oggetto scelto | 12/12 |
flag-free/03-arw-flagfree.html | read64 / write64 in-gabbia (array PACKED_DOUBLE falso) | 2/12 (instabile, riprova) |
| File | Proves | Verified |
|---|
rce-no-aslr/exploit-calc.html | apre una vera finestra xcalc tramite execve dal renderer | si apre in ~1 secondo su uno stato pulito |
rce-no-aslr/exploit-exit.html | il renderer esegue lo shellcode iniettato per exit(66); #jmp fa girare RIP al suo interno | exit(66) 8/8 (strace) |