Share
## https://sploitus.com/exploit?id=C749C9BF-21A8-56DF-8C76-F0A96A5EC766
# CVE-2026-43499 (GhostLock) – XRing O1 Exploit Research

> Target: Xiaomi Pad 7 Ultra (jinghu)/Xiaomi 15S Pro (dijun), XRing O1 SoC 
> Kernel: 6.6.77-android15-8-g5770c661275f-abogki443185593-4k 
> Reference: Littlenine Ennea’s dijun case study 
> Nature: Kernel security research / Vulnerability exploitation and adaptation 

## Attack Chain

```
[1] kernelsnitch leaks the mm_struct (futex hash side channel) 
 ↓ No device interface dependency, pure side channel 
[2] prepare_good_kernel_page: Heap spraying (fake_task/fake_w0/fake_fops) 
 ↓ skb spray to the kernel SLUB mechanism 
[3] futex_wait_requeue_pi leaves a dangling pi_blocked_on (stack UAF) 
 ↓ rt_mutex_cleanup_proxy_lock skips remove_waiter 
[4] pselect reuses the same stack; core_sys_select overrides the waiter field 
 ↓ waiter->lock = &pselect_user_lock (user space, PAN-negative) 
[5] consumer sched_setattr triggers rt_mutex_adjust_prio_chain 
 ↓ PI chain: waiterβ†’lockβ†’owner(fake_task)β†’fake_w0.pi_tree 
[6] rb_insert writes *(target)=value (write at arbitrary address) 
 ↓ write_left = target-8, write_pc = value 
[7] Writing task->cred = init_cred + selinux_enforcing=0 β†’ root 

## Real-machine testing progress (2026-07-18)

Stage-by-stage testing (test_minimal.so, STAGE 1-7):

| Stage | Test Content | Result |
|---|---|---|
| STAGE 1 | Basic initialization (disable_rseq/set_limit/pin_to_core) | OK |
| STAGE 2 | Futex basics (FUTEX_LOCK_PI/UNLOCK_PI) | OK |
| STAGE 3 | kernelsnitch_setup (cpu_count=10) | OK |
| STAGE 4 | clone_leak_child (find_collisions) | OK |
| STAGE 5 | Brute-force leak of mm_struct | OK β˜… Successful leak |
| STAGE 6 | prepare_good_kernel_page (spray) | OK β˜… Page prepared |
| STAGE 7 | run_main_route_threads (PI chain triggered) | OK β˜… Consumer succeeded 40 times |

**Example of mm_struct leak caused by kernelsnitch**: ffffff80186c5500, ffffff8109969400, ffffff818fe72d00

**PI chain trigger log**:
```
pselect returned attempt=1 ret=0 errno=0 calls=40 success=40 delay=50000
```

## Current bottlenecks

### 1. Values written by the PI chain are not write_pc (most critical)

The PI chain trigger was successful (consumer executed 40 times for sched_setattr), but the write did not take effect (SELinux remained Enforcing). Reason: The value written by rb_insert might be **the stack address of waiter’s pi_tree_entry** (stack address, not 0), not write_pc. Test: `set_pselect_write(SELINUX_ENFORCING-8, 0, 0)` expected `*selinux_enforcing=0`, but SELinux remains Enforcing. Need to disassemble the `rb_insert` part of `rt_mutex_adjust_prio_chain` (0xffffffc081052868) to confirm the actual written value. 

### 2. From mm_struct to task_struct (reading mm->owner)

kernelsnitch leaked the mm_struct address. To write `task->cred`, the task_struct address is required, which involves reading mm_struct + 0x408 (mm->owner). However, all known kernel read paths are unavailable:
- configfs_read_once has a bug (see pitfall)
- `/dev/ashmem` is blocked by SELinux (Permission denied)
- `/proc/self/stat` has kstkesp=0 (kptr_restrict)
- BPF/debugfs is unreadable

Available resource: `/dev/uhid` (crw-rw-rw-, readable by everyone) β€” pending research

### 3. `/dev/ashmem` blocked by SELinux

`/dev/ashmem` has permissions 666, but SELinux blocks shell domain access. This prevents verification of the configfs path through fops. Direct writing of `cred` does not require ASHM verification, but reading `mm->owner` does. 

## Pitfalls encountered

### Pitfall 1: Configfs_read_once failure is not a fix for Android 16

Disassembly of `configfs_bin_read_iter` (0xffffffc080488c9c) confirms:
- Correct offsets for CFG_* fields (0x50/0x58/0x60/0x64 match exactly)
- The failure is due to a code bug: setting needs_read_fill=0 and not setting buffer_size results in 0
- This bug exists on Pixel devices as well; `configfs_read_once` may never succeed

### Pitfall 2: Route deviation (fops hijacking vs. cred direct writing)
- **dijun** uses cred direct writing: `mm leaked` β†’ two walks of `write_task` β†’ `cred/real_cred=init_cred`.
- **jinghu** uses fops hijacking: writes to `ashmem_misc_fops` β†’ verifies with `configfs` β†’ leaks `kernel_base` β†’ installs `root`.
- The fops route depends on `configfs`; cred direct writing is simpler.

### Pitfall 3: `page_base=0` issue
In STAGE 6, `page_base` is set, but in STAGE 7, `do_pselect_fake_lock_route` sees `0`. The reason is unclear (possibly multi-threaded visibility or compiler optimizations). Solution: Reimplemented `prepare_good_kernel_page` in STAGE 7.

### Pitfall 4: `pr_error` calls `exit(-1)`
When SYSCHK fails, `pr_error` calls `exit(-1)`, causing the process to exit after `ashmem_open` fails, and subsequent results cannot be seen. Solution: Redefine `pr_error` to not exit, or modify `open_ashmem_device` to not involve SYSCHK.

### Pitfall 5: `rt_mutex_waiter` layout
Disassembly verification shows that `pi_tree_entry` is at `+0x28` (not `+0x18`), with additional `prio/deadline` fields. `fake_task.pi_blocked_on` must point to the start of the `fake_w0` structure (not `pi_tree_entry`), otherwise the second read of the `lock` field is incorrect.

### Pitfall 6: `psselect` does not block
`readfds` is all zero, and `exceptfds=NULL` β†’ `do_select` returns immediately with `ret=0` without checking any files. Fix: Set `exceptfds` to `pipe read end`, making `psselect` block until a timeout occurs.

### Key data
- Addresses (KASLR=0, vmlinux β†’ runtime minus 0xc080000000)
 - `KIMAGE_TEXT_BASE = 0xffffffc080000000`
 - `init_task = 0xffffff80020de280`
 - `init_cred = 0xffffff80020f0548`
 - `selinux_state = 0xffffff8002315f68`
 - `ashmem_misc_fops = 0xffffff800223b5e8`

### `task_struct` offsets
- `cred=+0x818`, `real_cred=+0x820`, `pi_blocked_on=+0x938`
- `pi_waiters=+0x920`, `real_parent=+0x628`, `comm=+0x830`

### `mm_struct` offset
- `owner=+0x408` (MM_OWNER_OFF)

### `rt_mutex_waiter` layout (disassembly verification)
- `+0x00 tree_entry` (rb_node 24B)
- `+0x18 prio` (u32)
- `+0x20 deadline` (u64)
- `+0x28 pi_tree_entry` (rb_node 24B) – used for PI tree traversal
- `+0x40 pi_prio` (u32)
- `+0x50 task` (ptr)
- `+0x58 lock` (ptr) – key field

### Writing primitives
- `set_pselect_write(target-8, value, 0)`
 - `fake_w0.pi_tree.rb_left = target-8`
 - `fake_w0.pi_tree.__rb_parent_color = value`
 - `PI chain rb_insert: *(target) =???` (to be confirmed whether it’s `value` or stack address)

### Key functions in disassembly
- `ashmem_open` = `0xffffffc080c7af5c` (struct ashmem_area=0xdc0, name@+0x0)
- `configfs_bin_read_iter` = `0xffffffc080488c9c`
- `configfs_read_iter` = `0xffffffc080488978`
- `rt_mutex_adjust_prio_chain` = `0xffffffc081052868`
- `rt_mutex_cleanup_proxy_lock` = `0xffffffc081052634`

## Pending tasks (actions needed)
### Priority 1: Confirming the value written in the PI chain
Analyze the `rb_insert` part of `rt_mutex_adjust_prio_chain` to determine whether the written value is `write_pc` or the stack address. This determines whether any value can be written.

### Priority 2: Solving the issue with reading `mm->owner`
Find a kernel reading method that doesn’t rely on `configfs/ashmem`. Read from `mm_struct+0x408` to get the `task_struct`. Candidates: `/dev/uhid`, `perf_event`, `eBPF`.

### Priority 3: Child mm β†’ parent task
`kernelsnitch` runs in `clone_leak_child`, leaking the child mm. To write the parent cred, read from `child_task->real_parent(+0x628)` to get the parent `task_struct`. Or change `kernelsnitch` to run on the parent.

### Priority 4: Multiple walks (slot mechanism)
`dijun` uses two walks (`slot_idx=1` writes `real_cred`, `slot_idx=2` writes `cred+selinux`). The slot mechanism needs to be implemented.

### Priority 5: Crash at #0x800
The `rt_mutex_setprio` function triggers a bug when the `pi_blocked_on` path is not empty (about 20 rounds). `dijun` avoids this by using `csettle_us=500` (microsecond timing).

## Pending tasks (actions needed)

## File List

```
share-poc-XRing-O1/
β”œβ”€β”€ README.md Project overview (attack chain/progress/holes/critical data/tasks)
β”œβ”€β”€ RESEARCH_NOTES.md Research notes (bug principles/technical approach/breakthroughs)
β”œβ”€β”€ docs/
β”‚ └── findings.md Summary of key findings (configfs bug/kernelsnitch work/PI chain write issues)
β”œβ”€β”€ src/ Full source code (compilable)
β”‚ β”œβ”€β”€ test_minimal.c Phase-by-phase test programs (STAGE 1-7: isolation/panic points)
β”‚ β”œβ”€β”€ main_cred.c Cred direct writing framework (run_exploit 3 times: walk through cred writing)
β”‚ β”œβ”€β”€ target.h Jinghu offset configuration (address/struct offsets)
β”‚ β”œβ”€β”€ common.h Core header files (global variables declarations/macro definitions/extern declarations)
β”‚ β”œβ”€β”€ util.c kernelsnitch encapsulation + prepare_kernel_page + configfs_read_once
β”‚ β”œβ”€β”€ fops.c do_pselect_fake_lock + PI chain triggering + cfi verification
β”‚ β”œβ”€β”€ slide.c KASLR bypass (boot_id writing verification)
β”‚ β”œβ”€β”€ pipe.c Pipe phase (install_pipe_physrw)
β”‚ β”œβ”€β”€ root.c install_android_root (root installation after privilege escalation)
β”‚ β”œβ”€β”€ offset.h Target configuration entry point (include target.h)
β”‚ β”œβ”€β”€ su_daemon.c su daemon (root shell service)
β”‚ β”œβ”€β”€ su_blob.S Embedded su_daemon binary (.incbin, requires build/embed/resources)
β”‚ β”œβ”€β”€ wallpaper_blob.S Embedded wallpaper binary (.incbin, requires assets/resources)
β”‚ └── kernelsnitch/ Core implementation of kernelsnitch leak (futex hash side channel)
β”‚ β”œβ”€β”€ kernelsnitch.h Complete implementation of mm_struct leak (450 lines, brute-force search)
β”‚ β”œβ”€β”€ futex_hash.h Futex hashing function (hash bucket calculation)
β”‚ β”œβ”€β”€ timeutils.h Time measurement tools (rdtsc etc.)
β”‚ └── utils.h Macros for pr_info/pr_error/SYSCHK etc.
β”œβ”€β”€ disasm/ Disassembly of key functions (extracted from vmlinux_extracted)
β”‚ β”œβ”€β”€ chain_disasm.txt β˜… rt_mutex_adjust_prio_chain (core of PI chain! Confirm rb_insert write value)
β”‚ β”œβ”€β”€ do_select_disasm.txt do_select (pselect stack layout, waiter coverage)
β”‚ β”œβ”€β”€ do_select_full_disasm.txt Full disassembly of do_select
β”‚ β”œβ”€β”€ core_sys_select_disasm.txt core_sys_select (register overwriting of waiter field)
β”‚ β”œβ”€β”€ futex_requeue_full.txt Full process of futex_wait_requeue_pi (stack UAF trigger)
β”‚ β”œβ”€β”€ futex_wait_requeue_pi_full.txt futex_wait_requeue_pi (vulnerability trigger point)
β”‚ └── adjust_pi_disasm.txt rt_mutex_adjust_pi (entry of PI chain)
β”‚ └── refs/ Reference data
β”‚ β”œβ”€β”€ kallsyms.txt Kernel symbol table (766 bytes, some key symbols)
β”‚ └── kernel_config.txt Kernel configuration (219KB, extracted from /proc/config.gz)
```

### Compilation Instructions

The.incbin resources for su_blob.S/wallpaper_blob.S (build/embed/su_daemon_aarch64_pie, assets/wallpaper.webp) are not included in the repository. These resources need to be obtained from the CyberMeowfia upstream (IonStack/CVE-2026-43499/exploit/). The compilation command is as follows:
```bash
# Required to be in the CyberMeowfia/exploit directory (incbin resources provided)
NDK=android-ndk-r27d/toolchains/llvm/prebuilt/windows-x86_64
clang --target=aarch64-linux-android35 --sysroot=$NDK/sysroot \
 -O2 -fPIC -I src -DTARGET_CONFIG_H="targets/jinghu/target.h" \
 src/test_minimal.c src/main.c src/util.c src/slide.c src/fops.c \
 src/pipe.c src/root.c src/su_blob.S src/wallpaper_blob.S \
 -shared -o test_minimal.so -pthread -fuse-ld=lld
```

## Contact

Those with research interests or ideas are welcome to discuss. Key contact points: **Confirmation of rb_insert write values in the PI chain + kernel reading of mm->owner**. ---

*This research is solely for security research and defense purposes.*