Einführung
Im ersten Teil haben wir eine kurze Beschreibung des Mechanismus des verschlüsselten SNI (eSNI) gegeben. Wir zeigten, wie man mithilfe dessen modernen DPI-Systemen (am Beispiel des DPI von Beeline und dem verbotenen RKN-Rutracker) ausweichen kann, und untersuchten eine neue Variante des Domain-Fronting auf Grundlage dieses Mechanismus.
Im zweiten Teil des Artikels wenden wir uns den praktischeren Aspekten zu, die für RedTeam-Spezialisten in ihrer schwierigen Arbeit nützlich sein werden. Schließlich ist unser Ziel nicht der Zugang zu gesperrten Ressourcen (für so banale Dinge haben wir das gute alte VPN). Glücklicherweise gibt es viele VPN-Anbieter, wie man sagt, für jeden Geschmack, jede Farbe und jedes Budget.
Wir werden versuchen, den Mechanismus des Domain-Frontings für moderne RedTeam-Tools anzuwenden, wie zum Beispiel Cobalt Strike, Empire und andere, und ihnen zusätzliche Möglichkeiten für die Mimicry und das Ausweichen vor modernen Inhaltsfilter-Systemen zu bieten.
Beim letzten Mal haben wir den eSNI-Mechanismus in die OpenSSL-Bibliothek integriert und erfolgreich in dem jedem bekannten Tool curl verwendet. Aber mit curl allein, wie man sagt, ist man nicht satt. Natürlich möchte man etwas Ähnliches in Hochsprachen umsetzen. Leider enttäuscht uns eine flüchtige Suche im Internet, denn die vollständige Unterstützung des eSNI-Mechanismus ist nur in GOLANG realisiert. Somit ist unsere Auswahl nicht besonders groß: entweder wir programmieren in reinem C oder C++ unter Verwendung einer gepatchten OpenSSL-Bibliothek, oder wir verwenden einen separaten Fork von GOLANG von CloudFlare und versuchen, unser Toolkit dorthin zu portieren. Prinzipiell gibt es noch eine andere, klassischere aber gleichzeitig arbeitsintensivere Option – die Unterstützung von eSNI für Python zu implementieren. Schließlich verwendet Python ebenfalls OpenSSL für die Arbeit mit https. Aber diese Option lassen wir für jemand anderen zur Entwicklung übrig, während wir uns mit der Implementierung in Golang zufriedengeben, zumal unser Lieblings-Cobalt Strike hervorragend mit Kommunikationskanälen arbeitet, die von Drittmitteln aufgebaut sind (External C2 channel) – darüber werden wir am Ende des Artikels berichten.
Try Harder…
Eines der in Go umgesetzten Tools ist unsere Entwicklung für das Pivoting ins Netzwerk – der Tunnel , die übrigens heutzutage von Microsoft und Symantec als sehr schädliche Software erkannt wird, die darauf abzielt, die weltweite Stabilität zu stören…

Es wäre großartig, die vorherige Entwicklung auch in diesem Fall zu nutzen. Doch hier gibt es ein kleines Problem. Das liegt daran, dass rsockstun ursprünglich den Einsatz eines synchronen SSL-Kommunikationskanals mit dem Server vorsieht. Das bedeutet, dass die Verbindung einmal hergestellt wird und während der gesamten Laufzeit des Tunnels besteht. Und wie Sie verstehen können, ist das https-Protokoll nicht unbedingt für diesen Betriebsmodus geeignet – es funktioniert im Anfrage-Antwort-Modus, wobei jede neue http-Anfrage im Rahmen einer neuen tcp-Verbindung existiert.
Der Hauptnachteil eines solchen Schemas besteht darin, dass der Server dem Client keine Daten übermitteln kann, solange der Client keine neue http-Anfrage sendet. Aber zum Glück gibt es viele Varianten zur Lösung dieses Problems – der Streaming-Datenübertragung über das https-Protokoll (schließlich schaffen wir es ja irgendwie, unsere Lieblingsserien zu schauen und Musik von Portalen zu hören, die auf https arbeiten, und die Übertragung von Video und Audio ist nichts anderes als Streamen von Daten). Eine der Technologien zur Emulation einer vollständigen tcp-Verbindung über das http-Protokoll ist die WebSocket-Technologie, deren Hauptziel die Schaffung einer vollwertigen Netzwerkverbindung zwischen Client und Webserver ist.
Zu unserem Glück (hurra!!!) ist diese Technologie standardmäßig in allen CloudFlare-Tarifplänen integriert und funktioniert hervorragend in Kombination mit eSNI. Genau das werden wir nutzen, um unseren Tunnel zu lehren, Domain-Fronting zu verwenden und sich vor modernen DPI zu verstecken.
Ein wenig über WebSockets
Zuerst werden wir kurz und einfach über WebSockets sprechen, damit alle eine Vorstellung davon haben, woran wir arbeiten werden.
Die WebSocket-Technologie ermöglicht es, temporär von einer http-Verbindung auf einen Standard-Streaming-Datenübertragungsmodus über einen Netzwerk-Socket umzuschalten, ohne die bestehende tcp-Verbindung zu unterbrechen. Wenn der Client auf WebSocket umschalten möchte, setzt er in seiner http-Anfrage mehrere http-Header. Zwei verpflichtende Header sind Connection: Upgrade und Upgrade: websocket. Er kann auch die Version des WebSocket-Protokolls zwangsweise angeben (Sec-Websockset-Version: 13) und eine Art base64-Bezeichner für den Websocket (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). Der Server antwortet mit dem http-Code 101 Switching Protocols und setzt ebenfalls die Header Connection, Upgrade und Sec-WebSocket-Accept. Der Prozess des Wechsels wird im Screenshot unten veranschaulicht:

