Android NAT-T keepalive offload bypasses VPN lockdown
An unprivileged Android app can bypass Always-on VPN lockdown and leak physical network traffic via the public NAT-T socket-keepalive offload API.
read it Essential reading for mobile security engineers tracking Android networking internals and persistent VPN confinement failures.
summary
Android's Always-on VPN lockdown fails to block traffic sent via the public NAT-T socket-keepalive offload API. A normal unprivileged application can create an encapsulation socket and initiate keepalives that route directly through physical Wi-Fi hardware, circumventing per-UID VPN policy and leaking the real source IP. The root cause stems from a 2019 framework change where public encapsulation sockets were merged into a raw-fd Binder method, and subsequent resource ownership validation checks were reverted. Runtime confirmation across Pixel, Samsung, and Nothing devices proves this is a device-class exposure affecting modern Android versions.
key points
- The vulnerability requires no root, ADB, or dangerous permissions, only standard networking capabilities like INTERNET.
- Keepalive offload packets emit below the ordinary application socket layer, completely evading VPN lockdown firewall and routing rules.
- Historical code analysis shows resource validation and lifetime locking were briefly added in 2019 but reverted due to deadlock concerns.
who should read it
Android security researchers, OS developers, and network administrators managing mobile VPN deployments.
discussion skeptical
consensus
Commenters express frustration that Google closed the bug report without action, though GrapheneOS acknowledges the issue and is exploring fixes. Many note that Android's complex networking architecture has persistent edge cases that make airtight traffic confinement difficult.
pushback
Some debate whether Google's bug triage indicates policy limitations or willful neglect, while others discuss usability trade-offs of mandatory device PIN requirements for VPN lockdown.
Android has a way to bind the socket to the interface: Network.bindSocket, this is a setsockopt(SO_BINDTODEVICE) wrapper with access control.
The access to it is controlled by the VPN application. Some applications could be allowed to connect directly when the VPN is active and routing all the traffic by default, some could use VPN if configured not to use it by default.
However starting with Linux kernel 5.7, the unprivileged userspace can now call setsockopt(SO_BINDTODEVICE) directly and use VPN or non-VPN interface even if restricted by the VPN client.
Not fixed in any Android (incl. …
It introduces a related unpatched kernel-level VPN bypass vector involving unprivileged SO_BINDTODEVICE usage.
Generated by an LLM from the article and thread. Check the sources before relying on it.