
CVE-2026-43499 GhostLock, Google TV가 탑재된 Chromecast(sabrina)용 루트 익스플로잇
Linux 커널의 futex PI(우선순위 상속) 서브시스템에서 발생하는 use-after-free 취약점인 CVE-2026-43499를 통해 Chromecast with Google TV(sabrina)의 루트 익스플로잇.
커널 5.15.170(PGO+BOLT+LTO, clang 17)을 실행하는 Android 14 기반의 잠긴 부트로더 기기에서 루트 권한을 획득합니다.
GhostLock은 rt_mutex_start_proxy_lock()의 -EDEADLK 롤백 경로에서 호출되는 remove_waiter()의 버그를 악용합니다. 이 함수는 대기자 태스크의 pi_blocked_on 대신 requeuer의 pi_blocked_on(이미 NULL)을 지웁니다. 대기자의 pi_blocked_on은 절대 지워지지 않아, 커널 스택에 해제된 rt_mutex_waiter에 대한 dangling pointer가 남습니다.
이후 sched_setattr이 rt_mutex_adjust_pi -> rt_mutex_adjust_prio_chain을 트리거하고, 이는 dangling pointer를 따라가 재할당된 힙 페이지의 공격자 제어 데이터를 통해 PI 체인을 순회하여 rb_erase를 통한 임의 쓰기 프리미티브를 제공합니다.
ucounts = NULL을 가진 자체 포함 네임스페이스 -- cap_capable이 첫 반복에서 일치하고 inc_rlimit_ucounts가 한 번의 루프 후 종료되어 &init_user_ns 유출 필요성을 제거합니다PERF_SAMPLE_TID 필터가 호출 스레드의 레지스터 스냅샷만 집계되도록 보장하여, 핫 시스템 서비스가 mode-vote를 지배하는 것을 방지합니다5.15.170-android14-11-gf4a1f03072af(aarch64, PGO+BOLT+LTO, clang 17.0.2)CONFIG_FUTEX_PI=y, CONFIG_IO_URING=y, perf_event_paranoid=-1, SELinux enforcing, panic_on_oops=1, user namespaces 없음export ANDROID_NDK_HOME=/path/to/android-ndk
make -j$(nproc)
Android NDK가 필요합니다(r27로 테스트됨). 정적으로 링크된 aarch64 바이너리를 생성합니다.
adb push ghostlock /data/local/tmp/
adb shell "cd /data/local/tmp && ./ghostlock --cred"
익스플로잇은 약 20초가 걸립니다(힙 스프레이 + KernelSnitch 무차별 대입). 성공하면 프로세스가 uid=0 자격 증명으로 /system/bin/sh를 exec합니다.
이 익스플로잇은 Claude Opus 4.6(Anthropic)이 GLM-5.3(Z.ai)을 커널 익스플로잇 컨설턴트로 두고 이식 및 개발했습니다.
kernel/futex/, kernel/locking/rtmutex.c, lib/rbtree.c이 익스플로잇은 보안 연구 및 교육 목적으로 공개되었습니다. 연구자가 소유한 기기를 대상으로 합니다. 소유하지 않은 기기나 승인 없이 사용하지 마십시오.
MIT
| 단계 | 기법 |
|---|
| KASLR 유출 | PERF_SAMPLE_IP를 사용한 perf_event_open(TID 게이트) |
| Task struct 유출 | PERF_SAMPLE_REGS_INTR를 사용한 perf_event_open(TID 게이트, 선형 맵 주소의 최빈값) |
| mm_struct 유출 | KernelSnitch -- futex 해시 충돌 타이밍 사이드 채널 |
| 힙 스프레이 | SLUB discard choreography(memfd-close, CPU-partial overflow) + io_uring_setup(256) order-2 페이지 회수 |
| 스택 오버레이 | AF_UNIX SOCK_SEQPACKET sendmsg -- move_addr_to_kernel이 128바이트 sockaddr을 커널 스택에 복사하여 dangling waiter의 task/lock/prio 필드를 덮어씀 |
| Walk 트리거 | 단조 nice 사다리(7 -> 14 -> 19)를 사용한 sched_setattr이 PI 체인 walk를 발동 |
| 쓰기 프리미티브 | rb_erase Case 1a가 child(fake_cred)를 parent->rb_right = task->cred에 씀 |
| Cred 교체 | task->cred = fake_cred(uid=0, 모든 caps, 스프레이 페이지에 자체 포함된 fake user_namespace) |
| 루트 셸 | execve("/system/bin/sh") -- exec 경로의 commit_creds가 fake_cred를 깨끗한 slab credential로 복사 |