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-84118-who-labeled-the-crit-as-a-high — Proof-of-concept for CVE-2026-84118, a SpiderMonkey GC use-after-free leading to out-of-bounds read/write and potential code execution. Includes deterministic SEGV trigger and research on escalation to arbitrary read and ASLR defeat. | Kitploit
Tools/GitHubGitHub/sneakynachos/cve-2026-84118-who-labeled-the-crit-as-a-high
Memory ForensicsVulnerability AnalysisExploitationWeb SecurityBinary Exploitation
GitHubsneakynachos/cve-2026-84118-who-labeled-the-crit-as-a-high

CVE-2026-84118-who-labeled-the-crit-as-a-high

Proof-of-concept for CVE-2026-84118, a SpiderMonkey GC use-after-free leading to out-of-bounds read/write and potential code execution. Includes deterministic SEGV trigger and research on escalation to arbitrary read and ASLR defeat.

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share
View Repository
111h 1m agoNot yet reviewed

CVE-2026-84118 — SpiderMonkey GC use-after-free (Bug 2057457)

This is one of those where Mozilla labeled a Critical as a High. So someone might want to go have Firefox update the original bug hunters payment if that's the case.

THE TLDR: You can do an OOB R primitive on a controlled pointer and then start going up and down from that pointer. So if you point the controlled pointer at an array then congrats you can do fun things.

The POC only has the SEGV part, so the rest is a fun adventure into pointing into an array and jumping into WASM of your choosing.

Deterministic release-build SEGV proof-of-concept for Firefox/SpiderMonkey 154.0.1. Fixed in 155 (AtomMarking rework). Mozilla severity: HIGH.

Running the PoC

root@kitploit:~
/path/to/release/js poc.js

Reproduced on 154.0.1: 3/3 on a plain optimized shell (SIGSEGV, exit 139), 5/5 on a release+gczeal shell, identical fault every run:

root@kitploit:~
SEGV on address 0x000000000014 (READ)
JS::shadow::Zone::gcState()  <- zone == nullptr, +0x14 offset

Root cause

154 conflates per-zone atom mark bitmaps with reference tracking. A Symbol held only weakly — here, captured as a weak Value field inside a Baseline CacheIR IC stub — and referenced from an uncollected zone is never re-marked when the atoms zone is collected in a zone-scoped GC. The result is a stale edge to a swept/relocated cell.

The release-build kill shot is a second, static bug: MightBeForwarded<JS::Symbol> is hardcoded false, so IsForwarded returns false without checking (gc/Marking-inl.h:92-97) and every pointer-fixup path gated on it silently skips the edge. When the atoms zone is compacted, strong edges are updated correctly, but this weak CacheIR edge is left permanently dangling at the victim Symbol's old address. (Debug builds catch it as Assertion failure: !t->isForwarded(), gc/Marking-inl.h:94.)

How the PoC turns the dangling edge into a SEGV

The PoC does not just leave a stale pointer to reusable heap — it gets the backing page munmapped, so the very next dereference faults.

  1. Per-zone chunk isolation. GC chunk pools are per-zone (Zone::availableChunks/fullChunks), so atoms-zone chunks contain only atoms-zone arenas. A chunk's fate can be controlled purely by atom/symbol lifetimes.
  2. Heap grooming. 300 "magnet" Symbols (kept alive) occupy an early chunk and serve as the compaction destination. 150,000 disposable Symbols then fill ~4 fresh 1 MB chunks, so the victim Symbol lands mid-spray in a chunk containing only doomed spray symbols.
  3. The weakly-held-symbol dance (lines 21-31, bytecode-fragile — must stay verbatim): c.eval(b) makes the top-level call IC capture the Symbol in a weak stub field; b = undefined drops the last strong root; the spray is dropped; two zone-scoped GCs (gc("zone"), schedulezone(c) + schedulezone("") + gc("zone") — the empty string schedules the atoms zone) sweep the dead spray, compact the victim into the magnet arena, skip the weak-edge fixup, and free the victim's arena. Its chunk is now completely empty.
  4. munmap. gcparam("minEmptyChunkCount", 0) makes every empty chunk expirable; during sleep(0.5) the BackgroundDecommitTask runs -> -> — a true munmap of the victim chunk. (Arena-level decommit is only — readable zeros — whole-chunk release is the only munmap path, which is why chunk isolation in step 1 matters.)

Beyond the null-page crash

This is not just a DoS. Demonstrated escalations (PoCs and logs in the GC-NDAY/ and CHAIN/ research directories):

  • Attacker-chosen fault address (segv9): after the munmap, a minorgc() semispace flip makes the nursery claim the freed VA; a raw dense-double spray writes an attacker qword over the stale arena header's zone field. The sweep then dereferences attacker_ptr + 0x14 — deterministic 3/3 at 0x424242424256.
  • Verdict control + repeatable reads: the loaded value selects the weak-edge liveness verdict; a "LIVE" verdict keeps the dangling edge and re-dereferences it on every subsequent major GC (attacker-chosen reads
    • a 1-bit script-observable oracle). A "DEAD" verdict self-heals.
  • Two-bug chain: a real js::Zone* leaked via a separate speculative side channel was planted through this refill; the collector consumed the forged arena metadata without faulting (a 1-bit-perturbed control plant crashes), demonstrating ASLR-defeat -> UAF-plant composition.
  • Bounded negative: direct fakeobj through this stub family is architecturally blocked — compaction fixes strong edges correctly, only weak edges dangle, and their sweep consumers are branch-only.
Download Tool
expireEmptyChunkPool
FreeChunkPool
UnmapPages
madvise(MADV_DONTNEED)
  • The faulting dereference. The final gc() sweeps JIT data: sweepJitDataOnMainThread -> ICEntry::traceWeak -> TraceWeakCacheIRStub reads the stale Symbol pointer, then SweepingTracer::onEdge (gc/Marking.cpp:3195) performs chunk arithmetic on the stale address to load the arena header's zone field. The page is gone/zeroed, so zone == nullptr, and zone->isGCSweeping() reads nullptr + 0x14 (shadow::Zone::gcState) -> SIGSEGV. At the fault, rcx holds the stale Symbol pointer and rdx the munmapped chunk base.