iOS 15 iCloud Private Relay Vulnerability Identified
Tags: opensource, javascript, webdev, computerscience
Published: 2021-09-20 19:06:49

*Apple’s new iCloud Private Relay service allows users to hide their IP addresses and DNS requests from websites and network service providers. In this article, we’ll demonstrate how this security feature can be circumvented and discuss what users can do to prevent their data from being leaked.*

*You’ll need to turn on iCloud Private Relay to test the vulnerability. At the moment iCloud Private Relay is available only in Safari on iOS 15 for iCloud+ subscribers.*

# [Try the demo on our blog](https://fingerprintjs.com/blog/ios15-icloud-private-relay-vulnerability/)

*Please note that this leak only occurs with iCloud Private Relay on iOS 15—the vulnerability has been fixed in MacOS Monterey beta.*

## IP addresses and online privacy

Online privacy is a hot topic of debate these days. Internet users don’t like being tracked online; on the other hand, advertisers need to glean user behavior insights to display the most relevant (and profitable) ads. Unfortunately, this requires access to sensitive and private information that users may not be willing to share.

Your IP address is one such piece of information used for [tracking your activity](https://hal.inria.fr/hal-02435622/document) across websites. These numerical labels (e.g., `1.2.3.4`) are relatively stable and unique even if they aren’t dedicated, and can be used to determine your location with building-level precision.

Another way interested parties can track your online activity is to analyze your domain name system (DNS) requests. Like users and their devices, a website is also identified by its IP address. However, visitors need only type in the associated domain name (e.g., example.com) into the address bar, since DNS does the hard part of translating alphanumeric domain names into numeric IP addresses for proper routing.

This of course makes the internet a lot more user friendly, but also creates another way for interested parties to track your online behavior. DNS requests contain the names of the websites you visit, so they can be used to see your browsing history and your interests. DNS requests are unencrypted unless DNS-over-HTTPS is implemented, allowing your internet service provider and other parties between you and the DNS server to see your unencrypted DNS requests.

## What is iCloud Private Relay

Browser vendors (most notably Apple) have in recent years made a concerted effort to improve the security and privacy of their users. iCloud Private Relay is Apple’s latest feature for protecting users against these online tracking techniques.

According to [Apple’s website](https://www.apple.com/):

> “iCloud Private Relay is a service that lets you connect to virtually any network and browse with Safari in an even more secure and private way. It ensures that the traffic leaving your device is encrypted and uses two separate internet relays so no one can use your IP address, location, and browsing activity to create a detailed profile about you.”

The service works by proxying network/HTTP traffic (including DNS requests) from the Safari browser, as well as unencrypted HTTP traffic from applications. By doing this, Apple claims that network providers no longer can see your DNS requests and unencrypted HTTP traffic; similarly, websites visited will only see your iCloud-assigned proxy IP address.This address is drawn from a pool shared between multiple iCloud Private Relay users, grouped by their approximate location (Apple provides a [public table](https://mask-api.icloud.com/egress-ip-ranges.csv) of proxy IPs/locations).

The following diagram from Apple illustrates how iCloud Private Relay handles HTTPS requests and what each party sees:

![iCloud Private Relay Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nthk4i475ihbgqt1bn48.png)

iCloud Private Relay is available exclusively for iCloud+ subscribers running iOS 15 or macOS 12 Monterey with the Safari browser. Unfortunately, it’s unavailable in [several countries](https://www.reuters.com/world/china/apples-new-private-relay-feature-will-not-be-available-china-2021-06-07/) due to regulatory limitations.

You can learn more about iCloud Private Relay in [this video](https://developer.apple.com/videos/play/wwdc2021/10096/) from Apple.

## How to get the real IP of a client that uses iCloud Private Relay

If you read the IP address from an HTTP request received by your server, you’ll get the IP address of the egress proxy. Nevertheless, you can get the real client’s IP through WebRTC. The process is described in detail below.

If you want to go straight to the leak explanation, skip the following “what is” sections.

### What is WebRTC

WebRTC (web real-time communication) is a browser API for websites to establish direct communication between website visitors (i.e., peer-to-peer). The communication allows sending and receiving audio, video, and arbitrary data between browsers without requiring an intermediate server. All modern browsers support WebRTC natively; for example, Google Hangouts is one of the more popular applications that uses WebRTC — it works with all browsers and launches meetings with a click/tap, no plugin installation required.

WebRTC is a complex API. More information is available from [this Google guide](https://codelabs.developers.google.com/codelabs/webrtc-web/); however, we will only cover the aspects required for the IP address leak.

In order for browsers to communicate with each other, they first need to connect. This sounds simple, but in actuality is not a trivial task because— unlike servers—a website visitor’s device has no public IP to connect to. The ICE protocol was created to solve this problem.

### What is ICE

ICE (interactive connectivity establishment) is a framework used by WebRTC. It lets two browsers find and connect with each other for direct, peer-to-peer communications.When one browser wants to connect to another, it will collect all possible hosts in a process called “collecting ICE candidates”. An ICE candidate is a piece of text that includes the host (IP address or domain name), port, protocol and other information. The browser will return the ICE candidates to the browser application.

The following is an example of an ICE candidate (see the full format description in the [RFC document](https://datatracker.ietf.org/doc/html/rfc5245#section-15.1)):

![ICE candidate structure](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mcjs1v300tc9yn6kurik.png)

Let’s say Alice wants to connect to Bob. Alice’s browser will collect all its ICE candidates and send them to Bob through the website’s server. This is the only time when a server is needed, any further communication will happen directly between Alice and Bob. When Bob receives Alice's ICE candidates from the server, it will try to connect to Alice using the addresses and ports from the list until it finds one that works.

Different types of ICE candidates exist—for this demonstration, we’re concerned with the Server Reflexive Candidate. You can recognize it by a `typ srflx` substring. It contains an IP address and a port from a STUN server that allows Bob to connect through Alice's NAT.

### What are NAT and STUN

[NAT](https://en.wikipedia.org/wiki/Network_address_translation) (network address translation) is a protocol that allows multiple devices to connect to the internet using a single internet connection and public IP address. Every home router implements NAT—in fact, your device is most likely behind NAT now.

Devices inside a network using NAT have no public IP addresses, so they can’t be accessed from the internet directly. The [STUN](https://en.wikipedia.org/wiki/STUN) (session traversal utilities for NAT) protocol was created to solve this problem.

A STUN server performs one small but critical task: it returns your public IP address and port number. We won’t cover NAT traversal in-depth (you can [learn more here](https://tailscale.com/blog/how-nat-traversal-works/)); just know that Alice gets an ICE candidate containing her public IP address and port returned by the STUN server, and Bob can connect to her using this IP address and port.

This is what happens when a WebRTC connection is established from behind a NAT:

![WebRTC connection diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3sfn8tc7qyyxczk95rfd.png)

WebRTC requests two types of IP addresses from the STUN server: IPv4 and IPv6. If the STUN server and the user’s network supports IPv6, WebRTC will receive 2 ICE candidates: one with the IPv4 address and the other with the IPv6 address.

### The Leak

Because Safari doesn’t proxy STUN requests through iCloud Private Relay, STUN servers know your real IP address. This isn’t an issue on its own, as they have no other information; however, Safari passes ICE candidates containing real IP addresses to the JavaScript environment. De-anonymizing you then becomes a matter of parsing your real IP address from the ICE candidates — something easily accomplished with a web application.

So, in order to get real IP addresses, you need to create a peer connection object with a STUN server, collect the ICE candidates, and parse the values. This method requires no user permissions and works on both HTTP and HTTPS pages. Additionally, it’s fast (the time it takes for a couple of parallel network requests) and leaves no traces in browser development tools.

First, create a peer connection object with at least one STUN server. We’ll use Google's server because it’s widely referenced in examples across the internet.

```
js
const peerConnection = new RTCPeerConnection({
  iceServers: [{
    urls: 'stun:stun.l.google.com:19302'
  }]
})
```

Subscribe to the `icecandidate` event to receive ICE candidates. There will be STUN candidates and candidates of other types, so you need to filter the STUN candidates and parse their IP addresses.

```js
const ips = []

function isSTUNCandidate(candidate) {
  return candidate.includes(' typ srflx ')
}

function parseIP(candidate) {
  return candidate.split(' ', 5)[4]
}

peerConnection.onicecandidate = event => {
  if (event.candidate) {
    const candidateString = event.candidate.candidate
    if (isSTUNCandidate(candidateString)) {
      ips.push(parseIP(candidateString))
    }
  } else {
    // There will be no other ICE candidates
    // Print the result
    console.log('IPs', ips)
  }
}
```

Finally, create a data channel and an offer to make WebRTC start collecting ICE candidates.

```js
peerConnection.createDataChannel('')

peerConnection.createOffer().then(description => {
  peerConnection.setLocalDescription(description)
})
```

The process will complete when you receive an `icecandidate` event with null candidate. The `ips` array will contain your real IPs (IPv4 and IPv6 depending on your network connection). For the complete code, please visit [our GitHub repository](https://github.com/fingerprintjs/blog-private-relay-ip-leak-demo/blob/master/src/main_page.ts#L131).

## How to protect yourself from the leak

Using a real VPN instead of iCloud Private Relay will proxy all your network traffic—including STUN requests and other browser traffic—so that no one except you and the VPN provider can see your real IP address. A myriad of VPN apps are available in the App Store.

Disabling JavaScript in your Safari’s browser settings will turn off WebRTC and provide protection from this leak. However, many websites require Javascript to function properly.

To fix this vulnerability, Apple will need to modify Safari so it routes all traffic through iCloud Private Relay. The FingerprintJS Team has already reported this issue to them.

## Get in touch

* Try our [open source browser fingerprinting library](https://github.com/fingerprintjs/fingerprintjs)
* Try [FingerprintJS Pro](https://fingerprintjs.com/), which combines browser fingerprinting with additional identification techniques and machine learning for 99.5% accuracy. It’s free for 10 days with unlimited API calls.
* Join our team to work on exciting research in online privacy and cybersecurity: [work@fingerprintjs.com](mailto:work@fingerprintjs.com)