On Android 16, a regular app with no special permissions can leak the user’s real IP, even with “Always-On VPN” + “Block connections without VPN” turned on. Those two settings are supposed to be the hard guarantee that nothing leaves the device outside the tunnel. They don’t hold here.
The trick is that the app doesn’t send the packet itself. It hands the bytes and a destination tosystem_server(UID 1000, exempt from VPN routing), then exits. A moment latersystem_serveropens a UDP socket on the physical Wi-Fi interface and fires those bytes at the destination. The VPN never sees them. The destination sees your real public IP. Lockdown filters app UIDs, not system ones, so the packet walks straight past the gate the user explicitly locked.
According to Mullvad, the Android team is not discussing the issue publicly and it's already patched in GrapheneOS:
The bug was reported to the Android Security Team, but was closed as Won’t Fix (Infeasible), as described in the linked article. After consulting with the report author (https://x.com/cybaqkebm), we reported the issue on the Android issue tracker. However, at the time of writing the issue is marked as inaccessible by Google for unknown reasons.
In contrast, GrapheneOS, a security-focused Android-based OS, quickly patched the issue in its codebase.
GrapheneOS with the incredibly common W
I want off this ride.
GrapheneOS patch adds a UID 1000 guard in ConnectivityService before the socket bind. Stock 15/16 still lets any app hand off to system_server and leak the real interface.