Bypassing SNI filtering : Feat DPITunnel, Zapret and Geneva

Hey Everyone, i was recently browsing a anti-censorship awesome list and i came across few tools with some really cool tricks to bypass SNI based filtering mechanisms, i think these tools deserves more attention and are really worth sharing so here we are.

Understanding SNI

Before we dive into how these tools actually works lets understand what SNI is, SNI stands for Server Name Indication its a part of the TLS protocol (a header to be exact) which lets the webserver decide which TLS certificate has to be offered to a client when it is hosting multiple websites. Lets fire up wireshark and take a quick look at this SNI header, we will use the filter ssl.handshake.extension.type == "server_name" to make our lives easier and try connecting to 0x00sec.org, now wireshark should hide everything except the Client Hello packets which are sent during the TLS handshake, lets take a look...

expanding the server_name TLS extension reveals us the SNI header, now some of you must be wondering - isn't TLS supposed encrypt everything ?, actually no lets see why. Say we have a webserver hosting a few sites (on the same ip) via plain HTTP, when a user requests a site the webserver just looks at the HTTP Host header field in the clients HTTP request serves up the appropriate site, everything works fine.

========================
GET /index.html HTTP/1.1    _
Host: ctf.0x00sec.org        \              ======================
                              \             WEBSERVER (133.7.133.7)
                                --------->    --server.conf--
========================      / <---------      ctf.0x00sec.org ->  /var/www/html/ctf
GET /index.html HTTP/1.1    _/                  0x00sec.org     ->  /var/www/html/forum
Host: 0x00sec.org

Now lets add TLS to the mix, to establish a TLS connection we first need the TLS certificates which are unique to each site1, and to get the appropriate TLS certs we need to tell the server which site we are trying to connect to, this is where SNI comes in, it "indicates" what "server name" we are trying to connect to so that the server can give us the appropriate certificates, well now why is the SNI field unencrypted ?, thats simply because we havent established a secure connection yet and SNI is required to establish that secure connection in the first place! ( its somewhat like the chicken and egg problem). During the time in which SNI was introduced(around 2003), TLS simply did not want to deal with the overhead of coming up with a way to hide the SNI field and decided to let it be (in other words it was acceptable, a hostname was not really that confidential), the same TLS standard continue to be widely followed/used today.

The Impact Of Unencrypted SNIs

Blocking sites/services has been a favourite past time of many ISPs and governments around the world, in the early days everything was plaintext and intercepting proxies did the job, as networks grew in size and ssl started gaining popularity it became unrealistic to inspect traffic with proxies, everybody switched over to filtering DNS requests although this worked quite well for some time, with the advent of technologies like DoH(DNS over Https) and DoT(DNS over TLS) DNS filtering too became a thing of the past, when all hope seemed lost firewall vendors turned to that one part of TLS that remained unencrypted... SNI!!.

How SNI Inspection Works

Lets say we want to connect to 0x00sec.org from our pc, the TCP connection would look like this:

                                           [+------+------+]
OUR-PC [123.123.1.3 : 62123 ] =============[F-I-R-E-W-A-L-L]============> [443  : 133.7.133.7] 0x00sec.org
          Our Ip      Random               [+------+------+]              HTTPS    Servers Ip
                    Source Port                                       Standard Port

The blocking process is fairly straight forward, every time a TLS client hello is sent, the firewall just extracts the SNI field(the hostname) from the packet and compares it against a blocklist, when a match is found the hello packet can simply be dropped preventing the client from establishing the TLS connection. Dropping TLS client hello's as i mentioned before is only practical in smaller networks with limited traffic (i.e in cases of colleges, corporate networks), in case of ISPs inspecting and dropping packets on the gateway would be very costly and impractical due the heavy load ISPs generally are under, thus ISPs usually mirror the traffic2 into a inspection device, the inspecting device matches the extracted SNI field against a blocklist as well, but to block the traffic it forges a TCP reset packet with the ip and port of the server (in our case 133.7.133.7 and 443 ) and sends it to client (i.e 123.123.1.3 on port 62123), fooling the client into thinking that the server has terminated the TCP connection thus effectively preventing the client from further communicating with the server, this method might not be 100% accurate since the reset packet can get lost during transmission, but that is within a acceptable margin, this method is what makes censorship possible at the level of ISPs.

Bypassing SNI Inspection - A Review of Techniques Used For Circumvention

Before we begin lets consider a simple representation of a TLS client hello packet for the sake of conversation.

  +---------------------------+-----------------------------+
  | SRC IP: 123.123.1.3       |     DST ISP: 133.7.133.7    |     // IP
  +---------------------------+-----------------------------+
  | SRC PORT: 45637           |     DST PORT: 443           |     // TCP
  +---------------------------+-----------------------------+
  | TLS Ver |..TLS.Stuff....  | SNI:Server-Name: 0x00sec.org|     // TLS  Client Hello  
  +---------------------------+-----------------------------+

