Frontering domen na bazie TLS 1.3. Część 2

Wprowadzenie

W pierwszej części artykułu przedstawiliśmy krótki opis mechanizmu encrypted SNI (eSNI). Pokazaliśmy, jak można na jego podstawie unikać wykrywania przez nowoczesne systemy DPI (na przykład na podstawie DPI Beeline i zablokowanego ruskiego trackeru), a także zbadaliśmy nową wersję frontingu domeny opartą na tym mechanizmie.

W drugiej części artykułu przejdziemy do bardziej praktycznych aspektów, które będą przydatne specjalistom RedTeam w ich trudnej pracy. W końcu naszym celem nie jest uzyskanie dostępu do zablokowanych zasobów (do takich banałów mamy starą dobrą VPN). Na szczęście dostawców VPN istnieje niezliczona ilość, jak mówią, na każdy gust, kolor i budżet.

Postaramy się zastosować mechanizm frontingu domeny do nowoczesnych narzędzi RedTeam, takich jak Cobalt Strike, Empire itp., i dać im dodatkowe możliwości w zakresie mimikry i unikania nowoczesnych systemów filtrowania treści.

Ostatnim razem wdrożyliśmy mechanizm eSNI w bibliotece OpenSSL i skutecznie wykorzystaliśmy go w znanym wszystkim narzędziu curl. Ale tylko curl'em, jak mówi przysłowie, nie będziesz syty. Oczywiście chcielibyśmy wdrożyć coś podobnego w językach wyższego poziomu. Niestety, szybkie poszukiwania w sieci rozczarowują nas, ponieważ wsparcie dla mechanizmu eSNI jest w pełni realizowane tylko w GOLANG. Tak więc wybór nie jest zbyt duży: albo piszemy w czystym C lub C++ z użyciem zmodyfikowanej biblioteki OpenSSL, albo korzystamy z osobnego forka GOLANG od CloudFlare i próbujemy przenieść nasze narzędzia tam. Istnieje jeszcze jedna opcja, bardziej klasyczna, ale jednocześnie czasochłonna – wdrożyć wsparcie eSNI dla Pythona. W końcu Python również korzysta z OpenSSL do pracy z https. Ale tę opcję zostawimy do rozwoju komuś innemu, a sami zadowolimy się realizacją w golangu, zwłaszcza że nasz ulubiony Cobalt Strike doskonale potrafi współpracować z kanałem komunikacyjnym zbudowanym za pomocą zewnętrznych środków (External C2 channel) – o tym opowiemy na końcu artykułu.

Try Harder…

Jednym z narzędzi zaimplementowanych w Go jest nasze opracowanie do pivotowania wewnątrz sieci – tunel rsockstun, która, nawiasem mówiąc, obecnie jest wykrywana przez oprogramowanie firm Microsoft i Symantec jako bardzo złośliwe oprogramowanie, mające na celu naruszenie stabilności światowej…

Frontering domen na bazie TLS 1.3. Część 2

Fajnie byłoby wykorzystać poprzednie opracowanie i w tym przypadku. Ale pojawia się mały problem. Chodzi o to, że pierwotnie rsockstun zakłada użycie synchronizowanego SSL kanału komunikacyjnego z serwerem. Oznacza to, że połączenie nawiązywane jest raz i istnieje przez cały czas działania tunelera. A jak wiadomo, protokół https nie do końca jest przystosowany do takiego trybu pracy – działa w trybie żądanie-odpowiedź, gdzie każde nowe żądanie http istnieje w ramach nowego połączenia tcp.

Główną wadą takiego schematu jest to, że serwer nie może przesyłać danych do klienta, dopóki klient nie wyśle nowego żądania http. Ale na szczęście istnieje wiele opcji rozwiązania tego problemu – przesyłania danych strumieniowo przez protokół http (w końcu jakoś udaje nam się oglądać nasze ulubione seriale i słuchać muzyki z portali działających na https, a przesyłanie wideo i audio to nic innego jak przesyłanie danych strumieniowo). Jedną z technologii do emulacji działania pełnoprawnego połączenia tcp nad protokołem http są technologie WebSockets, których główną ideą jest organizacja pełnoprawnego połączenia sieciowego między klientem a serwerem www.

