Introduction
Recently, I read on Twitter that SNI spoofing can be used to bypass deep packet inspection.
This is the Tweet:
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.
- @nullenc0de on Twitter (https://twitter.com/nullenc0de/status/1159805999332638720)
I wanted to understand how this works and how it can be done, so started playing around with curl
.
Server Name Indication (SNI)
During the TLS handshake in the client hello message, the client tells the server to which hostname the connection should be established to. This is similar to the Host
header in the HTTP protocol but on the TLS layer.
This mechanism is called Server Name Indication (SNI) and defined in RFC 6066 (TLS Extension Definitions):
3. Server Name Indication
TLS does not provide a mechanism for a client to tell a server the
name of the server it is contacting. It may be desirable for clients
to provide this information to facilitate secure connections to
servers that host multiple 'virtual' servers at a single underlying
network address.
In order to provide any of the server names, clients MAY include an
extension of type "server_name" in the (extended) client hello.
[...]
Since the TLS handshake is not encrypted, this servername is sometimes used by proxies to filter network traffic and e.g. only allow or block connections to specific hostnames.
If you are in control of the TLS client (browser, commandline tool, etc.), you can influence the SNI in the TLS handshake and provide an arbitrary value.
Proxy Bypass
This can be used to bypass proxies which block connections depending on the SNI. This can then be used to exfiltrate data or download malware from sites which are otherwise not accessible.
This filtering technique is for example used in Juniper network security devices (https://www.juniper.net/documentation/us/en/software/junos/utm/topics/concept/utm-web-filtering-overview.html):
Other vendors implement the same filtering mechanisms. This might not work on proxies which perform TLS splitting, because then, these proxies can perform additional checks. However, it’s always worth a try and you might be lucky and find a weak configured proxy 😃.
Also, this can only be done for transparent proxies, because otherwise the proxy will
do the DNS name resolution after the CONNECT
request and then it’s not possible
to connect to an arbitrary IP address.
SNI Spoofing
The following command can be used to perform SNI spoofing using curl
:
curl -v -s -L -k --resolve allowed.test:443:203.0.113.23 -H "Host: example.org" https://allowed.test
--resolve
: Tellcurl
to use a custom address for a specific hostname-k
: Ignore certificate warnings (this is the case, because the SNI does not match the subject/SAN from the certificate)allowed.test
: Hostname of allowed hostexample.org
: Hostname of blocked host203.0.113.23
: IP address of blocked host
This has how the effect, that curl will establish a TLS connection to the IP address 203.0.113.23
with a spoofed SNI of allowed.test
but tell the webserver in the Host header that the HTTP request was desired for the hostname example.org
.
The curl output shows that the TLS connection was established to the provided IP address but with a servername allowed.test
. The certificate from the server shows that the connection was established to the servername example.org
:
The same can be seen on the network layer using Wireshark:
If you think about it for a while, it’s not that complicated 👍.
References
- Tweet from @nullenc0de on Twitter: https://twitter.com/nullenc0de/status/1159805999332638720
- RFC 6066: TLS Extension Definitions: https://datatracker.ietf.org/doc/html/rfc6066