
Google Chrome CVE-2026-6307 PoC
Reproducción local y weaponización de la confusión de tipos en la desoptimización JS-to-Wasm de V8 descrita por Nebula, llevada a un xcalc abierto desde el renderizador de Chrome for Testing 146.0.7680.165 (el bug está corregido en 147.0.7727.101).
Todos los payloads son benignos (una calculadora / exit(0x42) / jmp $).
Todo se ejecuta contra un navegador deliberadamente desactualizado en un laboratorio local; no lo apuntes a nadie más.
https://github.com/user-attachments/assets/d450f064-ba52-4f39-a88b-367f31217143
El operador de igualdad de JSToWasmFrameStateFunctionInfo omite la comparación de la firma de Wasm, por lo que CSE/GVN fusiona estados de frame que difieren solo en la firma. Una desoptimización perezosa materializa entonces un i64 de Wasm como un externref etiquetado (y viceversa), dando un addrof/fakeobj completo de 64 bits cuyos punteros no están comprimidos en cage (las referencias de Wasm usan la representación de registro etiquetado, devueltas a través de kReturnRegister0).
Todo lo siguiente fue verificado en vivo contra 146.0.7680.165. Los números por carga son tasas de acierto medidas en ejecuciones repetidas (la confusión es sensible al GC; cada página se relanza en una compilación sucia). Ninguna ejecución produjo un falso positivo.
--allow-natives-syntaxLa mayoría de los PoCs de bugs de V8 fuerzan la optimización con %OptimizeFunctionOnNextCall, un intrínseco solo de prueba protegido por --allow-natives-syntax que ninguna víctima real habilita. Estos impulsan el bug sin flags en absoluto, moldeando la retroalimentación de calentamiento para que la confusión de desoptimización se active naturalmente. Cada dirección se filtra en tiempo de ejecución — nada está hardcodeado. Ejecútalos headless sin flags.
rce-no-aslr/ lo lleva a ejecución de código: una función JS optimizada g prepara shellcode execve como dobles literales JIT (constantes movabs, cada una terminando en eb rel8 para encadenar la siguiente), el almacenamiento confuso parchea un nop; jmp en el código de g, y llamar a g() desliza RIP hacia el shellcode.
Esta compilación usa --allow-natives-syntax (%Optimize fija el payload JIT de g para que el GC de desoptimización no pueda eliminarlo) y --no-memory-protection-keys (para que el almacenamiento confuso pueda parchear la página JIT — de lo contrario PKU de V8 la marca como solo lectura). ASLR está desactivado (randomize_va_space=0) por lo que las direcciones son deterministas (A_MAP = 0x5555bbc0128a). El renderizador también necesita --disable-seccomp-filter-sandbox --no-zygote, porque de lo contrario seccomp bloquea el execve del shellcode.
Linux x86_64 nativo, ASLR desactivado, Chrome 146 en PATH, un display X en :0 (Xvfb o X real). rce-no-aslr/run.sh establece todas las flags y sirve sobre http.
cd rce-no-aslr
./run.sh calc # abre un xcalc real sobre http://127.0.0.1:8080, en :0
./run.sh exit # strace confirma exit(66) del renderizador
./run.sh jmp # gdb confirma RIP dentro del shellcode inyectado
./demo.sh calc # estado limpio + reintento automático, navegador headful de tamaño completo (para grabación)
./demo.sh msgbox # igual, pero la ventana emergente es una caja con la marca "0xSha :: pwned"
pocmode.sh calc|msgbox cambia lo que lanza el execve("/usr/bin/xcalc") del RCE (una calculadora real vs la caja con marca). Las primitivas sin flags solo necesitan un navegador:
chrome --headless=new --no-sandbox http://127.0.0.1:8080/flag-free/01-primitives-flagfree.html
flag-free/ la contribución sin flags (NO --allow-natives-syntax)
00-reachability desencadenante + diferencial de parche (vuln 146 vs parcheado 147)
01-primitives addrof / fakeobj
02-store almacenamiento fuera de cage
03-arw en-cage read64 / write64
rce-no-aslr/ el RCE visible (determinista, compilación de laboratorio sin ASLR)
exploit-calc.html / exploit-exit.html
run.sh, demo.sh, pocmode.sh, xcalc-msgbox.sh
Informe del bug y causa raíz: Nebula Security (enlazado arriba). El desencadenante sin flags, las primitivas y los trabajos visibles en este repositorio son reproducciones/extensiones verificadas en un laboratorio local.
| Archivo | Demuestra | Verificado |
|---|
flag-free/00-reachability-flagfree.html | desencadena el estado confuso; se activa en 146 vulnerable, no en 147 parcheado | 6/15 por carga |
flag-free/01-primitives-flagfree.html | addrof / fakeobj (fakeobj(addrof(o))===o) | 8/12 |
flag-free/02-store-flagfree.html | almacenamiento fuera de cage coloca un valor elegido en un objeto elegido | 12/12 |
flag-free/03-arw-flagfree.html | en-cage read64 / write64 (array PACKED_DOUBLE falso) | 2/12 (inestable, reintentar) |
| Archivo | Demuestra | Verificado |
|---|
rce-no-aslr/exploit-calc.html | abre una ventana real de xcalc mediante execve desde el renderizador | aparece en ~1s en un estado limpio |
rce-no-aslr/exploit-exit.html | el renderizador ejecuta el shellcode inyectado para exit(66); #jmp hace girar RIP dentro de él | exit(66) 8/8 (strace) |