
Kernel exploit research achieving temporary root on Amazon Fire 7 (Fire OS 7.3.3.1) via the Mali kbase JIT use-after-free CVE-2022-38181, with a modprobe_path overwrite chain.
AI-assisted project. This research, exploit development, and documentation were produced with AI assistance using the models GLM-5.3 and DeepSeek V4.1 Flash.
Root exploit research for the Amazon Fire 7 9th gen (mustang, MT8163, Mali-T720) on the final firmware — Fire OS 7.3.3.1, PS7331.4463N, kernel 4.9.117 (built 2025-05-03, SPL 2024-08-01).
Goal: LineageOS. Bootloader path is dead on this unit (patched bootrom — preloader-only via CMD short), so the only remaining route is a software kernel exploit.
# one-shot: build, run the exploit (retries across the probabilistic reclaim),
# install a setuid-root su and verify it as an unprivileged user
nix-shell -p android-tools --run './run.sh' # add -p zig too if no zig
On success:
/data/metrics/su id # run a command as root
/data/metrics/su # interactive root shell
The reclaim wins roughly 1 boot in 3 and a loss panics/reboots the tablet;
run.sh just waits for the reboot and retries. SELinux is forced Permissive as
part of the exploit, so root is runtime-only — a reboot restores stock and
you re-run run.sh.
Prebuilt st3 and su (armv7 static) are committed, so no toolchain is needed
to run. ./run.sh --build rebuilds them from poc/*.c if you have zig.
GhostLock (below) is parked: MTK's BUG_ON rtmutex variant + zero kernel-address disclosure from shell = architectural dead end on this build (sessions 2-4). The kbase JIT UAF was re-diagnosed (the destroy-worker "unconditional panic" was the JIT_FREE deref, log lost to adbd death mid-panic) and stage 2 is now oracle-proven — see SESSION 5 section.
rtmutex remove_waiter() futex-PI stack-UAF (NebuSec disclosure 2026-07, fix 3bfdc63936dd
landed 2026-04). Vulnerable range 2.6.39–7.1 → our 4.9.117 (May 2025) is affected.
Verified on our exact build:
CONFIG_FUTEX=y, rtmutex compiled in, bug present verbatim:
rtmutex.c:1108-1111 uses current->pi_lock/current->pi_blocked_on (should be
waiter->task); buggy call site rtmutex.c:1723 (rt_mutex_start_proxy_lock error path)WAIT_REQUEUE_PI/CMP_REQUEUE_PI), no device node,
nothing SELinux-gated — the kbase path's fatal obstacles don't exist heresched_setattr → __sched_setscheduler → rt_mutex_adjust_pi(p) at
sched/core.c:4706 — derefs stale pi_blocked_on ✓futex.c:1975 passes this->rt_waiter,
declared in futex_wait_requeue_pi at futex.c:2880) → waiter stamps its own freed frame
via arm32 select (nr 142) fd_setsDEBUG_RT_MUTEXES off
→ compact 48-byte rt_mutex_waiter (tree_entry@0, pi_tree_entry@0xc, task@0x18,
lock@0x1c, prio@0x20, deadline@0x28)modprobe_path @ 0xc111488c (string self-located in
vmlinux; KALLSYMS_ALL off so data symbols need this trick) → unknown-binfmt exec → root
script (setenforce 0, disable OTA, su)refs/: NebuSec/CyberMeowfia (original), GhostLock-5.10 (Fire OS 8 port,
full 32-bit ARM trigger in src/exp32/), ghostlock-...-4.19-k40 (Qualcomm 4.19 Android port)exp32/main.crt_waiter frame offset vs do_sys_select fd_set area — disassemble
our vmlinux (do_sys_select stack_fds vs futex_wait_requeue_pi frame), expose
STAMP_NFDS/STAMP_WAITER_OFF as tunablesFailed critical init step 3/dev/mali0 world-RW + SELinux gpu_device, kbase r26p0-01rel0selroot 2-packet chain: zero selinux_state.enforcing, rewrite
fake entry to commit_creds(&init_cred). uid=0, SELinux Permissive.mustang, Fire OS 7.3.3.1 PS7331.4463N/00315758630404.9.117-g08fe75b-dirty, built Sat May 3 01:25:15 UTC 2025 (Linaro GCC 6.3-2017.05)/dev/kb, /dev/dkb (Amazon kernel-backup partitions) root:drmrpc 0660 — lockedmali_kbase r26p0-01rel0 (Midgard, Mali-T720), inside NVD affected range r4p0–r31p0mali_kbase_mem.c:2721 kbase_jit_destroy_worker frees region, never clears kctx->jit_alloc[id]mali_kbase_softjobs.c:1270 kbase_jit_free_finish derefs the stale jit_alloc[ids[j]]mali_kbase_mem.c:3138 kbase_jit_backing_lost → destroy path (fires during reclaim)0xc0008000 VA / 0x40080000 PA)ARM_SW_DOMAIN_PAN → ret2usr viable; CONFIG_PANIC_ON_OOPS=y (failed attempts = reboot)SLAB_FREELIST_RANDOM/HARDENED, no CONFIG_USER_NS/USERFAULTFD/NF_TABLESCONFIG_MODULES=y, no STATIC_USERMODEHELPER → modprobe_path overwrite = root_IOC_TYPE 0x80)MEM_ALLOC union is 32 bytes (in has 4 × u64 incl. extent)BASE_MEM_PROT_GPU_RD|WR (bits 2|3), not legacy R|Wmmap(fd, offset=3<<12, PROT_NONE)JOB_SUBMIT stride must equal sizeof(base_jd_atom_v2) = 48 (base_jd_prio/base_jd_dep_type are u8 typedefs)MEM_JIT_INIT (nr 14, v2 struct), alloc/free are soft jobs via JOB_SUBMIT
(BASE_JD_REQ_SOFT_JIT_ALLOC=0x209, ...FREE=0x20a; jc=user ptr, nr_extres=count)info->gpu_alloc_addr (a GPU VA you must
pre-allocate and pass)