ProtonVPN TCP Accleration SYN+ACK Spoofing Analysis

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.

  • Side Note: Neither of these companies are paying me, and they shouldn’t. This isn’t a recommendation or condemnation of either VPN provider. I’d recommend you do your own research when choosing a VPN provider. I’m _mattata on Twitter, you should give me a follow. I do stuff like this often.

Digging in.

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?

A Primer on TCP Handshakes

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:

StepClientServer
1(SYN) –>Recieve SYN
2Recieve 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.

How does ProtonVPN make the port appear open?

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.

Why would ProtonVPN “spoof” a SYN+ACK packet?

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.

VPN Accelerator Toggle

Sharing is caring!