
PD2229B의 43499(ghostlock) 타당성 연구
| 항목 | 값 |
|---|---|
| 취약점 유형 | rt_mutex / futex PI 경로의 스택 기반 Use-After-Free |
| 도입 버전 | Linux 2.6.39-rc1 (2011년 5월, commit 8161239a8bcc) |
| 패치 버전 | 메인라인 7.1 (commit 3bfdc63936dd), 각 안정 브랜치: 6.1.175 / 6.6.140 / 6.12.86 / 6.18.27 / 7.0.4 |
| 선행 조건 | CONFIG_FUTEX_PI=y (주류 커널 기본 활성화) |
| CVSS | 7.8 High |
| 익스플로잇 안정성 | NebuSec 원체인 97%, 약 5초 내 root 획득 |
| kernelCTF 현상금 | $92,337 USD |
영향받는 커널 범위:
| 항목 | 값 |
|---|---|
| SoC | Qualcomm SM8475 (Snapdragon 8 Gen 1 Plus) |
| 커널 버전 | 5.10.233-gki-g6e61de9f5b58 #1 SMP PREEMPT (Nov 24 2025) |
| Android 버전 | 15 (OriginOS 5) |
| 빌드 시간 | 2025/11/25 |
| Android 패치 | 2025/11/01 |
CONFIG_FUTEX_PI | y ✅ |
CONFIG_UNMAP_KERNEL_AT_EL0 | y (KPTI 활성화) |
kptr_restrict | enforced |
CONFIG_IO_URING | y ✅(vmlinux 심볼로 확인, seccomp가 io_uring_setup 차단하지 않음) |
1. KASLR 우회 → prefetch timing / PR_SET_MM_MAP auxv
2. UAF 트리거 → 3스레드 PI 의존성 교착 → FUTEX_CMP_REQUEUE_PI가 -EDEADLK 반환
3. 스택 재활용 → PR_SET_MM_MAP이 auxv를 waiter 스택 프레임에 복사
4. rb_erase 제한적 쓰기 → inet6_protos[IPPROTO_UDP] 덮어쓰기
5. CEA + ROP → 제어 흐름 탈취
6. core_pattern 변조 → root shell (97% 성공률)
| 단계 | 상태 | 설명 |
|---|---|---|
| 1. KASLR 우회 | ✅ 구현 완료 | perf_event_open + callchain sampling 사이드 채널 (OPPO Find X6 Pro 5.15.149 적응 방식과 동일) |
| 2. UAF 트리거 | ✅ 구현 완료 | 3스레드 PI 교착 성공, FUTEX_CMP_REQUEUE_PI가 -EDEADLK 반환 |
| 3. 스택 재활용 프리미티브 | ❌ 구조적 실패 | 알려진 모든 syscall의 스택 프레임이 waiter와 겹치지 않음 (3절 상세 참조) |
| 4. rb_erase 제한적 쓰기 | ❌ 차단됨 | 선행 조건 미충족 |
| 5. inet6_protos 덮어쓰기 | ❌ 시작 안 됨 | 4단계에 의존 |
| 6. ROP / 제어 흐름 탈취 | ❌ 시작 안 됨 | 5단계에 의존 |
| 7. root shell | ❌ 시작 안 됨 | 6단계에 의존 |
futex 경로 총 깊이:
__arm64_sys_futex 0x90
+ do_futex 0xc0
+ futex_wait_requeue_pi 0x1b0
= 0x300
waiter 위치 = SYS_SP - 0x300 + 0x20 = SYS_SP - 0x2e0
pselect 경로:
core_sys_select 스택 프레임 0x1c0, stack_fds는 sp+0x50
커버 구간: SYS_SP - 0x1c0 + 0x50 = SYS_SP - 0x170부터
waiter (SYS_SP - 0x2e0)와의 차이 0x170 (368바이트) → 겹치지 않음
| # | 방법 | 시스템 콜 | PD2229 결과 | 핵심 실패 원인 |
|---|---|---|---|---|
| 1 | stamp_prctl | PR_SET_MM_MAP | ❌ EPERM | Android 하드 차단 |
| 2 | stamp_pselect | pselect6 (NFDS=320) | ❌ 겹치지 않음 | waiter가 fd_set 아래 120B |
| 3 | stamp_pselect | pselect6 (NFDS>336) | ❌ 힙 할당 | kvmalloc 경로 |
| 4 | stamp_socket | MCAST_JOIN_SOURCE_GROUP | ❌ 커버 부족 | lock 필드만 기록 |
| 5 | stamp_sendmsg | sendmsg | ❌ waiter에서 80B | 스택 프레임 0x90 깊이 부족 |
| 6 | stamp_sendmmsg | sendmmsg | ❌ waiter에서 112B | 스택 프레임 부족 |
| 7 | stamp_recvmmsg | recvmmsg | ❌ task/lock 0으로 초기화 | 0x1d0 스택 프레임이 핵심 포인터 파괴 |
| 8 | stamp_process_vm | process_vm_writev | ❌ 겹치지 않음 | 자식 스레드 스택 별도 할당 |
| 9 | stamp_keyctl | KEYCTL_INSTANTIATE_IOV | ❌ EOPNOTSUPP | 시스템 콜 미지원 + 스택 프레임 0x40 |
| 10 | stamp_tcp | TCP_ZEROCOPY_RECEIVE | ❌ 스택 프레임 부족 | 예상 깊이 불충분 |
| 11 | stamp_futex | FUTEX_CMP_REQUEUE_PI 재귀 | ❌ 논리적으로 불가능 | UAF 윈도우 파괴 |
| 12 | binder ioctl | BINDER_WRITE_READ | ❌ EACCES | shell에 /dev/binder 권한 없음 |
| 13 | poll | poll | ❌ 힙 할당 | pollfd가 힙에 있음 |
| 14 | epoll_wait | epoll_wait | ❌ 프레임 너무 얕음 | 스택 프레임 0xE0 |
| 15 | sched_setattr | sched_setattr | ❌ 깊이 부족 | 스택 프레임 0xb0 |
| 16 | timerfd_settime | timerfd_settime | ❌ 스택 복사 없음 | 대형 스택 버퍼 없음 |
| 17 | io_uring_register | IORING_REGISTER_* | ❌ 이중 실패 | ①스택 프레임 0xF0에 불과, copy_from_user 대상이 sp+0x8로 고정 (waiter의 sp+0x1D0과 424바이트 차이); ②io_uring_setup이 seccomp에 차단되지 않았지만(ENOSYS 대신 EFAULT 반환), 스택 레이아웃 불일치 |
17/17 전부 실패.
PD2229의 SM8475 5.10 GKI 컴파일러(PGO + LTO + BOLT)가 생성한 스택 레이아웃으로 인해 core_sys_select의 stack_fds와 futex_wait_requeue_pi의 rt_mutex_waiter가 구조적으로 겹치지 않는다. 이는 컴파일러가 결정한 객관적 사실이며, 익스플로잇 기법의 문제가 아니다.
JoinChang 저장소에 명시되어 있음: "The pselect stack overlay only works when the freed rt_mutex_waiter lands within the user-controllable region of the stack_fds buffer" — PD2229는 이 조건을 충족하지 않는다.
PR_SET_MM_MAP이 auxv를 커널 스택에 복사PR_SET_MM_MAP이 Android에서 EPERM으로 차단됨PSELECT_SHIFT = -2"pselect가 waiter 구조를 조작할 수 없음 — NFDS >336일 때 fd_set이 힙에 있음; configfs/ashmem 미지원 (ashmem SET_NAME 잘림); 다른 모든 커널 쓰기 경로 차단됨 (/proc/self/mem, /dev/mem, binder)"
"pixel10에서 익스플로잇 가능한 버전의 pselect stack_fds가 정확히 rt_waiter와 커널 스택에서 겹치는데, 이 부분이 실제로 가장 골치 아픈 곳이다. OPPO findN2 커널에서는 이 두 호출의 스택 부분이 완전히 겹치지 않거나, 겹쳐도 제어할 수 없다. 다른 제어 가능한 커널 스택 시스템 콜을 찾아 스택을 구성해야 하며, 단순히 오프셋을 적응시키는 것으로는 성공할 수 없다. oppo의 rt_waiter는 pselect stack_fds와 완전히 겹치지 않는다"