Sploitus

Exploit for Incorrect Authorization in Qualcomm Aqt1000 Firmware

githubexploit · 2026-08-20

Exploit Code

README101 lines
## https://sploitus.com/exploit?id=AA2FA77B-3D49-5C79-AB20-4A11831C6BFE
# CVE-2025-21479 – Qualcomm Adreno GPU privilege escalation vulnerability exploitation (SM8475 / iQOO Neo8)

The local privilege escalation experiment code for **CVE-2025-21479** targets the Qualcomm Adreno GPU (KGSL) on the **vivo V2301A** (iQOO Neo8 / PD2301, Snapdragon 8+ Gen 1 / SM8475, Adreno A730, Android 14, kernel version 5.10). In principle, the vulnerability exploitation involves using the `CP_SMMU_TABLE_UPDATE` command of KGSL to control the SMMU of the GPU, thereby gaining ** arbitrary physical memory read/write** capabilities. This allows disabling SELinux, locating kernel symbols, and obtaining a root shell—without the need to flash the device or unlock the Bootloader. > **This is limited to security research only on devices that you own or have authorized access to.**

---

## What does it do?

1. **Physical GPU memory primitives.** There are defects in the access checks for the microcode of Adreno A730 (eureka_panther), where `$12, 0x3` incorrectly treats the *Self-Draw-State* (SDS) packet as a *RingBuffer* (RB). Therefore, by placing the forged `CP_SMMU_TABLE_UPDATE` into the SDS block in user mode, the GPU’s TTBR0 can be overwritten, causing the GPU page table to point to arbitrary physical pages—allowing the GPU to read from and write to arbitrary physical memory.

2. **Stabilized read/write operations.** The mapped memory is hardened into a 2 MB self-mapped area, supporting fast bulk reads using functions like `fast_pread_bytes`.

3. **Disabling SELinux.** The `selinux_state.enforcing` field in physical memory is cleared. Subsequently, the kernel image is dumped from the fixed physical base address `0xA8000000` (default limit: 43 MB)—since SM8475 lacks physical KASLR. The kallsyms structure is directly parsed from the raw dump, so virtual KASLR does not affect it.

4. **Root payload.** A segment of AArch64 shellcode (`prepare_kernel_cred(0) → commit_creds()`, with seccomp disabled) is used to patch `__do_sys_capset`. The forked child process calls `capset()`, becoming withuid 0, restoring the overwritten kernel code. The child process then executes the specified command again with the domain `u:r:shell:s0` (default `sh`). The final kernel code remains identical to before execution.

## Test device

| Model | vivo V2301A (iQOO Neo8, codename `PD2301`, motherboard `taro`) |
| SoC | SM8475 (Snapdragon 8+ Gen 1) + Adreno 730 |
| Android | 14 (`UP1A.231005.007`, SDK 34, qssi-user) |
| Kernel | `5.10.198-gki-gfb2f5774872c` |
| SELinux | Enforcing (disabled during testing)**

Full device information can be found in `device_info.txt`.

## Repository structure

```
├── source/
│   ├── cheese.c            # Main exploit program (includes two files below)
│   ├── kallsyms_lookup.c   # Kernel symbol resolver for the original kernel image
│   └── adrenaline.h        # KGSL ioctl/command definitions (by hawkes)
├── exploit                 # Precompiled ARM64 binary (dynamic linking, minSdk 21, NDK r30-beta2)
│   ├── device_info.txt         # Information about the target device
│   └── README.md
```

Note: `cheese.c` includes `kallsyms_lookup.c` and `adrenaline.h`. These three files must be placed in the same directory during compilation.

## Compilation

Any recent Android NDK (r25+) can be used. Example:

```sh
# Static linking (self-contained, runs on any API level; larger size)
aarch64-linux-android34-clang -o cheese source/cheese.c -static
```

# Dynamic linking (smaller size, depends on the device’s built-in libc; API level must match the device’s requirements)

