
CVE-2024-29943, aber mit wasm
Eine End-to-End-Exploit-Kette für CVE-2024-29943, die beliebige
native Codeausführung erreicht, indem Shellcode als f64.const-Immediates
in die WebAssembly-JIT-Codepage von SpiderMonkey geschmuggelt wird — kein
ROP, kein VirtualProtect, keine externe Toolchain.
Verifiziert funktionierend gegen die SpiderMonkey-Shell aus dem
Nightly von mozilla-central vom 2024-03-20 (JavaScript-C126.0a1, Linux x86-64,
Pre-Fix): Die Kette läuft bis zum Ende durch und die Payload wird ausgeführt
(write(1, "PWNED by wasm!\n"); exit(0)).
CVE-2024-29943 ist ein sec-kritischer IonMonkey-Range-Analysis-Bug, der bei Pwn2Own 2024 von Manfred Paul ausgenutzt wurde und Firefox < 124.0.1 betrifft (Bugzilla 1886849, CVSS 9.8).
MObjectKeysLength::computeRange gab einen falschen Integer-Bereich für
Object.keys(x).length zurück, nachdem elidierbar gemacht
worden war (Regression durch Bug 1845728). Die Ion-Range-Analyse kam zu dem
Schluss, dass ein Schleifenzähler nicht negativ werden könne, und eliminierte
Bounds-Checks, die weiterhin erreichbar waren, was zu einem Out-of-Bounds-
Read/Write auf einem führte.
Object.keysUint8ArrayObject.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
Zwei Details kosteten uns jeweils einen Segfault, um sie zu lernen, deshalb sind sie hier festgehalten:
eagerInterpEntryOffset_ im heap-residenten FuncExport
— der Interpreter berechnet codeBase + offset und springt dorthin.Die WASM-JIT-Page-Shellcode-Technik stammt aus WasmBlazeFox; dieses Repo demonstriert, dass die Technik mit einem modernen, in freier Wildbahn ausgelieferten Browser-Bug anstelle des ursprünglichen Training-Bugs von 2018 kombinierbar ist.
poc.js — minimaler Trigger für den Range-Analysis-Bug.exploit.js — vollständige Kette: Primitives + WASM-JIT-Shellcode-Stage.gen_wasm.py — assembliert das WASM-Modul, das Shellcode einbettet, und
verifiziert den Konstanten-Round-Trip. python3 gen_wasm.py mysc.bin für
eine andere Payload. Die Standard-Payload ist ein reiner Syscall-Beweis
write(1, "PWNED by wasm!\n"); exit(0), der keine Symbolauflösung
benötigt (funktioniert also auch in der nackten jsshell). Für die klassische
libxul-basierte system("gnome-calculator")-Payload siehe WasmBlazeFox ex6.test.gdb — Breakpoints und ptype /o-Helfer zum Ableiten von
Objektmodell-Offsets für einen gegebenen Build.afbdf6822c9e9f9b6d44b9ea6904cb10878126b1 (Firefox ~124, pre-124.0.1),
Linux x86-64 — oder hole eine Pre-Fix-Nightly-jsshell, z. B.
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 ist erforderlich, weil die Bounds-Check-
Eliminierung darauf beruht, dass Index-Masking deaktiviert ist (siehe die
Bugzilla-Kommentare).
f64.const-Immediates inline im
Code-Segment emittiert werden. Die Ion-Kompilierung könnte sie
constant-falten.WASM_INSTANCE_OFF_CODE = 0xa8, MetadataTier.funcExports + 448,
FuncExport + 8, usw.) wurden aus den Headern des verwundbaren Commits
abgeleitet und gegen die Nightly-jsshell vom 2024-03-20 bestätigt; für
andere Builds mit test.gdb neu ableiten.