
Amazon Fire 7 (Fire OS 7.3.3.1) पर Mali kbase JIT use-after-free CVE-2022-38181 के माध्यम से अस्थायी root प्राप्त करने वाला Kernel exploit शोध, जिसमें modprobe_path overwrite chain शामिल है।
AI-सहायित परियोजना। यह शोध, एक्सप्लॉइट विकास, और दस्तावेज़ीकरण AI सहायता से मॉडल GLM-5.3 और DeepSeek V4.1 Flash का उपयोग करके तैयार किए गए थे।
अंतिम फर्मवेयर पर Amazon Fire 7 9th gen (mustang, MT8163, Mali-T720) के लिए रूट एक्सप्लॉइट शोध — Fire OS 7.3.3.1, PS7331.4463N, kernel 4.9.117 (निर्मित 2025-05-03, SPL 2024-08-01)।
लक्ष्य: LineageOS। इस यूनिट पर बूटलोडर पथ समाप्त है (पैच किया गया bootrom — केवल preloader, CMD शॉर्ट के माध्यम से), इसलिए एकमात्र शेष मार्ग सॉफ़्टवेयर कर्नेल एक्सप्लॉइट है।
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
The reclaim लगभग 3 में से 1 boot जीतता है और हारने पर tablet panic/reboot हो जाता है;
run.sh बस reboot का इंतज़ार करता है और फिर से try करता है। SELinux को exploit के हिस्से के रूप में
जबरन Permissive किया जाता है, इसलिए root runtime-only है — reboot करने पर stock वापस आ जाता है और
आप run.sh फिर से चलाते हैं।
Prebuilt st3 और su (armv7 static) commit किए गए हैं, इसलिए चलाने के लिए किसी toolchain की ज़रूरत नहीं है।
./run.sh --build उन्हें poc/*.c से rebuild करता है अगर आपके पास zig है।
GhostLock (नीचे) parked है: MTK का BUG_ON rtmutex variant + shell से zero kernel-address disclosure = इस build पर architectural dead end (sessions 2-4)। kbase JIT UAF का फिर से निदान किया गया (destroy-worker "unconditional panic" वह JIT_FREE deref था, log adbd death mid-panic में खो गया) और stage 2 अब oracle-proven है — SESSION 5 section देखें।
rtmutex remove_waiter() futex-PI stack-UAF (NebuSec disclosure 2026-07, fix 3bfdc63936dd
2026-04 में landed)। Vulnerable range 2.6.39–7.1 → हमारा 4.9.117 (May 2025) affected है।
हमारे exact build पर verified:
CONFIG_FUTEX=y, rtmutex compiled in, bug verbatim मौजूद:
rtmutex.c:1108-1111 current->pi_lock/current->pi_blocked_on का उपयोग करता है (होना चाहिए
waiter->task); buggy call site rtmutex.c:1723 (rt_mutex_start_proxy_lock error path)WAIT_REQUEUE_PI/CMP_REQUEUE_PI), कोई device node नहीं,
कुछ भी SELinux-gated नहीं — kbase path की fatal obstacles यहाँ मौजूद नहीं हैंsched_setattr → __sched_setscheduler → rt_mutex_adjust_pi(p) at
sched/core.c:4706 — stale को deref करता है ✓exp32/main.c का portrt_waiter frame offset vs do_sys_select fd_set area — हमारे vmlinux को
disassemble करें (do_sys_select stack_fds vs futex_wait_requeue_pi frame), STAMP_NFDS/STAMP_WAITER_OFF को
tunables के रूप में expose करेंFailed critical init step 3/dev/mali0 world-RW + SELinux gpu_device, kbase r26p0-01rel0mustang, 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), NVD affected range r4p0–r31p0 के अंदरmali_kbase_mem.c:2721 kbase_jit_destroy_worker region free करता है, कभी kctx->jit_alloc[id] clear नहीं करताmali_kbase_softjobs.c:1270 kbase_jit_free_finish stale jit_alloc[ids[j]] को deref करता हैmali_kbase_mem.c:3138 kbase_jit_backing_lost → destroy path (reclaim के दौरान fire होता है)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 32 bytes है (in में 4 × u64 incl. extent)BASE_MEM_PROT_GPU_RD|WR (bits 2|3) शामिल होना चाहिए, legacy 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 typedefs हैं)MEM_JIT_INIT (nr 14, v2 struct), alloc/free soft jobs हैं JOB_SUBMIT के ज़रिए
(BASE_JD_REQ_SOFT_JIT_ALLOC=0x209, ...FREE=0x20a; =user ptr, =count)Panic eviction के दौरान ही होता है (evictable_reclaim_scan_objects → backing_lost →
destroy worker) — dangling refs (jit_alloc[], evict list) पर चला जाता है इससे पहले कि हम कभी
JIT_FREE submit करें। Stage 2 को race जीतनी होगी: freed kbase_va_region को अपने
MEM_ALLOC spray से reallocate करें जबकि pressure अभी भी चल रहा हो।
poc/stage2.c — stage-2 exploit (modes: step/uaf/spstep/spfree/spray/keys)
— spray 700 = पूरा oracle run; survive करता है और pause करता है (clean up के लिए kill करें)poc/mustang_jit_uaf.c — stage-1 PoC (modes: jit N / control N / pressure N)poc/build.sh — zig cross-build (static musl armv7)kernel/vmlinux — exact OTA build से recovered symbols (vmlinux-to-elf)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
## संदर्भ
- GHSL-2022-054 सलाह: https://securitylab.github.com/advisories/GHSL-2022-054_Arm_Mali/
- Mo का Pixel 6 एक्सप्लॉइट राइटअप: https://github.blog/2023-01-23-pwning-the-all-google-phone-with-a-non-google-bug/
- Fire HD 10 (trona) मिसाल, वही बग परिवार: ericpardee.github.io/fire-hd-ownership
- Amazon OSS पोर्टल: amazon.com gp/help/customer/display.html nodeId=200203720
- XDA अनलॉक थ्रेड (इस hw rev के लिए मृत): xdaforums.com/t/fire-7-2019-mustang-unbrick-downgrade-unlock-root.3944365/
## सत्र 3 परिशिष्ट (गहन syscall-stamp सर्वेक्षण)
मापी गई कॉपी-स्रोत गहराइयाँ (निरपेक्ष बनाम syscall-entry sp0; waiter -0x1d8..-0x1a8 तक फैला):
- sendto sockaddr @ -0xc4 | process_vm iov @ -0x104 | recvmsg iov @ -0x11c
- sendmsg iov @ -0x12c | recvmmsg iov @ -0x154 | select fds @ -0x174
- pselect6 fds @ -0x19c | **sendmmsg iov @ -0x1bc (सर्वोत्तम — waiter+0x00 से 0x1c कम)**
- poll entries @ -0x3e0 (पूरी तरह नीचे; गलत तरफ)
इस सत्र में बाहर किए गए:
- io_submit श्रृंखला बहुत उथली (~-0x130) | semtimedop: CONFIG_SYSVIPC=n (स्टब)
- configfs माउंटेड लेकिन शून्य सबसिस्टम पंजीकृत (कोई mkdir लक्ष्य नहीं)
- /sys/kernel/debug, /config: shell के लिए SELinux-अस्वीकृत
- /proc/sys/kernel: getdents काम करता है (29 प्रविष्टियाँ सूचीबद्ध), केवल pid_max खोलने योग्य;
kptr_restrict/hotplug/hostname/domainname पढ़ना सभी अस्वीकृत
- लिखने का मान हमेशा waiter+0 होता है (kernel stack addr, executable, shellcode
+0x1c पर): rb_link_node *link = node, insert_color parent-color लिखता है — बिना
tree-field stamping के कोई नियंत्रित-मान वेरिएंट संभव नहीं (अंतराल -0x1d8..-0x1bc)
- Double-deref dispatch फ़ील्ड पढ़ते हैं *(waiter+0)=1, *(waiter+4/+8)=0 — BLX 1/0
(nf_hooks, net_families, inet[6]_protos, seq_file->op सभी मृत)
- timer_list.function@+0xc और work_struct.func@+0xc पढ़ेंगे *(waiter+0xc) =
pi_tree self-ptr = EXECUTABLE waiter+0xc — लेकिन कोई पथ waiter+0 को
timer/work के रूप में कतार में नहीं डालता (link corruption / कोई अप्रत्यक्ष कतार स्रोत नहीं)
- Tree-root-nonzero (sysctl handler स्लॉट) = .text के माध्यम से rb-tree के रूप में
नियतात्मक pointer-chase; एक शून्य शब्द पर समाप्त होता है — offline-simulable, लेकिन
किसी उपयोगी writable स्लॉट में उतरना असंभाव्य है
सत्र 4 के लिए शेष सुराग:
1. ioctl गहरे पथ: dev_ioctl ifreq कॉपी (40B उपयोगकर्ता डेटा) — SyS_ioctl→sock_ioctl→dev_ioctl
श्रृंखला की गहराई -0x1d8 की तुलना में मापें
2. sendmmsg से 0x1c अधिक गहरी कोई अन्य कॉपी (अभी तक कुछ नहीं मिला)
3. यदि stamp-surface खोज विफल हो: walk-chained निर्माणों पर पुनर्विचार करें या
अभी तक गिने न गए writable-zero-called स्लॉट वर्गों की खोज करें
## सत्र 4 — दो सफलताएँ
### 1. MTK का rtmutex_common.h ही पूरा रहस्य है
MTK ने upstream के NULL-safe rt_mutex_top_waiter को इससे बदल दिया:```c
w = rb_entry(lock->waiters_leftmost, struct rt_mutex_waiter, tree_entry);
BUG_ON(w->lock != lock); // compiled to: ldr sb,[lock+8]; ldr r3,[sb+0x1c]; cmp; bne→udf#0x12
NO NULL CHECK + BUG_ON. हर all-zero anchor *(NULL+0x1c) पर मर जाता है; garbage anchors udf पर मर जाते हैं। THE WALK REQUIRES: lock->waiters_leftmost (lock+8) को एक fake waiter W (writable) की ओर इशारा करना चाहिए जिसमें W->lock (+0x1c) == lock हो।
Walk flow पूरी तरह mapped (rt_mutex_adjust_prio_chain @ 0xc0189a58):
/proc/self/task//stat field 28 (kstkesp) SHELL context से syscall-blocked threads के लिए REAL kernel SP लौटाता है (verified: nonzero values observed)।
The walk fires deterministically (dead-lock probe: crash every time, clean code). The write cannot land because of a 3-way kernel-hardening coincidence:
The stamp window (waiter+0x1c..0x5b) is the only controlled+known-content memory, but its ADDRESS is the unknown we need. Self-referential constructions all require stamping a kernel address as a constant — circular without a leak.
Their 5.4 kernel has UPSTREAM rtmutex_top_waiter (NULL-safe: if (!leftmost) return NULL) — empty-tree anchors survive, their write landed on null_fops
(writable on their kernel). Even THEY are stuck at dispatch ("ioctl reboot").
Mustang's 4.9.117 MTK tree has the BUG_ON variant — Fire OS 8 tablets
(GhostLock-5.10) succeeded because their 5.10 kernels are upstream-style.
Tested and dead from shell domain:
CONCLUSION: GhostLock on mustang requires a kernel-address disclosure that this kernel does not expose to the shell domain. The self-referential fake lock cannot be constructed without it.
(a) Boot-deterministic grind: reboot → calibrate stack address via crash oracle (~20-30 reboots), verify reproducibility. Long shot — late-boot thread stack allocation unlikely stable. (b) PIVOT back to kbase CVE-2022-38181 with accumulated assets: exact-build vmlinux + full source + toolchain + O_SYNC trace discipline + deep 4.9 knowledge. Original blocker (destroy-worker panic during JIT eviction) is a spray-timing problem, now better understood. (c) Stop at honest ~45%: trigger proven, walk mapped to the instruction, write blocked by MTK BUG_ON + no-leak.
Recommended: (b) — the kbase bug is verified-present in this exact source, had a working trigger, and its blocker is mechanical, not architectural.
step mode (alloc id=1 → DONT_NEED → 700MB pressure → MEM_QUERY, NO free)
survives: query=-1 (region freed by destroy worker, rbtree-clean).
The worker path is byte-identical to the legal JIT_FREE-under-pressure flow.
Session-1's crash was always the JIT_FREE dangling deref; its log line was
lost because the panic kills adbd mid-flush. Verified twice more with uaf
mode (bare free → panic, same log cutoff). The GHSL-2022-054 flow is fully
live on this build.
kbase_jit_free(kctx, reg) @ 0xc058495c with fully-controlled fake reg:
reg->cpu_alloc NULL → backed size 0 → trim block skipped (0xc0584978)kctx+0x147dd (byte) + kctx+0x147de+bin_id (byte)mark_reclaim(reg->gpu_alloc) @ 0xc059b158: chain
K=*(gpu_alloc+0x38) → *(K+0x1429c)==0 skips mm-atomics →
atomic_sub nents@K+0x141c8, D=*(K+4) → atomic_sub nents@D+0x538.
With nents=0 all writes are no-op stores (strex of same value).reg->flags |= 0x100000 (write into fake, benign)list_add(gpu_alloc->evict_node, &kctx->evict_list): evict_list head
@ kctx+0x1427c; writes into gpu_alloc+0x18/0x1c (must be writable)r3=*(reg+0x3c) prev, r2=*(reg+0x38) next
→ *(next+4)=prev; *(prev+0)=next — two arbitrary write-whats-wheres,
then relink of reg+0x38 into jit_pool_head @ kctx+0x148e89 candidates; S=0xc118b7ec (xfrm data, dormant on this device):
*(S+8)=0 (nents), *(S+0x18)=S+0x18 (empty evict_node → no WARN),
K=*(S+0x38)=0xc118b820 → *(K+0x1429c)=0, K/D+0x141c8/+0x538 all in
writable data. Oracle targets staged: init_uts_ns.name.nodename=0xc110d561
("(none)", readable via uname), scratch P=0xc118bd58 (xfrm zeros).
Avoid S=0xc111cba4 (tracepoint-adjacent). CONFIG_DEBUG_RODATA=y → all write
targets must be in .data/.bss (bss 0xc11d9000-0xc12d9000).
add_key (CONFIG_KEYS=y): SELinux-denied for shell. Dead.setxattr value buffer: kvmalloc(96)+copy_from_user happens BEFORE the
SELinux check → alloc dance is SELinux-proof even when the call fails;
transient (freed at syscall end), bytes persist at +4..95 (freelist ptr
clobbers +0..3 = rblink, unused by kbase_jit_free)spray 700 run 2026-09-11: 5895 regions sprayed during pressure, JIT_FREE
on dangling id=1 completed on a reclaimed region, then
JIT_ALLOC(0x40, bin 0) walked jit_pool_head and returned sprayed region
#4251's VA (0x142701000) — the exact region the dangling pointer consumed.
Process kill after: kctx teardown clean, no crash. Stage 2 complete:
deterministic UAF redirect with controlled object type + contents.
Region-type reclaim gives legal-dance survival but jit_node is INIT'd self → no unlink primitive. Need raw bytes at +0x38/+0x3c:
*(N+4)=P with P=userland shellcode page (no PAN!) —
candidates: const fops are .rodata (DEBUG_RODATA) → target non-const
fn ptr in .data, or binfmt formats list head, or sysctl proc_handler
(verify table writability). fallback: modprobe_path via byte-chained
writes (values must be writable addrs — use pointer-shaped targets)poc/stage3.c):
kern_table[pid_max].proc_handler @ 0xc1113f40 (writable
.data, verified via string-pointer scan + handler == proc_dointvec_minmax)b +8; W2 writes N at P = handler field)read /proc/sys/kernel/pid_max
(readable from shell); runs in own task context → creds apply to usinotify_handle_event kmallocs name_len+0x1d,
name bytes (fully controlled, NUL/slash-free constraint) at event+0x1c;
name_len=60 → kmalloc-96; queued → held; 4 instances → 4 events per
rename; SELinux-OK from shell. Fake redesigned NUL-free: cpu_alloc
points at S (nents@S+8 = 0 → same semantics as NULL)| variant | result |
|---|---|
| concurrent rename sprayers during storm |
Working hypothesis: storm-junk race — between the destroy worker freeing the slot (mid-storm) and child-kill/quiet, residual reclaim activity takes the sole-free-slot of the victim slab with non-payload bytes. Region spray (stage 2) wins because it allocates continuously DURING the storm; renames can't.
iso mode): identical timing, trailing with
commit-0 REGIONS + stage-2 pool-reuse oracle → ORACLE HIT
→ timing/reachability are FINE; events are the problemcurrent->mm is a kernel thread's → the elegant
"point the fake's pointers into our own userspace mmap" design (no PAN!)
FAULTS nondeterministically. 5/5 crashes with an otherwise-perfect fake.K=*(S+0x38) readable, *(K+0x1429c)==0 at runtime
(skips mm-atomics), K+0x141c8 writable, D=*(K+4) → D+0x538
writable, nents *(S+8) preferably 0. The 9 offline S-candidates were
validated against FILE bytes — runtime drift (xfrm/tracepoint init)
makes them unverified. A wrong chain = crash = reboot (~3 min cycle).A. Physmap-spray fake (ret2dir, classic arm32 no-PAN): spray ~450MB of user pages each containing the fake pattern baked for ONE guessed address G (G&0xfff = 0x141 for NUL-free name bytes; S=G; K=G-0x141b4 so K+4 lands in-page; K+0x141c8/+0x1429c → G+0x10/+0xd4 in-page; D=G+0x300; stray sub-0 stores hit random mapped RAM - harmless with nents=0). Spray doubles as the eviction pressure (dirty anon = unevictable → only ~100-200MB extra needed). Odds ≈ 45% (page hit) × ~50% (foreign K+0x1429c word is zero... if K+0x1429c kept in-page per layout above, odds = page-hit only). Miss = crash = reboot, retry. B. Brute-force the 9 static S-candidates (xfrm 0xc118b7ec first, tracepoint-adjacent 0xc111cba4 second...): 1 reboot per candidate, benign-oracle payload first, weapon on hit. C. pagemap-based exact G (dead: PFNs masked) — do not revisit.
/tmp/opencode/ksrc2/kernel/mediatek/mt8163/4.9/ (arch/arm incl.
mustang.dtsi, mm/, fs/eventpoll.c, fs/notify) ksrc/platform.tar से।
निष्कर्ष:
physmap_retouch() जोड़ा गया (trailing/deref से
पहले सभी स्प्रे पेज फॉल्ट करके वापस लाए जाते हैं)Regions reclaim: 3/3. Events: 0/~12 प्रयास जिनमें 28K exclusive allocations शामिल हैं, head start और correct-by-construction fake के साथ। यदि events p_hit(G)≈0.35 के साथ reclaim करते हैं, तो पाँच pmap misses ≈ 11.6% — संभव पर अब असंभावित (~10-15%)। या तो events संरचनात्मक रूप से यह स्लॉट नहीं ले सकते (कारण अज्ञात — वही कैश, वही संदर्भ, वही टाइमिंग) या हमारे G अनुमान व्यवस्थित रूप से चूक रहे हैं (highmem-boundary skew, allocator placement)।
vmalloc=496M slub_max_order=0 slub_debug=O loglevel=8 initcall_debug
Regions victim slot 3/3 लेते हैं; events 0/13. वही कैश (source + disasm स्तर पर सिद्ध), वही प्रक्रिया संदर्भ, वही pinned cpus, वही post-kill timing, exclusive head start के साथ हज़ारों allocations। तंत्र अज्ञात। शेष संदिग्ध: allocation-rate/frequency का partial-list rotation के साथ सहसंबंध (region ioctls ~1ms अंतराल पर बनाम event renames ~100µs अंतराल पर — विपरीत दिशाएँ?), या SLUB freelist ordering विवरण slub_max_order=0 के अंतर्गत जो... अस्पष्ट।
[+] uname.nodename="X?lhost" (was "localhost") <<< ORACLE HIT
**पूरी raw-byte श्रृंखला लाइव डिवाइस पर काम करती है**: इवेंट मुक्त किए गए
region slot को reclaim करता है → kbase_jit_free हमारे fake को deref करता है (S=physmap page from
the kbm spray, G=0xc154b2a4) → unlink हमारे दो writes को निष्पादित करता है।
benign payload (nodename write) के साथ बार-बार सत्यापित।
### खोजों की सत्र श्रृंखला
1. kbm pages GFP_HIGHUSER थे → HIGHMEM, physmap को अदृश्य
(mali_kbase_mem_pool.c:164!) — zonelist spill से ठीक किया गया: 260 × 2MB
spray > highmem-free → अतिरिक्त ZONE_NORMAL (physmap) में गिरता है
2. पहले weapon प्रयास क्रैश हुए: W1 target N+4 एक USER page था —
unlink kworker ctx (no mm) में चलता है → fault। इसे page+0x600 पर physmap pattern में
ring-0 shellcode बेक करके ठीक किया गया (arm32 non-LPAE पर direct map RWX)
— kernel-resident code, ret2usr की आवश्यकता नहीं
3. ctl_table offset bug: proc_handler entry+0x14 पर है, +0x18 पर नहीं (मूल scan
में यह सही था; मेरा define गलत था) — extra1 लिख रहा था
4. Over-pressure regression मिला और revert किया गया: kid budget 20×100MB +
trailing 10s ने reclaim तोड़ दिया; काम करने वाला config 2 kids/200MB +
5s/+3200 trailing है (revert के बाद benign hit 1/1)
5. **diag2: W2 → &pid_max global (0xc1114d7c) → read हमारा मान लौटाता है
(-1055861411 = 0xc110d55d as int32) — write + readback PROVEN**
### शेष रहस्य (बंद होने से एक प्रयोग दूर)
सही handler address (0xc1113f3c) के साथ diag1: W1 fire होता है (nodename
बदलता है), W2 निष्पादित हुआ होगा (अगला instruction) — फिर भी pid_max reads
साफ़ मान लौटाते हैं → जो handler field हम लिखते हैं वह वह नहीं है जिससे
inode dispatch करता है। diag3 (queued; hit boot चाहिए): W2 →
entry->data FIELD (0xc1113f2c) जो nodename की ओर इंगित करता है — यदि read तब
nodename-bytes-as-int दिखाता है, तो हमारी entry IS live है और केवल handler
offset किसी तरह गलत है; यदि अप्रभावित, तो inode एक shadow table copy का उपयोग करता है और हम live वाले की खोज करते हैं।
### Hit-rate वास्तविकता
प्रति-boot coin flip (~25-40%), clustered; कई safe-miss/crash boots
लगातार सामान्य हैं। लगभग 3-4 boots में 1 hit होता है। काम करने वाला
config EXACTLY रखें (2 kids, 5s trailing, 260-region kbm, G=0xc154b2a4)।
### Session-9 TODO
1. hit boot पर diag3 पूरा करें ("W1 FIRED" तक roll करें)
2. यदि entry live है: handler offset को अनुभवजन्य रूप से पुनः जाँचें (proc_dostring का address
handler के रूप में लिखें... N का मान उपयोगी होना चाहिए — unlink का उपयोग entry->data लिखने के लिए करें और pivot करें: उदाहरण के लिए,
data=selinux_enforcing-adjacent...)
3. यदि shadow table है: live वाले का पता लगाएँ — kallsyms में कोई data symbol नहीं है;
उम्मीदवार: /proc/sys व्यवहार स्कैन करें, या .data में header list pattern के माध्यम से दूसरा ctl_table
region खोजें (0x20-stride entries
handler=proc_dointvec_minmax और maxlen=4 के साथ — सभी को enumerate करें और
प्रत्येक को diag-write करें)
4. वैकल्पिक target class जो dispatch को पूरी तरह बचाती है: .data
function pointers जो shell-reachable paths से कॉल होते हैं (audit आवश्यक)
5. write primitive स्वयं DONE है — root के लिए अब कोई भी विश्वसनीय kernel-address
target पर्याप्त है
## SESSION 9 — nf-WEAPON: reclaim+unlink+G-hit PROVEN IN-WEAPON; केवल hook walk शेष है
### नया trigger design (sysctl-handler path को पूरी तरह प्रतिस्थापित करता है)
User का विचार kernel memory में अनूदित: कोई SUID file नहीं (system
dm-verity RO है; primitive kernel RAM लिखता है)। इसके बजाय: **fake netfilter
hook**। इस kernel में NEW
nf_hook_entries API का Android-common backport है, लेकिन LINKED LIST के रूप में कार्यान्वित (nf_hook_slow + helper 0xc09897d4 के disasm से सत्यापित):
- `__ip_local_out(net, sk, skb)` entries CELL को
**[net+0x58c]** से लोड करता है, state+0x1c में संग्रहीत करता है, nf_hook_slow को कॉल करता है
- walk: `entry = *cell`; while(entry){ if (state->[4] <= entry->[0x20])
call entry->[0xc](entry->[0x14], skb, state); entry = entry->[0]; }
— अर्थात **fn@entry+0x0c, priv@entry+0x14, priority@entry+0x20,
next@entry+0x00**; state+4 = INT_MIN threshold (हमेशा पास होता है)
- init_net = **0xc1104548** (CONFIG_NET_NS=n → sock_net() constant को inline करता है; 6678 movw/movt refs, histogram champion; nf_hook_slow के स्वयं के literal से cross-confirmed)। TARGET: **[init_net+0x58c] = 0xc1104ad4**
- LOCAL_OUT hook SENDER की process context में चलता है → हमारे hookfn का
commit_creds(prepare_kernel_cred(0)) उस process को root करता है जिसने
packet भेजा। Trigger = sendto(127.0.0.1:9) UDP।
### nf mode layout (poc/stage3.c, mode `nf 200`)
- kbm pattern pages (page-relative, single source of truth — पुराने
weapon में THREE bugs थे जो अब ठीक हैं: proc_handler@+0x14 not +0x18; W1
target KERNEL mem होना चाहिए (kworker ctx, no mm); baked code at
page+0x600 vs entry G+0x600=page+0x8a4 mismatch):
- +0x2ac/+0x2bc/+0x2c0/+0x2dc/+0x3a8: fake phy-alloc chain (अपरिवर्तित)
- +0x600: nf_code hookfn (marker store + prepare_kernel_cred +
commit_creds + return NF_ACCEPT(1))
- +0x700: fake entry {next=0, fn=PM_PAGE+0x600, priv=0, prio=0x100}
- +0x740: cell → PM_PAGE+0x700
- payload: N = PM_PAGE+0x740 (0xc154b740), P = 0xc1104ad4
→ W1: *(cell+4)=P (हमारे page में गिरता है), W2: *(init_net+0x58c)=cell
### SELF-DIAGNOSING instrumentation (kbm_scan_for)
kbm CPU mappings रखे जाते हैं; free के बाद हम हर sprayed
page को एक ज्ञात word के लिए स्कैन करते हैं:
- scan(HOOKS_PTR_ADDR) at page+0x744 → reclaim + unlink सिद्ध करता है + बताता है
कौन सा phys page G guess को back करता है
- scan(0x600d600d) at page+0x7f0 → सिद्ध करता है कि hookfn EXECUTED
(nf_code इस marker को अपनी 2nd action के रूप में लिखता है)
### THE RUN THAT MATTERS (2026-09-11, late session 9)```
[+] W1 CONFIRMED: region 135 page+0x185000 <- 0xc1104ad4 (reclaim + G hit!)
[+] nf trigger done, uid=2000 euid=2000
पूरी हथियार श्रृंखला लाइव बूट पर चली: इवेंट ने स्लॉट पुनः प्राप्त किया, नकली चला, unlink निष्पादित हुआ, G-guess पेज (0xc154b000) वास्तव में हमारा था (region 135 page 389)। W2 = *(0xc1104ad4)=cell आसन्न निर्देश है — उसे निष्पादित होना ही था। फिर भी UDP sendto ने हमें रूट नहीं किया → विफलता हुक पथ के अंदर है: walk semantics, [state+0x1c] plumbing, priority compare, या entry fields। (hookfn-ran-vs-not को अलग करने के लिए मार्कर प्रयोग जोड़ा गया; सत्र समाप्ति से पहले केवल crash-boots — अभी तक कोई साफ़ डेटा नहीं।)
pmap 200) पर्यावरण sanity check है (warm होने पर 4/4 hits;
cold होने पर crash-miss)nf 200 चलाएँ जब तक W1-CONFIRMED लाइन
न आ जाए, फिर MARKER लाइन पढ़ें:
a. marker PRESENT, uid!=0 → shellcode के creds विफल (prepare_kernel_cred/commit_creds addrs जाँचें; blx encodings)
b. marker ABSENT → walk ने हमें कभी कॉल नहीं किया: दूसरे
marker से सत्यापित करें जो... अगली diagnostics: hookfn जो केवल
marker लिखता है और 1 लौटाता है (कोई creds नहीं) — यदि फिर भी अनुपस्थित:
ls -la सत्यापित करें; marker files छोड़ेंpoc/stage3.c modes: pin/root/drain{,2,3,4,5}/iso — पूर्ण हथियार +
oracle + isolation harness, O_SYNC crash-point forensicsinit_net: 0xc1104548 __stack_chk_guard है (movw/movt
histogram stack-canary loads से दूषित था — 2025 ने पूरी nf योजना उस पर बनाई)।
असली init_net = 0xc1185040 (पुष्ट: ip_send_skb(net,...) इस literal के साथ
कॉल किया गया; ~994 refs सभी net stack में)। IPv4 LOCAL_OUT cell =
init_net+0x58c = 0xc11855cc।nf_iterate [init_net+0x58c] को स्वयं
nf_hook_ops pointer मानता है — यह fn@+0xc, priv@+0x14, prio@+0x20
सीधे उस मान से पढ़ता है। session-9 का fake entry
PM_PAGE+0x700 पर था जिसमें cell उसकी ओर इंगित करता था (cell पर अप्रयुक्त
next/fn fields → → crash)। fake entry को
(0xc154b740) पर ही रहना चाहिए। इसे ठीक करने पर, hook call
सिद्ध हुआ ( mode = SAFE_FN सभी 200 sendtos को साफ़ तरीके से process करता है)।commit_creds(prepare_kernel_cred(0)) / override_creds(&init_cred) uid 0 देता है
लेकिन kernel SELinux SID में पहुँचता है, जिसे यह Fire OS policy /data या
/sys/fs/selinux/enforce लिखने की अनुमति नहीं देती (सत्यापित: EACCES)।
असली init SID (7) भी अस्वीकृत (fake struct cred test)। enforcing_setup
__init है (freed → crash)। mark_reclaim के atomic_sub को nents=1 चाहिए जो
shrink_cpu_mapping के early-exit को तोड़ देता है।
जीत: exploit process kbm CPU mappings रखता है, इसलिए fake nf entry को packets के बीच in place फिर से लिखा जा सकता है:
selroot mode: entry = {fn=0xc01d503c (mov r3,#0;str r3,[r0];bx lr), priv=0xc1213ea8 (selinux_state.enforcing)}।*(enforcing)=0 → SELinux Permissive।kbm_cpu[reg]+off के माध्यम से {fn=commit_creds, priv=&init_cred} पर फिर से लिखें।commit_creds(&init_cred) → permissive SELinux
के साथ uid 0 → उपयोगी root, सब एक reclaim में, कोई chain आवश्यक नहीं।[+] W1 CONFIRMED: region 67 page+0xad000 <- 0xc11855cc (reclaim + G hit!) [*] sendto 0 -> -1 errno=1 uid=0 euid=0 [+] nf trigger done, uid=0 euid=0 <<< HOOK RAN - commit_creds OK [+] ROOT: uid=0 euid=0 [+] setenforce write=1 [+] su copied bytes=236220
- `getenforce` → **Permissive**; paused st3 है `Uid: 0 0 0 0`,
`CapEff: 3fffffffff`.
- `/data` **nosuid** के रूप में माउंट है इसलिए एक setuid `su` काम नहीं कर सकता। एक छोटा
`rootshell` (UDP भेजें → स्वयं पर `commit_creds` → `execl sh`) एक
इंटरैक्टिव रूट शेल देता है: `uid=0(root) context=u:r:kernel:s0`.
- रूट `/dev/block/by-name/*` को पढ़/लिख सकता है (`dd if=boot ...` ठीक)।
### Stage-3 कोड स्थिति (`poc/stage3.c`)
- मोड: `nf <mb> [probe|uprobe|oc|chain|fc <sid>|notrig|selroot]`
- `selroot` काम करने वाला हथियार है। मुख्य स्टेटिक्स: `init_net=0xc1185040`,
`HOOKS_PTR_ADDR=0xc11855cc`, `ENFORCING_ADDR=0xc1213ea8`,
`ZERO_GADGET=0xc01d503c`, `commit_creds=0xc014993c`,
`init_cred=0xc1114f54`.
- पोस्ट-एक्सप्लॉइट सीधे सिसकॉल हैं (कोई `system()` नहीं); kctx को जीवित रखें
(`pause()`) ताकि teardown क्रैश से बचा जा सके।
### शेष (Stage 4/5)
- रीबूट के बाद भी बने रहना (verity / boot image / recovery), क्योंकि सेल
हाईजैक + permissive SELinux केवल रनटाइम हैं और एक्सप्लॉइट को दोबारा चलाने के लिए
~1/3 reclaim सिक्का उछाल चाहिए।
- `su` को एक गैर-nosuid होम (`/system`) या एक लॉन्चर चाहिए जो दोबारा ट्रिगर करे।
## SESSION 11 — PERSISTENCE RECON (Track B + Track A) और RE हैंडऑफ
लक्ष्य स्थायी रूट था। दो ट्रैक तय किए गए:
- **Track B**: verified boot (dm-verity / SELinux) अक्षम करें ताकि `/system` को पैच किया जा सके।
- **Track A**: बूट पर एक्सप्लॉइट दोबारा चलाएँ।
दोनों एक ही अवरोधक पर आते हैं: **LK को डिवाइस को `eng`/`unlocked` मानने पर मजबूर करें।**
### Verified-boot तथ्य (सटीक बिल्ड)
- Bootloader लॉक, AVB `green`, `ro.boot.unlocked_kernel=false`, `ro.boot.secure_cpu=1`,
`rpmb_state=1`. Bootrom पैच (कोई BROM नहीं); preloader केवल CMD शॉर्ट के माध्यम से।
- `/system` **lk-built kernel cmdline से Android dm-verity द्वारा** माउंट है:
`root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=b6404ef3-… "`,
`veritykeyid=id:f3530e18f64d11fc25eb2dd762979f078de990bf`, `androidboot.veritymode=eio`,
`skip_initramfs` (system-as-root)। `dm-0` = `system` नाम का verity डिवाइस; `dm-1` = `/vendor`।
- LK: Amazon **UFBL**, `ro.boot.lk_version=0x0006`, बिल्ड `0db73c9-20231025_030009`;
preloader `pl_version=0x000a`, बिल्ड `80c6fcb-20230523_065640`। `/dev/block/by-name/lk` = mmcblk0p5 (1 MB)।
- पूर्ण GPT (16 पार्टीशन, कोई `persist`/`seccfg`/`nvram`/`protect`/`para` नहीं):
`proinfo` p0, `PMT` p1, `kb` p2, `dkb` p3, `lk` p4, `tee1` p5, `tee2` p6, `metadata` p7,
`MISC` p8, `reserved` p9, `boot` p10, `recovery` p11, `system` p12, `vendor` p13,
`cache` p14, `userdata` p15। eMMC boot0 (1 MB) = preloader (`EMMC_BOOT` magic);
boot1 (4 MB) = IDME स्टोर।
### LK (UFBL) स्टेटिक निष्कर्ष
`lk.img` हेडर: `88 16 88 58 | 00052974 | "LK"`; 0x200 पर ARM वेक्टर टेबल, बाकी Thumb-2,
position-independent/relocated (literal pools `ldr+add pc` का उपयोग करते हैं, इसलिए naive base-relative disasm विफल होता है)।
प्रासंगिक स्ट्रिंग्स (फ़ाइल ऑफ़सेट): `amzn_image_verify`, `amzn_verify_unlock`, `amzn_verify_code_internal`,
`unlock_code`, `unlock code error`, `unlock failed`, `$Common Kernel Signing Engineering CA0`,
`seccfg`, `para`, `ENV_v1`, `LK_ENV`, `Kfos_flags`/`Kdev_flags`/`Kusr_flags`/`Kunlock_code`/`Kunlock_version`,
`FOS_FLAGS_{NONE,ADB_ON,ADB_ROOT,CONSOLE_ON,RAMDUMP_ON,VERBOSITY_ON,ADB_AUTH_DISABLE,FORCE_DM_VERITY,DM_VERITY_OFF,BOOT_DEXOPT}`,
`[DM-VERITY] verify for system(root) is enabled`, `[DM-VERITY] verify off by fos_flags`,
`[DM-VERITY] disabled by fos_flags on eng devices or unlocked device`,
`[SELINUX] set to permissive mode by dev_flags`, `androidboot.prod=1|0`, `androidboot.unlocked_kernel=%s`.
**निष्कर्ष: LK `fos_flags`/`dev_flags` सुरक्षा प्रभावों को eng/unlocked पर गेट करता है।**
### IDME स्टोर (eMMC **boot1**) — लिखने योग्य, स्थायी, LK और Android द्वारा पढ़ा जाता है
- 0x0 पर Magic `beefdeed` + `"2.1\0"` + count(0x19=25); आइटम 0x10 से।
- आइटम प्रारूप: `char name[16]; u32 size; u32 type(=1); u32 magic(=0x124); u8 data[size] (pad4)`।
- आइटम ऑफ़सेट (प्रिस्टीन): `board_id@0x10 serial@0x3c mac_addr@0x68 mac_sec@0x94 bt_mac_addr@0xd0
bt_mfg@0xfc product_name@0x198 productid@0x1d4 productid2@0x210 region@0x24c bootmode@0x26c
postmode@0x28c bootcount@0x2ac manufacturing@0x2d0 unlock_code@0x4ec sensorcal@0x908 alscal@0x9c4
KB@0xa00 DKB@0x1e1c device_type_id@0x2238 dev_flags@0x2274 fos_flags@0x2298 usr_flags@0x22bc
wifi_mfg@0x22e0 unlock_version@0x26fc`। मान ASCII हैं (फ्लैग **हेक्स स्ट्रिंग्स** हैं)।
- रनटाइम रीड: `/proc/idme/<name>` (केवल-पढ़ने के लिए)। अंतिम-बूट मान कैश्ड; boot1 पर लिखा गया
अगले बूट पर प्रभावी होता है। लिखने के पथ के लिए `/sys/block/mmcblk0boot1/force_ro` (रूट) को साफ़ करना आवश्यक है।
- **पुष्टि की गई कि LK boot1 पढ़ता है**: `serial` बदलने से अगले बूट पर `ro.boot.serialno` बदल गया।
लेकिन LK **serial को 16 बाइट्स तक काट देता है** और `fos_flags=0x80`, `dev_flags=0xff`,
all-ones, आदि को अनदेखा कर दिया — verity/selinux/`prod` अपरिवर्तित। इसलिए serial के माध्यम से cmdline इंजेक्शन विफल होता है।
### IDME फ्लैग्स के Android-साइड उपभोक्ता
- `/init.fosflags.sh` (सेवा `fosflags`, `u:r:fosflags:s0`): `FOS_FLAGS_ADB_ON=0x1`,
`CONSOLE_ON=0x4`, `RAMDUMP_ON=0x8`, `VERBOSITY_ON=0x10`, `ADB_AUTH_DISABLE=0x20`,
`BOOT_DEXOPT=0x100`। सत्यापित: फ्लैग सेट करने से प्रभाव पड़ता है (`sys.usb=adb`, `noadbauth=1`)।
- **adbd** (unstripped ARM ET_EXEC; `.text` VA 0x8160 / फ़ाइल 0x160; fileoff = VA-0x8000):
- `amzn_is_root_allowed` @0x2d5b8 = `amzn_is_dev_unlocked() && (fos_flags & 0x2)`
- `amzn_is_adb_auth_disable_allowed` @0x2d5e8 = `fos_flags & 0x20` (ungated)
- `amzn_is_dev_unlocked` @0x2d5fc = `/proc/cmdline` में `androidboot.prod=0` **या**
`androidboot.unlocked_kernel=true` है
- `fos_read_debug_flags` @0x2d724 `/proc/idme/<name>` पढ़ता है और **हेक्स** पार्स करता है
- `restart_root_service` @0xcb74 / `restart_unroot_service` @0xcc64
- स्ट्रिंग्स: `amzn_fos: ADB: Auto-root succeeded`, `… eng_device=%d`, `… unlocked_kernel=%d`,
`adbd cannot run as root in production builds`, `ro.debuggable`
- `adb root` → "cannot run as root in production builds" (`ro.debuggable=0`) — इसलिए auto-root गेट
संतुष्ट होने पर भी, AOSP prod चेक कमांड पथ को गेट करता है।
### Session-10 रूट स्थायी क्यों नहीं होता
- SELinux permissive + सेल हाईजैक + रूट केवल रनटाइम हैं।
- `/data/metrics` एक **vpartition** है: `/system/bin/vpartition.sh` हर बूट पर `/data/vp/metrics.img`
(ext4, non-nosuid/noexec) को `/data/metrics` पर माउंट करता है; वहाँ लिखा गया `su` रीबूट के बाद **नहीं**
बचता। (यही कारण है कि setuid `su` ने uid 0 दिया लेकिन **शून्य caps**।)
### Track A (बूट-टाइम पुनः-एक्सप्लॉइट) — अवरुद्ध
- कोई init `.rc` ट्रिगर नियंत्रणीय कोड निष्पादित नहीं करता (सभी आयात सत्यापित; `persist.*` ट्रिगर केवल
`start` निश्चित सेवाएँ; `/system`/`/vendor` में स्क्रिप्ट्स)।
- रूट सेवाएँ `/data` कॉन्फ़िग्स पढ़ती हैं लेकिन उनसे कभी exec नहीं करतीं (`perfmonitord`, `amazonfiled`,
`vpartition.sh`, `kisd`, …)।
- एकमात्र बूट executor = एक **ऐप**, लेकिन एक्सप्लॉइट का paused फुटप्रिंट **VmRSS 534 MB**
(`kbm` स्प्रे) है → lmkd इसे मार देता है; साथ ही एक खोया reclaim पैनिक करता है (`PANIC_ON_OOPS`) → bootloop।
- **adbd auto-root** मौजूद है लेकिन LK-built cmdline (`prod=0`/`unlocked_kernel=true`) पर गेटेड है।
### निष्कर्ष / अगला लक्ष्य (चुना गया: Track B RE)
सब कुछ LK को `eng`/`unlocked` रिपोर्ट कराने पर निर्भर है। पहुँच में:
`androidboot.prod=1|0` और `androidboot.unlocked_kernel=false` LK द्वारा सेट किए जाते हैं। LK को रिवर्स करें ताकि पता चले:
1. यह `fos_flags`/`dev_flags`/`usr_flags` (`K*` आइटम) कहाँ पढ़ता है और सटीक गेट क्या है;
2. `prod`/`unlocked` निर्धारण (IDME आइटम? buildvariant? `amzn_verify_unlock` परिणाम?);
3. `amzn_verify_unlock` (libtomcrypt RSA verify) बायपास या कमजोर unlock_code/version पथ के लिए;
4. `seccfg`/`para`/`ENV_v1`(LK_ENV) स्टोरेज (किसी भी डंप किए गए पार्टीशन में नहीं — शायद tee-protected);
5. preloader (`boot0`, `EMMC_BOOT`) में बग के लिए।
यदि इनमें से कोई भी हमें eng/unlocked सेट करने देता है (स्थायी रूप से, boot1 या रॉ पार्टीशन राइट के माध्यम से), तो
`FOS_FLAGS_DM_VERITY_OFF` system(root) verity को अक्षम कर देता है और `/system` को स्थायी रूप से पैच किया जा सकता है।
### आर्टिफैक्ट्स (इस सत्र से)
`/tmp/opencode/mustang-dumps/` (होस्ट रीबूट पर साफ़ हो सकता है): `lk.img`, `boot1.img` (प्रिस्टीन),
`boot.img`, `MISC.img`, `metadata*.img`, `pmt.img`, `mbr.img`, `kb.img`, `dkb.img`, `reserved.img`,
`cache.img`, `boot0.img`, `boot1.img`, `adbd.bin`, `perfmonitord.bin`, `amazonfiled.bin`।
हेल्पर्स: `tools/findinitnet.py`, `findgadget*.py`, `findstores.py`, `adbd_sym.py` (/tmp में);
रेपो में `run.sh`, `poc/stage3.c` (`selroot`), `poc/su.c`, `rootcmd.sh` हैं।
### उपयोगी कमांड```
# IDME read
/data/metrics/su sh -p -c 'for f in fos_flags dev_flags usr_flags serial region device_type_id unlock_version; do echo -n "$f="; cat /proc/idme/$f; echo; done'
# write boot1 (root; su lives only until reboot -> re-run run.sh first)
/data/metrics/su sh -p -c 'echo 0 > /sys/block/mmcblk0boot1/force_ro; dd if=/data/local/tmp/boot1.img of=/dev/block/mmcblk0boot1 bs=4096 count=4; sync; echo 1 > /sys/block/mmcblk0boot1/force_ro'
# dump a partition to host
adb exec-out '/data/metrics/su dd if=/dev/block/by-name/lk bs=4096 2>/dev/null' > lk.img
लक्ष्य: LK को डिवाइस को eng/unlocked मानने के लिए बाध्य करना, या preloader/LK बग खोजना,
ताकि verity/SELinux को स्थायी रूप से अक्षम किया जा सके। परिणाम: प्रासंगिक
LK कोड पथ को end-to-end उलट दिया गया; उपलब्ध स्टोर के माध्यम से यह फ्लिप पहुँच से बाहर है।
कोई डिवाइस ब्रिक नहीं हुआ; एक boot1 प्रयोग को मूल स्थिति में वापस लाया गया।
lk.img एक छोटे ARM स्टब (फ़ाइल 0x200) से शुरू होता है। 0x224 पर स्थित relocator
0x200 से एक लिटरल गंतव्य तक कॉपी करता है और एक लिटरल एंट्री पर ब्रांच करता है:
इसलिए 0x200 से अधिक या बराबर ऑफ़सेट के लिए रनटाइम एड्रेस = 0xFF400000 + फ़ाइल ऑफ़सेट।
स्टब के बाद सब कुछ Thumb-2 है, position-independent। स्ट्रिंग्स
ldr rT,[pc,#imm] (T1 ऑफ़सेट = imm8*4; ldr.w ऑफ़सेट = imm12) के बाद
add rT, pc से बनाई जाती हैं; लक्ष्य (add+4) + *pool है। एक मजबूत स्कैनर जो ARM स्टब और लिटरल पूल्स को पार कर लेता है, tools/lk_xref.py के रूप में जोड़ा गया (16- और
32-बिट रूपों को संभालता है, हर 2 बाइट्स स्कैन करता है)। नीचे दिए गए सभी ऑफ़सेट फ़ाइल ऑफ़सेट हैं;
रनटाइम एड्रेस के लिए 0xFF400000 जोड़ें।
0x20b4 unlock_code IDME आइटम (0x400 बाइट्स, इस यूनिट पर सभी शून्य) पढ़ता है और amzn_verify_unlock (0x222c -> 0x20f0) चलाता है। वह फ़ंक्शन libtomcrypt को चलाता है (दर्जनों /features/libtomcrypt/src/pk/asn1/der/... पथ और
RSA verify), और इमेज में सर्टिफिकेट सामग्री एम्बेडेड है:
Sunnyvale / Amazon Lab126 / "$Common Kernel Signing Engineering CA0" 0x317d9+ पर, साथ ही डायग्नोस्टिक्स
Image FAILED AUTHENTICATION on PRODUCTION device (0x3166e),
Authentication failed on engineering device with production certificate (0x316a0),
Image FAILED AUTHENTICATION on ENGINEERING device (0x31703),
Image AUTHENTICATED with PRODUCTION certificate (0x31736)।
कोई खाली-कोड / लंबाई / संस्करण शॉर्टकट नहीं है: verify(zeros) != 0, इसलिए
( में पुष्टि की गई)। या को फ्लिप करने के लिए या तो एक वैध
Amazon-हस्ताक्षरित (निजी कुंजी अनुपलब्ध) या वेरिफायर में एक कोड-एक्ज़ीक्यूशन बग की आवश्यकता होती है। 0x20b4/0x222c/0x20f0 में स्थिर रूप से कुछ भी शोषणीय (bounds/size) नहीं पाया गया। =>
सुरक्षा फ्लैग 0x57c पर गेटर के माध्यम से पढ़े जाते हैं। अनुभवजन्य परीक्षण:```
dd if=/dev/block/mmcblk0boot1 ... ; reboot /proc/idme/fos_flags -> 00000080 (persisted, Android sees it) ro.boot.veritymode -> eio (unchanged!) root=/dev/dm-0 dm="system none ro,0 1 android-verity PARTUUID=..." (unchanged) androidboot.prod=1 / secure_cpu=1 / buildvariant=user (unchanged)
`fos_flags=0x80` `FOS_FLAGS_DM_VERITY_OFF` है; डिकोड किया गया गेट verity को बंद कर देता **अगर** getter ने इसे लौटाया होता। ऐसा नहीं हुआ। गेट जीवित है, डेड कोड नहीं: इसका one-time cache sentinel इमेज में `-1` है
(`*(u32*)0x50c74 == 0xffffffff`), इसलिए फ़ंक्शन ने वास्तव में
`check_flag("fos_flags",0x80)` पथ निष्पादित किया और 0 प्राप्त किया। इसलिए getter (कम से कम
verity-protection समय पर) boot1 IDME आइटम्स को **नहीं** पढ़ रहा है।
अन्य संभावित स्टोर **LK env** है, जो वस्तुतः
`"para"` नामक पार्टीशन से लोड होता है (loader 0x12fd4, magic `ENV_v1`, checksum @0x3ffc)। LK की स्वयं की
पार्टीशन तालिका (0x4fcc0..0x50340) में preloader/proinfo/nvram/protect1/
protect2/persist/seccfg/secro/**para**/logo/custom/expdb/tee1/tee2/metadata/
system/cache/userdata सूचीबद्ध हैं — लेकिन टैबलेट के वास्तविक GPT में **केवल 16 entries** हैं, सभी
प्रकार `af3dc60f838472478e793d69d8477de4` की:
#0 proinfo 0x400 #1 PMT 0x1c00 #2 kb 0x4000 #3 dkb 0x4800 #4 lk 0x5000 #5 tee1 0x5800 #6 tee2 0x8000 #7 metadata 0xa800 #8 MISC 0x1e400 #9 reserved 0x1e800 #10 boot 0x22800 #11 recovery 0x2a800 #12 system 0x34800 #13 vendor 0x644000 #14 cache 0x6b4800 #15 userdata 0x7ae800
इस उत्पाद पर **कोई `para`, `seccfg`, `nvram`, `protect`, या `persist` पार्टीशन नहीं है** (और `PMT`/`pmt.img` डंप सभी शून्य हैं)। इसलिए LK env खाली है, `Kfos_flags`/`Kdev_flags` कुंजियाँ कभी मौजूद नहीं होतीं, और सभी `fos_flags`/`dev_flags` जाँचें 0 पर हल होती हैं — चाहे IDME आइटम में कुछ भी हो। boot1 IDME आइटम Android (`/init.fosflags.sh`, `adbd`, `/proc/idme/*`) द्वारा उपभोग किए जाते हैं, लेकिन LK के सुरक्षा गेट्स द्वारा नहीं।
### निष्कर्ष — स्थायी eng/unlock क्यों अवरुद्ध है
1. `unlocked_kernel` के लिए Amazon-हस्ताक्षरित `unlock_code` (RSA/libtomcrypt, एम्बेडेड CA) आवश्यक है। ऑफ़लाइन जाली नहीं बनाई जा सकती; कोई verifier बग नहीं मिला। **हार्ड ब्लॉक।**
2. `DM_VERITY_OFF` / `selinux=permissive` फ़्लैग LK env (`para`/`ENV_v1`) से उपभोग किए जाते हैं, जो इस GPT पर मौजूद नहीं है। IDME `fos_flags` को LK द्वारा अनुभवजन्य रूप से अनदेखा किया जाता है (0x80 बना रहा, verity `eio` ही रहा)। **हार्ड ब्लॉक** जब तक पार्टीशन टेबल संशोधित न हो।
3. एक सफल `fos_flags=0x80` भी केवल `androidboot.veritymode=disabled` और एक गैर-`dm-0` `root=` सेट करेगा; यह अनलॉक नहीं करेगा, और SELinux को permissive होने के लिए उसी अनुपस्थित env से `dev_flags` की आवश्यकता होगी।
4. इसलिए Track A (बूट-समय पुनः-शोषण) ठीक SESSION 11 की तरह ही अवरुद्ध है: इसका एकमात्र अनलॉक पथ वही LK गेट है।
### शेष मार्ग (भविष्य, उच्च जोखिम; प्रयास नहीं किया गया)
- **एक `para`/`ENV_v1` स्टोर संश्लेषित करें**: `userdata` के बाद खाली स्थान में `para` नाम की GPT प्रविष्टि जोड़ें (प्राइमरी + बैकअप GPT दोनों को अद्यतन करना होगा) (userdata LBA 0x3a3dfde पर समाप्त होता है; डिस्क = 30535680 सेक्टर), फिर `fos_flags=0x80` और `dev_flags=0x40` के साथ एक env बनाएँ (चेकसम +0x3ffc पर = 0x3ffc पर बाइट योग)। verity-off का यह एकमात्र शेष मार्ग है। जोखिम: प्राइमरी/बैकअप GPT को भ्रष्ट करना ब्रिक कर सकता है; और यह **सिद्ध नहीं हुआ** कि verity गेट वास्तव में `para` पढ़ता है (केवल यह कि यह boot1 IDME नहीं है)।
- **Preloader (`boot0`/`EMMC_BOOT`) बग**: इस सत्र में रिवर्स नहीं किया गया। boot0 लिखना तब तक वर्जित है जब तक एक प्राचीन प्रति और एक रिकवरी पथ मौजूद न हो।
- **Verifier शोध**: इंजीनियरिंग-प्रमाणपत्र पथ (0x316a0/0x31703) केवल तभी पहुँच योग्य है जब एक डिवाइस पहचान "इंजीनियरिंग" के रूप में स्वीकृत हो और इंजीनियरिंग कुंजी द्वारा हस्ताक्षरित कोड हो; कोई निजी कुंजी उपलब्ध नहीं है।
### आर्टिफैक्ट्स / पुनरुत्पादकता
- जोड़ा गया टूल: `tools/lk_xref.py` — बेस-स्वतंत्र LK स्ट्रिंग-xref रिज़ॉल्वर।
- उपयोग किए गए डंप: `/tmp/opencode/mustang-dumps/lk.img`, `boot1.img` (प्राचीन), `boot0.img`, `mbr.img` (GPT), `pmt.img` (सभी शून्य)।
- boot1 प्रयोग इमेज (fos_flags=0x80) `/tmp/opencode/s12/boot1_f80.img` पर रखी गई; **डिवाइस प्राचीन boot1 पर पुनर्स्थापित** (सत्यापित `/proc/idme/fos_flags` -> `0`)।
### उपयोगी कमांड (root आवश्यक; `./run.sh` से पुनः-सशस्त्र करें)```
# re-arm runtime root (~1/3 per boot)
./run.sh --no-build
# confirm LK's decisions without a UART
/data/metrics/su /system/bin/sh -p -c 'cat /proc/cmdline'
# watch: root=/dev/dm-0 dm="system ... android-verity ..." (verity on)
# androidboot.veritymode=eio ; androidboot.selinux=enforce ; prod=1
# IDME read (Android copy; NOT what LK's gates use)
for f in fos_flags dev_flags usr_flags unlock_version serial; do cat /proc/idme/$f; echo; done
1949:20ff = MTK प्रीलोडर, HID ट्रांसपोर्ट)जब बंद अवस्था में USB में प्लग किया जाता है, तो टैबलेट 1949:20ff
(Lab126) के रूप में एन्यूमरेट होता है — न Android और न ही 0e8d:0003 बूटरोम। डिस्क्रिप्टर:```
bInterfaceClass 3 (HID), iConfiguration "HID", iInterface "HID Interface"
HID report descriptor = 05 01 09 00 a1 01 c0 (empty collection!)
EP 0x81 IN interrupt 4 bytes, bInterval 4
EP 0x01 OUT interrupt 4 bytes, bInterval 4
iSerial = GCC0X90805310009 (the IDME serial)
**पहचान।** `0x20FF` को mtkclient की `config/usb_ids.py` में **"MTK Preloader"** के रूप में सूचीबद्ध किया गया है (MediaTek VID `0x0e8d` के अंतर्गत: `0xe8d:{0x0003 Brom, 0x2000/0x2001/0x20ff/0x3000 Preloader}`)। Amazon ने preloader PID को बनाए रखा और VID को `0x1949` में बदल दिया, तथा इसे एक dummy report descriptor के साथ HID endpoint pair के रूप में प्रस्तुत किया। तो यह **MediaTek preloader / USBDL mode** है, जो LK से *नीचे* का एक चरण है — यहाँ power-off + plug द्वारा पहुँचा जाता है, CMD short द्वारा नहीं।
descriptor strings "HID"/"HID Interface" `lk.img`, `boot0.img`, `boot.img` या अन्य dumps में मौजूद नहीं हैं, अर्थात् यह mode किसी ऐसे component द्वारा उत्पन्न होता है जिसे हमने dump नहीं किया (bootrom/TEE) या runtime पर assembled होता है।
**यह क्यों मायने रखता है।** `aftv2-tools` द्वारा उपयोग किया जाने वाला Amazon preloader इसी exact byte stream पर built-in, **Download-Agent-less** commands को expose करता है:```
handshake : host A0 0A 50 05 -> dev 5F F5 AF FA
0xD1 read32 (addr, n_words) : echo cmd/addr/n, 00 00, n*u32, 00 00
0xD4 write32(addr, words[]) : echo cmd/addr/n, 00 00, n*u32, 00 00
aftv2-tools/read_mmc.py read32/write32 का उपयोग MSDC कंट्रोलर को पोक करने के लिए करता है
(MT8173 पर बेस 0x11230000; MT8163 के लिए सत्यापित करें) और raw eMMC
ब्लॉक को पढ़ता/लिखता है, बिना DA के और इस प्रकार बिना AVB/verity के रास्ते में। यदि mustang का preloader
0xD1/0xD4 स्वीकार करता है, तो यह स्थायी अनलॉक (patch boot /
lk) का सीधा रास्ता है, जो RSA अनलॉक कोड और अनुपस्थित LK env से स्वतंत्र है।
lsusb -d 1949:20ff # note Bus/Dev, e.g. Bus 001 Device 003 sudo chmod 666 /dev/bus/usb/001/003
nix-shell -p python3Packages.pyusb --run
'python3 tools/mtk_preloader_hid.py handshake'
nix-shell -p python3Packages.pyusb --run
'python3 tools/mtk_preloader_hid.py read32 0x00100000 4'
`tools/probe_preloader.py` न्यूनतम handshake-only probe है;
`tools/mtk_preloader_hid.py` पूर्ण transport है (`handshake`/`read32`/
`write32`; `write32` guarded है और eMMC register map की पुष्टि होने तक इसका उपयोग नहीं किया जाना चाहिए)।
### स्थिति / अगले कदम
- **अपुष्ट**: क्या mustang का preloader वास्तव में 0xD1/0xD4 implement करता है
(handshake probe यह तय करता है)। यदि करता है, तो aftv2 eMMC read/write
path संभवतः सीधे port किया जा सकता है।
- फिर: MT8163 MSDC base खोजें (kernel DT या preloader), एक partition dump करें
(`read_mmc`), फिर preloader से `boot.img`/`lk` patch करें और reboot करें।
- यह SESSION 12 में सब कुछ से *निचला* boot stage है, इसलिए यह
`amzn_verify_unlock` या LK env gate पर निर्भर नहीं करता।
- protocol और eMMC layout की पुष्टि होने से पहले इसके विरुद्ध SP Flash Tool / mtkclient writes न चलाएँ।
pi_blocked_onfutex.c:1975 this->rt_waiter pass करता है,
जो futex_wait_requeue_pi में futex.c:2880 पर declared है) → waiter arm32 select (nr 142) fd_sets
के ज़रिए अपना ही freed frame stamp करता हैDEBUG_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 vmlinux में self-located;
KALLSYMS_ALL off इसलिए data symbols को यह trick चाहिए) → unknown-binfmt exec → root
script (setenforce 0, disable OTA, su)refs/ में references: NebuSec/CyberMeowfia (original), GhostLock-5.10 (Fire OS 8 port,
src/exp32/ में पूरा 32-bit ARM trigger), ghostlock-...-4.19-k40 (Qualcomm 4.19 Android port)selrootselinux_state.enforcingcommit_creds(&init_cred)uid=0jcnr_extresinfo->gpu_alloc_addr के ज़रिए लिखा जाता है (एक GPU VA जिसे आपको
pre-allocate करके pass करना होता है)| evictable object | pressure | result |
|---|
| none | 900 MB | survived |
| none | 1300 MB | panic (system lowmem bug — unrelated) |
| normal region + DONT_NEED | 700 MB | survived |
| JIT region + DONT_NEED | 700 MB | panic in reclaim path |
kernel/config-* — running device से /proc/config.gz dumpksrc/ — Amazon OSS source (platform.tar + extracted midgard-r26p0 tree)/tmp/opencode/mustang_ota.bin (sha256 6068515a… fireos-archive से match करता है)
और 2.2 GB kernel-source tarball ~/Desktop/amazon-mustang/ पर रखा हैksrc/kernel/mediatek/mt8163/4.9/drivers/misc/mediatek/gpu/gpu_mali/mali_midgard/midgard-r26p0/| renames stall (journal/GFP_NOFS) → 128 total → garbage deref |
| pre-drain 12K events + pressure + small trailing | crash at deref |
| + kill-child-at-eviction (10ms poll) | crash at deref |
| + cpu0-pinned lifecycle (drain3) | crash at deref |
| stepped pressure (drain4 v1) | children freed memory on exit → no eviction (validated legal path) |
| run | परिणाम |
|---|
| pmap G=c2a412a4 400MB | deref पर क्रैश |
| pmap G=c2f4b2a4 480MB | क्रैश; +0 post-kill renames (480MB fs को घुटता है) |
| iso2 (spray + regions + oracle) | REGION HIT — स्प्रे reclaim नहीं तोड़ता |
| mix v1 (समवर्ती events+regions) | क्रैश; confounded (घूमते event थ्रेड्स) |
| pmap G=c2a7d2a4 350MB + retouch | क्रैश; +4452 renames OK |
| mix2 (क्रमिक: 2s events फिर regions) | क्रैश; +7126 renames (28K event allocs), 2715 regions |
fn=0PM_PAGE+NF_CELL_OFFprobekernel_x_end के ऊपर XN है: arch/arm/mm/mmu.c
map_lowmem() lowram को kernel text MT_MEMORY_RWX के नीचे map करता है, लेकिन
kernel_x_end के ऊपर सब कुछ MT_MEMORY_RW → PMD_SECT_XN (line 509)।
0xc154b600 पर baked shellcode prefetch-abort करता है। Payload एक असली kernel
function pointer होना चाहिए, physmap में code नहीं।| लिटरल (फ़ाइल ऑफ़सेट) | मान | अर्थ |
|---|
| 0x270 | 0xFF4002F8 | str r4,[r6] स्क्रैच |
| 0x274 | 0xFF40027C | गंतव्य (base+0x27C) |
| 0x278 | 0xFF54A440 | कॉपी अंत (BSS सहित) |
| 0x27C | 0xFF400484 | एंट्री पॉइंट |
| ऑफ़सेट | फ़ंक्शन |
|---|
0xdf7c | is_secure_or_prod() -> byte[ [[g]+0 ] + 0x163 ]; g = global @0x52838। इस यूनिट पर 1। |
0x20b4 | verify_stored_unlock() = memset(buf,0,0x100); 0x57c के माध्यम से IDME/env unlock_code (0x100) पढ़ता है; bl 0x222c; (verify==0) लौटाता है। |
0x222c / 0x20f0 | amzn_verify_unlock(code,len) — libtomcrypt RSA/PKCS#1 verify (नीचे देखें)। |
0xda3e | is_unlocked() = is_secure_or_prod() && verify_stored_unlock()। |
0x29a28 | is_verity_disabled() = (fos_flags & 0x80) && !(is_secure_or_prod() && verify_stored_unlock()); global @0x50c74 में कैश किया गया। |
0x29974 | SELinux cmdline बिल्डर: dev_flags & 0x20 -> androidboot.selinux=enforce, dev_flags & 0x40 -> ...=permissive (प्रत्येक byte[+0x162] द्वारा गेटेड)। |
0x118xx/0x11bxx | kernel cmdline बिल्डर (unlocked_kernel, prod=1/0, verifiedbootstate, rpmb_state, secure_cpu, संस्करण, root=)। |
0x27af8 | UART गेट: fos_flags & 0x4 -> printk.disable_uart=0, अन्यथा =1। |
0x12fd4 | LK env लोडर: पार्टीशन "para", 0x4000 बाइट्स, मैजिक ENV_v1, चेकसम = 0x3ffc पर वर्ड की तुलना में 0x3ffc पर बाइट्स का योग। |
0x1efd0 | नाम से पार्टीशन लुकअप ("para", "boot", ... के लिए उपयोग किया जाता है)। |
0x57c | कॉलबैक स्लॉट @0x58218 के माध्यम से गेटर डिस्पैचर; स्लॉट @0x58200..0x5821c 0x5a8-0x734 पर एक टेबल से रजिस्टर किए जाते हैं। |
0x2a19c | fastboot oem unlock: bl 0x222c(code,len); सफल होने पर 0x408 के माध्यम से unlock_code (0x100) लिखता है। |
unlocked_kernel=false/proc/cmdlineandroidboot.unlocked_kernelandroidboot.produnlock_code