Front-end domain based on TLS 1.3. Part 2

Introduction



In the first part of the article, we gave a brief description of the encrypted SNI (eSNI) mechanism. They showed how, on its basis, it is possible to evade detection by modern DPI systems (using the Beeline DPI and forbidden ILV rutracker as an example), as well as exploring a new version of domain front-end based on this mechanism.



In the second part of the article, we will move on to more practical things that RedTeam will be useful to specialists in their difficult work. In the end, our goal is not to gain access to blocked resources (for such commonplace things, we have a good old VPN). Fortunately, there are a great many VPN providers, as they say, for every taste, color and budget.



We will try to apply the domain-fronting mechanism for modern RedTeam tools, for example, such as Cobalt Strike, Empire, etc., and give them additional opportunities for mimicking and evading modern content filtering systems.



Last time, we implemented the eSNI mechanism in the OpenSSL library and successfully used it in the familiar curl utility. But one curl, as they say, will not be full. Of course, I want to implement something similar in high-level languages. But, unfortunately, a cursory search of the vastness of the network disappoints us, because support for the eSNI mechanism is fully implemented only in GOLANG. Thus, our choice is not very large: we either write in pure C or C ++ using the patched OpenSSL library, or we use a separate GOLANG fork from CloudFlare and try to port our tools there. In principle, there is another option, more classic, but at the same time time-consuming, is to implement eSNI support for python. After all, Python also uses OpenSSL to work with https. But we will leave this option for development to someone else, and we will be content with the implementation on the Golang, especially since our beloved Cobalt Strike is perfectly able to work with a communication channel built by third-party tools (External C2 channel) - we will talk about this at the end of the article.



Try Harder ...



One of the tools implemented on Go is our development for pivoting inside the network - the rsockstun tuner , which, incidentally, is now detected by Microsoft and Symantec tools as very malicious software aimed at violating world stability ...







It would be great to use the previous development in this case. But here a small problem arises. The fact is that rsockstun initially implies the use of a synchronous SSL communication channel with the server. This means that the connection is established once and exists for the entire duration of the tunnel operation. And, as you understand, the https protocol is not designed for this mode of operation - it works in the request-response mode, where each new http-request exists within the framework of a new tcp connection.



The main disadvantage of this scheme is that the server cannot transfer data to the client until the client sends a new http request. But, fortunately, there are many options for solving this problem - streaming data via the http protocol (in the end, we manage to somehow watch our favorite TV shows and listen to music from portals running on https, and video and audio transmission is not that other than streaming data). One of the technologies for emulating the operation of a full-fledged tcp connection over the http protocol is WebSockets technology, the main essence of which is the organization of a full-fledged network connection between the client and the web server.



To our luck (hooray hooray !!!), this technology is included by default in all CloudFlare tariff plans and works great in combination with eSNI. This is exactly what we will use in order to teach our tunnel to use domain-fronts and hide from modern DPI.



A bit about WebSockets



First of all, we will briefly and in simple words tell about web sockets so that everyone has an idea of ​​what we will work with.



Web socket technology allows you to temporarily switch from an http connection to standard streaming data through a network socket without breaking the established tcp connection. When a client wants to switch to a web socket, he sets several http headers in his http request. Two required headers are Connection: Upgrade and Upgrade: websocket . It can also force the websocket protocol version ( Sec-Websockset-Version: 13 ) and something like the base64 web socket identifier ( Sec-WebSocket-Key: DAGDJSiREI3 + KjDfwxm1FA == ). The server responds with http-code 101 Switching Protocols and also sets the Connection, Upgrade and Sec-WebSocket-Accept headers. The switching process is illustrated in the screenshot below:







After that, the WebSocket connection can be considered complete. Any data from both the client and the server will now be supplied not with http, but with WebSocket headers (they begin with byte 0x82). Now the server does not need to wait for a request from the client in order to transfer data, as tcp connection is not broken.



There are several libraries in the web socket golan. The most popular of these are Gorilla WebSocket and standard WebSocket . We will use the latter, because it is simpler, smaller and works, as they say, a little faster.



In the rsockstun client code, we need to replace the calls to net.dial or tls.dial with the corresponding calls to WebSocket:











We want to make the client part of our tunnel universal and able to work both through a direct ssl connection and through the WebSockset protocol. To do this, we will create a separate function func connectForWsSocks (address string, proxy string) error {...} by analogy with connectForSocks () and we will use it to work with web sockets if the server address specified when the client starts will start with ws: or wss: (in the case of Secure WebSocket).



For the server side of the tunnel, we will also make a separate function for working with web sockets. An instance of the http class will be created in it and an http connection handler (wsHandler function) will be set:







And we will put all the logic for processing the connection (authorizing the client with a password, installing and ending the yamux session) in the WebSocket connection handler:







We compile the project, start the server part:



./rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rd
      
      





And then the client part:



 ./rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rd
      
      





And we check the work on the local host:











We pass to domain-fronting



We sort of figured out how to deal with web sockets. Now let's go directly to eSNI and domain front-end. As mentioned earlier, to work with DoH and eSNI, we need to take a special branch of the golang from CloudFlare . We need a branch with eSNI support (pwu / esni).



We clone it to ourselves locally or download and expand the corresponding zip:



 git clone -b pwu/esni https://github.com/cloudflare/tls-tris.git
      
      





Then we need to copy the GOROOT directory, replace the corresponding files from the cloned branch and set it as the main one. To save the developer from this headache, the guys from CloudFlare prepared a special script - _dev / go.sh. Just run it. The script along with makefile will do everything themselves. For fun - you can look inside the makefile for details.