aarch64-linux-android34-clang -o exploit_dyn source/cheese.c  
# To be compatible with devices running API 21+, use aarch64-linux-android21-clang instead.  

## How to use  
```sh  
adb push exploit /data/local/tmp/  
adb shell chmod 755 /data/local/tmp/exploit  

# Enter the interactive root shell  
adb shell /data/local/tmp/exploit  

# Or execute any command as root  
adb shell /data/local/tmp/exploit sh -c 'id'  
```  
After successful execution, you will get a shell with `uid=0`, and SELinux is disabled for this session. After gaining privilege, the process will re-execute `argv[1..]` (defaulting to `sh`). Each execution includes a countdown ("pte wait 3 second") and a progress message showing 43 MB of kernel reading ("Read X / 43 MB").  

### Environment variables  
| Variable | Description  
|---|---  
| `CHEESE_PHYADDR=` | Forces the specified physical address (skips the built-in `gPhyAddrs` detection table).  
| `CHEESE_ATTEMPT=` | Uses `gPhyAddrs[n]` instead of `gPhyAddrs[0]`. If GPU initialization fails, `maybe_retry` will automatically retry.  
| `CHEESE_SWAPPER_PG_DIR_OFF=` | Skips the swapper_pg_dir scan and uses the specified offset directly.  
| `CHEESE_READ_FULL_KERNEL=1` | Reads the complete kernel image (default limit: `DEFAULT_KERNEL_SIZE_MB` = 43 MB).  
| `CHEESE_KALLSYMS_MAX_MB=` | Overrides the maximum size for kallsyms reading.  
| `CHEESE_FORCE_MANUAL_PATCHFINDER=1` | Forces manual patchfinder use (for `init_cred`, `prepare_kernel_cred`).  
| `CHEESE_SKIP_GPU=1` | Skips GPU read/write phases (for debugging purposes only).  
| `CHEESE_NO_RETRY=1` | Exits immediately after failure, without retrying.  

## Porting to other devices  
Device-related constants are located at the top of `source/cheese.c`:  
- `KERNEL_PHYS_BASE` — Physical address of the kernel (default: `0xA8000000`, SM8475).  
- `IDMAP_PG_DIR_OFFSET` — Offset for `idmap_pg_dir` (default: `0x245a000`).  
- `selinux_state_offset` — Offset for locating `selinux_state` (default: `0x729030`; actual physical address = `idmap_pg_dir physical address + this offset`).  
- `gPhyAddrs[]` — List of candidate physical addresses to try.  
- `DEFAULT_KERNEL_SIZE_MB` — Default limit for kernel reading.  

Prerequisites: Adreno A730/A7xx series GPUs (affected firmware), kernels with resolvable kallsyms in physical dumps, and no physical KASLR.  

### Common issues  
- **Can’t get GPU read/write**: Failed GPU initialization. Try `CHEESE_ATTEMPT=1..n` or manually specify `CHEESE_PHYADDR`. For debugging, add `CHEESE_NO_RETRY=1`.  
- **Kallsyms parsing failed**: First, use `CHEESE_READ_FULL_KERNEL=1` to read the complete image (with an increased limit using `CHEESE_KALLSYMS_MAX_MB`), then consider `CHEESE_FORCE_MANUAL_PATCHFINDER=1`. This tool does not write or persistently store anything: the kernel code and page tables are restored before termination; the device remains identical after restarting.  

### Thanks  
- [@zhuowei](https://github.com/zhuowei) — Researcher of CVE-2025-21479, also author of the open-source “cheese” vulnerability exploitation framework used by this tool.  
- cheese / cheese-cake open-source lineage — SDS/TTBR0 techniques, kernel-dump-based kallsyms parsing.  
- hawkes — 2020 “adrenaline.h” KGSL header file, providing ioctl interface definitions.  

### Disclaimer  
This repository is intended solely for educational and security research purposes. Please use it on devices that you own or have authorized access to. The author assumes no responsibility for any misuse of this tool. Using this tool may void your warranty or violate relevant terms of service.