daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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)