Nach diesem Schritt kann die Einrichtung der WebSocket-Verbindung als abgeschlossen betrachtet werden. Alle Daten, sowohl vom Client als auch vom Server, werden jetzt nicht mehr mit http-, sondern mit WebSocket-Headern versehen (sie beginnen mit dem Byte 0x82). Der Server muss nun nicht mehr auf eine Anfrage des Clients warten, um Daten zu übertragen, da die tcp-Verbindung nicht unterbrochen wird.
In Go gibt es mehrere Bibliotheken für die Arbeit mit WebSockets. Die beliebtesten sind und die Standard- . Wir werden letzteres verwenden, da es einfacher, kompakter und angeblich etwas schneller ist.
Im Client-Code von rsockstun müssen wir die Aufrufe von net.dial oder tls.dial durch die entsprechenden WebSocket-Aufrufe ersetzen:


Wir möchten den Client-Teil unseres Tunnels universell gestalten, sodass er sowohl über eine direkte SSL-Verbindung als auch über das WebSocket-Protokoll arbeiten kann. Dazu erstellen wir eine separate Funktion func connectForWsSocks(address string, proxy string) error {…} analog zu connectForSocks() und werden sie für die Arbeit mit WebSockets verwenden, falls die bei der Client-Start angegebenen Serveradresse mit ws: oder wss: (im Fall von Secure WebSocket) beginnt.
Für den Serverteil des Tunnels werden wir auch eine separate Funktion für die Arbeit mit WebSockets erstellen. Darin wird eine Instanz der http-Klasse erstellt und der http-Verbindungshandler (Funktion wsHandler) festgelegt:

Die gesamte Logik der Verbindungsverarbeitung (Client-Authentifizierung mit Passwort, Einrichtung und Beendigung der yamux-Sitzung) werden wir im WebSocket-Verbindungshandler unterbringen:

Wir kompilieren das Projekt und starten den Serverteil:
.\/rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rdUnd dann den Client-Teil:
.\/rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rdUnd wir überprüfen die Funktionalität auf dem lokalen Host:


