
CVE-2024-29943, mas com wasm
Uma cadeia de exploração ponta a ponta para CVE-2024-29943 que alcança
execução arbitrária de código nativo ao contrabandear shellcode para a página
de código JIT do WebAssembly do SpiderMonkey como imediatos f64.const — sem
ROP, sem VirtualProtect, sem toolchain externa.
Verificado como funcional contra o shell do SpiderMonkey da nightly do
mozilla-central de 2024-03-20 (JavaScript-C126.0a1, linux x86-64, pré-correção):
a cadeia executa até o fim e o payload é executado
(write(1, "PWNED by wasm!\n"); exit(0)).
CVE-2024-29943 é um bug sec-critical de análise de intervalos do IonMonkey usado no Pwn2Own 2024 por Manfred Paul, afetando Firefox < 124.0.1 (Bugzilla 1886849, CVSS 9.8).
MObjectKeysLength::computeRange retornava um intervalo inteiro incorreto para
Object.keys(x).length depois que Object.keys passou a ser elidível
(regressão introduzida pelo bug 1845728). A análise de intervalos do Ion
concluiu que um contador de laço não poderia ficar negativo e eliminou
verificações de limites que ainda eram alcançáveis, produzindo uma
leitura/escrita fora dos limites em um .
Uint8ArrayObject.keys range-analysis bug -> OOB r/w on Uint8Array
-> corrupt adjacent ArrayBuffer -> addrof / fakeobj / arbitrary R/W
-> instantiate hand-built WASM module (shellcode as f64.const immediates)
-> WasmInstanceObject -> wasm::Instance -> wasm::Code -> CodeTier
-> ModuleSegment.bytes_ (the JIT code page, RX)
-> scan page for marker constant
-> overwrite FuncExport.eagerInterpEntryOffset_ (plain heap)
with the shellcode's offset within the page
-> call the export through the interpreter -> payload runs
Dois detalhes custaram um segfault cada para descobrirmos, então estão registrados aqui:
eagerInterpEntryOffset_ no FuncExport residente no heap — o
interpretador calcula codeBase + offset e salta para lá.A técnica de shellcode na página JIT do WASM vem do WasmBlazeFox; este repositório demonstra que a técnica se compõe com um bug moderno de navegador em produção no lugar do bug de treinamento original de 2018.
poc.js — disparo mínimo para o bug de análise de intervalos.exploit.js — cadeia completa: primitivas + estágio de shellcode no JIT do WASM.gen_wasm.py — monta o módulo WASM embutindo o shellcode e verifica o
round-trip da constante. python3 gen_wasm.py mysc.bin para usar um payload
diferente. O payload padrão é uma prova de pura syscall
write(1, "PWNED by wasm!\n"); exit(0) que não precisa de resolução de
símbolos (então também funciona no jsshell puro). Para o payload clássico
baseado em libxul system("gnome-calculator"), veja WasmBlazeFox ex6.test.gdb — breakpoints e auxiliares ptype /o para derivar offsets do
modelo de objetos em uma build específica.afbdf6822c9e9f9b6d44b9ea6904cb10878126b1 (Firefox ~124, pré-124.0.1),
Linux x86-64 — ou pegue um jsshell nightly pré-correção, por exemplo
archive.mozilla.org/pub/firefox/nightly/2024/03/2024-03-20-21-16-35-mozilla-central/jsshell-linux-x86_64.zip.LD_LIBRARY_PATH=<jsshell dir> ./js --no-threads --ion-offthread-compile=off \
--spectre-mitigations=off poc.js # trigger only (segfault)
LD_LIBRARY_PATH=<jsshell dir> ./js --no-threads --ion-offthread-compile=off \
--spectre-mitigations=off exploit.js # full chain ("PWNED by wasm!")
--spectre-mitigations=off é necessário porque a eliminação da verificação de
limites depende de o mascaramento de índice estar desabilitado (veja os
comentários no Bugzilla).
f64.const são
emitidos inline no segmento de código. A compilação Ion poderia dobrá-los
como constantes.WASM_INSTANCE_OFF_CODE = 0xa8, MetadataTier.funcExports + 448,
FuncExport + 8, etc.) foram derivados dos headers do commit vulnerável
e confirmados contra o jsshell nightly de 2024-03-20; rederive com
test.gdb para outras builds.