Jan 8, 2022
7 mins read
I was a Private Internet Access (PIA) customer for many, many years. Some recent changes spurred me to look for a new VPN provider and I ended up landing on ProtonVPN which I’ve been using for a few months now.
And I noticed something… TCP (SYN, ACK) messages aren’t “real” when using ProtonVPN. This isn’t necessarily a bad thing or a good thing or even a thing that most people notice because it’s generally not problematic. But it is a non-standard VPN thing that I noticed, so I wanted to write about it from the practical perspective of a user, how it works at a high level, and why something like this is being done.
Using the Windows ProtonVPN client: I log in, hit the “Quick Connect” button, and boom! I’m now connected over a VPN.
Cool right?
I open up a terminal and use netcat with the verbose flag to initiate a connection to this site (remyhax.xyz) on TCP port 9999 and get the following result:
$ nc -vvv remyhax.xyz 9999
Connection to remyhax.xyz 9999 port [tcp/*] succeeded!
There’s only one problem. TCP port 9999 isn’t actually open for connections on this server. It isn’t real. Attempting to send any data through this connection results in it instantly closing. So why would it say the port is open?
To understand what’s happening here (and why a VPN provider might do this), we’ll take a quick aside to the basic components of a TCP connection.
When I use netcat to connect to remyhax.xyz, netcat is the client and remyhax.xyz is the server.
TCP connections provide reliable communication between a client and server through use of Positive Acknowledgement with Re-transmission. Basically, it’s a whole lot of “hey you ready?”, “yeah, I’m ready”, “okay, I’m gonna send it”, “just gonna send it”.
TCP Handshake:
Step | Client | Server |
---|---|---|
1 | (SYN) –> | Recieve SYN |
2 | Recieve SYN+ACK | <–(SYN, ACK) |
3 | (ACK)–> | Recieve ACK |
4+ | Send Payload (Data)–> | Recieve Data |
SYN informs the server that the client is likely to start communication.
SYN + ACK The server ACKnowledges that it received the SYN (among other things).
ACK The client ACKnowledges that the server responded.
And thus a reliable connection is established and data can be transmitted.
Let’s re-run our previous nc -vvv remyhax.xyz 9999
command, but this time let’s record the traffic in Wireshark and map it to the TCP handshake steps listed above.
Step 1: Client (SYN) –> Server
Transmission Control Protocol
Source Port: 52816 (52816)
Destination Port: distinct (9999)
Sequence Number: 0 (relative sequence number)
Acknowledgment Number: 0
1010 .... = Header Length: 40 bytes (10)
Flags: 0x002 (SYN)
Step 2: Client <– Server (SYN+ACK)
Transmission Control Protocol
Source Port: distinct (9999)
Destination Port: 52816 (52816)
Sequence Number: 0 (relative sequence number)
1010 .... = Header Length: 40 bytes (10)
Flags: 0x012 (SYN, ACK)
Step 3: Client (ACK) –> Server
Transmission Control Protocol
Source Port: 52816 (52816)
Destination Port: distinct (9999)
Sequence Number: 1 (relative sequence number)
Acknowledgment Number: 1 (relative ack number)
1000 .... = Header Length: 32 bytes (8)
Flags: 0x010 (ACK)
At this point, given that we know TCP port 9999 is not actually open on my remyhax.xyz server we can conclude that ProtonVPN is injecting the packet contained in step 2 and sending it to the client in order to prompt the client to continue with the handshake and send data.
Speed.
First and foremost, as I stated this behavior isn’t inherently bad. In fact, when using a VPN one of the major issues encountered is latency, or the time that it takes for a packet to traverse from one end of a connection to the other.
As described above, TCP connections are established with 3-Way handshake of which the server only responds with one packet (SYN+ACK). ProtonVPN injecting this packet allows the client to continue setting up the handshake without the need to wait (latency) for the real server to confirm the port is open. This appears to be part of the functionality of their “VPN Accelerator” feature which can be read about more in depth here.
For clients/applications that expect a fully completed TCP 3-Way handshake, this increases the speed at which the client can complete the TCP handshake and begin sending data. In the case that the port is not actually open on the server, ProtonVPN does send another type of packet which allows things to continue functioning as expected.
Transmission Control Protocol
Source Port: distinct (9999)
Destination Port: 52816 (52816)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x014 (RST, ACK)
The ProtonVPN tunnel notifies the client to terminate the TCP session using (RST+ACK), thereby closing the connection so that it doesn’t remain open waiting for the server to respond on a port that isn’t actually open.
Of course, not all applications complete the TCP 3-Way handshake, such as nmap
which in some modes will only send the TCP SYN
to determine if a port is open on a server. Since ProtonVPN is injecting the SYN+ACK
, all of these ports appear open when they may actually be closed.
Starting Nmap 7.80 ( https://nmap.org ) at 2022-01-08 23:25 EST
Nmap scan report for remyhax.xyz (51.222.30.138)
Host is up (0.021s latency).
Other addresses for remyhax.xyz (not scanned): 2607:5300:205:200::2098
rDNS record for 51.222.30.138: vps-fe71f5b5.vps.ovh.ca
PORT STATE SERVICE
1/tcp open tcpmux
3/tcp open compressnet
4/tcp open unknown
6/tcp open unknown
7/tcp open echo
9/tcp open discard
13/tcp open daytime
17/tcp open qotd
19/tcp open chargen
20/tcp open ftp-data
21/tcp open ftp
22/tcp open ssh
23/tcp open telnet
24/tcp open priv-mail
(...)
Nmap done: 1 IP address (1 host up) scanned in 8.44 seconds
Running the same nmap
command while not connected to ProtonVPN reveals an accurate listing of open ports, but note how long the scan took by comparison.
Starting Nmap 7.80 ( https://nmap.org ) at 2022-01-08 23:28 EST
Nmap scan report for remyhax.xyz (51.222.30.138)
Host is up (0.045s latency).
Other addresses for remyhax.xyz (not scanned): 2607:5300:205:200::2098
rDNS record for 51.222.30.138: vps-fe71f5b5.vps.ovh.ca
Not shown: 914 closed ports, 80 filtered ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
8080/tcp open http-proxy
8090/tcp open opsmessaging
9090/tcp open zeus-admin
Nmap done: 1 IP address (1 host up) scanned in 22.77 seconds
The nmap
TCP SYN scan took 8.44 seconds while connected to ProtonVPN and produced incorrect results. The same scan while disconnected from the VPN too 22.77 seconds, but produced accurate results.
Luckily, this feature is available to quickly disable in their official client although disabling it only actually appears to work on the windows client. Behavior on Linux clients remains unchanged even after turning it off.
Sharing is caring!