1) Split the Client hello at SNI field : As i have mentioned before the filtering process is interested in the Server Name indicated by the SNI, this process expects the entire TLS Client Hello to be present in a single packet3, to evade this filtering process we segment the TLS data in half such that SNI server name is not completely in one portion, we then transmit those portions as separate TCP segments.

  SEGMENT 1: 
  +---------------------------+-----------------------------+
  | SRC IP: 123.123.1.3       |     DST ISP: 133.7.133.7    |     // IP
  +---------------------------+-----------------------------+
  | SRC PORT: 45637           |     DST PORT: 443           |     // TCP
  +---------------------------+-----------------------------+
  | TLS Ver |..TLS.Stuff....  |   SNI:Server-Name: 0x00     |     // Partial TLS Client Hello  
  +---------------------------+-----------------------------+


  SEGMENT 2: 
  +---------------------------+-----------------------------+
  | SRC IP: 123.123.1.3       |     DST ISP: 133.7.133.7    |     // IP
  +---------------------------+-----------------------------+
  | SRC PORT: 45637           |     DST PORT: 443           |     // TCP
  +---------------------------+-----------------------------+
  | sec.org................................................ |     // Partial TLS Client Hello  
  +---------------------------+-----------------------------+

when the first TCP segment is seen by the filter, it would immediate recognize it as TLS Client hello since the first portion has the appropriate TLS application header, but when it extracts the SNI servername it only gets the partial hostname (ex: 0x00 instead of 0x00sec.org), since the partial hostname doesnt match against any of the blocklists the packet is let through, as far as the second segment is concerned it has no meaningfull application header and hence is not processed by the filter and let through.

2) Split and send the client hello in random order : This is variation of the first method except we send the segments in reverse or random order4, this is done in order to bypass certain filters which can reassemble segments but expects the segments to be sent in the right order.

3) Use TCP window size to fragment the server hello : Most filtering processes only analyze outbound packets (i.e packets originating from clients, TLS client hello in our case), but there are a few which analyze inbound packets as well, in such a case if the server hello is analyzed, it would contain a "certificate common name" (which again is nothing but hostname for which the TLS certificate was issued), which might match against the blocklist. In this situation we need to achive something similar to the first technique but from the servers end, we thus advertise a small TCP Window size which would force the server to split the server hello across multiple TCP segments hence achieving evasion.

4) Send Fake client hello with an allowed SNI, but with a TTL not long enough to reach server : In some scenarios, once a TLS client hello has been analyzed by the filter and allowed to pass, that particular TCP stream is ignored, any further inbound or outbound packets from that stream will not be analyzed, this is done inorder to reduce the amount of processing that a filter has to perform since a TLS client hello is sent only once per TLS session. We now can send a TLS client hello with a fake SNI-Server Name of a random allowed site (say wikipedia.org) but with a TTL(time to live) value just enough to reach the filter but not the actual server, thus effectively preventing our TCP stream from being further analyzed, now we can send the actual TLS client hello and go on with our business.

        ____________                      ____\ /____                      __^____^__
      <[Your Machine]>                   |Your Router|                    [|Firewall|]          
  +-----------------------+        +-----------------------+        +-----------------------+
  | DST: 133.7.133.7      |        | DST: 133.7.133.7      |        | DST: 133.7.133.7      |
  | SNI: wiki.org | TTL: 3|  ====> | SNI: wiki.org | TTL: 2|  ====> | SNI: wiki.org | TTL: 1| ===>|
  +-----------------------+        +-----------------------+        +-----------------------+    ||
                                                                                                 ||
  |<=============================================================================================<|
  ||   
  ||    [[Some interm Router]]               [[ The Webserver - 0x00sec.org @ 133.7.133.7]]
  ||  +-----------------------+             
  ||  | DST: 133.7.133.7      |   \/                  < packet doesnt arrive >
  |==>| SNI: wiki.org | TTL: 0|   /\
      +-----------------------+
      TTL Becomes Zero Packet 
            is dropped

5) Corrupting the checksum : This is variation of the 4th technique, but here instead of using TTL to prevent the fake TLS client hello from reaching the server, we allow the client hello to reach the server, but we corrupt its checksum value, filters usually dont validate the checksum of packets to avoid overhead, but the client and server are fully compliant TCP implementations and will drop any packets with bad checksums, we can then send the real client hello and go on with our business. (This method has few caveats check note 5)

        ____________                      __^____^__                      
      <[Your Machine]>                   [|Firewall|]              [[ Webserver - 0x00sec.org]] 
  +-----------------------+        +-----------------------+        +-----------------------+
  | DST: 133.7.133.7      |        | DST: 133.7.133.7      |        | DST: 133.7.133.7      |
  | SNI: wiki.us| BADCHKSM|  ====> | SNI: wiki.us| BADCHKSM|  ====> | SNI: wiki.us| BADCHKSM|
  +-----------------------+        +-----------------------+        +-----------------------+

                                   . wiki.us is not blocked          \/   Packet has bad         
                                     allow packet to pass            /\   checksum drop it

                                   . Further packets from the
                                     TCP stream is no longer
                                     processed

