Another “critical” “VPN” “vulnerability” and why Port Fail is bullshit
The morning of November 26 brought me interesting news: guys from Perfect Privacy disclosed the Port Fail vulnerability, which can lead to an IP address leak for clients of VPN services with a “port forwarding” feature. I was indignant about their use of the word “vulnerability”. It’s not a vulnerability, just a routing feature: Traffic to VPN server always goes via ISP, outside of VPN tunnel. Pretty obvious thing, I thought, which should be known by any network administrator. Besides that, the note is technically correct, so nothing to worry about. But then the headlines came, and shit hit the fan.
The news article published on Geektimes, which originally had a clickbait title, said that Private Internet Access — one of the biggest VPN service provider — paid $5000 for this “vulnerability”. “$5000 for a lame, absolutely obvious thing?”, I thought, “Unbelievable!”. So I wrote a comment about another routing feature. One no less obvious than the one discussed in the article. Something that anyone who configured multiple ISPs on a single PC at least once should know: “A reply to an incoming request will not necessarily go out via the same interface and IP address the request came in through. The remote side is definitely not expecting this.” Think about a VPN instead of a second ISP. There is a possibility that when we send a request to the real computer’s IP, we get a reply from it’s VPN IP.
Why is that?
When you connect to the VPN, your default route (which was previously set to your ISP), is now set to the VPN address. Most port listening applications rely on operating system to build and send correct response for the incoming packet. It works all the time with only one connection, but it changes for multiple links depending on operating system and a protocol:
- OpenVPN (def1) — UDP go via VPN interface, TCP works correctly
- IPsec IKEv2 — UDP go via VPN interface, TCP is dropped
- OpenVPN (def1) — UDP goes via VPN interface, TCP is dropped
- IPsec IKEv2 — UDP goes via VPN interface, TCP works correctly
- OpenVPN (def1) — both UDP and TCP go via VPN interface if rp_filter is set to 0 and get dropped if rp_filter=1
There is a nice configuration setting called rp_filter in Linux which makes sure that an incoming packet will only be accepted if a reply can use the same network path. This is enabled on most modern distributions by default. However, for some reason it is not set in some of them, for example, in Debian.
It’s a pity that other operating systems don’t provide that functionality.
What could go wrong?
As you can see, a threat is posed only by the applications listening to incoming connections on a UDP port. There aren’t many of those applications on a regular home user’s PC. But usually there are at least some of them.
There are special organizations (intellectual property infringement monitoring companies) that monitor BitTorrent swarms on the behalf of copyright holders in countries such as the USA, Germany, France, Austria, Canada, and Great Britain.
That companies connect to BitTorrent trackers and DHT network, looking at swarms for the files they are interested in. They collect all IP addresses and ports of clients connected to that swarm.
Afterwards they send emails or letters to the IP address owner, including all the information of what the user of that IP address has downloaded or seeded, and exact file name and time. They also often include a fine to be paid.
Citizens of the above countries often use a VPN, one based in a “less developed” country, to protect themselves from such villains. The companies are not amused by this.
So, how can these monitoring companies abuse said feature?
- User with the “real” (routable) IP address connects to the VPN, runs BitTorrent client and downloads or seeds several files. The BitTorrent client opening a port on the user’s router using UPnP if needed.
- Copyright monitoring company collects all IP addresses and ports of the users seeding or downloading those files.
- Company sends BitTorrent UDP packets to the whole routable internet address range on the ports collected earlier. It could be done within a minutes using 10G link.
- Client’s BitTorrent software gets incoming UDP packet via their routable (ISP-supplied) IP address and sends reply via VPN interface.
- Company gets real IP address of a client.
In my opinion, it’s quite hard to exploit this feature as most of BitTorrent clients use port randomization. Sending BitTorrent handshakes to all the possible ports on the whole internet is technically possible, but hard to do on a regular basis. Although there are some clients which use usual BitTorrent ports like 8999 or 6881.
You can’t connect to the client using a default network stack, but it’s possible to modify one to allow such connections.
Using this technique it’s possible to disclose the real IP of a Skype accounts you’re interested in. There are a bunch of Skype IP resolvers which can give you the VPN IP address and port number of a Skype user using only their Skype login. Then you need to use the same thing a copyright monitoring company would use — send some UDP packets to the whole internet on the exact port. It’s remarkable but Skype will send you a reply for almost any data! The nping utility from nmap package suits our needs very well:
# nping --udp -p 13318 --data-string 'hellothere!' -c 1 serv.valdikss.org.ru
Starting Nping 0.7.00 ( https://nmap.org/nping ) at 2015-12-20 19:54 MSK
SENT (0.0157s) UDP 18.104.22.168:53 > 22.214.171.124:13318 ttl=64 id=10802 iplen=39
RCVD (0.0859s) UDP 126.96.36.199:4272 > 188.8.131.52:53 ttl=54 id=1534 iplen=32
Max rtt: N/A | Min rtt: N/A | Avg rtt: N/A
How to mitigate this issue?
Although I don’t consider this issue a very big problem, I’m still interested in technical methods to mitigate it.
It’s pretty easy to do so in Linux. Just set the following sysctl option to 1 for IPv4 if it’s still not set
My VPN interface is tun0 and internet interface is wlp3s0, so I did this:
# sysctl net.ipv4.conf.all.rp_filter=1
# sysctl net.ipv4.conf.default.rp_filter=1
# sysctl net.ipv4.conf.tun0.rp_filter=1
# sysctl net.ipv4.conf.wlp3s0.rp_filter=1
You should add the following iptables rule for IPv6:
# ip6tables -t raw -A PREROUTING -m rpfilter --invert -j DROP
There is a powerful Windows Filtering Platform which is helpful to fix this issue on Windows. It allows you to write versatile firewall rule sets right in a user-space. But if you feel like you’re limited, you can go write kernel-space driver. Just as with DNS Leaks on Windows 10, I decided to write an OpenVPN plugin which works like a simplified version of Reverse Path Forwarding. It utilizes the following logic:
- Permit all new unicast UDP IPv4 packets from 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, active network adapters subnets; all new unicast UDP IPv6 packets from fd00::/8, fe80::/10 and active network adapters subnets.
- Block all new unicast UDP IPv4 and IPv6 packets to non-VPN interface.
While it can’t be called proper Reverse Path Forwarding, it works pretty well. Responses to UDP requests can only go via the VPN tunnel if they came from non-routable network ranges and would be dropped by the VPN server because they’re unroutable. (Unroutable in this case means they only work inside the local network, which is the VPN provider’s internal network). Applications which use UDP (like BitTorrent Sync) won’t break inside your LAN and your neighbors on ISP subnet are still allowed to connect to you.
You can download the plugin here: https://github.com/ValdikSS/openvpn-block-incoming-udp-plugin
It’s a bit harder to properly fix this issue on OS X. PF does not allow stateful matching of new UDP packets. You’re forced to block all incoming UDP packets except if from unroutable addresses, ISP subnetwork and IP address of a VPN server itself. It’s bad because you won’t be able to use, for example, ISP DNS servers without whitelist them explicitly. Anyway, use something like this:
echo 'pass in quick proto udp from 10.0.0.0/8 to any
pass in quick proto udp from 192.168.0.0/16 to any
pass in quick proto udp from 172.16.0.0/12 to any
pass in quick proto udp from 169.254.0.0/16 to any
pass in quick proto udp from 184.108.40.206/32 to any
block in quick on ! utun1 proto udp to any' | sudo pfctl -Ef -
Where 220.127.116.11 is an IP address of a VPN server and utun1 is a VPN interface.
If you’re such a villain and want to try to exploit this issue, Linux netfilter subsystem is waiting for your turn. Just use the following iptables rule and you will get all the Martian Packets:
iptables -I INPUT -m conntrack -p udp --sport 4455 --ctstate NEW -j LOG
Where 4455 is a port you’re interested in.
Information about this “feature” was sent to 11 VPN providers and only 6of them replied: Private Internet Access, Perfect Privacy and Mullvad have released updated software which blocks incoming connections (PIA and Mullvad used my plugin, yay!). Astrill said they not going to fix it as this is a client issue and has nothing to do with VPN. Well, technically they’re correct, but their software already has some fixes for client side issues like DNS, IPv6 and WebRTC leaks. I don’t understand why they don’t add another client-side leak-related fix, it’s not that hard. Cryptostorm guys pointed me to Windows registry key which could enable Reverse Path Forwarding but it doesn’t work, and TorGuard administrator didn’t reply after some additional details.
I almost forgot, there is a new version of OpenVPN 2.3.9. It has several Windows-related fixes, and a long awaited option “block-outside-dns” which fixes DNS leaks on Windows 8.1 and 10.
That’s how I got my $5000 from Private Internet Access, additional $1000 from Perfect Privacy and $1300 from Mullvad for such a bullshit issue. I feel kinda uneasy about this. Part of that money will go to OpenVPN and strongSwan developers.