android-vpn-bypass.md (4778B)
1 --- 2 title: "Android VPN Bypass" 3 section: "Mobile" 4 sectionSlug: "mobile-pentesting" 5 sourcePath: "src/mobile-pentesting/android-app-pentesting/android-vpn-bypass.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/mobile-pentesting/android-app-pentesting/android-vpn-bypass.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Android VPN Bypass 14 15 ## Hidden Binder APIs, Confused Deputies and VPN Lockdown Bypasses 16 17 A hidden Java API can remain reachable when its underlying **Binder transaction is exposed to apps** and the service omits a caller-identity or permission check. A useful triage pattern is: **untrusted app → Binder registration of attacker-controlled state → privileged system component later replays that state**.<sup>[[1]](#references)[[3]](#references)</sup> 18 19 Typical red flags: 20 21 * A Binder method accepts a **`ParcelFileDescriptor`**, socket-like object, or raw **`byte[]`** payload from an app. 22 * The AIDL/service path has **no** `@EnforcePermission`, `enforceCallingOrSelfPermission()`, `checkCallingPermission()`, UID allowlist, or SELinux restriction. 23 * The privileged side later **recreates the socket or packet** as `system_server`/system UID and sends it on a network chosen from saved metadata. 24 * The payload is treated as protocol-specific data but the implementation never validates that it really matches the expected frame type. 25 26 The following pattern was reported in the Android 16 QUIC graceful-close implementation. Treat transaction numbers and method layouts as build-specific; confirm them against the target framework rather than copying a constant from another release.<sup>[[1]](#references)</sup> 27 28 1. The app enumerates visible networks with `ConnectivityManager.getAllNetworks()` and `getLinkProperties()`. 29 2. Instead of `Network.bindSocket()` (which enforces VPN lockdown), it creates `new DatagramSocket(new InetSocketAddress(<physical-ip>, 0))`. That path reaches the kernel `bind()` syscall, which only checks whether the local IP exists on an interface. 30 3. A later UDP `connect()` does **not** transmit traffic but helps associate the socket with the physical network `netId`/`SO_MARK`. 31 4. The app passes the connected socket plus attacker-controlled bytes to a Binder method. 32 5. When the socket dies, a privileged component recreates the flow and sends the bytes as **UID 1000**, bypassing per-app VPN policy because the policy is enforced against the original app UID, not the privileged deputy. 33 34 Minimal direct-transaction pattern from an app when the SDK method is hidden but the Binder entry point is reachable:<sup>[[1]](#references)[[2]](#references)</sup> 35 36 ```java 37 IBinder svc = (IBinder) Class.forName("android.os.ServiceManager") 38 .getMethod("getService", String.class).invoke(null, "connectivity"); 39 Parcel data = Parcel.obtain(); 40 try { 41 data.writeInterfaceToken("android.net.IConnectivityManager"); 42 data.writeTypedObject(pfd, 0); 43 data.writeByteArray(payload); 44 svc.transact(TXN_REGISTER, data, null, IBinder.FLAG_ONEWAY); 45 } finally { 46 data.recycle(); 47 } 48 ``` 49 50 Practical review workflow: 51 52 * Decompile `framework*.jar`, APEX jars and vendor service jars, then map `Stub.onTransact()` cases to method names and parameter types. 53 * Prioritise transactions that combine **network objects + raw bytes + delayed execution**. 54 * Check whether the privileged path calls helpers such as `Network.bindSocket()`, `Os.write()`, or creates a fresh `DatagramSocket`/`Socket` using attacker-influenced metadata. 55 * Compare **kernel-local operations** (`bind()`, UDP `connect()`) with **Android policy-aware wrappers** (`Network.bindSocket()`) because the former may shape metadata without triggering the higher-level policy gate. 56 * For confirmation, register the state, kill the app/process, and watch for a later packet from the device's real interface or another action performed by the privileged service. 57 58 Defensive clues: 59 60 * Binder methods that should be protocol-specific must validate the payload format instead of accepting arbitrary bytes. 61 * Privileged services should re-check the **original caller UID/network policy** before replaying traffic or reconstructing sockets on behalf of apps. 62 * Platform updates are the primary remediation for a system-service flaw; application-layer VPNs cannot reliably patch a confused deputy in `system_server`. 63 64 ## References 65 66 - [1] [The Tiny UDP Cannon: An Android VPN Bypass](https://lowlevel.fun/posts/tiny-udp-cannon-android-vpn-bypass/) 67 - [2] [0x33c0unt/quic-vpn-bypass PoC](https://github.com/0x33c0unt/quic-vpn-bypass) 68 - [3] [Android Open Source Project — Binder overview](https://source.android.com/docs/core/architecture/ipc/binder-overview)