Viewing WireGuard Traffic with Tcpdump

Part of the Wireguard series:


On the article, WireGuard VPN Walkthrough, Glen posted the tantalizing question:

How would you verify/confirm that the link is definitely encrypted? If you use OpenVPN and use Wireshark to sniff the packets, you see the OPENVPN protocol listed in the captured dump. Is there an equivalent for Wireguard?

For testing, here are my assumptions:

Plain HTTP is not secure so let’s watch for our request to httpbin. We’ll be able to snoop using

tcpdump -n -v -i eth0 port 80

Exectuing the eth0 curl statement, we’ll be able to clearly see our HTTP request and response:

02:58:45.452812 IP (tos 0x0, ttl 64, id 31717, offset 0, flags [DF], proto TCP (6), length 129)
    192.168.1.6.40318 > 54.235.130.91.80: Flags [P.], cksum 0x7b68 (incorrect -> 0x26e5), seq 283436622:283436699, ack 1472925892, win 229, options [nop,nop,TS val 323460534 ecr 112785182], length 77: HTTP, length: 77
        GET /ip HTTP/1.1
        Host: httpbin.org
        User-Agent: curl/7.47.0
        Accept: */*

02:58:45.504943 IP (tos 0x20, ttl 232, id 59362, offset 0, flags [DF], proto TCP (6), length 372)
    54.235.130.91.80 > 192.168.1.6.40318: Flags [P.], cksum 0x675f (correct), seq 1:321, ack 77, win 105, options [nop,nop,TS val 112785194 ecr 323460534], length 320: HTTP, length: 320
        HTTP/1.1 200 OK
        Connection: keep-alive
        Server: gunicorn/19.7.1
        Date: Thu, 26 Apr 2018 00:15:35 GMT
        Content-Type: application/json
        Access-Control-Allow-Origin: *
        Access-Control-Allow-Credentials: true
        X-Powered-By: Flask
        X-Processed-Time: 0
        Content-Length: 33
        Via: 1.1 vegur

        { 
          "origin": "90.90.90.90"
        }

Let’s look at what executing the wg1 curl will look like with:

tcpdump -n -v -i wg1 port 80
tcpdump: listening on wg1, link-type RAW (Raw IP), capture size 262144 bytes
03:10:13.382048 IP (tos 0x0, ttl 64, id 26588, offset 0, flags [DF], proto TCP (6), length 129)
    10.192.122.2.42904 > 54.225.185.38.80: Flags [P.], cksum 0x753d (incorrect -> 0x2b18), seq 354179898:354179975, ack 3265920867, win 216, options [nop,nop,TS val 323632516 ecr 2420503305], length 77: HTTP, length: 77
        GET /ip HTTP/1.1
        Host: httpbin.org
        User-Agent: curl/7.47.0
        Accept: */*

03:10:13.861353 IP (tos 0x0, ttl 234, id 3103, offset 0, flags [DF], proto TCP (6), length 371)
    54.225.185.38.80 > 10.192.122.2.42904: Flags [P.], cksum 0xf0d4 (correct), seq 1:320, ack 77, win 105, options [nop,nop,TS val 2420503425 ecr 323632516], length 319: HTTP, length: 319
        HTTP/1.1 200 OK
        Connection: keep-alive
        Server: gunicorn/19.7.1
        Date: Thu, 26 Apr 2018 00:27:03 GMT
        Content-Type: application/json
        Access-Control-Allow-Origin: *
        Access-Control-Allow-Credentials: true
        X-Powered-By: Flask
        X-Processed-Time: 0
        Content-Length: 32
        Via: 1.1 vegur

        { 
          "origin": "100.100.100.100"
        }

Don’t worry, in this example we’re sending plaintext to a Wireguard interface and receiving plaintext back, which is what our tcpdump command is showing. However, our Wireguard interface doesn’t actually have the capability to send network data anywhere. It’ll have eth0 transmit the encrypted payload to our VPN server. This means that listening on eth0 but executing the wg1 curl will show encrypted contents. If eth0 can’t read the contents then no one else will either.

We’ll update our tcpdump command as we won’t be communicating TCP over port 80. We know we’ll be communicating with our VPN server, so only capture traffic between us and the server.

tcpdump -n -X -i eth0 host 100.100.100.100

Since we’ll be seeing encrypted packets, they won’t be printable. To display the contents, we’ll view the data hex encoded (which is the -X option).

I’ve also removed the IPv4 header and UDP header, so we can just focus on the data. Below is the our HTTP GET request.

0x0000:  .... .... .... .... .... .... .... ....  ................
0x0010:  .... .... .... .... .... .... 0400 0000  ................
0x0020:  bef2 24a4 0600 0000 0000 0000 002f b736  ..$........../.6
0x0030:  7448 8e01 778f 7e13 adb8 e66c e307 3d39  tH..w.~....l..=9
0x0040:  bfbf f53d a194 211b f0e5 6cab c561 1f5c  ...=..!...l..a.\
0x0050:  2c38 906b 1bec 183a 8e41 8bab 3a59 ca0b  ,8.k...:.A..:Y..
0x0060:  e1ef dda8 e882 4f8a 6590 f517 2d9a 2077  ......O.e...-..w
0x0070:  4830 8673 b26b 1a16 7f3b e358 f3ec b14f  H0.s.k...;.X...O
0x0080:  8d97 b15d 9ad3 3962 3e1f 5d6c 96be 0518  ...]..9b>.]l....

Notice that the data starts with 0400 0000. If we cross reference this with Wireguard’s documented protocol, we can confirm that the data begins with an 8bit 4 followed by 24 bits of 0, so we can rest assured that we’ve set up Wireguard correctly. One could dig a little deeper in the subsequent bits to capture the receiver index part of the protocol, but as a heuristic, 0400 0000 is decent. Keep in mind Wireguard doesn’t try to obsfuscate data, so an internet provider could reasonably try to detect and block Wireguard traffic.

The creator of wireguard had this to say:

WireGuard does not aim to evade DPS [deep packet inspection], unfortunately. There are several things that prevent this from occurring:

  • The first byte, which is a fixed type value.
  • The fact that mac2 is most often all zeros.
  • The fixed length of handshake messages.
  • The unencrypted ephemeral public key.

So Wireguard isn’t the panacea for those trying to evade sophisticated and unfriendly firewalls (and Wireguard never billed itself as that). It’s a great VPN that can be combined with other tools to match one’s desired needs.

Comments

If you'd like to leave a comment, please email [email protected]