After working out the script, when compiling the project, we will need to indicate as GOROOT the local directory prepared by the script. In our case, it looks like this:



 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….
      
      





Next, we need to implement in the tunnel the functionality of requesting and parsing public eSNI keys for the desired domain. In our case, these will be public eSNI keys from CloudFlare front-end servers. To do this, we will create three functions:



 func makeDoTQuery(dnsName string) ([]byte, error) func parseTXTResponse(buf []byte, wantName string) (string, error) func QueryESNIKeysForHost(hostname string) ([]byte, error)
      
      





The names of the functions, in principle, speak for themselves. We will take the filling from the esni_query.go file, which is part of tls-tris. The first function creates a network packet with a query to the CloudFlare DNS server using the DoH protocol (DNS-over-HTTPS), the second parses the query results and receives the values ​​of the public domain keys, and the third is a container for the first two.



Next, we introduce the functionality of the eSNI key request for the domain into our newly created connect function for the web socket connectForWsSocks . Where the server part functions, set the TLS parameters, and also set the name of the fake "cover domain":







It should be noted here that initially, the tls-tris branch was not designed for the use of domain-fronting. Therefore, it does not pay attention to the fake name of the server (an empty serverName field is transmitted as part of the client-hello package). In order to fix this, we will have to add the corresponding FakeServerName field to the TlsConfig structure. We cannot use the standard ServerName field of the structure, because it is used by internal tls mechanisms and if it differs from the original, then the tls-handshake will end with an error. The description of the TlsConfig structure is contained in the tls / common.go file - we have to fix it:











In addition, we will have to make changes to the tls / handshake_client.go file to use our FakeServerName field when generating the TLS handshake:







That's all! You can compile the project and check the work. But before you run the test, you must configure your CloudFlare account. Well, how do you say to set up - just create a cloudflare account and bind your domain to it. All chips related to DoH, WebSocket and ESNI are included in CloudFlare by default. After the DNS records are updated - you can check the domain by running the eSNI key request:



 dig +short txt _esni.df13tester.info
      
      









If you see something similar for your domain, then everything works for you and you can proceed to testing.



Launch Ubuntu VPS, for example, on DigitalOcean. PS In our case, the VPS IP address just issued by the provider was in the ILV black lists. So do not be surprised if something similar happens to you. I had to use a VPN to get to my VPS.



We copy the compiled rsockstun to VPS (this, by the way, is another charm of the golang - you can compile the project on your own and run it on any Linux, observing only the bit capacity of the system) and start the server part:







And then the client part:







As we can see, the client successfully connected to the server through the CloudFlare front-end server using a web socket. To verify that the tunnel works exactly like a tunnel, you can make a curl request through the local socks5 open on the server:







Now let's see what DPI sees in the communication channel:







First, the tunnel tuner, using the DoH mechanism, accesses the Cloudflare DNS server for eSNI keys for the destination domain (packages No. 1-19), and then accesses the front-end server and establishes a TLS connection, hiding under the domain www.google.com (this value by default, when a fake domain is not set at client startup). To specify your fake domain, you must use the -fronfDomain parameter:











Now one more thing. By default, CloudFalre's account settings are set to Flexible SSL. This means that https requests to Cloudflare front-end servers from clients will be redirected in unencrypted (http) form to our server. That is why we started the server part of the tunnel in non-ssl mode (-listen ws: 0.0.0.0), and not (-listen wss: 0.0.0.0).







In order to switch to the full encryption mode, you must select Full , or Full (strict) in the case of the presence of this certificate on the server. After switching the mode, we will be able to accept connections from CloudFlare via the https protocol. Remember to generate a self-signed certificate for the server side of the tunnel.







An annoying reader will ask: “What about the client account under Windows? Indeed, for sure, the main application of the tunnel is to raise back-connect from corporate machines and servers, and there, as a rule, it is always Windows. How can I compile a tunnel for Windows, and even with a specific TLS stack? ”And now we will present another chip that shows how convenient the golang is. We compile for windows directly from Kali, simply by adding the GOOS = windows parameter:



 GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"
      
      





Or a 32-bit option:



 GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"
      
      





Everything! And no more troubles are needed. It really works!







The –w and –s compiler flags are needed in order to remove excess garbage from the executable file, making it less by a couple megabytes. Additionally, it can then be packaged using UPX to further reduce size.



Instead of a conclusion



In the article we, using an example of a tuner written on a golang, clearly demonstrated the use of the new technology of domain-fronting, implemented on a rather interesting feature of the TLS 1.3 protocol. Similarly, you can adapt the existing toolkit written on the golang to work through the CloudFlare server, for example Merlin , the well-known C2, or force CobaltStrike Beacon to use eSNI domain-fronts when working with Teamserver via the External C2 Channel , implemented on the golang, or on standard C ++ using the patched version of OpenSSL, which we talked about in the last part of the article. In general, fantasy has no limits.



The example of the tunnel and CloudFlare is presented in the form of a concept and it is still difficult to say about the distant prospects of this type of domain-front-facing. At the moment, eSNI support is only available at CloudFlare and, in principle, nothing prevents them from disabling this kind of front-end and, for example, breaking tls connections when SNI and eSNI do not match. In general, the future will show. But for now, the prospect of working under the cover of kremlin.ru seems quite attractive. Is not it?



Updated tunnel code, as well as compiled executable exe files, are located in a separate project branch on github . It’s best to write about all possible tunnel problems on the project page on GitHub.



All Articles