Na nasze szczęście (hurra!!!), ta technologia jest włączona domyślnie we wszystkich planach taryfowych CloudFlare i świetnie współpracuje z eSNI. Właśnie ją zamierzamy wykorzystać, aby nauczyć nasz tunelować technologię domain fronting i ukrywać się przed nowoczesnym DPI.

Kilka słów o WebSockets

Przede wszystkim w skrócie i prostymi słowami opowiemy o WebSockets, aby wszyscy mieli pojęcie o tym, z czym będziemy pracować.

Technologia WebSockets pozwala tymczasowo przełączyć się z połączenia http na standardowe przesyłanie strumieniowe danych przez gniazdo sieciowe, nie zrywając przy tym ustanowionego połączenia tcp. Gdy klient chce przełączyć się na WebSocket, w swoim żądaniu http wysyła kilka nagłówków http. Dwie obowiązkowe nagłówki to: Connection: Upgrade i Upgrade: websocket. Może on również wymusić wersję protokołu websocket (Sec-Websocket-Version: 13) oraz coś w rodzaju identyfikatora base64 dla websocketu (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). Serwer odpowiada kodem http 101 Switching Protocols i również ustawia nagłówki Connection, Upgrade i Sec-WebSocket-Accept. Proces przełączania jest zobrazowany na zrzucie ekranu poniżej:

Frontering domen na bazie TLS 1.3. Część 2

Po tym można uznać, że nawiązanie połączenia WebSocket zostało zakończone. Wszystkie dane zarówno od klienta, jak i od serwera będą teraz używać nagłówków WebSocket (zaczynają się od bajtu 0x82). Teraz serwer nie musi czekać na zapytanie od klienta, aby przesłać dane, ponieważ połączenie tcp nie jest zrywane.

W Go do pracy z websocketami istnieje kilka bibliotek. Najbardziej popularne to Gorilla WebSocket oraz standardowa WebSocket.. Skorzystamy z tej drugiej, ponieważ jest prostsza, mniejsza i, jak mówią, działa nieco szybciej.

W kodzie klienta rsockstun musimy zastąpić wywołania net.dial lub tls.dial odpowiednimi wywołaniami WebSocket:

Frontering domen na bazie TLS 1.3. Część 2

Frontering domen na bazie TLS 1.3. Część 2

Chcemy, aby część kliencka naszego tunelowania była uniwersalna i mogła działać zarówno przez bezpośrednie połączenie ssl, jak i przez protokół WebSocket. W tym celu stworzymy osobną funkcję func connectForWsSocks(address string, proxy string) error {…} na podobieństwo connectForSocks() i będziemy ją używać do pracy z websocketami, jeśli adres serwera podany przy uruchomieniu klienta zacznie się od ws: lub wss: (w przypadku Secure WebSocket).

Dla serwerowej części tunelowania również stworzymy osobną funkcję do pracy z websocketami. W niej będzie tworzony egzemplarz klasy http i ustawiany handler http (funkcja wsHandler):

Frontering domen na bazie TLS 1.3. Część 2

A całą logikę obsługi połączenia (autoryzacja klienta według hasła, nawiązanie i zakończenie sesji yamux) umieścimy w handlerze połączenia WebSocket:

Frontering domen na bazie TLS 1.3. Część 2

Kompilujemy projekt, uruchamiamy część serwerową:

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

A następnie część kliencką:

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

I sprawdzamy działanie na lokalnym hoście:

Frontering domen na bazie TLS 1.3. Część 2

Frontering domen na bazie TLS 1.3. Część 2

Przechodzimy do domeny fronting

Z websocketami chyba się uporaliśmy. Teraz przejdźmy bezpośrednio do eSNI i domeny fronting. Jak już wcześniej wspomniano, do pracy z DoH i eSNI musimy wziąć specjalną gałąź golanga od firmy CloudFlare. Potrzebujemy gałęzi z obsługą eSNI (pwu/esni).

Klonujemy ją lokalnie lub pobieramy i rozpakowujemy odpowiedni zip:

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

Następnie musimy skopiować katalog GOROOT, zamienić odpowiednie pliki z klonowanej gałęzi i ustawić go jako główny. Aby uwolnić programistów od tej uciążliwości, chłopaki z CloudFlare przygotowali specjalny skrypt – _dev/go.sh. Po prostu go uruchamiamy. Skrypt wraz z makefile zrobi wszystko sam. Dla ciekawości – możecie zajrzeć do makefile po szczegóły.

