The not-so-smooth-wall


Note - I have published this article roughly 6 months after it is written (after I has left the school this relates to!)

Preface

As with most schools, the school I go to has a firewall, which is primarily used to filter out inappropriate sites - this filter however blocks a lot of useful material, which is often frustrating. The implementation also has extremely large privacy/security risks associated.1

All traffic entering and leaving the network at my school is routed through a Smoothwall firewall, which also ran other services such as Squid. This firewall monitors+filters internet traffic, as well as caching HTTP responses.

The firewall is configured in such a way that all traffic flowing through it has to be valid HTTP or HTTPS traffic on ports 80 and 443 respectively - any connections on other ports were forbidden (with the exception of services such as DNS etc.), meaning I cannot use any off-the-shelf VPNs . Since the firewall has no idea what is in the HTTPS traffic, all the HTTPS traffic is intercepted by the firewall, which signed it with its own self-signed certificate (aka MITM). Of course, this would result in an SSL error on the client browser, since the certificate authority is not trusted. Therefore, we were required to install the corresponding certificate into the root certificate stores on our devices.

I will also mention that the Firewall did not intercept all traffic - there seemed to be a short whitelist, including sites owned by Microsoft, such as office.com, azure.com, etc. These sites were signed by their original SSL certs.

Initial ideas

I has quite a few ideas on how to tunnel traffic through the firewall, although many were simply bad, such as DNS Tunneling.

As I mentioned sites before sites such as microsoft.com were not intercepted - so I wondered whether I could send a request with a host header of microsoft.com, whilst the destination is is a server owned by me, meaning that the the HTTPS requests were not intercepted? No. The firewall would actually do a DNS lookup on the host header to validate that it is in fact the destination IP of the request. This seems to be a Squid feature.2

Azure?

I already has a relatively simple setup on Microsoft Azure, such as a simple VM, Public IP, and basic storage (I receive £100 free credit a year as a student). As I soon discovered, I could add a DNS name - in this case inbound.uksouth.cloudapp.azure.com. Amazingly requests to this domain were not intercepted since it matched, *.azure.com, which is evidently on the firewalls’ whitelist. This meant I could send HTTPS request to it without them being intercepted.

alt text

This is however not enough, as routing traffic over HTTPS is not a great idea.

WebSockets

Routing traffic over WebSockets, however, made a lot of sense and there were lots of projects that did so. WebSocket traffic is however blocked by the firewall - that is, except for hosts on the whitelist!

Implementing this idea

Instead of writing the WebSocket transport layer myself, I instead opted to use v2ray, a project that is initially created to bypass the Chinese Firewall, and configured as so:

Client setup

The setup on the client is fairly straightforward - just the v2ray binary, as well as the appropriate configuration:

{
  "inbounds": [
    {
      "port": 1010,
      "listen": "0.0.0.0",
      "protocol": "socks",
      "sniffing": {
        "enabled": false,
        "destOverride": ["HTTP", "tls"]
      },
      "settings": {
        "auth": "noauth",
        "udp": true
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "inbound.uksouth.cloudapp.azure.com",
            "port": 443,
            "users": [
              {
                "id": "b831381d-0000-4d53-ad4f-8cda48b30812",
                "alterId": 0
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "wsSettings": {
          "path": "/ray",
          "headers": {
             "Cache-Control" : "max-age=0" // prevents squid cache interference!
          }
        }
      }
    }
  ]
}

This would make v2ray listen on port 1010 for socks traffic. It also created a WebSocket connection to the server (wss://inbound.uksouth.cloudapp.azure.com/ray). All I has do do then is to tell windows to proxy traffic to socks=localhost:1010

Server setup

The server setup is a little more interesting, since I is dealing with HTTPS requests instead of HTTP requests. This meant the server has to be listening for HTTPS traffic - with a valid certificate (the firewall would return an error if a server provided an invalid certificate). I therefore turned to nginx and Lets Encrypt, both I has been using for a while for my website. I could use the HTTP-01 challenge to generate a certificate that is valid for inbound.uksouth.cloudapp.azure.com, and I then configured nginx to use this certificate and reverse proxy the bare HTTP requests to the v2ray server.

Below is a snippet of the nginx config I used:

server {
    server_name _;
    listen [::]:443 ssl ipv6only=on;
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/inbound.uksouth.cloudapp.azure.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/inbound.uksouth.cloudapp.azure.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    location /wsapp/ {
        proxy_pass HTTP://localhost:9090;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_set_header Host $host;
    }
    location /ray {
        if ($http_upgrade != "websocket") {
            return 404;
        }
        proxy_redirect off;
        proxy_pass HTTP://127.0.0.1:10000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

The location /ray will also return a 404 if the connection is not a WebSocket, effectively making the server seem innocuous. Below is the v2ray server configuration:

{
  "inbounds": [
    {
      "port": 10000,
      "listen":"127.0.0.1",
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "b831381d-0000-4d53-ad4f-8cda48b30812",
            "alterId": 0
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
        "path": "/ray"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

I finally wrote a simple systemd service to start it on boot.

Conclusion

So I is now longer restricted by the firewall - and the connection could also reach high speeds (Well over 50Mb/s). I always love a good challenge, so I really enjoyed this adventure. Hopefully you have too!


  1. HTTPS interception has a lot of serious security risks: firstly you are relying on its TLS implentation to be correct, and the client has no knowledge about to details of the TLS connection, such as the TLS ciphers, or what certificate is being used. Secondly, if the MITM proxy were to be compromised, this would allow full access to cookies, logins etc.., which is what TLS/SSL was designed to avoid! ↩︎

  2. http://www.squid-cache.org/Doc/config/host_verify_strict/ ↩︎