Part 2: How Third-Party Developers Closed the tun0 Leak in AmneziaVPN on Android

Third-party contributors to AmneziaVPN have detailed their fix for a VPN tunnel bypass affecting excluded applications on Android. The vulnerability allows any app, even those disallowed from the VPN, to bind sockets directly to the tun0 interface using SO_BINDTODEVICE and thereby discover the VPN server address. The team implemented a packet filter inside the client that queries Android via ConnectivityManager.getConnectionOwnerUid to determine packet ownership and drops traffic from unknown UIDs. The solution was integrated into both the Xray and AmneziaWG traffic paths, with the AmneziaWG hook placed inside amneziawg-go after packet parsing. Testing with leak_probe.py showed zero successful bypass attempts out of six methods when the filter was active, compared to six out of six without it. The developers submitted three pull requests and released a side-loaded test build, while noting remaining limitations such as raw sockets and tethering scenarios.

HabrTranslated from Russian

Read at source

Third-party developers have published a detailed technical breakdown of how they closed a long-standing VPN tunnel leak affecting AmneziaVPN on Android. The issue, first reported in issue #2457, allows applications excluded from the VPN tunnel to bind sockets directly to the tun0 interface and thereby learn the address of the VPN server.

The root cause lies in Linux kernel behavior on versions 5.7 and newer. When an excluded application calls setsockopt(SO_BINDTODEVICE, "tun0"), the kernel falls back to treating the destination as an on-link neighbor if no matching routing rule exists for the UID. This bypasses the UID-based split-tunneling rules installed by VpnService and addDisallowedApplication.

The fix introduces a packet filter that inspects every outbound packet read from the tunnel descriptor. For each new flow the filter calls ConnectivityManager.getConnectionOwnerUid through a JNI bridge to obtain the UID. If the returned UID is INVALID_UID or belongs to the VPN client itself, the packet is dropped. In exclude mode this single rule effectively blocks all bypass attempts because excluded applications are never reported as owners.

The implementation was added to both traffic paths inside AmneziaVPN. On the Xray path the check sits inside the gVisor stack before SOCKS connections are created. On the AmneziaWG path a lightweight hook was placed inside amneziawg-go immediately after packet padding, maintaining a small cache of UID decisions to avoid repeated JNI calls.

Performance measurements on Android 16 showed no measurable increase in connection latency at 5000 new flows per second. When disabled, the filter adds only a single atomic read per packet and contributes no code to non-Android builds.

The authors stress that they are not official maintainers and that the three submitted pull requests remain unmerged. A side-loaded test build is available for verification. The article concludes with an extensive list of remaining edge cases, including raw sockets, IPv6, tethering, and certain UDP behaviors, together with a checklist for other VPN clients wishing to implement similar protections.

Meridian digestThe week's most consequential security stories, distilled into one briefing.See the digest

More in Vulnerabilities & Exploits

CISA Adds Cisco Secure Firewall and GitLab Flaws to Known Exploited Vulnerabilities Catalog
Telegram for Android Patches Voice Call Vulnerability Enabling Code Execution
Yandex Patches Millions of Stations After eMudhra Certificate Keys Compromised