
CVE-2024-4947에 대한 교육용 proof-of-concept로, V8 Maglev 타입 혼동을 다루며, 샌드박스가 없는 d8 빌드에서 트리거부터 임의 코드 실행까지의 전체 체인을 시연합니다.
CVE-2024-4947(V8 Maglev 타입 혼동)에 대한 완전하고 자체 포함된(self-contained) 익스플로잇 체인으로, 초기 타입 혼동 트리거에서부터 임의 코드 실행에 이르기까지 전체를 아우릅니다. 이 PoC는 비-샌드박스(non-sandboxed) d8 빌드를 대상으로 실행되며, 코드 실행 증명으로 CVE-2024-4947-PWNED를 stdout에 출력합니다.
⚠️ 이는 패치된 공개 취약점에 대한 연구/교육용 PoC입니다. V8 셸의 개발 빌드(
d8 --allow-natives-syntax)를 대상으로 하며 — 실제 Chrome 브라우저가 아닙니다. 면책 조항을 참조하세요.
$ ./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
52바이트 셸코드는 write(1, "CVE-2024-4947-PWNED\n", 20); exit_group(0)입니다.
CVE-2024-4947은 V8 Maglev JIT 컴파일러의 타입 혼동 취약점으로, Chrome 125.0.6422.60(커밋 b3c01ac1e60a)에서 패치되었습니다. Lazarus APT 그룹에 의해 실제 환경(in the wild)에서 악용되었습니다. Maglev가 모듈 네임스페이스 객체(JSModuleNamespace)에 대한 스토어를 컴파일할 때 잘못된 AccessInfo를 사용합니다. 즉, 스토어가 올바른 속성-저장(property-store) 경로를 거치지 않고, 인접 객체의 map 필드에 제어된 값을 쓰는 단순한 mov [[obj + 4], rax]로 컴파일됩니다.
악용은 다음과 같이 진행됩니다:
NAME_DICTIONARY_TYPE (0xB2) map으로 손상시켜, V8이 객체의 해시를 저장하는 위치를 변경합니다.new WeakRef(...)를 트리거하여 해시 쓰기가 인접 객체의 length 슬롯에 떨어지게 합니다 → 경계를 벗어난(out-of-bounds) 접근.여기서부터 고전적인 V8 툴킷, 즉 addrOf/fakeObj, 그리고 임의 in-cage 읽기/쓰기를 얻습니다.
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
| # | 단계 | 세부 내용 |
|---|---|---|
| 1 | 트리거 | opt()가 혼동된 스토어를 통해 가짜 map을 씁니다. new WeakRef(m)이 corruptArray.length를 OOB시킵니다. |
| 2 | 엔진 | addrOf/fakeObj; doubleArray의 length를 손상시켜 4-정렬된 모든 cage 주소에 임의 4바이트 R/W를 수행합니다. |
| 3 | 64비트 브리지 | WasmTrustedInstanceData.memory0_start(+0x18)은 컴파일된 wasm i32.load8_u/i32.store8이 사용하는 raw 64비트 포인터이며 소프트웨어 경계 검사가 없습니다(범위를 벗어나면 가드 페이지에 부딪혀 → SIGSEGV → wasm 트랩). 이를 임의 주소로 리다이렉트하고 memory0_size(+0x20)를 크게 설정하면 → 임의 64비트 R/W. |
| 4 | 코드 공간 찾기 | jump_table_start(+0x38)은 외부 코드 공간(4 GB cage 밖)을 가리키는 raw 64비트 포인터입니다. |
| 5 | 셸코드 쓰기 | 이 빌드에서 점프-테이블 영역은 RWX입니다(V8이 런타임에 엔트리를 패치함): 52바이트 셸코드를 jt+0x100의 여유 공간(slack)에 씁니다. |
| 6 | 슬롯 재지정 | func0의 점프-테이블 슬롯은 5바이트 e9 <rel32>입니다(target = slot + 5 + rel32). rel32를 jt+0x100으로 설정합니다. |
| 7 | 디스패치 트리거 | inst.exports.r(0) → JSToWasmWrapper가 slot 0을 통해 디스패치 → 셸코드 실행. |
대부분의 공개 CVE-2024-4947 PoC는 샌드박스 d8 또는 실제 Chrome 렌더러(v8_enable_sandbox=true)를 대상으로 하며, 타입 혼동/OOB 프리미티브까지만 진행합니다. 이 PoC는 노-샌드박스(no-sandbox) d8을 대상으로 하며 체인을 코드 실행까지 끝까지 이어갑니다. 중요한 차이점은 다음과 같습니다:
| 차원 | 일반적인 공개 PoC 방식 | 이 PoC |
|---|---|---|
| 대상 빌드 | 샌드박스 d8 / Chrome 렌더러 | v8_enable_sandbox=false d8 — trusted 포인터가 직접(direct)이며 외부 코드 공간이 활성화됨 |
| 64비트 R/W 브리지 | JSTypedArray.external_pointer 덮어쓰기 | 해당 경로는 코드 범위를 읽을 때 크래시가 발생합니다(실증적으로 확인됨); 대신 **memory0_start**를 덮어쓰고 raw wasm load/store를 사용 |
| 최종 코드 실행 경로 | 코드 공간은 W^X → JIT-스프레이 + 간접 리다이렉트 | 점프-테이블 영역은 RWX → 직접 셸코드 쓰기 + e9 rel32 슬롯 패치 |
| 점프-테이블 슬롯 형식 | 문서들은 일반적으로 movabs rax, imm64; jmp rax(12바이트)를 가정 | 측정 결과: 5바이트 e9 <rel32>, target = slot + 5 + rel32 |
| 필드 오프셋 | 샌드박스 빌드 레이아웃 | 노-샌드박스 WasmTrustedInstanceData: jump_table_start@+0x38, memory0_start@+0x18, memory0_size@+0x20; WasmInstanceObject.trusted_data@+0x0c |
| 깨끗한 종료 | — | d8은 멀티스레드입니다: exit(60)가 아닌 exit_group(231)을 사용해야 하며, 그렇지 않으면 프로세스가 멈춥니다 |
각 행 뒤에 있는 실증적 발견 사항을 포함한 전체 기술 문서는 docs/walkthrough.md를 참조하세요.
.
├── 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
익스플로잇은 Module.mjs(취약한 모듈 네임스페이스 객체)를 자신의 디렉터리에서 import하므로, 두 파일을 함께 두십시오(또는 import 경로를 조정하십시오).
git, 약 10GB 여유 디스크가 있는 모든 Linuxdepot_tools (git clone https://chromium.googlesource.com/chromium/tools/depot_tools)# 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는 ninja -C out.gn/x64.release_nosandbox -j6 d8를 사용합니다.
out.gn/x64.release_nosandbox/d8 --allow-natives-syntax --module exploit/exploit_rce.mjs
예상 출력은 다음과 같이 끝납니다:
CVE-2024-4947-PWNED
그리고 프로세스는 상태 0으로 종료됩니다.
%PrepareFunctionForOptimization/%OptimizeMaglevOnNextCall런타임 호출에는--allow-natives-syntax가 필요합니다. 이것이 이 PoC가 실제 브라우저에서 실행될 수 없는 이유입니다 — 설계상 d8 셸 개념 증명(PoC)이기 때문입니다.
V8 익스플로잇 연구에 실제로 재사용 가능한(그리고 쉽게 알기 어려운) 부분들:
JSTypedArray.external_pointer 리다이렉션이 실패하면 대신 WasmTrustedInstanceData.memory0_start를 리다이렉트하십시오. 컴파일된 wasm 바이트 load/store에는 소프트웨어 경계 검사가 없습니다. 시그널 트랩 핸들러는 가드 페이지 폴트만 변환하므로, 매핑되었지만 쓰기 불가능한 영역은 트랩이 아닌 크래시로 이어집니다.try/catch로 바이트 단위로 프로빙하십시오. wasm "out of bounds" 트랩은 가드 페이지(매핑 안 됨)를 의미하고, 실제 SIGSEGV는 매핑되었지만 RX 상태를 의미합니다.e9 rel32 슬롯 형식 — 많은 라이트업이 가정하는 movabs 형식이 아닌 5바이트 상대 점프(relative jump)입니다. --print-wasm-code 디스어셈블리로 검증되었습니다.이 저장소는 교육 및 방어적 보안 연구 목적으로만 제공됩니다. 개발 전용(development-only) V8 빌드를 대상으로 패치된 취약점의 악용을 시연합니다. 이는 현대 Chrome을 공격하는 무기가 아니며, V8 샌드박스를 우회하지 않고, 기본값이 아닌 --allow-natives-syntax 플래그를 필요로 합니다. 저자는 어떠한 오용에 대해서도 책임지지 않습니다.
트리거 프리미티브(가짜 NAME_DICTIONARY_TYPE map + WeakRef 해시 쓰기)는 패치 이후 공개된 in-the-wild Lazarus 익스플로잇에 대한 공개 분석을 따릅니다. 여기에는 Google 및 Exodus Intelligence의 CVE-2024-4947 라이트업이 포함됩니다. 이후 단계(점프-테이블 프로빙, 브리지, 슬롯 패치)는 이 작업 중 이 특정 빌드를 대상으로 실증적으로 도출되었습니다. JIT-스프레이 폴백 개념은 공개된 V8 익스플로잇 기법(예: CVE-2024-5830 make_array 패턴)을 활용합니다.
MIT — LICENSE를 참조하세요.