These are the most common techniques used by circumvention tools, but there are many others, one of tools Geneva

goes the extra mile by developing tailored bypass mechanisms depending on the censor, Geneva is really cool project and you can read more about it here

Attempts To Solve The Unencrypted SNI Problem

  • ESNI/ECH (Encrypted SNI/ Encrypted Client Hello): There are RFC drafts discussing these possibilities but there are also legitmate concerns regarding their implementation, ESNI/ECH would completely blind firewall and other filtering devices in educational institutions, SNI also serves as a possible IOC for SEIMs, but it is possible to block ESNI/ECH and force the client to fall back to regular methods, The Great Firewall has been observed doing this since mid 2020

Alternative Use Cases Of These Tools

  • In pentest, red teaming scenarios :
    • Can enable access to blocked services (ex: telegram), which can be used for exfiltration
    • Provide better opsec for your beacon/agent by hiding the C2 hostnames from the firewall

Scenarios In Which These Tools Likely Will Not Help

  • The fragmentation attack performed by these tools will not work against proxies which perform SNI filtering (ex: Squid), since proxies generally reassemble TCP fragments before inspection.
  • In cases of ip filtering (ex: blackholeing 133.7.133.7)
  • In cases where UDP is being filtered out (DPITunnel only works for TCP traffic)

Notes

  1. There are exception to certificates being unique to a site, in case of subdomains, a single wild card certificate (ex: certificate for *.0x00sec.org) can be utilized by all the subdomains of a site.

  2. Port Mirroring or a similar feature is usually available in switches and other routing devices where a (physical) port can be configured for monitoring purposes, a copy of all packets flowing through the device is sent to this port, a machine can be connected to this monitoring port with its network interface in promiscous mode, all passing traffic can now be inspected by listening on this interface without any processing overhead on the gateway.

  3. Most filtering implementation expect the entire client hello to be part of a single TCP packet, because that generally is the case, though it is possible to segment the client hello across multiple TCP packets, in such a scenario the filtering process would be required to reassemble the tcp segments before analyzing them, this is computationally costly and thus not implemented in mainstream filtering products, one exception in this case is "The Great Firewall" which is said to have reassembly capability.

  4. Out of order delivery is feature of TCP, the reciever buffers segments recieved in random order untill all segments of a sequence arrive, it then reassembles and delivers them to the application layer.

  5. Many home routers may drop packets with bad checksum, the kernel paramter net.netfilter.nf_conntrack_checksum can be set to 0 to prevent this.

Tools :

  1. https://github.com/Kkevsterrr/geneva/ (Linux) (recommended)

  2. https://github.com/zhenyolka/DPITunnel-cli (Linux) (recommended, personal choice)

  3. https://github.com/bol-van/zapret (Linux,BSD) (recommended)

  4. https://github.com/ValdikSS/GoodbyeDPI (Linux)

  5. https://github.com/SadeghHayeri/GreenTunnel (windows)

  6. https://github.com/macronut/ghostcp (windows)

  7. https://github.com/zhenyolka/DPITunnel-android (android)

References :

  1. https://github.com/bol-van/zapret/blob/master/docs/readme.eng.md

  2. https://geneva.cs.umd.edu/papers/geneva_ccs19.pdf

  3. https://hal.inria.fr/hal-01202712/document

  4. https://blog.torproject.org/learning-more-about-gfws-active-probing-system

  5. https://habr.com/ru/post/335436/

Further Reading :

  1. https://geneva.cs.umd.edu/posts/india-sni-filtering/

  2. https://geneva.cs.umd.edu/posts/iran-whitelister/

  3. https://geneva.cs.umd.edu/posts/china-censors-esni/esni/

  4. https://citizenlab.ca/2018/04/planet-netsweeper/

  5. How the Great Firewall Discovers Hidden Circumvention Servers

  6. https://geneva.cs.umd.edu/posts/

10 Likes

Very interesting analysis and post about SNI and DPI from an enterprise routers view with offensive operations in mind. I didn’t know about SNI and it’s use in DPI, as that’s still not a super deeply explored field for me.

Thanks for the content!

3 Likes

Im making a matrix of what firewall products can actually be bypassed, could you test DPITunnel against any commercial firewall accessible to you ?

I was able to test it against sophos and can confirm that it works.

I can. But I don’t believe we have a license that encompasses DPI, I’ll still test it though.

1 Like

Thank you!, what product do you use by the way ?

Sent you a DM… (character limit)

1 Like

This topic was automatically closed after 121 days. New replies are no longer allowed.

Another neat trick many of you can use is to pass the --enable-quic --quic-version=h3-29 flag while starting chromium/brave/chrome to force enable http3/QUIC, you can verify that it works by visiting: https://http3.is. (ofcourse the said webserver would have to support QUIC), since no SNI’s are involved most ISP implementations can be bypassed.

1 Like