We discovered two new traffic leak attacks against VPN clients. Based on our own experiments against 66 VPN clients this appears to be a general issue in many VPN clients. For more information, see our TunnelCrack website and our paper.
This README describes how to test VPN clients for these new traffic leak attacks. A high-level description of the attacks can be found below, and after this background we explain how you can test VPN clients.
You can use the following Bibtex entry to cite our work:
@inproceedings{usenix2023-tunnelcrack,
author = {Nian Xue and Yashaswi Malla and Zihang Xia and Christina P\"opper and Mathy Vanhoef},
title = {Bypassing Tunnels: Leaking {VPN} Client Traffic by Abusing Routing Tables},
booktitle = {32nd USENIX Security Symposium (USENIX Security 23)},
year = {2023},
isbn = {978-1-939133-37-3},
address = {Anaheim, CA},
pages = {5719--5736},
url = {https://www.usenix.org/conference/usenixsecurity23/presentation/xue},
publisher = {USENIX Association},
month = aug,
}
Our attacks manipulate the client's routing table such that traffic will be sent outside the VPN tunnel, i.e., without encryption. Normally, when the VPN is not enabled, a client's routing table might look like the following:
[mathy@zbook-mathy ~]$ ip route
default via 192.168.1.1 dev wlp0s20f3
192.168.1.0/24 dev wlp0s20f3 scope link
The IP address of the client in this example is 192.168.1.101
. The two output lines mean:
-
The first line says that by default all outgoing IP packets are forwarded via 192.168.1.1. Here 192.168.1.1 is the router. The rule also specifies
dev wlp0s20f3
meaning the packets are sent over thewlp0s20f3
Wi-Fi network card. All combined, all outgoing IP packets are by default sent to the router using the Wi-Fi network card. -
The second line is an exception to the above rule: all IP packets to 192.168.1.0/24, so to IP addresses between 192.168.1.0 and 192.168.1.255, are sent over
dev wlp0s20f3
. So they're sent over the Wi-Fi network card. Moreover,scope link
means these IP addresses are directly reachable: the packets can directly be sent to their destination instead of first being forwarded to the router.
When a VPN is enabled, a client's routing table might look like this:
[mathy@zbook-mathy ~]$ ip route
default via 10.8.0.1 dev tun0
76.26.140.111 via 192.168.1.1 dev wlp0s20f3
192.168.1.0/24 dev wlp0s20f3 scope link
Here the IP address of the VPN server is 76.26.140.111
. The first rule says
that by default, all outgoing IP packets are sent over dev tun0
. Here tun0
is a
virtual network card representing the encrypted VPN tunnel. In other words, by default
all packets are sent through the VPN tunnel. There are two exceptions:
-
The second rule says that packets with as destination the VPN server must be sent to the router using the Wi-Fi network card. This exception avoids a rooting loop where already-encrypted VPN packets would otherwise get encrypted again.
-
The third rule is the same as when the VPN wasn't enabled: all packets to the local network (notice the
scope link
) are directly transmitted over the Wi-Fi network card to the destination (so not through the VPN tunnel). This assures that local devices in the network, such as printers, remain accessible when using the VPN.
Our LocalNet attacks abuse the routing exception to the local network, with as main goal to leak traffic in plaintext outside the VPN tunnel. For example, to leak traffic to 216.165.47.10, the adversary acts as a rogue Wi-Fi network, and for instance advertises the IP range 216.165.47.0/24 for the local network. As a result, the VPN client will send all packets to the local network, so all IP packets with as destination 216.165.47.0/24, in plaintext outside the VPN tunnel.
The Testing LocalNet Attacks section to test VPN clients for these LocalNet attacks.
Our ServerIP attacks abuse the routing exception to the VPN server's IP address, with as main goal to leak traffic in plaintext outside the VPN tunnel. When not combined with DNS spoofing, a vulnerable VPN client will leak all traffic to the VPN server's IP address in plaintext. On its own this has low impact.
However, when a client uses plaintext DNS to look up the VPN server's IP address, traffic
to any IP address can be leaked. For example, when the VPN client looks up the IP address
of the VPN server by sending a plaintext DNS request to get the IP address of server1.vpn.com
,
the adversary can spoof the DNS response to return any IP address, e.g., 216.165.47.10.
As a result, all traffic to 216.165.47.10 will now be sent outside the VPN tunnel (see
the above routing background).
The instructions under Testing ServerIP Attacks can be used to test VPN clients for these ServerIP attacks.
We used the create_ap
tool to create a Wi-Fi network for the tests. The generic installation
instructions are available online. On some
Linux distributions you can install it using the package manager. On Ubuntu you need to install
the following dependencies:
sudo apt install hostapd wireshark
A standard AP can be created using the command:
sudo create_ap wlan1 wlan0 testnetwork abcdefgh
This will create a Wi-Fi network called testnetwork with password abcdefgh. The arguments wlan1 and wlan0 depend on your machine:
-
The argument wlan0 refers to your builtin network card and may be different on your machine. Find out this name by executing
ip addr
and picking the interface that is assigned an IP address. -
The argument wlan1 refers to the Wi-Fi dongle you plugged in. Find out its name on your machine by executing
ip addr
before and after plugging in the Wi-Fi dongle and seeing which interface was added.
You should now be able to connect to the created Wi-Fi network. To inspect the traffic of any client
connect to the AP start Wireshark and listen for packets on the ap0
interface (or on the interface
of the Wi-Fi dongle in case it doesn’t support virtual interfaces).
Errors and warnings:
-
If you get the error "ERROR: Failed to initialize lock" then execute
sudo rm /tmp/create_ap.all.lock
. -
If you get the warning "Your adapter does not fully support AP virtual interface" this means your Wi-Fi dongle cannot simultaneously act as a client and AP. This shouldn’t be a problem in our experiments, but if you can't start the Wi-Fi network, try using a different Wi-Fi dongle.
A quick-and-dirty method to test for this vulnerability is to make your router hand out
non-RFC1918 IP addresses for the local network, e.g., using 216.165.47.0/24
for the
local network. Then enable the VPN and try to visit http://nyu.edu
or directly visit
http://216.165.47.10
. This should have as result that the NYU website won't load and
in Wireshark you should see ARP requests for the IP address 216.165.47.10
.
Alternatively, start the create_ap
script to hand out public IP addresses. For example,
if we want to intercept traffic to nyu.edu
, which has IP address 216.165.47.10
, the
hotspot has to hand out IP addresses from a subnet that contains that IP address. This
can be done by starting create_ap
as follows:
sudo create_ap wlan1 wlan0 testnetwork abcdefgh -g 216.165.47.10
Now connect with the created AP and enable the VPN client. Open Wireshark. Then try to
visit http://216.165.47.10
in a browser. If you see TCP SYN packets to 216.165.47.10
it means the VPN app is vulnerable: you can use the Wireshark filter tcp.flags.syn == 1
to easily filter for plaintext TCP SYN packets.
Some additional notes:
-
The adversary can also use 0.0.0.0/1 or similar for the local network to leak nearly all IP-based traffic.
-
We found that some VPN clients will not leak traffic but instead block traffic to the target website/subnet. We still consider this a security risk. For instance, the attack can then be abused to: (1) block the IP address of an update service; (2) prevent the lookup of revoked TLS certificates; (3) prevent modern browsers from contacting servers that can tell whether a website is safe to visit; or (4) Block other security-sensitive services.
-
With Windows clients, we found that when the website is blocked, Windows may still send ARP requests for the IP address being contacted. This is a privacy risk because it can be abused to identify when a victim is trying to visit a specific IP address or website.
We conjecture that this is because the Windows firewall was configured to block packets to local IP addresses, but was not configured to block the initiation of connections to local IP addresses.
Start the create_ap
script and then connect with the device being tested:
sudo create_ap wlan1 wlan0 testnetwork abcdefgh
Now start capturing frames on ap0
. After starting to capture frames, connect to the
VPN server and then use Wireshark to identify the VPN server's IP address. Then visit
http://$VPN_SERVERIP
. If you cannot see plaintext TCP SYNs in Wireshark then the VPN
client cannot be vulnerable (you can use the Wireshark filter tcp.flags.syn == 1
to
easily filter for plaintext TCP SYN packets). If the VPN protocol is using TCP or UDP
then also try to visit http://$VPN_SERVERIP:$PORT
where you add the port used by the
server.
In case you do see a plaintext TCP SYN packet, the next step is to test whether the
VPN client used plaintext DNS to find the VPN server's IP address. To determine this,
you can use the Wireshark filter dns.a == $VPN_SERVERIP
. If you see any results, then
the VPN client is highly likely to be vulnerable.
Some additional notes:
-
We found that the VPN protocol being used (OpenVPN, IPsec, WireGuard, etc) may influence whether there are plaintext leaks towards the VPN server's IP address.
-
Some VPN clients will only leak traffic on specific ports. You can check this manually by browsing to
http://SERVERIP:PORT
and using Wireshark with the filtertcp.port == PORT
to detect plaintext TCP SYN packets to this port. For instance, the VPN client might only leak traffic on the same port as used by the encrypted VPN tunnel. -
In rare occasions, the selected server in the VPN client may also affect the result of the test. This was for example the case when testing Cisco AnyConnect.
- 8 August 2023: Updated the README to have all the instructions in a single markdown file.
The version submitted to the USENIX Security Artifact Evaluation can be found under
the tag
usenix-ae
.