Lass uns zum Domain-Fronting übergehen
Mit WebSockets scheinen wir soweit klarzukommen. Lassen Sie uns nun direkt zu eSNI und Domain-Fronting übergehen. Wie bereits erwähnt, benötigen wir für die Arbeit mit DoH und eSNI einen speziellen Zweig von Go von der Firma . Wir benötigen den Zweig mit Unterstützung für eSNI (pwu/esni).
Wir klonen ihn lokal oder laden ihn herunter und entpacken die entsprechende Zip-Datei:
git clone -b pwu/esni https://github.com/cloudflare/tls-tris.gitDann müssen wir das GOROOT-Verzeichnis kopieren, die entsprechenden Dateien aus dem geklonten Branch ersetzen und es als Hauptverzeichnis festlegen. Um den Entwicklern diese Kopfschmerzen zu ersparen, haben die Jungs von CloudFlare ein spezielles Skript vorbereitet – _dev/go.sh. Wir führen es einfach aus. Das Skript erledigt zusammen mit dem Makefile alles selbst. Aus Interesse – Sie können gerne ins Makefile schauen, um die Details zu erfahren.
Nach dem Ausführen des Skripts müssen wir beim Kompilieren des Projekts das lokale Verzeichnis, das vom Skript vorbereitet wurde, als GOROOT angeben. In unserem Fall sieht das so aus:
GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….Als Nächstes müssen wir im Tunnel einen Funktionsaufruf und das Parsen der öffentlichen eSNI-Schlüssel für die benötigte Domain implementieren. In unserem Fall werden dies die öffentlichen eSNI-Schlüssel von den Frontend-Servern von CloudFlare sein. Dazu erstellen wir drei Funktionen:
func makeDoTQuery(dnsName string) ([]byte, error)
func parseTXTResponse(buf []byte, wantName string) (string, error)
func QueryESNIKeysForHost(hostname string) ([]byte, error)Die Funktionsnamen sprechen im Grunde für sich. Die Inhalte werden wir aus der Datei esni_query.go entnehmen, die Teil von tls-tris ist. Die erste Funktion erstellt ein Netzwerkpaket mit der Anfrage an den DNS-Server von CloudFlare unter Verwendung des DoH-Protokolls (DNS-over-HTTPS), die zweite analysiert die Ergebnismenge der Anfrage und erhält die Werte der öffentlichen Schlüssel der Domain, während die dritte als Container für die ersten beiden fungiert.
Als Nächstes fügen wir unsere neu erstellte Funktion für die WebSocket-Verbindung ein. connectForWsSocks die Funktionalität zum Abfragen der eSNI-Schlüssel für die Domain. Dort, wo der Serverteil läuft, stellen wir die TLS-Parameter ein und definieren auch den Namen der gefälschten "Cover-Domain":

Hier sei erwähnt, dass der Branch tls-tris ursprünglich nicht für die Verwendung von Domain-Fronting ausgelegt ist. Folglich wurde darin der gefälschte Servernamen (in dem client-hello-Paket wird ein leeres Feld serverName übermittelt) keine Beachtung geschenkt. Um dies zu beheben, müssen wir das entsprechende Feld FakeServerName in die Struktur TlsConfig einfügen. Das Standardfeld ServerName in der Struktur können wir nicht verwenden, da es von internen Mechanismen von tls verwendet wird, und wenn es von dem ursprünglichen abweicht, wird der tls-Handshake mit einem Fehler enden. Die Beschreibung der Struktur TlsConfig befindet sich in der Datei tls/common.go – die müssen wir anpassen:


Zusätzlich müssen wir Änderungen an der Datei tls/handshake_client.go, um unser Feld FakeServerName bei der TLS-Handshake-Bildung zu verwenden:

Das war's! Sie können das Projekt kompilieren und die Funktionalität überprüfen. Aber bevor Sie den Test starten, müssen Sie Ihr CloudFlare-Konto einrichten. Nun, wie sagt man einrichten – einfach ein Konto bei CloudFlare erstellen und Ihre Domain damit verknüpfen. Alle Funktionen, die mit DoH, WebSocket und ESNI verbunden sind, sind standardmäßig in CloudFlare aktiviert. Nachdem die DNS-Einträge aktualisiert wurden, können Sie die Funktionalität der Domain überprüfen, indem Sie einen eSNI-Schlüsselabfrage durchführen:
dig +short txt _esni.df13tester.info 
Wenn Sie etwas Ähnliches für Ihre Domain sehen, funktioniert alles und Sie können mit dem Testen fortfahren.
Wir starten einen Ubuntu VPS, zum Beispiel bei DigitalOcean. P.S. In unserem Fall war die gerade ausgegebene IP-Adresse des VPS auf den Blacklists des RKN. Also wundern Sie sich nicht, wenn Ihnen etwas Ähnliches passiert. Ich musste ein VPN benutzen, um auf meinen VPS zuzugreifen.
Wir kopieren den bereits kompilierten rsockstun auf den VPS (übrigens, das ist eine weitere tolle Eigenschaft von Go – Sie können das Projekt bei sich kompilieren und auf jedem Linux-System ausführen, solange Sie die Architektur beibehalten) und starten den Serverteil:

Und dann den Clientteil:

Wie wir sehen, hat sich der Client erfolgreich über den Frontend-Server von CloudFlare mit dem Server verbunden, indem er WebSocket verwendet. Um zu überprüfen, dass der Tunnel tatsächlich als Tunnel funktioniert, können Sie eine curl-Anfrage über den lokalen socks5, der auf dem Server geöffnet ist, durchführen:

