This is the first part of a series of blog posts about techniques to bypass web filters, looking at increasingly advanced techniques with each part.

Back in 2019 (yes, quite some time ago), I saw a Tweet1 about how SNI spoofing can be used to bypass web filters:

Ok, so SNI spoofing is cooler than I thought. It’s easy to bypass these deep packet inspection devices and Next Gen firewall filters.

Want to visit a “malicious” website? Getting blocked? Just change the SNI value to a windows update address.

Paul Seekamp @nullenc0de (Tweet)

I never heard about SNI spoofing before and so I was wondering what it is, how it works and how it can be used to bypass those pesky web filters. So, I did some research and experiments. If you want to have the answers to these questions, read along.

A Short Introduction to Web Filters / Proxies

Before we start exploring the various techniques for bypassing web filters / proxies, I would like to provide a brief introduction to what web filters are and the different configurations they can have.

A web filter is a system that examines internet traffic, specifically HTTP and HTTPS requests, to determine whether a request is legitimate or should be blocked. These filters are often referred to as HTTP proxies. In many cases, web filters are integrated into firewalls (also known as application level firewalls, enterprise gateways, next generation firewalls, and the like), which have besides standard TCP/IP firewalling additional features like intrusion detection capabilities. A web filter can also be operated on a dedicated proxy which is only responsible for inspecting and filtering web traffic.

Web Filter / Proxy Modes

HTTP proxies can be operated in two modes: the “normal” mode, also known as “explicit” or “non-transparent” mode, and the “transparent” or “invisible” mode.

Normal / Explicit / Non-Transparent Proxy

In normal mode, the proxy server acts as an intermediary between the client and the target website. When a client makes a request to access a website, a new connection is first established to the proxy. The client tells the proxy via the HTTP CONNECT method to which target website it wants to establish a connection. After confirming to the client that the connection was established, the proxy will forward requests to the target system and responses back to the client.

Interesting to know is also that the client does not perform any DNS lookups, it just sends the hostname to the proxy. The proxy will then perform the DNS lookup for the provided hostname.

The following diagram shows an example where Alice connect via HTTP to example.net via a proxy. This proxy is configured on the client. The connection to the proxy is established via the HTTP CONNECT method. The proxy then connects to the target website. Further request and responses are forwarded between the client and website:

HTTP request via a “normal” proxy

This can be seen in Wireshark as well. The client first establishes a connection to the proxy via the HTTP CONNECT method and tell the proxy the target server’s hostname ①. After the connection is established, the client sends an HTTP request to the proxy ②. This request and the according response are then forwarded between the client and server ③:

Traffic of an HTTP request via a “normal” proxy

The same works when HTTPS / TLS is used. All TLS messages are forwarded to the target server after establishing the connection to the proxy via the CONNECT method. Since the TLS tunnel is established between the server and the client, the proxy cannot read the encrypted data.

HTTPS connection established via a “normal” proxy

Transparent / Invisible Proxy

In transparent mode, the proxy server intercepts the user’s requests without requiring any configuration on the client. Clients may not even be aware that the traffic is being sent through a proxy. The proxy server still processes the requests and responses, but it does so without altering the original request headers. This mode is often used in environments where administrators want to enforce policies without requiring user intervention.

The following diagram shows an example where Alice connects via HTTP to example.net via a transparent proxy server. This proxy server is not configured on the client. No additional connection to the proxy is established. The network traffic is just routed trough the proxy where the packets are just forwarded:

HTTP request via a “transparent” proxy

In this case, the DNS lookup has to be performed on the client. because this is the only connection that is actively established.

TLS Inspection

A proxy can either simply inspect and forward requests as seen above or perform TLS inspection. TLS inspection means that the TLS traffic is decrypted so it can be analyzed more thoroughly. For this to work correctly, the client must have the proxy’s root certificate installed. Otherwise, the client will refuse to connect and get a TLS “unknown CA” error.

When a HTTPS connection through a TLS inspecting proxy in “normal” mode is established, the proxy forwards the TLS handshake to the target server ① and completes the TLS handshake ②. The proxy then finishes the TLS handshake with the client ③, but with an new generated certificate signed by the proxy’s CA. Since the TLS tunnel is established between the client and the proxy and another between the proxy and the server, the proxy can read all the transmitted data.

Traffic of an HTTP request via a “normal” proxy that performs TLS inspection

Side note: There are proxies that first fully complete the TLS handshake with the client by directly send a ServerHello back and only establish a connection to the target server after this TLS connection is successfully established. How this is implemented depends on the used proxy.

