
Amazon Fire 7(Fire OS 7.3.3.1)에서 Mali kbase JIT use-after-free CVE-2022-38181을 통해 임시 root를 획득하고, modprobe_path 덮어쓰기 체인을 사용하는 커널 익스플로잇 연구.
AI 지원 프로젝트. 이 연구, 익스플로잇 개발 및 문서는 GLM-5.3과 DeepSeek V4.1 Flash 모델을 사용한 AI 지원으로 작성되었습니다.
최종 펌웨어 — Fire OS 7.3.3.1, PS7331.4463N, kernel 4.9.117 (built 2025-05-03, SPL 2024-08-01) — 에서 Amazon Fire 7 9th gen (mustang, MT8163, Mali-T720) 을 위한 루트 익스플로잇 연구.
목표: LineageOS. 이 기기에서는 부트로더 경로가 막혀 있으므로 (패치된 bootrom — CMD 단락을 통한 preloader 전용), 남은 유일한 경로는 소프트웨어 커널 익스플로잇이다.
nix-shell -p android-tools --run './run.sh' # add -p zig too if no zig
성공 시:```
/data/metrics/su id # run a command as root
/data/metrics/su # interactive root shell
reclaim은 대략 3번의 부팅 중 1번 성공하며, 실패하면 태블릿이 패닉/재부팅된다;
run.sh는 재부팅을 기다렸다가 재시도할 뿐이다. SELinux는 익스플로잇의 일부로
Permissive로 강제되므로 root는 런타임 전용이다 — 재부팅하면 순정 상태로 돌아가고
run.sh를 다시 실행하면 된다.
미리 빌드된 st3와 su(armv7 정적)가 커밋되어 있으므로 실행에 툴체인은 필요 없다.
zig가 있으면 ./run.sh --build로 poc/*.c에서 다시 빌드할 수 있다.
GhostLock(아래)은 보류 상태다: MTK의 BUG_ON rtmutex 변형 + 셸에서의 커널 주소 노출 부재 = 이 빌드에서는 구조적 막다른 길(세션 2-4). kbase JIT UAF는 재진단되었고(destroy-worker의 "무조건적 패닉"은 JIT_FREE 역참조였으며, 로그는 패닉 도중 adbd 종료로 유실됨) stage 2는 이제 오라클로 입증되었다 — SESSION 5 섹션 참조.
rtmutex remove_waiter() futex-PI 스택 UAF (NebuSec 공개 2026-07, 수정 3bfdc63936dd
2026-04 반영). 취약 범위 2.6.39–7.1 → 우리의 4.9.117 (2025년 5월)이 영향받음.
우리의 정확한 빌드에서 검증됨:
CONFIG_FUTEX=y, rtmutex가 컴파일되어 포함됨, 버그가 그대로 존재:
rtmutex.c:1108-1111이 current->pi_lock/current->pi_blocked_on을 사용(원래는
waiter->task여야 함); 버그가 있는 호출 지점 rtmutex.c:1723 (rt_mutex_start_proxy_lock 오류 경로)WAIT_REQUEUE_PI/CMP_REQUEUE_PI), 디바이스 노드 없음,
SELinux로 제한되는 것 없음 — kbase 경로의 치명적 장애물이 여기에는 존재하지 않음sched_setattr → __sched_setscheduler → sched/core.c:4706의
rt_mutex_adjust_pi(p) — 오래된 pi_blocked_on을 역참조 ✓futex.c:1975가 this->rt_waiter를 전달,
futex.c:2880의 futex_wait_requeue_pi에서 선언됨) → waiter가 arm32 select (nr 142) fd_sets를
통해 자신의 해제된 프레임을 스탬프함DEBUG_RT_MUTEXES 꺼짐
→ 컴팩트한 48바이트 rt_mutex_waiter (tree_entry@0, pi_tree_entry@0xc, task@0x18,
lock@0x1c, prio@0x20, deadline@0x28)modprobe_path @ 0xc111488c (문자열은
vmlinux에서 자체 위치 확인; KALLSYMS_ALL이 꺼져 있어 데이터 심볼에는 이 트릭이 필요) → unknown-binfmt exec → root
스크립트 (setenforce 0, OTA 비활성화, su)refs/의 참조 자료: NebuSec/CyberMeowfia (원본), GhostLock-5.10 (Fire OS 8 포팅,
src/exp32/의 완전한 32비트 ARM 트리거), ghostlock-...-4.19-k40 (Qualcomm 4.19 Android 포팅)exp32/main.c의 포팅rt_waiter 프레임 오프셋 대 do_sys_select fd_set 영역 — 우리의
vmlinux 디스어셈블(do_sys_select stack_fds 대 futex_wait_requeue_pi 프레임), STAMP_NFDS/STAMP_WAITER_OFF를
튜너블로 노출Failed critical init step 3/dev/mali0 world-RW + SELinux gpu_device, kbase r26p0-01rel0selroot 2-패킷 체인: selinux_state.enforcing을 0으로 만들고,
fake entry를 commit_creds(&init_cred)로 재작성. uid=0, SELinux Permissive.mustang, Fire OS 7.3.3.1 PS7331.4463N/00315758630404.9.117-g08fe75b-dirty, 빌드 Sat May 3 01:25:15 UTC 2025 (Linaro GCC 6.3-2017.05)/dev/kb, /dev/dkb (Amazon 커널 백업 파티션) root:drmrpc 0660 — 잠김mali_kbase r26p0-01rel0 (Midgard, Mali-T720), NVD 영향 범위 r4p0–r31p0 내mali_kbase_mem.c:2721 kbase_jit_destroy_worker가 영역을 해제하지만, kctx->jit_alloc[id]를 절대 지우지 않음mali_kbase_softjobs.c:1270 kbase_jit_free_finish가 오래된 jit_alloc[ids[j]]를 역참조mali_kbase_mem.c:3138 kbase_jit_backing_lost → destroy 경로 (reclaim 중에 발동)0xc0008000 VA / 0x40080000 PA)ARM_SW_DOMAIN_PAN 없음 → ret2usr 가능; CONFIG_PANIC_ON_OOPS=y (실패한 시도 = 재부팅)SLAB_FREELIST_RANDOM/HARDENED 없음, CONFIG_USER_NS/USERFAULTFD/NF_TABLES 없음CONFIG_MODULES=y, STATIC_USERMODEHELPER 없음 → modprobe_path 덮어쓰기 = root_IOC_TYPE 0x80)MEM_ALLOC union은 32바이트 (in에 extent 포함 4 × u64)BASE_MEM_PROT_GPU_RD|WR (비트 2|3)가 포함되어야 함, 레거시 R|W가 아님mmap(fd, offset=3<<12, PROT_NONE)JOB_SUBMIT stride는 sizeof(base_jd_atom_v2) = 48과 같아야 함 (base_jd_prio/base_jd_dep_type은 u8 typedef)MEM_JIT_INIT (nr 14, v2 구조체), alloc/free는 JOB_SUBMIT을 통한 소프트 잡
(BASE_JD_REQ_SOFT_JIT_ALLOC=0x209, ...FREE=0x20a; jc=user ptr, nr_extres=count)info->gpu_alloc_addr를 통해 기록함 (미리 할당해서 전달해야 하는 GPU VA)| evictable 객체 | 압박 | 결과 |
|---|---|---|
| 없음 | 900 MB | 생존 |
| 없음 | 1300 MB | 패닉 (시스템 lowmem 버그 — 무관) |
| 일반 영역 + DONT_NEED | 700 MB | 생존 |
| JIT 영역 + DONT_NEED | 700 MB | reclaim 경로에서 패닉 |
패닉은 eviction 자체 중에 발생함 (evictable_reclaim_scan_objects → backing_lost →
destroy worker) — dangling 참조(jit_alloc[], evict 리스트)는 우리가 JIT_FREE를 제출하기
훨씬 전에 순회됨. Stage 2는 경쟁에서 이겨야 함: 압박이 여전히 진행 중일 때 우리 자신의
MEM_ALLOC 스프레이로 해제된 kbase_va_region을 재할당해야 함.
poc/stage2.c — stage-2 익스플로잇 (모드: step/uaf/spstep/spfree/spray/keys)
— spray 700 = 전체 오라클 실행; 생존하고 일시정지함 (정리하려면 kill)poc/mustang_jit_uaf.c — stage-1 PoC (모드: jit N / control N / pressure N)poc/build.sh — zig 크로스 빌드 (정적 musl armv7)kernel/vmlinux — 정확한 OTA 빌드에서 복구한 심볼 (vmlinux-to-elf)kernel/config-* — 실행 중인 기기에서 덤프한 /proc/config.gzksrc/ — Amazon OSS 소스 (platform.tar + 추출된 midgard-r26p0 트리)/tmp/opencode/mustang_ota.bin (sha256 6068515a…가 fireos-archive와 일치)
및 2.2 GB 커널 소스 타르볼은 ~/Desktop/amazon-mustang/에 보관ksrc/kernel/mediatek/mt8163/4.9/drivers/misc/mediatek/gpu/gpu_mali/mali_midgard/midgard-r26p0/nix-shell -p zig --run 'zig cc -target arm-linux-musleabihf -static -O2 -o juaf poc/mustang_jit_uaf.c' adb push juaf /data/local/tmp/juaf && adb shell chmod 755 /data/local/tmp/juaf adb shell /data/local/tmp/juaf jit 700 # full trigger (~reboots device) adb shell /data/local/tmp/juaf control 700 # no-JIT control adb shell /data/local/tmp/juaf pressure 700 # raw memory-pressure control
## 참고 자료