
CVE-2026-98260(posix-cpu-timers UAF)에 대한 Android 커널 LPE 연구: 트리거 PoC, fake-cred 및 real-cred uid-0 셸 체인, 그리고 Shizuku/KernelSU 부트스트랩 앱.
한국어 | 简体中文
posix-cpu-timers 종료 경로의 use-after-free에 대한 Android 로컬 권한 상승 연구입니다. 이 저장소에는 트리거 전용 PoC, fake-cred 검증 익스플로잇, 실제 cred uid-0 셸 단계, Shizuku 기반 KernelSU 앱, 그리고 취약점 사전 검사가 포함되어 있습니다.
브랜치
magicos-11.0.0.109-6.12.58: 이 브랜치는 듀얼 프로파일입니다. APK와 네이티브 바이너리는 런타임에 정확한uname -r을 기준으로 프로파일을 선택합니다. MagicOS 10.0.0 / 6.12.38이 검증된 프로파일이며, MagicOS 11.0.0.109 / 6.12.58은 정적으로 적응되었고 기기 측 런타임 검증은 아직 대기 중입니다. APK는 두 개의 226-심볼ko-syms테이블을 모두 포함하며 일치하는 것을 선택합니다.
| 항목 | 값 |
|---|---|
| CVE | CVE-2026-98260 |
| 분류 | LPE (use-after-free, timerqueue rb-tree erase) |
| 대상 | Honor MagicPad3 Pro 13.3 YLP-W00, MagicOS 10.0.0 / 11.0.0.109 (Android 16), 브랜치 magicos-11.0.0.109-6.12.58 |
| 커널 | 6.12.38-android16-5-gfde7767f6ef6-abogki481467632-4k (검증됨)6.12.58-android16-6-g3a7ac1674240-abogki541502914-4k (적응됨) |
| 프로파일 상태 | 듀얼 프로파일; 6.12.38 검증됨, 6.12.58 런타임 검증 대기 중 |
| SELinux | Enforcing |
| 패치 | 테스트된 빌드에 없음 |
| 완화 기법 | KASLR 켜짐, SMEP/SMAP/PAN 켜짐, KASAN 컴파일되었으나 비활성화됨 |
| 부트로더 | 잠김, 잠금 해제 불가; 파티션 쓰기 없음 |
| 구성 요소 | 상태 | 결과 |
|---|---|---|
src/poc.c | 6.12.38에서 검증됨 | 파괴적 트리거; reap 시 cleanup_timerqueue에서 패닉 발생 |
src/exp.c | 6.12.38에서 검증됨 | fake cred; getuid()==0 && geteuid()==0; exec 불가 |
src/uid0.c | 6.12.38에서 검증됨 | 실제 cred; u:r:shell:s0에서 uid-0 셸 |
app/ | 6.12.38에서 검증됨 | Shizuku + KernelSU 부트스트랩; u:r:ksu:s0에서 uid 0 |
src/vuln_precheck.h | 추가됨 | 프로파일/환경 게이트; --check / --force |
| 6.12.58 프로파일 | 적응됨 | 사이트, selinux_state 및 해당 226-심볼 ko-syms 테이블 재도출됨; 기기 실행 대기 중 |
| 듀얼 프로파일 APK | 6.12.38에서 테스트됨 | uname -r에 의한 프로파일 및 ko-syms 선택; 6.12.38 라이브-KSU 경로 통과 |
현재 릴리스: v1.4.3-app.
v1.4.2-app 사전 릴리스는 2026-10-30에 철회되었으며, 해당 태그는 기록용으로만 유지됩니다. 릴리스 기록과 알려진 격차는 docs/release-notes.md를 참조하십시오.
src/poc.c trigger only; the reap panics on the tested kernel
src/exp.c fake-cred chain; verifies uid 0 via getuid/geteuid
src/uid0.c real-cred chain; zeroes cred->uid/gid and execs sh
src/kernel_profile.h per-release call sites, symbols and structure offsets
src/trace_ids.h runtime kmem trace event IDs; unprivileged probe
src/vuln_precheck.h shared profile/environment gate
app/ Android root app (Shizuku + KernelSU bootstrap)
docs/analysis.md root cause, exploit chain and device notes
scripts/dump_device.sh root device dump: all .ko/modinfo, kernel/config/policy, perf/tracefs, vendor mount, harness, active boot.img
Makefile single build entry (NDK cross-compile, app APK)
README.zh-CN.md Chinese version of this document
CLOCK_THREAD_CPUTIME_ID posix 타이머를 생성한 뒤 de_thread() 이후 execve()가 실패하는 비리더 스레드는 타이머 노드를 큐에 남겨둔다. exec를 중단시키는 SIGKILL은 스레드가 exit_itimers()를 통해 종료되게 하는데, 이 함수는 이미 종료 중인 태스크의 타이머를 해제할 수 없다. k_itimer는 해당 timerqueue_node가 여전히 태스크의 posix_cputimers 큐에 연결된 상태에서 해제된다. 부모가 좀비를 회수하면 release_task() -> posix_cpu_timers_exit() -> cleanup_timerqueue()가 해제된 노드에 대해 timerqueue_del()/rb_erase()를 호출한다.
이 erase가 쓰기 프리미티브다. rb_erase()는 제어된 __rb_parent_color 값을 제어된 rb_left 대상에 저장한다. 익스플로잇은 slab/page 회수를 정교하게 조율하여 해제된 노드가 공격자의 페이로드로 채워진 페이지에 위치하도록 한 뒤, 대상과 값을 선택한다.
관찰된 panic trace, YLP-W00 오프셋, tracefs 기반 leak, SLUB/RCU 조율, 2단계 fake-cred 체인 및 단일 쓰기 real-cred 체인은 docs/analysis.md를 참조하라.
요구 사항: /opt/android-ndk-r29에 위치한 Android NDK r29 (NDK=로 재정의 가능). 대상은 aarch64이며 기본 API 24이다. 앱은 추가로 Android SDK (build-tools 37.0.0, platform 37), JDK, 그리고 ~/.local/share/android-cve-env/cve98260-app 아래의 Shizuku jar가 필요하다 (APP_DEPS=로 재정의 가능). 빌드 산출물은 build/에 남으며 git에서 무시된다. Shizuku jar는 사용자가 제공하며, scripts/fetch-deps.sh가 deps.lock (SHA-256)에 대해 검증하고 아무것도 다운로드하지 않는다.
| 대상 | 출력 / 동작 |
|---|---|
make / make poc | build/poc |
make exp | build/exp |
make uid0 | build/uid0 (정적; 셸 단계가 자기 자신을 재실행) |
make build-all | poc + exp + uid0 + apk 빌드 |
make host | 호스트 측 qemu 검증용 build/poc.static |
make emu | qemu에서 build/poc.static 실행 (--force 포함) |
make apk | app/CVE98260Root.apk |
make clean | build/ 제거 |
make help | 대상 목록 표시 |
poc, exp, uid0는 커널을 건드리기 전에 프로파일 게이트를 호출한다. 이 게이트는 정확한 프로파일 릴리스 (6.12.38 검증됨, 6.12.58 적응됨)를 허용하고, arm64와 4 KiB 페이지를 요구하며, exp/uid0의 경우 tracefs/kmem 가용성과 (fake-cred 체인의 경우) panic_on_warn=0도 확인한다. 그 외의 릴리스는 미확인 상태이며 프로그램은 2로 종료한다:
build/uid0 --check # print the verdict, do not trigger
build/uid0 --check --force # same, overriding an unconfirmed environment
커널을 독립적으로 검증한 후에만 --force 또는 CVE98260_FORCE=1을 사용하십시오. --force는 환경 검사만 완화합니다. exp, uid0 및 앱은 여전히 kernel_profile.h의 정확한 프로필을 요구합니다. 이 게이트는 의도적으로 런타임 프로브가 아닙니다. 버그에 도달하는 프로브는 그 자체로 오래된 timerqueue 노드를 남기게 되기 때문입니다.
YLP-W00에서 비특권 셸은 /proc/sys/kernel/panic_on_warn 또는 /proc/cmdline을 읽을 수 없습니다. 알려진 프로필의 경우 게이트는 6.12.38에서 검증된 값 0을 가정합니다. 동일한 가정이 런타임 검증이 완료될 때까지 적응된 6.12.58 프로필에도 사용됩니다. 알 수 없는 릴리스는 차단된 상태로 유지됩니다.
익스플로잇은 일반 셸 프로세스(uid 2000, SELinux u:r:shell:s0)로 시작하며 su를 사용하지 않습니다. su를 통해 실행하지 마십시오. 테스트되는 조건은 비특권 시작입니다.
make uid0
adb push build/uid0 /data/local/tmp/uid0
adb shell chmod 755 /data/local/tmp/uid0
adb shell /data/local/tmp/uid0 # runs id and prints the context
adb shell /data/local/tmp/uid0 shell # interactive sh -i
전체 체인 전에 비파괴 모드를 사용하세요:
adb shell /data/local/tmp/uid0 --probe-ids # discover kmem trace event IDs
adb shell /data/local/tmp/uid0 leak # timer/cred leak only; no UAF/reap
kmem trace 이벤트 ID는 부팅 시 할당되며 커널 빌드 간에 안정적이지 않습니다(6.12.38은 318/319/322/324를 사용). --probe-ids는 쓰기 가능한 rss_stat/trigger를 통해 한 번에 하나의 이벤트를 격리하고 원시 레코드 타입을 탐색합니다. 일반 실행은 자동으로 프로브합니다. leak은 run_plant()에 도달하지 않고 [base], 전체 timer_site/slide, 그리고 cred 할당 사이트를 출력합니다.
root는 정적이며, 그 자식은 비공개 실제 cred를 얻기 위해 바이너리를 재실행하므로 푸시된 경로에 남아 있어야 합니다(/proc/self/exe를 통해 exec됨).
성공하면 출력은 다음과 같이 끝납니다:
[child] woke uid=0 euid=2000
[child] setuid(0)=0 errno=0 resuid=0/0/2000
uid=0(root) gid=0(root) groups=0(root),...
context=u:r:shell:s0
ROOT_SHELL_OK
adb 세션이 중단될 수 있는 경우, 익스플로잇을 분리된 상태로 시작하십시오. 대기 중인 프로세스는 절대 종료해서는 안 됩니다(제한 사항 참조):
adb shell 'setsid /data/local/tmp/uid0 >/data/local/tmp/uid0.log 2>&1 &'
검증되지 않은 상태(피해자 slab이 해제되지 않음, 대상 페이지가 다른 할당에 의해 유실됨, 예상치 못한 reap 상태)에서는 프로그램이 reap 없이 대기한다. 오래된 노드에 대한 통제되지 않은 reap은 이 커널을 패닉시킨다. 재부팅만이 유일한 안전한 정리 방법이다.
app/은 YLP-W00에서 권한 없는 Shizuku 셸로부터 KernelSU를 부트스트랩하는 Android UI이다. 익스플로잇 코어를 네이티브 라이브러리로 재사용하며 src/poc.c와 src/exp.c는 그대로 둔다. UI는 의도적으로 작게 유지된다: Shizuku를 승인하고, 에셋을 배포하고, 체인을 시작하고, 임시 파일을 정리하고, 표시되는 로그를 내보내거나 복사한다.
make apk # -> app/CVE98260Root.apk
adb install -r app/CVE98260Root.apk
일반적인 흐름:
libmagicroot.so를
/data/local/tmp/cveapp 아래에 배치합니다.kernelsu가 이미 활성 상태라면 su -c id만 검증합니다.앱은 실행이 시작되면 이전의 인증/배포 라인을 지우고 원격 run.sh 출력을
UI로 스트리밍합니다. 네이티브 워커는 또한
/data/local/tmp/cve98260-worker-<pid>.log에 추가로 기록하므로, UI 파이프가
닫혀도 증거가 소실되지 않습니다. 导出日志는 /sdcard/MagicRoot.log를
덮어씁니다. 작업이 실행 중일 때는 처음 네 개의 버튼이 비활성화되며, 작업이
종료되면 다시 활성화됩니다.
이 앱은 YLP-W00 전용입니다. 매니저의 KMI 모듈, 패키징된 프로필별
ko-syms-6.12.38.txt / ko-syms-6.12.58.txt, 그리고 선택된 프로필의
selinux_state 주소를 사용합니다. 범용 루팅 앱이 아닙니다. 네이티브
라이브러리는 체인 전에 공유 사전 검사를 호출하며, 라이브 실행 시에는
magicroot --check도 먼저 실행합니다.