Po wykonaniu skryptu, przy kompilacji projektu, będziemy musieli wskazać jako GOROOT lokalny katalog przygotowany przez skrypt. W naszym przypadku wygląda to tak:

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

Następnie musimy zaimplementować w tunelu funkcjonalność do zapytania i parsowania publicznych kluczy eSNI dla odpowiedniej domeny. W naszym przypadku będą to publiczne klucze eSNI z serwerów frontowych CloudFlare. W tym celu stworzymy trzy funkcje:

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

Nazwy funkcji w zasadzie mówią same za siebie. Wypełnienie weźmiemy z pliku esni_query.go, który jest częścią tls-tris. Pierwsza funkcja tworzy pakiet sieciowy z zapytaniem do serwera DNS CloudFlare, używając protokołu DoH (DNS-over-HTTPS), druga – parsuje wyniki zapytania i pobiera wartości publicznych kluczy domeny, a trzecia jest kontenerem dla pierwszych dwóch.

Następnie wprowadzamy naszą nowo stworzoną funkcję dołączenia do WebSocket. connectForWsSocks funkcjonalność zapytania kluczy eSNI dla domeny. Tam, gdzie działa część serwerowa, ustawiamy parametry TLS oraz definiujemy nazwę fałszywego „domeny przykrywającej”:

Frontering domen na bazie TLS 1.3. Część 2

Należy zauważyć, że początkowo gałąź tls-tris nie była przystosowana do zastosowania domain fronting. W związku z tym nie poświęcono uwagi fałszywej nazwie serwera (w pakiecie client-hello przekazywane jest puste pole serverName). Aby to naprawić, będziemy musieli dodać odpowiednie pole FakeServerName do struktury TlsConfig. Standardowego pola ServerName w tej strukturze nie możemy używać, ponieważ wykorzystują je wewnętrzne mechanizmy tls i jeśli będzie różnić się od oryginalnego, to tls-handshake zakończy się błędem. Opis struktury TlsConfig znajduje się w pliku tls/common.go – który musimy poprawić:

Frontering domen na bazie TLS 1.3. Część 2

Frontering domen na bazie TLS 1.3. Część 2

Dodatkowo musimy wprowadzić zmiany w pliku tls/handshake_client.go, aby użyć naszego pola FakeServerName podczas formowania negocjacji TLS:

Frontering domen na bazie TLS 1.3. Część 2

To wszystko! Można kompilować projekt i sprawdzać działanie. Ale zanim uruchomisz test, musisz skonfigurować konto CloudFlare. Jak to powiedzieć, skonfigurować - po prostu utworzyć konto na CloudFlare i powiązać swoją domenę. Wszystkie funkcje związane z DoH, WebSocket i ESNI są domyślnie włączone w CloudFlare. Po zaktualizowaniu rekordów DNS - można sprawdzić działanie domeny, wykonując zapytanie o klucze eSNI:

dig +short txt _esni.df13tester.info 

Frontering domen na bazie TLS 1.3. Część 2

Jeśli widzisz coś podobnego dla swojej domeny - to znaczy, że wszystko działa i możesz przejść do testowania.

Uruchamiamy VPS Ubuntu, na przykład na DigitalOcean. P.S. W naszym przypadku właśnie przyznany przez dostawcę adres IP VPS znalazł się na czarnej liście RKN. Więc nie zdziw się, jeśli zdarzy się coś podobnego. Musiałem skorzystać z VPN, aby dostać się na swój VPS.

Kopiujemy na VPS już skompilowany rsockstun (w tym, swoją drogą, jeszcze jedna zaleta Golanga - można skompilować projekt na swoim sprzęcie i uruchomić go na dowolnym Linuksie, przestrzegając tylko architektury systemu) i uruchamiamy część serwerową:

Frontering domen na bazie TLS 1.3. Część 2

A następnie część kliencką:

Frontering domen na bazie TLS 1.3. Część 2

Jak widzimy, klient pomyślnie połączył się z serwerem przez serwer frontendowy CloudFlare za pomocą WebSocket. Aby sprawdzić, czy tunel działa jako tunel, można wykonać zapytanie curl przez lokalnego socks5, otwartego na serwerze:

Frontering domen na bazie TLS 1.3. Część 2