Jetzt schauen wir, was der DPI im Kommunikationskanal sieht:

Zuerst kontaktiert der Tunnel, unter Verwendung des DoH-Mechanismus, den DNS-Server von Cloudflare, um eSNI-Schlüssel für die Zieldomain abzufragen (Pakete Nr. 1-19), bevor er sich an den Frontend-Server wendet und eine TLS-Verbindung herstellt, wobei er sich hinter der Domain versteckt (dies ist der Standardwert, wenn beim Start des Clients keine gefälschte Domain angegeben wurde). Um Ihre eigene gefälschte Domain anzugeben, müssen Sie den Parameter -fronfDomain verwenden:
![]()

Jetzt noch ein weiterer Punkt. Standardmäßig ist im Konto bei CloudFlare der Modus "Flexible SSL" eingestellt. Das bedeutet, dass https-Anfragen von Clients an die Frontend-Server von Cloudflare unverschlüsselt (http) an unseren Server weitergeleitet werden. Deshalb haben wir den Serverteil des Tunnelers im non-ssl-Modus (-listen ws:0.0.0.0) und nicht (-listen wss:0.0.0.0) gestartet.

Um in den Modus voller Verschlüsselung umzuschalten, müssen Sie auswählen Voll, oder Voll (streng) im Falle des Vorhandenseins eines gültigen Zertifikats auf dem Server. Nach dem Wechsel des Modus können wir Verbindungen von CloudFlare über das https-Protokoll annehmen. Vergessen Sie nicht, ein selbstsigniertes Zertifikat für den Serverteil des Tunnelers zu generieren.

Ein aufmerksamer Leser könnte fragen: "Was ist mit dem Client für Windows? Schließlich wird der Tunneler hauptsächlich verwendet, um eine Back-Connect von Unternehmensmaschinen und -servern herzustellen, und dort läuft in der Regel immer Windows. Wie kann ich den Tunneler für Windows kompilieren, und das noch mit einem spezifischen TLS-Stack?" Jetzt präsentieren wir ein weiteres Feature, das zeigt, wie nützlich Go ist. Wir kompilieren direkt unter Windows aus Kali, indem wir einfach den Parameter GOOS=windows hinzufügen:
GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Oder die 32-Bit-Variante:
GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Fertig! Und keine weiteren Komplikationen nötig. Das funktioniert wirklich!

Die Compiler-Flags –w und –s sind notwendig, um überflüssigen Ballast aus der ausführbaren Datei zu entfernen und sie um ein paar Megabyte zu verkleinern. Zusätzlich kann sie später mit UPX gepackt werden, um die Größe weiter zu reduzieren.
Zum Abschluss
In diesem Artikel haben wir am Beispiel des in Go geschriebenen Tunnelers die Anwendung der neuen Domain-Fronting-Technologie veranschaulicht, die auf einem recht interessanten Merkmal des TLS 1.3-Protokolls basiert. Ähnlich kann das bestehende Werkzeug, das in Go geschrieben wurde, für die Arbeit über CloudFlare-Server angepasst werden, zum Beispiel — ein bekannter C2, oder lassen Sie CobaltStrike Beacon eSNI-Domain-Fronting bei der Arbeit mit dem Teamserver über , realisiert in Go, oder auf Standard-C++ mit einer gepatchten Version von OpenSSL, über die wir im vorherigen Teil des Artikels gesprochen haben. Kurz gesagt, der Fantasie sind keine Grenzen gesetzt.
Das Beispiel mit dem Tunneler und CloudFlare wird als Konzept präsentiert und es ist momentan schwer zu sagen, welche Perspektiven diese Art von Domain-Fronting bieten könnte. Derzeit wird eSNI nur von CloudFlare unterstützt und theoretisch gibt es nichts, was sie daran hindern könnte, solch ein Fronting abzuschalten und beispielsweise TLS-Verbindungen bei Nichtübereinstimmung von SNI und eSNI zu trennen. Die Zukunft wird es zeigen. Aber momentan sieht die Perspektive, „unter dem Deckmantel von kremlin.ru“ zu arbeiten, ziemlich verlockend aus. Oder nicht?
Der aktualisierte Tunnelcode sowie die kompilierte ausführbare exe-Dateien sind in einem separaten Branch des Projekts auf . Bei möglichen Problemen mit dem Tunnel ist es besser, ein Issue auf der Projektseite auf GitHub zu schreiben.
Quelle: habr.com
