celzero / rethink-app

DNS over HTTPS / DNS over Tor / DNSCrypt client, WireGuard proxifier, firewall, and connection tracker for Android.
https://rethinkfirewall.com/
Apache License 2.0
2.94k stars 148 forks source link

Bypass Private DNS #25

Closed ignoramous closed 3 years ago

ignoramous commented 4 years ago

Private DNS (supported on Android 9+), when set causes the system to ignore's VPN's DNS endpoint. This means, the app cannot effectively run or capture or block DNS requests at all.

Either find a way to bypass Private DNS, or disable the DNS option in the homescreen when Private DNS is set.

Private DNS is detectable through [LinkProperties#isPrivateDnsActive](https://developer.android.com/reference/android/net/LinkProperties#isPrivateDnsActive()) and could be fetched either from [LinkProperties#getPrivateDnsServerName](https://developer.android.com/reference/android/net/LinkProperties#getPrivateDnsServerName()) or [LinkProperties#getDnsServers](https://developer.android.com/reference/android/net/LinkProperties#getDnsServers()).

LinkProperities are updated on private dns changes as seen in the log below. Check if there's a broadcast or for Vpn to find out when such changes happen.

08-14 19:05:19.633  2588  3648 D QCNEJ/WlanStaInfoRelay: Updating link properties: {InterfaceName: wlan0 LinkAddresses: [ 0000::0000:0000:0000:1ac4/64,192.168.1.5/24 ] DnsAddresses: [ /1.1.1.1,/176.103.130.130 ] UsePrivateDns: true PrivateDnsServerName: dns.adguard.com ValidatedPrivateDnsAddresses: [176.103.130.130] Domains: null MTU: 0 TcpBufferSizes: 524288,1048576,4194304,524288,1048576,4194304 Routes: [ fe80::/64 -> :: wlan0,::/0 -> 0000::0000:0000:0000:1ac4 wlan0,192.168.1.0/24 -> 0.0.0.0 wlan0,0.0.0.0/0 -> 192.168.1.1 wlan0 ]}
ignoramous commented 4 years ago

There's no broadcast but potentially NetworkRequests can be sent to ConnectivityManager be notified of changes in NetworkCapabilities of a given Network. Setting / removing Private DNS triggers a NetworkCapbilities change or so we think by the way of: ConnectivityService#updateLinkProperties -> ConnectivityService#notifyNetworkCallbacks. Read also: DnsManager.java.

ignoramous commented 4 years ago

Even though ConnectivityManagerService#networkRequiresValidation returns false for TRANSPORT_VPN (because it is not NETWORK_NOT_VPN which the ConnectivityManagerService$mDefaultRequest requires), Private DNS transport is used by netd by the way of underlying TRANSPORT_*s type such as WIFI and CELLULAR (Private DNS didn't override VPN's DNS settings prior to Android 10. On Android 10, this commit made its way that meant VPN DNS is now overridden by the Private DNS setting). There' no way to prevent NetworkMonitor's probe, too, since it is resolved using Private DNS.

Our best bet is to write a heuristic to prevent resolution of Private DNS ip-addresses when netd tries to validate Private DNS host after it receives notification from ConnectivityManagerService by the way of NetworkMonitor#CMD_EVALUATE_PRIVATE_DNS by hooking on to ConnectivityManagerService$NetworkCallback#CALLBACK_IP_CHANGED and racing to block Private DNS host resolution.

ignoramous commented 4 years ago

Hot take: Blocking uid 0 on port 853 would do the trick.

What could go wrong...

ignoramous commented 4 years ago

What goes wrong is the device is without any internet connectivity when root uid 0 isn't allowed to connect on 853 post private dns validation by netd.

The check to determine if a private dns entry is valid bypasses the VPN (even in "lockdown mode" where connections outside of the VPN are typically not allowed under any circumstances and it doesn't matter if the apps are excluded from the VPN or not): system/netd/server/dns/DnsTlsTransport.cpp#188

ignoramous commented 4 years ago

So, on an Android One device, Private DNS is disabled when port 853 is blocked or DNS name resolution of the server is NXDOMAINd. NetworkMonitor then goes into a lifelong journey to keep resolving/connecting/validating the now blocked Private DNS hostname / endpoint and occasionally sends out captive_portal probes.

But, a lot of other apps continue to connect to the Internet just fine (TCP logs in the app indicate that much), but curiously enough, the DNS requests aren't going through the VPN anymore (DNS logs are scant and only show requests from NetworkMonitor's cursade to connect to the Private DNS endpoint).

ignoramous commented 4 years ago

May be this is the hint (LinkProperties' DNS servers set as the default whilst validation is being queued for Private DNS): android/server/connectivity/DnsManager.java#411? So, aren't these routed to the VPN (Vpn.Builder#addDnsServer right now only adds VPN's DNS server unlike other apps like Nebulo and NetGuard that always add the current LinkProperties DNS server)? If so, here's a potential DNS leak.

ignoramous commented 4 years ago

It turns out that some apps (WhatsApp, Instagram, Telegram to name a few) cache DNS responses and/or use pre-embed IP addresses when DNS resolution fail. Chrome and Firefox however couldn't resolve when port 853 is blocked and Private DNS was enabled... like we expect it to. WhatsApp et al continue to work since they do not exclusively rely on network/os provisioned DNS (DNS leaks do happen when Private DNS is blocked and netd falls back to use "system resolver" (block port 853) and VPN is set to Allow Bypass... but that's expected and by design.) That is, apps like Instagram and WhatsApp are able to connect to their servers without functioning DNS anyway (via bootstrapped IPs).

ignoramous commented 3 years ago

From Universal firewall page, users can now suppress all connections going through the tunnel to IPs that weren't resolved by user-set DNS.

Impl in #354 part of v053g.