Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-15718-who-put-ptrs-in-my-wasm — PoC exploit chain for CVE-2026-15718: SpiderMonkey wasm baseline compiler array.fill missing-sync -> invalid pointer -> addrOf/fakeobj -> arbitrary R/W -> RCE | Kitploit
Tools/GitHubGitHub/sneakynachos/cve-2026-15718-who-put-ptrs-in-my-wasm
Vulnerability AnalysisExploitationReverse EngineeringWeb SecurityPayload DevelopmentBinary Exploitation
GitHubsneakynachos/cve-2026-15718-who-put-ptrs-in-my-wasm

CVE-2026-15718-who-put-ptrs-in-my-wasm

PoC exploit chain for CVE-2026-15718: SpiderMonkey wasm baseline compiler array.fill missing-sync -> invalid pointer -> addrOf/fakeobj -> arbitrary R/W -> RCE

View Repository
11 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-15718 — who put ptrs in my wasm

Proof-of-concept exploit chain for CVE-2026-15718, a missing-sync() miscompilation in Mozilla SpiderMonkey's WebAssembly baseline compiler ("JavaScript: WebAssembly" component). A single zero-trip array.fill yields a fakeobj primitive; the chain escalates to arbitrary read/write of the host process and arbitrary native code execution (posix_spawnp("/bin/sh", ...)).

  • CVE: CVE-2026-15718 (CWE-763, release of invalid pointer)
  • Affected: Firefox < 152.0.6, Firefox ESR < 140.13, Thunderbird < 140.13
  • Fixed in: Firefox 152.0.6 / ESR 140.13 / Thunderbird 140.13, 2026-07-14
  • Upstream bug: Mozilla Bug 2045443 (MFSA-2026-67)
  • Fix commit: 4441102e62e8 ("Bug 2045443", one line)

Root cause

BaseCompiler::emitArrayFill() (js/src/wasm/WasmBaselineCompile.cpp) emits the fill loop without first calling :

array.fill
sync()
root@kitploit:~
   if (elementType.isRefRepr()) {
     freePtr(RegPtr(PreBarrierReg));
   }

+  sync();
+
   // Perform the fill loop using `numElements` as the loop variable ...

sync() flushes the baseline compiler's value stack (stk_) from registers to the machine stack. The fill loop's per-iteration imprecise post-barrier (emitPostBarrierEdgeImprecise) calls sync() internally — but only if the loop runs. The loop's exit label (done) is a join of two paths:

  • loop ran at least once (reftype elements): stk_ is synced — every operand lives in a machine-stack slot;
  • zero-trip (numElements == 0): stk_ is untouched — operands still live only in registers.

The compiler's static model after the join always reflects the synced path, so code emitted after array.fill reads operands from stack slots that a zero-trip execution never wrote. A reftype operand read back this way is an invalid (stale) pointer (CWE-763).

Primitive: plant/read slot technique

The desync is bidirectional across two frame-identical wasm functions:

  • plant (n = 1, loop runs): the in-loop sync() writes a value into the slot — a real ref (addrOf) or a controlled i64 (fakeobj setup);
  • read (n = 0, zero-trip): the slot is never written; the twin function reads the stale slot back — as i64 (leak) or as (ref $t) (fakeobj).

Exploit chain (poc/)

FileStageResult
poc-min.js (+ gen_trigger.py)desync democontrol path returns the correct value; zero-trip path returns a garbage pointer read from the never-written slot
chain.js (+ gen_chain.py, chain.tpl.js, build.sh)full chainaddrOf + fakeobj → fake WasmArrayObject → arbitrary R/W → XUL base leak → runtime Mach-O import walk → forged funcref → posix_spawnp("/bin/sh", ["-c", "touch /tmp/CVE-2026-15718-PWNED"])

Stage details in chain.js:

  1. plantSpray/plantCmd/plantFun/plantExt + leak — addrOf for wasm arrays, funcrefs and JS objects (externref).
  2. sprayV + fakeRd/fakeWr — fakeobj; fake WasmArrayObject (numElements_ @+16, data_ @+24, inline elements @+40) with the fake window kept in an out-of-line (malloc'd) array so GC cannot move it mid-scan.
  3. Leak a JSFunction's native entry (+0x20), scan back page-wise for the Mach-O magic (0xfeedfacf) → XUL base.
  4. Fully dynamic symbol resolution: walk XUL's Mach-O load commands via the arbitrary read (LC_SEGMENT_64/LC_SYMTAB/LC_DYSYMTAB + indirect symbol table) to locate _posix_spawnp's __stubs entry. No hardcoded offsets; the stub performs lazy binding on first call.
  5. Forge a funcref (typeDef @+0x40 copied from a real ref.func, call target @+0x38 = stub address) and invoke it through call_ref with posix_spawnp(pid, "/bin/sh", 0, 0, [sh, -c, cmd], 0).

Running

Tested with an xpcshell (or the js shell / browser) built from a pre-152.0.6 tree (the reference build here is Firefox 149.0a1, 2026-01-14):

root@kitploit:~
# minimal desync demo
xpcshell poc/poc-min.js

# full chain (regenerates chain.js from the template + generator)
(cd poc && sh build.sh)
rm -f /tmp/CVE-2026-15718-PWNED
xpcshell poc/chain.js    # creates /tmp/CVE-2026-15718-PWNED
ls -l /tmp/CVE-2026-15718-PWNED

The harness sets javascript.options.wasm_optimizingjit=false to pin the baseline tier during the long Mach-O scan (the bug is baseline-only; in the browser the trigger runs well under the tier-up threshold so this is not needed there). tools/xul_slots.py is an offline validator that lists XUL's named stub/GOT slots (used to cross-check the runtime walker).

Portability notes

Offsets are for macOS arm64, this exact tree/build (WasmArrayObject { +16 numElements, +24 data_, +40 inline }, JSFunction native @ +0x20, WasmFuncRef { +0x38/+0x48 call target, +0x40 typeDef }). All of them are re-derived at runtime by the chain's self-tests; only the structure layouts are assumed. No PAC concerns (XUL is an arm64, non-arm64e binary); the lazy-binding stub resolves the target on first call, so no dyld-shared-cache offsets are needed.

Scope / honest limitations

This PoC runs in an unsandboxed shell process. In a real Firefox attack the chain lands inside the content process, where the macOS sandbox denies posix_spawnp — a separate OS-sandbox-escape bug is needed for a full compromise (see e.g. the stage-2 analysis in CVE-2026-2796-and-CVE-2026-2768-escape-the-wasm-box).

Mitigations

  • Update to Firefox / Thunderbird ≥ 152.0.6 (ESR ≥ 140.13).
  • Defense in depth: javascript.options.wasm=false blocks the trigger vector.

References

  • MFSA-2026-67
  • NVD: CVE-2026-15718
  • Fix: https://github.com/mozilla-firefox/firefox/commit/4441102e62e8

Disclaimer

For security research, education, and defensive testing only. The vulnerability is patched in current Firefox/Thunderbird releases. Do not use against systems you do not own or have explicit authorization to test.

Download Tool