Úvod
V první části jsme poskytli stručný popis mechanismu encrypted SNI (eSNI). Ukázali jsme, jak se na jeho základě dá vyhnout detekci moderními systémami DPI (na příkladu bilyanovského DPI a zakázaného RKN rutrackeru) a prozkoumali jsme novou variantu domain-frontingu založenou na tomto mechanismu.
Ve druhé části článku se zaměříme na praktické aspekty, které budou užitečné specialistům RedTeam v jejich náročné práci. Nakonec naším cílem není přístup k zablokovaným zdrojům (na tyto prosté účely máme starý dobrý VPN). Naštěstí existuje spousta poskytovatelů VPN, jak se říká, na každou chuť, barvu a rozpočet.
Pokusíme se aplikovat mechanismus domain-frontingu na moderní nástroje RedTeam, jako je Cobalt Strike, Empire a další, a poskytneme jim další možnosti mimikry a vyhýbání se moderním filtrovacím systémům.
Minule jsme implementovali mechanismus eSNI do knihovny OpenSSL a úspěšně jsme ho použili v známém nástroji curl. Ale pouze s curlem, jak se říká, se nenasytíme. Samozřejmě bychom rádi realizovali něco podobného v jazycích vyšší úrovně. Bohužel, rychlé hledání po spletích internetu nás zklamalo, protože podpora mechanismu eSNI je plně realizována pouze v GOLANG. Takže naše volba není příliš široká: buď napíšeme v čistém C nebo C++ s použitím upravené knihovny OpenSSL, nebo použijeme samostatný fork GOLANG od CloudFlare a pokusíme se portovat naše nástroje tam. V zásadě existuje ještě jedna možnost, více klasická, ale zároveň pracná — implementovat podporu eSNI pro Python. Nakonec Python také používá OpenSSL pro práci s https. Ale tuto možnost necháme na vývoj někomu jinému, a sami se spokojíme s implementací v Go, zejména že náš oblíbený Cobalt Strike skvěle pracuje s externími komunikačními kanály (External C2 channel) – o tom se zmíníme na konci článku.
Zkuste to lépe…
Jeden z nástrojů realizovaných na Go – náš vývoj pro pivoting do sítě – tunel , která, mimochodem, je v současnosti detekována prostředky společnosti Microsoft a Symantec jako velmi škodlivý software zaměřený na narušení světové stability…

Bylo by skvělé použít předchozí vývoj i v tomto případě. Ale tady se objevil malý problém. Věc se má tak, že původně rsockstun předpokládá použití synchronního SSL kanálu komunikace se serverem. To znamená, že spojení se navazuje jednou a existuje po celou dobu provozu tunelového aplikace. A jak asi chápete, protokol https není úplně určen pro tento režim práce – funguje v režimu požadavek-odpověď, kde každý nový http-požadavek existuje v rámci nového tcp-spojení.
Hlavní nevýhoda takového schématu spočívá v tom, že server nemůže posílat data klientovi, dokud klient neodešle nový http-požadavek. Ale naštěstí existuje mnoho variant řešení této problematiky – streamovací přenos dat prostřednictvím http protokolu (nakonec jsme schopni nějakým způsobem sledovat naše oblíbené seriály a poslouchat hudbu z portálů fungujících na https, a přenos videa a audia není nic jiného než streamovací přenos dat). Jednou z technologií pro emulaci plnohodnotného tcp-spojení přes http protokol jsou technologie webových soketů (WebSockets), jejíž hlavním smyslem je organizovat plnohodnotné síťové spojení mezi klientem a webovým serverem.
Na naše štěstí (hurá!!!) je tato technologie ve všech tarifech CloudFlare zapnuta jako výchozí a skvěle funguje spolu s eSNI. Právě ji budeme používat k tomu, abychom naučili náš tunelový program používat domeňový fronting a skrývat se před moderními DPI.
Krátce o WebSockets
Nejdříve krátce a jednoduchými slovy vysvětlíme webové sokety, abychom měli všichni představu o tom, s čím budeme pracovat.
Technologie webových soketů umožňuje dočasně přepnout z http-spojení na standardní streamovací přenos dat prostřednictvím síťového soketu, aniž by bylo nutné přerušit existující tcp-spojení. Když chce klient přepnout na webový soket, ve svém http-požadavku nastaví několik http-záhlaví. Dvě povinná záhlaví jsou: Connection: Upgrade a Upgrade: websocket. Může také výslovně uvést verzi protokolu websocket (Sec-Websockset-Version: 13) a něco jako base64 identifikátor webového socketu (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). Server na to odpovídá http-kódem 101 Switching Protocols a také nastavuje hlavičky Connection, Upgrade a Sec-WebSocket-Accept. Proces přepnutí je zřetelně demonstrován na screenshotu níže:

Poté můžeme považovat nastavení WebSocket připojení za dokončené. Jakákoliv data, ať už od klienta, nebo od serveru, nyní budou opatřena nikoliv http, ale WebSocket hlavičkami (ty začínají bajtem 0x82). Nyní server nemusí čekat na žádost od klienta, aby přenesl data, protože tcp-připojení není přerušeno.
V Go pro práci s webovými sockety existuje několik knihoven. Nejznámější z nich jsou a standardní . Využijeme tu poslední, protože je jednodušší, menší a funguje, jak se říká, trochu rychleji.
V kódu klienta rsockstun musíme nahradit volání net.dial nebo tls.dial odpovídajícími voláními WebSocket:


Chceme, aby klientská část našeho tunelovacího programu byla univerzální a schopná pracovat jak přes přímé ssl-připojení, tak přes protokol WebSocket. K tomu vytvoříme samostatnou funkci func connectForWsSocks(address string, proxy string) error {…} podobně jako connectForSocks() a budeme ji používat pro práci s webovými sockety, pokud adresa serveru zadaná při spuštění klienta bude začínat s ws: nebo wss: (v případě zabezpečeného WebSocketu).
Pro serverovou část tunelovacího programu také vytvoříme samostatnou funkci pro práci s webovými sockety. V ní bude vytvořen instance třídy http a bude nastavovat zpracovatele http-připojení (funkce wsHandler):

A veškerou logiku zpracování připojení (autentizace klienta pomocí hesla, zřízení a ukončení yamux relace) umístíme do zpracovatele připojení WebSocket:

Kompilujeme projekt, spouštíme serverovou část:
.\/rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rdA poté klientskou část:
.\/rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rdA zkontrolujeme funkčnost na lokálním hostu:


Přejdeme k domain-frontingu
S webovými sockety jsme zdá se vyřešili. Nyní se pojďme přechodně zaměřit na eSNI a domain-fronting. Jak již bylo zmíněno dříve, pro práci s DoH a eSNI potřebujeme vzít speciální větev golangu od společnosti . Potřebujeme větev s podporou eSNI (pwu/esni).
Klonujeme ji k sobě lokálně nebo stáhneme a rozbalíme odpovídající zip:
git clone -b pwu/esni https://github.com/cloudflare/tls-tris.gitPoté je nutné zkopírovat adresář GOROOT, nahradit odpovídající soubory z naklonované větve a nastavit jej jako primární. Abychom uvolnili vývojáře od této starosti, kluci z CloudFlare připravili speciální skript – _dev/go.sh. Stačí jej spustit. Skript spolu s makefile si vše udělá sám. Pro zajímavost – můžete nakouknout dovnitř makefile pro podrobnosti.
Po provedení skriptu, při kompilaci projektu, budeme muset jako GOROOT uvést lokální adresář připravený skriptem. V našem případě to vypadá takto:
GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….Dále je nutné implementovat v tunelu funkci pro dotazování a parsování veřejných eSNI klíčů pro požadovanou doménu. V našem případě to budou veřejné eSNI klíče z front-end serverů CloudFlare. K tomu vytvoříme tři funkce:
func makeDoTQuery(dnsName string) ([]byte, error)
func parseTXTResponse(buf []byte, wantName string) (string, error)
func QueryESNIKeysForHost(hostname string) ([]byte, error)Názvy funkcí v podstatě hovoří samy za sebe. Obsah vezmeme z souboru esni_query.go, který je součástí tls-tris. První funkce vytváří síťový paket s dotazem na DNS server CloudFlare, používající protokol DoH (DNS-over-HTTPS), druhá parsuje výsledky dotazu a získává hodnoty veřejných klíčů domény, a třetí funguje jako kontejner pro první dvě.
Dále přidáváme do naší nově vytvořené funkce připojení webového socketu connectForWsSocks funkcionalitu pro dotazování eSNI klíčů pro doménu. Tam, kde funguje serverová část, nastavujeme parametry TLS a také stanovujeme jméno fiktivní "domény krytí":

Je třeba poznamenat, že původně nebyla větev tls-tris navržena pro použití s doménovým frontingem. Proto nebylo věnováno pozornost fiktivnímu jménu serveru (v rámci klientského hello paketu se předává prázdné pole serverName). Abychom to opravili, budeme muset přidat do struktury TlsConfig odpovídající pole FakeServerName. Standardní pole ServerName struktury nemůžeme použít, protože jej používají interní mechanismy tls, a pokud se bude lišit od původního, tls handshake skončí chybou. Popis struktury TlsConfig je obsažen v souboru tls/common.go – ten je třeba opravit:


Dále budeme muset provést změny v souboru tls/handshake_client.go, aby použít naše pole FakeServerName při vytváření TLS handshake:

Tímto jsme hotovi! Můžete zkompilovat projekt a zkontrolovat jeho funkčnost. Ale než začnete s kontrolou, je potřeba nastavit účet na CloudFlare. Abych to řekl jinak – jednoduše vytvořte účet na CloudFlare a připojte k němu svou doménu. Všechny funkce související s DoH, WebSocket a ESNI jsou ve výchozím nastavení na CloudFlare povoleny. Jakmile se DNS záznamy aktualizují – můžete zkontrolovat funkčnost domény provedením dotazu na eSNI klíče:
dig +short txt _esni.df13tester.info 
Pokud vidíte něco podobného pro svou doménu – znamená to, že všechno funguje a můžete přejít k testování.
Spouštíme Ubuntu VPS, například na DigitalOcean. P. S. V našem případě se ukázalo, že právě přidělená IP adresa VPS od poskytovatele se nachází na blacklistu RKN. Tak se nedivte, pokud se vám stane něco podobného. Museli jsme použít VPN, abychom se dostali na svůj VPS.
Kopírujeme na VPS již zkompilovaný rsockstun (v tom je mimochodem další výhoda Golangu – můžete zkompilovat projekt u sebe a spustit jej na libovolném Linuxu, stačí dodržet architekturu systému) a spouštíme serverovou část:

A poté klientskou část:

Jak vidíme, klient se úspěšně připojil k serveru přes frontendový server CloudFlare pomocí WebSocketu. Pro ověření, že tunel funguje skutečně jako tunel, můžete provést curl dotaz přes místní socks5, otevřený na serveru:

Nyní se podívejme, co DPI vidí v kanálu komunikace:

Nejprve tunelovací klient, pomocí mechanismu DoH, se obrací na DNS server CloudFlare pro eSNI klíče cílové domény (pakety č. 1-19), a poté se obrací na frontendový server a navazuje TLS spojení, přičemž se schovává za doménou (to je výchozí hodnota, když při spuštění klienta není zadána fiktivní doména). Pro určení své fiktivní domény je nutné použít parametr -fronfDomain:
![]()

Nyní ještě jedna věc. Ve výchozím nastavení má účet na CloudFlare nastaven režim Flexible SSL. To znamená, že https dotazy na frontendové servery CloudFlare od klientů budou přesměrovány v nešifrované (http) podobě na náš server. Právě proto jsme spouštěli serverovou část tunelovacího systému v non-ssl režimu (-listen ws:0.0.0.0), a ne (-listen wss:0.0.0.0).

Pro přepnutí do režimu plného šifrování je potřeba vybrat Full, nebo Full (strict) v případě existence platného certifikátu na serveru. Po přepnutí režimu budeme moci přijímat připojení od CloudFlare pomocí protokolu https. Nezapomeňte vygenerovat self-signed certifikát pro serverovou část tunelovače.

Pozorný čtenář se může zeptat: „A co klient na Windows? Vždyť hlavním použitím tunelovače je zbudování backconnectu z firemních počítačů a serverů, a tam většinou běží Windows. Jak mohu zkompilovat tunelovač pro Windows, když mám specifický stack TLS?“ A nyní si představme další vychytávku, která ukazuje, jak snadno se to dělá v Golangu. Kompilujeme pro Windows přímo z Kali, jednoduše přidáním parametru GOOS=windows:
GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Nebo 32bitovou variantu:
GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Hotovo! A žádné další komplikace nejsou potřeba. Opravu to funguje!

Kompilační flagem –w a –s slouží k odstranění zbytečného balastu z spustitelného souboru, čímž ho zmenšíme o pár megabytů. Dodatečně ho lze poté zabalit pomocí UPX, aby se ještě zmenšila velikost.
Na závěr
V článku jsme na příkladu tunelovače napsaného v Golangu názorně ukázali použití nové technologie domain-fronting, která vychází z fascinující rysy protokolu TLS 1.3. Podobným způsobem lze přizpůsobit existující nástroje napsané v Golangu tak, aby pracovaly s servery CloudFlare, například. — známý C2, nebo přinutit CobaltStrike Beacon využít eSNI domain-fronting při práci s Teamserver přes , implementovaný v Golangu, nebo také ve standardním C++ s použitím upravené verze OpenSSL, o které jsme hovořili v předchozí části článku. V podstatě, fantazii se meze nekladou.
Příklad s tunelovačem a CloudFlare je představen jako koncept a v tuto chvíli je obtížné říci, jaké budou dalekosáhlé vyhlídky tohoto typu domain-frontingu. V současnosti je podpora eSNI zavedena pouze u CloudFlare a teoreticky by nic nemělo bránit jejich vypnutí tohoto typu frontingu a například ukončení tls-připojení při nesouladu SNI a eSNI. Budoucnost ukáže. Ale prozatím se perspektiva práce "pod hlavičkou kremlin.ru" zdá být velmi lákavá. Co říkáte?
Aktualizovaný kód tunelového programu, jakož i zkompilované spustitelné exe soubory jsou umístěny v samostatné větvi projektu na . O všech možných problémech tunelového programu je nejlepší psát issue na stránce projektu na GitHubu.
Zdroj: habr.com
