Abusing WebRTC to Reveal Coarse Location Data in Signal
UPDATED 5/20/2020: The blog post was updated to reflect the availability of updated versions of Signal on the Google Play Store and Apple App Store.
Signal Private Messenger’s ease of use, multiplatform support, and end-to-end encryption for both text and calls have attracted millions of users per day. Even Edward Snowden, the well known American Whistleblower, claims “I use Signal every day.”
So this month, when I disclosed a way to leak a user’s DNS server simply by ringing their Signal number (CVE-2020–5753), I was happy to see how fast they patched it. Revealing a Signal user’s DNS server can potentially reveal coarse location, but as we will later see, in instances such as Google Public DNS (188.8.131.52/184.108.40.206) and others, this attack can narrow the location down to the Signal user’s city due to usage of EDNS Client Subnet.
The Signal team let us know that updated versions for Android and iOS will be available. Signal is an open source application and due to our disclosure policy, we disclose issues once any patch or information pertaining a vulnerability goes public, such as this case. From our investigation, the affected Android versions are Signal v4.59.0 and up, while for iOS the affected WebRTC update was introduced in 220.127.116.11.
For certain Signal users, this issue could be quite serious, while average users aren’t as likely to be impacted. It’s worth mentioning that this is not an issue in Signal’s code, but due to WebRTC doing DNS requests. Other messaging apps could also be vulnerable to this. Signal has since notified the Chromium team and submitted a proposed patch. Those discussions are ongoing.
Signal uses its own fork of WebRTC for voice/video calls with remote peers. In order for WebRTC to connect with a remote peer, it must discover a valid connection path for the local and remote peer to communicate. WebRTC makes use of “signalling” for this, which involves using an intermediate signaling server that exchanges each peer’s public/private IP addresses (ICE Candidates) so that each peer can discover each other and eventually establish an optimal connection.
As a protection, Signal will not send your public/private IP addresses if a user receives a call from someone who is not a contact. Additionally, if a Signal user wishes to hide their private/public IP addresses even from contacts who call, then it has an option “Always Relay Calls” in its privacy options, which will never include your public/private IPs in the ICE Candidates. Instead, it will just send the IP of a nearby Signal TURN server for the calling peer to connect to and relay the audio/video call through it instead. One interesting thing I observed about this signaling process is that it occurs before a Signal user decides to answer the call. I thought this could lead to an interesting attack surface, with the goal of having a completely unknown phone number ringing a remote Signal user’s phone to reveal some kind of network information even if they have “Always Relay Calls” enabled. Can this be done?
Exposing DNS Servers by Ringing Phone
Signal being open source made my life a little easier in figuring out how to do this. I mentioned some of these ICE Candidate protections in place. Due to these protections, I was forced to find something clever in order to circumvent and somehow leak information. In reading the RFC for ICE, I found that it supports domain names for ICE Candidates.
<connection-address>: is taken from RFC 4566 [RFC4566]. It is the IP address of the candidate, allowing for IPv4 addresses, IPv6 addresses, and fully qualified domain names (FQDNs).
And in 2018, WebRTC had support added for this https://webrtc-review.googlesource.com/c/src/+/85540/16. Since Signal uses WebRTC under the hood, I figured we could just slip in a domain name for one of the ICE Candidates so that when I ring a phone (and trigger the ICE Candidate exchange) the callee will have no other choice but to resolve the domain name I supply. If I own the authoritative nameserver for the domain I supply, I can log the IP that tries to query my nameserver. That IP address will be the DNS server being used by the remote Signal user, which is typically geographically near(ish) to the user. Again, the callee will perform this domain query before the call is even answered. In fact, I found that I could easily force a DNS lookup before the phone even “rings”, then shut down the call, so all the callee would see is “missed call” notification — no ring. Meanwhile, on my nameserver side, we see an incoming DNS query from the remote Signal user’s current DNS resolver.
To implement this, I simply modified this routine in Signal and compiled Signal App to my “attacker” phone. https://github.com/signalapp/Signal-Android/blob/3f7d0688fc76fd56ef0a562ae5293d91a67d6500/app/src/main/java/org/thoughtcrime/securesms/service/WebRtcCallService.java#L1688
In it, I added additional ICE Candidates to the iceCandidateParcels list. These additional ICE Candidates had my domain in them (which I own an authoritative nameserver for).
For each ICE Candidate (as well as each call), I generate a unique subdomain for two reasons:
- Identify the specific incoming requests in my nameserver logging
- Ensure it will bypass any DNS caching, that way I’m always forcing the callee’s DNS server to query my nameserver.
After testing this multiple times in various locations I found the DNS location accuracy was usually accurate within ~400 mile radius depending on ISP/DNS configuration and could answer questions such as, what country or sometimes part of the country someone is currently in. One other thing to consider is not only location identification through a DNS server, but identifying location through ISP switches. For example, I am at home now…exploiting myself with a burner Signal phone …I leak the DNS server that my phone is currently using on my home network and I see it’s a DNS server from Cox ISP in Southern California…which is correct, as I live in Southern California and use Cox ISP.
I now go outside and walk to Starbucks across the street, as I left the house and am now on 4G, I exploit myself again with another mysterious missed call to leak my new DNS server being used…
Ok…looks like it’s a Verizon DNS server up in San Jose, indicating I’m probably away from any known Wi-Fi network on a mobile carrier.
I then connect to Starbucks Wi-Fi. I did another Signal DNS leak call on myself which revealed an OpenDNS server IP address in the Southern California area.
With this information, an attacker knows I’m not home at this time. If the attacker cared more, they could even build a profile of frequent DNS servers I use to map out my usual locations such as “at work,” “at home,” “at coffee shop,” “at friend’s house,” etc.
I tested this with a few other locations and networks as well. Here is a fellow employee who volunteered for this test. The DNS server in this case came from Pennsylvania, which is the same state he resided in at the time I did the test.
A couple odd DNS curve balls did come. For example, in one instance I was in California connected to some restaurant’s Wi-Fi network and it had me resolving queries from…Miami, FL. Another test with a different coworker in Pennsylvania resolved from Toronto for one of the network tests, which was still around ~400 miles away but definitely pushing it.
In these instances, a repetitive attack over a week could better narrow down location as there will be more DNS servers leaked to better map out general location using multiple data points, additionally, if one of these DNS servers throughout an attack ends up using EDNS Client Subnet (such as Google Public DNS), then we can leak much finer location information…
Further Narrowing Location via EDNS Client Subnet
Some DNS servers, such as the commonly used Google Public DNS (18.104.22.168/22.214.171.124), use EDNS Client Subnet. This will forward part of the originating client’s IP address (First 3 octets) to the nameserver. This makes location narrowing much more fine tuned for this attack. Below we see a snippet explaining EDNS Client Subnet.
This allows resolvers to pass in part of the client’s IP address (the first 24/56 bits or less for IPv4/IPv6 respectively) as the source IP in the DNS message, so that name servers can return optimized results based on the user’s location rather than that of the resolver.
Let’s see some examples. In this scenario, we leak a volunteer’s DNS server in the midwest that is using EDNS Client Subnet. Our nameserver was forwarded the first 3 octets of the user’s ACTUAL IP address (last 2 octets blurred) in the Additional Records/Client Subnet field of the request.
Looking up this IP subnet, it narrowed the location just a couple miles away from his actual location.
I then tested this on myself, as I happened to be on a network using Google Public DNS. After ringing my number from my burner phone, we ended up revealing the exact city I was in after looking up this forwarded Client Subnet.
One of the biggest issues with this is that you cannot prevent some unknown Signal number from ringing your phone and trying to reveal your location/ISP info. It’s also very common to be on a network that is using EDNS Client Subnet at some point in your normal phone usage, which we have seen pinpoints your location down to the city. If you are concerned with this, I recommend updating Signal Android to version 4.59.11 or Signal iOS to version 3.8.4. If you are unable to update to these versions, I recommend using a mobile VPN app that tunnels DNS traffic. In my testing I found Private Internet Access and Psiphon VPN apps do just this and prevent leak from working.