Teraz zobaczmy, co widzi DPI w kanale komunikacyjnym:

Frontering domen na bazie TLS 1.3. Część 2

Najpierw tunel z wykorzystaniem mechanizmu DoH zwraca się do serwera DNS Cloudflare po klucze eSNI dla domeny docelowej (pakiety nr 1-19), a następnie zwraca się do serwera frontendowego i ustanawia połączenie TLS, ukrywając się przy tym za domeną www.google.com (to jest wartość domyślna, gdy podczas uruchamiania klienta nie jest podana fałszywa domena). Aby wskazać swoją fałszywą domenę, należy użyć parametru -fronfDomain:

Frontering domen na bazie TLS 1.3. Część 2

Frontering domen na bazie TLS 1.3. Część 2

Teraz jeszcze jedna kwestia. Domyślnie w ustawieniach konta na CloudFlare włączony jest tryb Flexible SSL. Oznacza to, że zapytania https do serwerów frontendowych Cloudflare od klientów będą przekierowywane w niezaszyfrowanej (http) formie na nasz serwer. Dlatego uruchomiliśmy część serwerową tunelera w trybie non-ssl (-listen ws:0.0.0.0), a nie (-listen wss:0.0.0.0).

Frontering domen na bazie TLS 1.3. Część 2

Aby przełączyć się w tryb pełnego szyfrowania, należy wybrać Full, lub Full (strict) w przypadku posiadania odpowiedniego certyfikatu na serwerze. Po przełączeniu trybu będziemy mogli akceptować połączenia od CloudFlare za pomocą protokołu https. Nie zapomnij wygenerować certyfikatu self-signed dla serwera tunelera.

Frontering domen na bazie TLS 1.3. Część 2

Dociekliwy czytelnik zapyta: „A co z klientem pod Windows? Przecież zapewne główne zastosowanie tunelera to uruchamianie back-connect z komputerów i serwerów korporacyjnych, a tam zazwyczaj zawsze jest Windows. Jak mogę skompilować tuneler pod Windows, jeszcze ze specyficznym stosem TLS?” A teraz przedstawimy jeszcze jedną funkcję, która pokazuje, jak wygodny jest Go. Kompilujemy pod Windows bezpośrednio z Kali, po prostu dodając parametr GOOS=windows:

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

Lub 32-bitowa wersja:

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

To wszystko! I więcej żadnych problemów. To naprawdę działa!

Frontering domen na bazie TLS 1.3. Część 2

Flagi kompilatora –w i –s są potrzebne, aby usunąć zbędne dane z pliku wykonywalnego, co sprawia, że jest on mniejszy o kilka megabajtów. Dodatkowo można go później spakować za pomocą UPX, aby jeszcze bardziej zmniejszyć rozmiar.

Zamiast zakończenia

W artykule na przykładzie napisanego w Go tunelera pokazaliśmy zastosowanie nowej technologii domain-frontingu, zrealizowanej na dość interesującej właściwości protokołu TLS 1.3. W podobny sposób można dostosować istniejące narzędzia napisane w Go do pracy przez serwery CloudFlare, na przykład Merlin — znany C2, lub zmusić CobaltStrike Beacon do użycia eSNI domain-frontingu przy pracy z Teamserver przez External C2 Channel, zrealizowany w Go, lub też w standardowym C++ z użyciem spatchowanej wersji OpenSSL, o której mówiliśmy w poprzedniej części artykułu. W skrócie, wyobraźni nie ma końca.

Przykład z tunelerem i CloudFlare przedstawiony jest jako koncepcja i na razie trudno mówić o dalszych perspektywach tego rodzaju domain-frontingu. Na ten moment wsparcie eSNI jest zrealizowane tylko przez CloudFlare i teoretycznie nic nie stoi na przeszkodzie, aby wyłączyli tego rodzaju fronting i, na przykład, przerywali połączenia tls przy niespójności SNI i eSNI. W skrócie, przyszłość pokaże. Ale na razie perspektywa pracy pod „maską kremlin.ru” wydaje się całkiem kusząca. Czyż nie?

Zaktualizowany kod tunelera, a także skompilowane pliki wykonywalne exe zostały umieszczone w oddzielnej gałęzi projektu na github. O wszelkich możliwych problemach z tunelerem najlepiej pisać issue na stronie projektu na GitHubie.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster