Domein-fronting op basis van TLS 1.3. Deel 2

Inleiding

In het eerste gedeelte artikel hebben we een kort overzicht gegeven van het mechanisme van encrypted SNI (eSNI). We hebben laten zien hoe je hiermee kunt ontsnappen aan detectie door moderne DPI-systemen (met als voorbeeld de DPI van Beeline en de verboden RKŠ tracker), en hebben een nieuwe variant van domain fronting op basis van dit mechanisme onderzocht.

In het tweede deel van het artikel gaan we over naar praktischer zaken die nuttig zullen zijn voor RedTeam-specialisten in hun uitdagende werk. Uiteindelijk is ons doel niet om toegang te krijgen tot geblokkeerde bronnen (voor zulke alledaagse dingen hebben we de oude vertrouwde VPN). Gelukkig zijn er talloze VPN-providers, voor elk wat wils, in elke kleur en voor elk budget.

We zullen proberen om het mechanisme van domain fronting toe te passen op moderne tools van RedTeam, zoals Cobalt Strike, Empire, enzovoorts, en ze extra mogelijkheden te geven voor mimiek en het ontwijken van moderne contentfilteringssystemen.

Bij de laatste keer hebben we het mechanisme eSNI in de OpenSSL-bibliotheek geĆÆmplementeerd en hebben we het met succes gebruikt in de iedereen bekende tool curl. Maar met alleen curl, zoals het gezegde luidt, kun je je niet vullen. Natuurlijk willen we iets dergelijks implementeren in hogere programmeertalen. Helaas teleurstelt een oppervlakkige zoektocht op internet, omdat de ondersteuning voor eSNI volledig is gerealiseerd alleen in GOLANG. Hierdoor is onze keuze niet echt groot: ofwel schrijven we in puur C of C++ met behulp van een gepatchte OpenSSL-bibliotheek, of we gebruiken een aparte fork van GOLANG van CloudFlare en proberen we onze toolset daarheen te porteren. In principe is er nog een meer klassieke, maar tegelijkertijd arbeidsintensieve optie: de ondersteuning van eSNI voor Python implementeren. Uiteindelijk gebruikt Python ook OpenSSL voor https-werk. Maar deze optie laten we aan iemand anders over om te ontwikkelen, terwijl wij tevreden zullen zijn met de implementatie in Golang, vooral aangezien onze geliefde Cobalt Strike uitstekend kan werken met communicatiekanalen die door externe middelen zijn gebouwd (External C2 channel) – daarover zullen we aan het einde van het artikel vertellen.

Try Harder…

Een van de op Go geĆÆmplementeerde tools – onze ontwikkeling voor het pivoteren binnen netwerken – tunneler rsockstun, die tegenwoordig overigens door de bedrijven Microsoft en Symantec wordt gedetecteerd als zeer schadelijke software, gericht op het verstoren van de wereldwijde stabiliteit…

Domein-fronting op basis van TLS 1.3. Deel 2

Het zou geweldig zijn om de eerdere ontwikkeling ook in dit geval te gebruiken. Maar er is een klein probleem. Het punt is dat rsockstun oorspronkelijk het gebruik van een synchronisch SSL-kanaal voor communicatie met de server veronderstelt. Dit betekent dat de verbinding ƩƩn keer wordt opgezet en gedurende de hele werking van de tunnel blijft bestaan. Zoals je begrijpt, is het https-protocol hier echter niet echt voor bedoeld - het werkt in een verzoek-antwoord modus, waar elke nieuwe http-verzoek binnen een nieuwe tcp-verbinding valt.

Het belangrijkste nadeel van deze opzet is dat de server geen gegevens naar de client kan verzenden totdat de client een nieuw http-verzoek doet. Maar gelukkig zijn er veel oplossingen voor dit probleem - het streamen van gegevens via het http-protocol (uiteindelijk weten we immers onze favoriete series te kijken en muziek te luisteren van platforms die op https draaien, en de overdracht van video en audio is niets meer dan datastreaming). Een van de technologieƫn om een volledige tcp-verbinding over het http-protocol te emuleren is de technologie van websockets (WebSockets), die als doel heeft een volledige netverbinding tussen de client en de webserver te organiseren.

Gelukkig (hoera!!!), is deze technologie standaard ingeschakeld in alle CloudFlare-abonnementen en werkt ze uitstekend samen met eSNI. Dat is precies wat we gaan gebruiken om onze tunneltool te leren het domain fronting te gebruiken en zich te verbergen voor moderne DPI.

Even iets over WebSockets

Allereerst zullen we kort en eenvoudig uitleggen wat websockets zijn, zodat iedereen een idee heeft van waar we mee werken.

De technologie van websockets maakt het mogelijk om tijdelijk over te schakelen van een http-verbinding naar standaard datastreaming via een netwerk-socket, zonder de opgezet tcp-verbinding te verbreken. Wanneer een client wil overschakelen naar een websocket, plaatst hij een aantal http-headers in zijn http-verzoek. Twee verplichte headers zijn: Connection: Upgrade en Upgrade: websocket. Ook kan hij de versie van het websocket-protocol verplicht specificeren (Sec-Websocket-Version: 13) en iets dat lijkt op een base64 identificatie voor de websockets (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). De server antwoordt met http-code 101 Switching Protocols en stelt ook de headers in Connection, Upgrade en Sec-WebSocket-Accept. Het proces van overschakeling wordt visueel weergegeven in de onderstaande afbeelding:

Domein-fronting op basis van TLS 1.3. Deel 2

Na deze stap kan de opzet van de WebSocket-verbinding als voltooid worden beschouwd. Gegevens van zowel de client als de server zullen nu worden voorzien van WebSocket-headers (deze beginnen met byte 0x82). De server hoeft nu niet meer te wachten op een verzoek van de client om gegevens te verzenden, aangezien de tcp-verbinding niet wordt verbroken.

In Go zijn er verschillende bibliotheken voor het werken met websockets. De meest populaire zijn Gorilla WebSocket en de standaard WebSocket. We zullen de laatste gebruiken, omdat deze eenvoudiger, kleiner is en, zoals men zegt, iets sneller werkt.

In de clientcode van rsockstun moeten we calls naar net.dial of tls.dial vervangen door de overeenkomstige WebSocket-calls:

Domein-fronting op basis van TLS 1.3. Deel 2

Domein-fronting op basis van TLS 1.3. Deel 2

We willen de clientzijde van onze tunneler universeel maken en in staat stellen te werken via zowel een directe ssl-verbinding als via het WebSocket-protocol. Hiervoor creĆ«ren we een aparte functie func connectForWsSocks(address string, proxy string) error {…} in navolging van connectForSocks() en we zullen deze gebruiken voor het werken met websockets in het geval dat het serveradres dat bij het starten van de client is opgegeven, begint met ws: of wss: (in het geval van Secure WebSocket).

Voor de serverzijde van de tunneler zullen we ook een aparte functie maken voor het werken met websockets. Hier wordt een instantie van de http-klasse aangemaakt en wordt een http-verbinding handler ingesteld (functie wsHandler):

Domein-fronting op basis van TLS 1.3. Deel 2

En de hele logica voor het afhandelen van de verbinding (authenticatie van de client via wachtwoord, opzetten en beƫindigen van de yamux-sessie) plaatsen we in de WebSocket-verbindingshandler:

Domein-fronting op basis van TLS 1.3. Deel 2

We compileren het project en starten de serverzijde:

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

En daarna de clientzijde:

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

En we testen de werking op de lokale host:

Domein-fronting op basis van TLS 1.3. Deel 2

Domein-fronting op basis van TLS 1.3. Deel 2

Laten we nu doorgaan naar domein-fronting

We lijken de websockets wel te begrijpen. Laten we nu de sprong maken naar eSNI en domein-fronting. Zoals eerder vermeld, moeten we een speciale Go-tak nemen van het bedrijf CloudFlare. We hebben de tak nodig met ondersteuning voor eSNI (pwu/esni).

We klonen deze lokaal of downloaden en unzippen het bijbehorende zip-bestand:

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

Daarna moeten we de GOROOT-map kopiĆ«ren, de bijbehorende bestanden uit de gekloonde tak vervangen en deze als de hoofdinstallatie instellen. Om de ontwikkelaar van deze hoofdpijn te verlossen, hebben de jongens van CloudFlare een speciaal script gemaakt – _dev/go.sh. Gewoon uitvoeren. Het script samen met de makefile doet alles zelf. Voor de interesse, je kunt een kijkje binnenin de makefile nemen voor meer details.

Na het uitvoeren van het script, moeten we bij het compileren van het project de lokale map die door het script is voorbereid opgeven als GOROOT. In ons geval ziet het er als volgt uit:

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

Vervolgens moeten we de functionaliteit voor het opvragen en parseren van openbare eSNI-sleutels voor het gewenste domein in de tunnel implementeren. In ons geval zijn dit de openbare eSNI-sleutels van de frontend-servers van CloudFlare. Hiervoor zullen we drie functies maken:

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

De functienamen spreken in principe voor zich. De inhoud zullen we halen uit het bestand esni_query.go, dat deel uitmaakt van tls-tris. De eerste functie creƫert een netwerkpakket met een verzoek naar de DNS-server van CloudFlare, waarbij gebruik wordt gemaakt van het DoH-protocol (DNS-over-HTTPS), de tweede parseert de resultaten van de aanvraag en verkrijgt de waarden van de openbare sleutels van het domein, en de derde fungeert als container voor de eerste twee.

Vervolgens voegen we onze net nieuw gemaakte functie voor het verbinden van de web socket toe. connectForWsSocks de functionaliteit voor het opvragen van eSNI-sleutels voor het domein. Waar de serverzijde functioneert, stellen we de TLS-parameters in en geven we de naam van het nep ā€˜afschermdomein’ op:

Domein-fronting op basis van TLS 1.3. Deel 2

Hier moet worden opgemerkt dat de tls-tris-tak in eerste instantie niet is ontworpen voor domein-frontring. Daarom is er geen aandacht besteed aan de nepservernaam (in het client-hello-pakket wordt een leeg veld serverName verzonden). Om dit te corrigeren, moeten we het overeenkomstige veld FakeServerName toevoegen aan de TlsConfig-structuur. Het standaardveld ServerName van de structuur kunnen we niet gebruiken, omdat interne mechanismen van TLS het gebruiken en als dit verschilt van de oorspronkelijke waarde, wordt de TLS-handshake beĆ«indigd met een fout. De beschrijving van de TlsConfig-structuur staat in het bestand tls/common.go – dat moeten we aanpassen:

Domein-fronting op basis van TLS 1.3. Deel 2

Domein-fronting op basis van TLS 1.3. Deel 2

Bovendien moeten we wijzigingen aanbrengen in het bestand tls/handshake_client.go, om onze FakeServerName-parameter te gebruiken bij het opstellen van de TLS-handshake:

Domein-fronting op basis van TLS 1.3. Deel 2

Dat is alles! Je kunt het project nu compileren en de werking controleren. Maar voordat je de controle uitvoert, moet je een CloudFlare-account instellen. Of beter gezegd, gewoon een account aanmaken op CloudFlare en je domein eraan koppelen. Alle functies met betrekking tot DoH, WebSocket en ESNI zijn standaard ingeschakeld in CloudFlare. Zodra de DNS-records zijn bijgewerkt, kun je de werking van het domein controleren door de eSNI-sleutels op te vragen:

dig +short txt _esni.df13tester.info 

Domein-fronting op basis van TLS 1.3. Deel 2

Als je iets vergelijkbaars voor jouw domein ziet, dan werkt alles en kun je doorgaan met testen.

Laten we een Ubuntu VPS opzetten, bijvoorbeeld bij DigitalOcean. P.S. In ons geval bleek het net uitgegeven IP-adres van de provider op de zwarte lijsten van de RKN te staan. Verwacht dus niet dat je daar niet mee te maken krijgt. We moesten een VPN gebruiken om toegang te krijgen tot onze VPS.

We kopiĆ«ren de al gecompileerde rsockstun naar de VPS (overigens, nog een voordeel van Golang – je kunt een project op je eigen systeem compileren en het op elke Linux-server draaien, zolang je maar de juiste architectuur aanhoudt) en starten het servergedeelte:

Domein-fronting op basis van TLS 1.3. Deel 2

En vervolgens het clientgedeelte:

Domein-fronting op basis van TLS 1.3. Deel 2

Zoals we zien, heeft de client succesvol verbinding gemaakt met de server via de frontend-server van CloudFlare met gebruik van een websoket. Om te controleren of de tunnel precies werkt als een tunnel, kunnen we een curl-aanroep doen via de lokale socks5 die op de server is geopend:

Domein-fronting op basis van TLS 1.3. Deel 2

Laten we nu kijken wat DPI ziet in de verbinding:

Domein-fronting op basis van TLS 1.3. Deel 2

Eerst vraagt de tunneler, gebruikmakend van het DoH-mechanisme, de DNS-server van Cloudflare om eSNI-sleutels voor het bestemmingsdomein (pakketten #1-19), en daarna neemt hij contact op met de frontend-server en stelt een TLS-verbinding in, waarbij hij zich verschuilt achter het domein www.google.com (dit is de standaardwaarde wanneer er geen nepdomein is opgegeven bij het starten van de client). Om je eigen nepdomein op te geven, moet je de parameter -frontDomain gebruiken:

Domein-fronting op basis van TLS 1.3. Deel 2

Domein-fronting op basis van TLS 1.3. Deel 2

Nu nog een punt. Standaard is de werkwijze van het CloudFlare-account ingesteld op Flexible SSL. Dit betekent dat https-verzoeken van clients naar de frontend-servers van CloudFlare onbeveiligd (http) naar onze server worden doorgestuurd. Om deze reden hebben we de serverkant van de tunneler in non-ssl-modus ( -listen ws:0.0.0.0) draaien, in plaats van ( -listen wss:0.0.0.0).

Domein-fronting op basis van TLS 1.3. Deel 2

Om over te schakelen naar volledige versleuteling, moet je kiezen voor Volledig, of Volledig (strikt) in het geval van een geldig certificaat op de server. Na het overschakelen van de modus kunnen we verbindingen van CloudFlare via het https-protocol accepteren. Vergeet niet om een self-signed certificaat voor het netwerkdeel van de tunnel te genereren.

Domein-fronting op basis van TLS 1.3. Deel 2

Een oplettende lezer vraagt: "En hoe zit het met de client onder Windows? Want waarschijnlijk is de belangrijkste toepassing van de tunnel het opzetten van een backconnect met bedrijfscomputers en servers, en daar draait meestal altijd Windows. Hoe compileer ik de tunnel onder Windows, en dan ook nog met een specifieke TLS-stack?" Laten we nu een andere functie verkennen die laat zien hoe handig Go is. We compileren onder Windows rechtstreeks vanuit Kali door gewoon de parameter GOOS=windows toe te voegen:

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

Of de 32-bits versie:

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

Klaar! En geen verdere complicaties nodig. Dit werkt echt!

Domein-fronting op basis van TLS 1.3. Deel 2

De compiler-flags -w en -s zijn nodig om extra rommel uit het uitvoerbare bestand te verwijderen, waardoor het een paar megabytes kleiner wordt. Bovendien kan het daarna worden verpakt met behulp van UPX om de grootte nog verder te verminderen.

Ter afsluiting

In het artikel hebben we, aan de hand van het in Go geschreven tunnel, een demonstratie gegeven van de toepassing van de nieuwe domein-fronting-technologie, gerealiseerd met een vrij interessante eigenschap van het TLS 1.3-protocol. Op een vergelijkbare manier kan bestaande tooling geschreven in Go worden aangepast voor gebruik via CloudFlare-servers, bijvoorbeeld Merlin — een bekende C2, of dwingen CobaltStrike Beacon om eSNI domein-fronting te gebruiken bij de communicatie met de Teamserver via External C2 Channel, gerealiseerd in Go, of in standaard C++ met behulp van een gepatchte versie van OpenSSL, waar we in het vorige deel van het artikel over hebben gesproken. Kortom, de mogelijkheden zijn eindeloos.

Het voorbeeld met de tunnel en CloudFlare wordt gepresenteerd als een concept en het is voorlopig moeilijk om te zeggen wat de verdere perspectieven van dit soort domein-fronting zijn. Momenteel is de ondersteuning voor eSNI alleen gerealiseerd bij CloudFlare en, in theorie, kan niets hen tegenhouden om dit soort fronting uit te schakelen en bijvoorbeeld TLS-verbindingen te verbreken bij een mismatch tussen SNI en eSNI. Kortom, de toekomst zal het leren. Maar voorlopig lijkt de mogelijkheid om onder "dekmantel van kremlin.ru" te werken behoorlijk verleidelijk. Of niet?

De bijgewerkte tunnelcode en gecompileerde uitvoerbare exe-bestanden zijn geplaatst in een aparte tak van het project op github. Voor alle mogelijke problemen met de tunnel kunt u het beste een issue schrijven op de projectpagina op GitHub.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers šŸ”„ Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster