
iOS 26.6의 XNU vfs_attr_pack_internal(getattrlist)에서 발생하는 경계를 벗어난 쓰기(Out-of-Bounds Write)에 대한 개념 증명(PoC)으로, 커널 힙 손상과 PAC 포인터 위조를 위한 익스플로잇 노트를 시연합니다.
vfs_attr_pack_internal의 getattrlist OOB 쓰기구성 요소: XNU VFS — vfs_attr_pack_internal (getattrlist syscall)
영향: iOS / iPadOS 26.6 (23G71) 및 이전 버전
수정 버전: iOS / iPadOS 26.6.1 (23G83)
유형: 호출자 버퍼에 대한 Out-of-Bounds 쓰기
영향: 커널 힙 손상; 커널 컨텍스트(커널 r/w 이후)에서 PAC 서명 포인터를 공격자 제어 버퍼에 쓸 수 있음
익명의 연구원이 발견
(Apple 보안 권고 — iOS 26.6.1 참조)
vfs_attr_pack_internal은 요청된 속성 비트맵을 반복하며 각 속성을 호출자의 버퍼에 순차적으로 씁니다. 속성 슬롯 51(SETXATTR_SLOT)을 쓰기 전에 현재 출력 오프셋이 bufsize를 초과했는지 확인하지 않습니다.
16자 파일 이름의 경우, 유효한 전체 속성 출력은 0x1a4(420) 바이트입니다. 슬롯 51은 오프셋 0x198(408바이트)에서 시작합니다. 호출자가 0x198 + 8보다 작은 버퍼를 제공하면, 커널은 버퍼 끝 너머로 8바이트를 씁니다:
; vfs_attr_pack_internal (영향 경로, 26.6 / 23G71)
; 슬롯 51 쓰기 — 저장 전 경계 검사 없음
str x_attr_data, [buf + 0x198] ; bufsize < 0x198 + 8인 경우 OOB
기기 내 프로브(iOS 26.6에서 16자 파일 이름에 대한 getattrlist)로 검증된 유효 속성 마스크:
| 필드 | 마스크 |
|---|---|
commonattr | 0xffe7ffff (이 커널에서는 비트 19, 20이 유효하지 않음) |
fileattr | 0x0000363f |
forkattr | 0xffffe003 |
16자 파일 이름에 이러한 마스크를 적용하면 총 출력 = 0x1a4 바이트입니다. bufsize < 0x1a0일 때마다 슬롯 51(buf+0x198)에서 OOB가 발생합니다.
iOS 26에서 KHEAP_TEMP 격리로 인해 OOB 쓰기가 사용자 공간에서 인접한 힙 할당을 유용하게 직접 손상시킬 수 없습니다. 이 연구에서는 이 버그를 커널 힙 전용으로 분류합니다:
vfs_attr_pack_internal을 트리거.iOS 26.6 탈옥 체인에서 이 버그는 최종 단계에서 호출자 버퍼에 인접한 커널 할당에 PACIA 위조 포인터를 쓰는 데 사용됩니다.
poc_getattrlist_oob.c는 세 번의 호출로 OOB 경계를 시연합니다:
PoC는 테스트 대상으로 16자 파일 이름(/var/root/aaaaaaaaaaaaaaaa)을 생성하고, 세 번의 getattrlist() 호출을 모두 실행한 후 반환 코드를 출력합니다. 커널 패닉 로그(트리거된 경우)는 buf+0x198에서 오류를 표시합니다.
/var/root/ 쓰기 권한(16자 테스트 파일 생성용; 경로는 쓰기 가능한 위치로 조정 가능)# 독립형 C 바이너리 — 프레임워크 불필요
clang -arch arm64 -o poc poc/poc_getattrlist_oob.c
# 기기 내 실행용:
clang -arch arm64 -isysroot $(xcrun --sdk iphoneos --show-sdk-path) \
-o poc poc/poc_getattrlist_oob.c
codesign -s "Apple Development" poc
| 날짜 | 이벤트 |
|---|---|
| 2026-08-17 | 수정 사항이 포함된 iOS 26.6.1 출시 |
| 2026-08-17 | Apple 보안 권고에 크레딧 게시 |
| 호출 | 버퍼 크기 | 예상 결과 |
|---|
| 1 | 64바이트 | 커널이 0x1a4바이트를 씀 → buf+0x198에서 OOB (끝에서 344B 초과) |
| 2 | 0x198바이트 | OOB 쓰기가 버퍼 끝에서 정확히 시작 |
| 3 | 0x1a4바이트 | 정확한 유효 크기 → 정상 성공 |