In 2021, the popularity of the WireGuard VPN protocol in Egypt was significant enough that it was added to the block list, causing frustration for customers of commercial VPN providers such as Cloudflare Warp+ and Mullvad Wireguard, as well as some corporate VPN users. Initial research suggests that the block list is specifically targeting the Handshake Initiate packets of the WireGuard protocol, which have a fixed size of 148 bytes and a distinct structure, with the first four bytes of a UDP packet being [0x01, 0x00, 0x00, 0x00].
To comprehend the detection and blocking of WireGuard by DPI, it is necessary to have a basic understanding of the underlying theory. The WireGuard protocol itself is straightforward, with its traffic being conveyed via typical UDP packets accompanied by a small header. Typically, only two packets are required to establish a WireGuard tunnel (three if Keepalive is included).
- When a WireGuard tunnel is to be established, the initiating party (the client) sends a Handshake Initiation packet to the other side (the server).
- If the server is available to accept the connection, it will respond with a Handshake Response Packet.
- Upon receipt of the Handshake Response, the client will then send a Keepalive Packet, which is a simple UDP packet containing only a WireGuard MessageTransportType(4) header. This process of updating the keys is repeated approximately every two minutes by repeating the described procedure.
Therefore, it appears that blocking WireGuard with DPI can be achieved by monitoring UDP packets with a size of 148 bytes and verifying the first four bytes for the recognizable signature [0x01, 0x00, 0x00, 0x00]. However, it is important to note that corporate implementations of WireGuard may utilize the reserved three bytes (the Reserved field) for their specific purposes (such as being used as a session identifier in Jamf Private Access). There is also a possibility that these reserved bytes will eventually be utilized in the official client. To maintain accuracy, it would be wise to focus solely on the first byte of the UDP packet. On the other hand, blocking all 148-byte UDP packets with a first byte of 0x01 might be considered too risky. The same caution should be applied to the Handshake Response Packet, which also has a fixed size of 92 bytes and a similar signature, with three reserved bytes [0x02, 0x00, 0x00, 0x00].
So, what could be a suitable criterion for blocking WireGuard with a low risk of false positives? It appears that DPI should first pass over packets that are “similar” to Handshake Initiate packets, while keeping track of the session state (IP addresses and ports). Only after a response packet that is “similar” to a Handshake Response is received should the packet be blocked. Although it is uncertain, indirect evidence suggests that this approach might be effective.
A brief background on the origin of this story: I wouldn’t have delved into this topic if I hadn’t come across a forum post that caught my interest. A user described a unique method for bypassing WireGuard blocking using another VPN. If you’re interested, you can read our exchange of messages. In short, he used Windscribe VPN to establish the WireGuard tunnel and then turned off Windscribe VPN while still using WireGuard (the official client with a Cloudflare Warp + configuration) without any issues of being blocked.
Throughout our conversation, additional information was gradually disclosed, including the state that, in such a unique manner, has motivated the population to further explore network technologies. My last visit to Egypt was in 2010, and I wouldn’t say I particularly miss it. However, it is currently one of the few readily accessible countries, and I have been seriously considering going there for diving. Of course, the inability to access my home networks would be a major inconvenience.
The next step was to determine an alternative to Windscribe VPN for obscuring the Handshake Initiation and Handshake Response packets. While it’s possible to create a low-cost, self-written service for encapsulating and forwarding these packets, the workload would be minimal (two packets every two minutes per tunnel), there is a more suitable candidate for this task that has been available for some time now: SOCKS5. The fifth version of this widely-used protocol supports UDP, which is exactly what we need.
Earlier, I wrote a post about my VPN client project that is based on Cloudflare’s implementation of the WireGuard protocol. As a result, I decided to add SOCKS5 support to it. During the testing of intermediate builds, some aspects of the DPI implementation were uncovered. For instance, if the first Handshake Initiation packet misses the SOCKS proxy and hits DPI, then any subsequent packets in that UDP session will be permanently blocked by DPI. Interestingly, a small portion of the packets still manage to get through (maybe they bypass the DPI). The opposite scenario may also be true, if the UDP session does not start with the Handshake Initiation-Handshake Response, then DPI will not block the session, even if these packets appear later. This could explain why the user’s tunnel did not break during the next Handshake Initiation after two minutes. However, this is only a speculative hypothesis at this point.
The following optional parameters have been added to the Wiresock VPN Client to provide support for SOCKS5:
- Socks5Proxy – specifies SOCKS5 proxy endpoint, e.g.
Socks5Proxy = socks5.sshvpn.me:1080or
Socks5Proxy = 126.96.36.199:1080
- Socks5ProxyUsername – specifies SOCKS5 username (optional)
- Socks5ProxyPassword – specifies SOCKS5 password (optional)
In order to test the SOCKS5 support in the Wiresock VPN Client, we also required a SOCKS5 server with support for UDP ASSOCIATE. I assume that most readers are capable of setting up such a server on their own, so I don’t want to unnecessarily extend the length of this post by providing a detailed guide. However, I can share my notes on installing Dante from Inferno Nettverk A/S on Ubuntu 20.04 in Oracle Cloud for those who are interested.
We tested SOCKS5 tunneling for both the Handshake Initiate/Response packets and all WireGuard tunnel packets. Both options effectively prevented blocking, but we ultimately chose the first option, as it provided better performance and was more resource-efficient. Among the unexpected outcomes, sending the Handshake Initiate through a SOCKS5 proxy “fixed” the ability to establish a nested WireGuard tunnel using the official WireGuard for Windows as an external tunnel and the Wiresock VPN Client as an internal one. This functionality was “broken” at some point between releases 0.5 and 0.5.3, as the official client suddenly stopped allowing the Handshake Initiate into the tunnel (perhaps due to work on supporting its own nested tunnels). However, after wrapping it in SOCKS, it stopped recognizing it, and everything started working again.
I would like to mention that it is relatively straightforward to create a specialized Windows application using Windows Packet Filter for the official WireGuard client, which will intercept outgoing Handshake Initiate packets and send them through SOCKS5. If there is interest, I will try to allocate time for its implementation.
That’s all for now. Hope it helps!
Thanks for the amazing post, I tried using shadowsocks to mask the handshake initiation packet, but still no success, here are my configuration:
ss-server -s 0.0.0.0 -s ::0 -p 443 -k test -m aes-256-gcm -u -t 5000 -d 188.8.131.52 -U
ss-tunnel -v -s -p 443 -l 1234 -L :51820 -k test -t 5000 -m aes-256-gcm -U
PrivateKey = xxx
Address = 10.44.0.3/32
DNS = 10.44.0.1
PublicKey = xxx
AllowedIPs = 0.0.0.0/0, 10.44.0.1/32
Endpoint = 127.0.0.1:1234
any idea if i am doing something wrong?
Please note that in your configuration, not only the Wireguard handshake, but the entire tunnel will be sent through shadowsocks, which will negatively affect the performance of the tunnel.
Personally, I have not tried to tunnel Wireguard through shadowsocks. Sorry, but I don’t have a ready answer.