A transparent proxy is also able to inspect the TLS traffic. The proxy will also establish a separate TLS connection with the client and with the server, but just by replacing the necessary packets when they pass through. This allows the proxy again to inspect the TLS traffic:

HTTP request via a transparent proxy that performs TLS inspection

What Is an SNI

Server Name Indication (SNI) is an extension to the TLS protocol defined in RFC 60662. This extension allows a client to tell the server the hostname it is trying to connect to in the ClientHello at the beginning of the TLS handshake. This is particularly useful for servers which host multiple websites on the same IP address, as it allows the server to provide the correct certificate to the client in the ServerHello message.

For example, if you access https://www.compass-security.com in your browser, the browser tells the webserver to connect to the host www.compass-security.com using the SNI extension in the ClientHello during the TLS handshake:

SNI in a ClientHello during a TLS Handshake

As a side note: The ClientHello in a TLS handshake is not encrypted in most versions of TLS, including TLS 1.3, unless specific extensions like Encrypted ClientHello (ECH)3 are used. Therefore, every system with access to the network traffic is able to see to which host a client connects, even if TLS is used.

The webserver then knows that the certificate for www.compass-security.com should be used to proceed in the ServerHello during the TLS handshake:

Subject of a certificate in a ServerHello during a TLS 1.2 Handshake

Whether the certificate is sent in cleartext depends on the used TLS version. In the screenshot above, TLS 1.2 was used where the certificate is always sent in cleartext to the client4.

In TLS 1.3, the certificate is not contained as cleartext in the ServerHello, but instead sent in an encrypted message to the client5:

No certificate in a ServerHello during a TLS 1.3 handshake.

SNI Based Web Filters

Now that we know what SNI is and how it works, let’s see what this has to do with web filters. Since the hostname in the SNI field in a TLS handshake is transmitted in cleartext, it provides an easy way for firewalls or proxies to identify and block traffic based on this hostname.

For example, this can be used to block users from accessing malicious websites or malware to communicate with their C2 server.

Bypassing Simple Web Filters Using SNI Spoofing

Imagine you have the following situation where Alice is able to access the website legit.example.net but access to evil.example.com is blocked by the firewall because of the SNI evil.example.com:

Connection is blocked to evil.example.com

Alice (or a malware on her computer) is now able to bypass the web filter by connecting to the IP address of evil.example.net (198.51.100.23) but with the SNI legit.example.net:

Blocking mechanism bypass by spoofing a legit SNI

This attack of requires control over the TLS request, which can be achieved by malware running on the system or by an attacker or user with system access attempting to bypass the web filter.

The final IP, TLS and HTTP packet structure looks like this:

SNI Spoofing

This technique only works if the evil host is capable of handling TLS handshakes with an SNI of the legit hostname. If the evil host is under attacker’s control, this can easily be done and some webservers automatically accept arbitrary SNIs. However, if the evil host cannot be controlled, this system may not deliver the correct content for arbitrary SNIs.

Example Using OpenSSL

A TLS connection as shown above can be established using e.g. openssl6:

openssl s_client -connect 198.51.100.23:443 -servername legit.example.net
  • The s_client subcommand of openssl can be used to establish TLS connections.
  • The -connect option is used to establish a TLS connection to the IP address of evil.example.com.
  • The -servername option is used to set the SNI to legit.example.net.

So, this command establishes a TLS connection to the IP address of evil.example.com (198.51.100.23) with the SNI of legit.example.net. This can also be seen in the network traffic:

Accessing evil.example.com with the spoofed SNI legit.example.net

When TLS 1.2 is used (which can be enforced using the -tls_1_2 option in the openssl command), the certificate in the response shows that the TLS connection was established to the server with the certificate of evil.example.com:

The server responds with the certificate of evil.example.com in the ServerHello

Example Using curl

The same can be achieved using curl7:

curl -k --connect-to legit.example.net::198.51.100.23: -H "Host: evil.example.net" https://legit.example.net
  • The -k option tells curl to ignore certificate warnings. The certificate would not be trusted, because the hostname does not match the subject / subject alternative names of the certificate.
  • The --connect-to option tells curl to connect to the provided IP address of the evil.example.com (198.51.100.23) host whenever the hostname legit.example.net is used.
  • The -H option tells curl to set the HTTP Host request header to evil.example.net, so that the webserver knows which virtual host the client wants to access. Depending on how the webserver is configured, this could also be omitted to use the legit hostname .

This establishes a TLS connection to the IP address of evil.example.com (198.51.100.23) with the SNI server name of legit.example.net. The legit hostname from the target URL is automatically used by curl in the SNI:

