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-2024-4947-PoC — Educational proof-of-concept for CVE-2024-4947, a V8 Maglev type confusion, demonstrating a full chain from trigger to arbitrary code execution on a non-sandboxed d8 build. | Kitploit
Tools/GitHubGitHub/l1m3syc/cve-2024-4947-poc
Vulnerability AnalysisExploitationWeb Application ExploitationPapers & ResearchLearning & EducationBinary Exploitation
GitHubl1m3syc/cve-2024-4947-poc

CVE-2024-4947-PoC

Educational proof-of-concept for CVE-2024-4947, a V8 Maglev type confusion, demonstrating a full chain from trigger to arbitrary code execution on a non-sandboxed d8 build.

View Repository
4 days 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-2024-4947 — V8 Maglev Type Confusion → Full RCE (d8 PoC)

A complete, self-contained exploitation chain for CVE-2024-4947 (V8 Maglev type confusion), from the initial type-confusion trigger all the way to arbitrary code execution. The PoC runs against a non-sandboxed d8 build and prints CVE-2024-4947-PWNED to stdout as proof of code execution.

⚠️ This is a research / educational PoC for a patched, public vulnerability. It targets a development build of the V8 shell (d8 --allow-natives-syntax) — not a real Chrome browser. See Disclaimer.


TL;DR

root@kitploit:~
$ ./v8-build-nosandbox.sh            # build the vulnerable d8 (WSL2 Ubuntu, ~10-20 min)
$ d8 --allow-natives-syntax --module exploit/exploit_rce.mjs
[engine] dblData0=0004f470 class=0
[inst] addr=0x001dc274 trusted_data(tagged)=0x00202bd5 td=0x00202bd4
[jt]   jump_table_start = 0x00000967426cd000 (external code space)
[bridge] memory0_start -> jt, memory0_size -> huge  (r/w reach jt+off)
[slot0] before: e9 3b 08 00 00
[shell] wrote 52 bytes @ jt+0x0100: VERIFIED
CVE-2024-4947-PWNED
$ echo $?   # 0

The 52-byte shellcode is write(1, "CVE-2024-4947-PWNED\n", 20); exit_group(0).


The vulnerability

CVE-2024-4947 is a type confusion in the V8 Maglev JIT compiler, fixed in Chrome 125.0.6422.60 (commit b3c01ac1e60a). It was exploited in the wild by the Lazarus APT group. When Maglev compiles a store to a module namespace object (JSModuleNamespace), it uses an incorrect AccessInfo — the store is compiled as a plain mov [[obj + 4], rax] that writes a controlled value into the map field of an adjacent object instead of going through the proper property-store path.

The exploitation proceeds by:

  1. Corrupting an object's map to a fake NAME_DICTIONARY_TYPE (0xB2) map, which changes where V8 stores the object's hash.
  2. Triggering new WeakRef(...) so the hash write lands in an adjacent object's length slot → out-of-bounds access.

From there we get the classic V8 toolkit: addrOf/fakeObj, then arbitrary in-cage read/write.


Exploit chain

root@kitploit:~
CVE-2024-4947 trigger (fake NAME_DICTIONARY map + WeakRef hash write)
  └─► OOB write ─► corrupt doubleArray length
        └─► in-cage 4/8-byte arbitrary R/W            (the "engine")
              └─► overwrite WasmTrustedInstanceData.memory0_start (+0x18)
                    & memory0_size (+0x20) → huge
                    └─► wasm load8_u / store8 = clean 64-bit R/W bridge
                          (no software bounds check in compiled code)
                          └─► read jump_table_start (+0x38) — external code space
                                └─► jump table region is RWX (this build)
                                      └─► write shellcode into the slack (jt+0x100)
                                            └─► repoint func0's `e9 rel32` slot at it
                                                  └─► call func0 → shellcode → RCE

Step-by-step

#StageDetail
1Triggeropt() writes a fake map via the confused store; new WeakRef(m) OOBs corruptArray.length.
2EngineaddrOf/fakeObj; corrupt doubleArray's length → arbitrary 4-byte R/W at any 4-aligned caged address.
364-bit bridgeWasmTrustedInstanceData.memory0_start (+0x18) is a raw 64-bit pointer used by compiled wasm i32.load8_u/i32.store8 with no software bounds check (out-of-range hits a guard page → SIGSEGV → wasm trap). Redirect it to any address + set memory0_size (+0x20) huge → arbitrary 64-bit R/W.
4Find code spacejump_table_start (+0x38) is a raw 64-bit pointer into the external code space (outside the 4 GB cage).
5Write shellcodeOn this build the jump-table region is RWX (V8 patches entries at runtime): write the 52-byte shellcode into the slack at jt+0x100.
6Repoint slotfunc0's jump-table slot is a 5-byte e9 <rel32> (target = slot + 5 + rel32). Set rel32 → jt+0x100.
7Trigger dispatchinst.exports.r(0) → JSToWasmWrapper dispatches via slot 0 → shellcode executes.

Differences from other public PoCs

Most public CVE-2024-4947 PoCs target the sandboxed d8 or a real Chrome renderer (v8_enable_sandbox=true) and stop at the type-confusion / OOB primitive. This PoC targets a no-sandbox d8 and carries the chain all the way to code execution. The differences that matter:

DimensionCommon public-PoC approachThis PoC
Target buildsandboxed d8 / Chrome rendererv8_enable_sandbox=false d8 — trusted pointers are direct, external code space is on
64-bit R/W bridgeoverwrite JSTypedArray.external_pointerThat path crashes when reading the code range (verified empirically); instead overwrite memory0_start and use raw wasm load/store
Final code-exec routecode space is W^X → JIT-spray + indirect redirectjump-table region is RWX → direct shellcode write + e9 rel32 slot patch
Jump-table slot formatdocs commonly assume movabs rax, imm64; jmp rax (12 bytes)measured: 5-byte e9 <rel32>, target = slot + 5 + rel32
Field offsetssandboxed-build layoutno-sandbox WasmTrustedInstanceData: jump_table_start@+0x38, memory0_start@+0x18, memory0_size@+0x20; WasmInstanceObject.trusted_data@+0x0c
Clean exit—d8 is multi-threaded: must use exit_group (231), not exit (60), or the process hangs

See docs/walkthrough.md for the full technical write-up, including the empirical findings behind each row.


Repository layout

root@kitploit:~
.
├── exploit/
│   ├── Module.mjs           # module namespace object corrupted by the trigger
│   ├── exploit_rce.mjs      # the full chain (trigger → arbitrary R/W → RCE)
│   └── shellcode.S          # assembly source for the 52-byte payload
├── build/
│   └── v8-build-nosandbox.sh # build the vulnerable no-sandbox d8 from V8 source
└── docs/
    └── walkthrough.md       # deep dive: bridge mechanics, layouts, gotchas

The exploit imports Module.mjs (the vulnerable module namespace object) from its own directory, so keep the two files together (or adjust the import path).


Build & run

Requirements

  • WSL2 (Ubuntu) or any Linux with a C/C++ toolchain, git, and ~10 GB free disk
  • Google depot_tools (git clone https://chromium.googlesource.com/chromium/tools/depot_tools)
  • V8 source at the vulnerable revision (see below)

Build the vulnerable d8

root@kitploit:~
# 1. fetch V8 at the vulnerable tag (12.4.254.16 is the pre-fix release)
export PATH="$HOME/depot_tools:$PATH"
cd ~/v8w && fetch v8 && cd v8
git checkout 12.4.254.16          # or the commit just before b3c01ac1e60a

# 2. first build a normal release d8 (needed to seed args.gn), then:
./v8-build-nosandbox.sh           # copies args.gn and appends v8_enable_sandbox = false

v8-build-nosandbox.sh uses ninja -C out.gn/x64.release_nosandbox -j6 d8.

Run

root@kitploit:~
out.gn/x64.release_nosandbox/d8 --allow-natives-syntax --module exploit/exploit_rce.mjs

Expected output ends with:

root@kitploit:~
CVE-2024-4947-PWNED

and the process exits with status 0.

--allow-natives-syntax is required for the %PrepareFunctionForOptimization / %OptimizeMaglevOnNextCall runtime calls. This is why the PoC cannot run in a real browser — it is a d8 shell proof-of-concept by design.


Reusable techniques

The genuinely reusable (and non-obvious) pieces for V8 exploitation research:

  1. Wasm memory-redirect 64-bit R/W bridge — when JSTypedArray.external_pointer redirection fails on a target region, redirect WasmTrustedInstanceData.memory0_start instead. Compiled wasm byte loads/stores carry no software bounds check; the signal trap handler converts only guard-page faults, so mapped-but-non-writable regions crash rather than trap.
  2. Jump-table writability probing — the wasm jump-table region's protection varies by build. Probe it byte-by-byte with try/catch; a wasm "out of bounds" trap means a guard page (unmapped), a real SIGSEGV means mapped-but-RX.
  3. e9 rel32 slot format — 5-byte relative jump, not the movabs form assumed by many write-ups. Verified against --print-wasm-code disassembly.

Disclaimer

This repository is provided for educational and defensive-security research only. It demonstrates exploitation of a patched vulnerability against a development-only V8 build. It is not a weapon against modern Chrome, does not bypass the V8 sandbox, and requires the non-default --allow-natives-syntax flag. The author is not responsible for any misuse.

Credits / lineage

The trigger primitive (fake NAME_DICTIONARY_TYPE map + WeakRef hash write) follows the public analyses of the in-the-wild Lazarus exploit published after the patch — including Google's and Exodus Intelligence's CVE-2024-4947 write-ups. The later stages (jump-table probing, bridge, slot patching) were derived empirically against this specific build during this work. The JIT-spray fallback concept draws on public V8 exploit techniques (e.g. the CVE-2024-5830 make_array pattern).

License

MIT — see LICENSE.

Download Tool