Accessing evil.example.com with the spoofed SNI legit.example.net and the HTTP host header evil.example.net

Note: The TLS traffic was decrypted in Wireshark just to show the HTTP Host header. A normal firewall / web filter which does not perform TLS inspection would not be able to see this information.

SNI Bypass on Explicit Proxy with TLS Inspection

In the curl command above, no explicit proxy was configured, which would typically be the case in a transparent proxy setup. First, I thought that SNI spoofing can only be done in transparent proxy setups, because in a setup where an explicit proxy is used, DNS resolution of the target hostname is done on the proxy and not on the client. How would a client be able to tell the proxy to establish a connection to another IP address when the DNS resolution is performed on the proxy?

After some experiments, it turned out that my assumption was wrong and this can easily be done, even with a TLS inspection proxy. The client can just tell the proxy to establish a connection to an IP address in the CONNECT method. The client can the perform a TLS handshake with the proxy containing the spoofed SNI. The proxy will then establish a connection to the provided IP address and use the spoofed SNI. The --proxy option of curl can be used to specify a proxy:

curl --proxy http://10.5.23.42:8080 -k --connect-to legit.example.net::198.51.100.23: -H "Host: evil.example.net" https://legit.example.net

The client will first establish a connection to the target IP address via the proxy using HTTP CONNECT ①. The proxy will then establish a connection to the provided IP address ② and send the spoofed SNI from the client ③. After this, the proxy will forward the data packets between the client and server:

SNI spoofing via an explicit proxy that performs TLS splitting

However, there are proxies that do not respect the IP address from the CONNECT request ① and just resolve the hostname of the SNI ② and connect to this IP address ③:

SNI spoofing via an explicit proxy that performs TLS splitting but does not use the provided IP address

This shows that not all proxy products work in the same way and bypasses highly depend on the used software or proxy configuration.

SNI Spoofing Protection and Limitations

It should be clear, that blocking/allowing TLS connections only by looking at the SNI is neither a very advanced, nor sound technique for web filters. I tried this bypass at several customers during penetration tests and client hardening / infrastructure reviews but was sadly never successful. If web filtering is implemented, more robust techniques are applied, like inspecting TLS traffic.

The web filter module of FortiGate from Fortinet for example, has the option to perform additional checks on the SNI:

Additional SNI checks on FortiGate

When the Server certificate SNI check option is set to Enable, FortiGate will use the server name from the certificate (either the subject or SAN) for checking whether a connection is allowed or blocked. Furthermore, if set to Strict8, the connection is directly terminated if the SNI and subject/SAN do not match.

This means to bypass such a web filter, you would also need to control the subject of the certificate in question, so it is properly aligned with the SNI you specified. Connecting to arbitrary blocked websites would therefore not be possible, because their certificate would not match the fake SNI . However, if you are also in control of the server, for example when running your C2 server, this can be done by installing a self-signed certificate issued to an allowed hostname. To prevent such attacks, a web filter should always correctly verify the certificate chain and terminate the connection for untrusted certificates.

Takeaway

In conclusion, SNI spoofing can be used to bypass some basic web filters but it’s not a technique that works against more sophisticated filter mechanisms when these are configured correctly.

In part 2 of this series, we will look into bypassing web filters via Host header spoofing.

References

  1. Tweet by @nullenc0de about bypassing SNI filters: https://twitter.com/nullenc0de/status/1159805999332638720 ↩︎
  2. RFC 6066, TLS Extension Definitions, Chapter 3, Server Name Indication: https://datatracker.ietf.org/doc/html/rfc6066#section-3 ↩︎
  3. Internet-Draft, TLS Encrypted Client Hello: https://www.ietf.org/archive/id/draft-ietf-tls-esni-22.html ↩︎
  4. RFC 5246, TLS 1.2, Certificate in Handshake: https://datatracker.ietf.org/doc/html/rfc5246#section-7.4.2 ↩︎
  5. RFC 8446, TLS 1.3, Encrypted Certificate in Handshake (diagram page 11): https://datatracker.ietf.org/doc/html/rfc8446#section-2 ↩︎
  6. OpenSSL manpage of s_client: https://docs.openssl.org/master/man1/openssl-s_client/ ↩︎
  7. curl(1) manpage: https://curl.se/docs/manpage.html ↩︎
  8. FortiGate Administration Guide, Configuring an SSL/SSH inspection profile: https://docs.fortinet.com/document/fortigate/7.6.0/administration-guide/709167/configuring-an-ssl-ssh-inspection-profile ↩︎ ↩︎