Introduzione
Nella prima parte abbiamo fornito una breve descrizione del meccanismo encrypted SNI (eSNI). Abbiamo mostrato come, sulla base di questo, sia possibile eludere il rilevamento da parte dei moderni sistemi DPI (prendendo come esempio il DPI di Beeline e il tracker RKN bloccato), e abbiamo anche esaminato una nuova variante del domain fronting basata su questo meccanismo.
Nella seconda parte dell'articolo ci concentreremo su aspetti più pratici che saranno utili agli specialisti RedTeam nel loro lavoro impegnativo. In fin dei conti, il nostro obiettivo non è semplicemente ottenere accesso a risorse bloccate (per tali semplici esigenze abbiamo il caro vecchio VPN). Fortunatamente, esistono moltissimi provider VPN, come si suole dire, per ogni gusto, colore e budget.
Cercheremo di applicare il meccanismo del domain fronting agli strumenti moderni di RedTeam, come Cobalt Strike, Empire, ecc., e fornire loro ulteriori funzionalità per mimetizzarsi e eludere i moderni sistemi di filtraggio dei contenuti.
Nella scorsa occasione, abbiamo implementato il meccanismo eSNI nella libreria OpenSSL e l'abbiamo utilizzato con successo nell'utility conosciuta da tutti, curl. Ma con solo curl, come si dice, non si mangia. Certo, vorremmo realizzare qualcosa di simile in linguaggi ad alto livello. Sfortunatamente, una rapida ricerca nel vasto universo della rete ci delude, poiché il supporto del meccanismo eSNI è pienamente implementato solo in GOLANG. Pertanto, le nostre opzioni non sono molte: o scriviamo in puro C o C++ utilizzando una libreria OpenSSL patchata, oppure utilizziamo un fork separato di GOLANG di CloudFlare e cerchiamo di portare i nostri strumenti lì. In teoria, c'è anche un'altra opzione, più classica ma anche laboriosa: implementare il supporto per eSNI in Python. Dopotutto, Python utilizza OpenSSL per lavorare con https. Ma lasceremo questa opzione a qualcun altro, mentre noi ci accontenteremo dell'implementazione in Golang, soprattutto perché il nostro amato Cobalt Strike sa già lavorare con un canale di comunicazione costruito con mezzi esterni (External C2 channel) – di cui parleremo alla fine dell'articolo.
Try Harder…
Uno degli strumenti implementati in Go è il nostro sviluppo per il pivoting all'interno della rete – il tunneler , che, tra l'altro, attualmente viene rilevato dai sistemi di Microsoft e Symantec come un malware altamente dannoso, volto a compromettere la stabilità mondiale...

Sarebbe fantastico utilizzare il precedente sviluppo anche in questo caso. Ma qui sorge un piccolo problema. Infatti, inizialmente rsockstun prevede l'uso di un canale di comunicazione SSL sincrono con il server. Ciò significa che la connessione viene stabilita una volta e rimane attiva per tutta la durata del lavoro del tunneler. Come si può capire, il protocollo https è un po' inadatto per questo tipo di funzionamento – opera in modalità richiesta-risposta, dove ogni nuova richiesta http esiste nell'ambito di una nuova connessione tcp.
Il principale svantaggio di tale schema è che il server non può inviare dati al client finché il client non invia una nuova richiesta http. Ma, fortunatamente, esistono molte soluzioni per questo problema – per la trasmissione continua di dati attraverso il protocollo http (dopotutto, riusciamo in qualche modo a guardare le nostre serie preferite e a ascoltare musica da portali che operano su https, e la trasmissione di video e audio non è altro che una trasmissione continua di dati). Una delle tecnologie per emulare un funzionamento completo della connessione tcp sopra il protocollo http è la tecnologia WebSockets, il cui scopo principale è organizzare una connessione di rete completa tra client e server web.
Per nostra fortuna (evviva!!!), questa tecnologia è attivata di default in tutti i piani tariffari di CloudFlare e funziona perfettamente insieme all'eSNI. E proprio questo utilizzeremo per insegnare al nostro tunneler ad applicare il domain fronting e a nascondersi dai moderni DPI.
Un po' sui WebSockets
Prima di tutto, faremo una breve spiegazione sui web socket, affinché tutti abbiano chiaro con cosa stiamo lavorando.
La tecnologia dei web socket consente di passare temporaneamente da una connessione http a una normale trasmissione continua di dati attraverso un socket di rete, senza interrompere la connessione tcp stabilita. Quando il client desidera passare al web socket, inserisce nella sua richiesta http alcuni header http. Due header obbligatori sono Connection: Upgrade e Upgrade: websocket. Può anche specificare forzatamente la versione del protocollo websocket (Sec-Websockset-Version: 13) e qualcosa come un identificatore base64 del web socket (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). Il server risponde con il codice http 101 Switching Protocols e stabilisce anche gli header Connection, Upgrade e Sec-WebSocket-Accept. Il processo di switch è illustrato nello screenshot qui sotto:

Dopo ciò, si può considerare completata l'installazione della connessione WebSocket. I dati da entrambi i lati, sia dal cliente che dal server, ora saranno forniti con intestazioni WebSocket (che iniziano con il byte 0x82). Ora il server non ha più bisogno di attendere una richiesta dal cliente per trasmettere dati, poiché la connessione TCP non si interrompe.
In Go, ci sono diverse librerie per lavorare con i WebSocket. Le più popolari sono e la libreria standard . Utilizzeremo quest'ultima, poiché è più semplice, più leggera e, si dice, un po' più veloce.
Nel codice del client rsockstun, dobbiamo sostituire le chiamate net.dial o tls.dial con le corrispondenti chiamate WebSocket:


Vogliamo rendere la parte client del nostro tunneler universale e capace di funzionare sia tramite una connessione SSL diretta, sia tramite il protocollo WebSocket. Per questo, creeremo una funzione separata func connectForWsSocks(address string, proxy string) error {…} per analogia con connectForSocks() e la utilizzeremo per lavorare con i WebSocket nel caso in cui l'indirizzo del server specificato all'avvio del client inizi con ws: o wss: (nel caso di Secure WebSocket).
Per la parte server del tunneler, creeremo anche una funzione separata per lavorare con i WebSocket. In essa verrà creato un'istanza della classe http e verrà impostato un gestore per la connessione http (funzione wsHandler):

E tutta la logica per gestire la connessione (autenticazione del cliente tramite password, apertura e chiusura di una sessione yamux) sarà inserita nel gestore della connessione WebSocket:

Compiliamo il progetto, avviando la parte server:
./rsockstun –listen ws:127.0.0.1:8080 –pass P@ssw0rdE poi avviamo la parte client:
./rsockstun -connect ws:127.0.0.1:8080 –pass P@ssw0rdE verifichiamo il funzionamento sulla macchina locale:


Passiamo al domain fronting
Sembra che abbiamo compreso i WebSocket. Ora passiamo direttamente all'eSNI e al domain fronting. Come già menzionato, per lavorare con DoH e eSNI, dobbiamo prendere un ramo speciale di Go dalla società . Ci serve il ramo con supporto per eSNI (pwu/esni).
Cloniamo localmente oppure scarichiamo e decomprimiamo il relativo zip:
git clone -b pwu/esni https://github.com/cloudflare/tls-tris.gitPoi dobbiamo copiare la directory GOROOT, sostituire i file appropriati del ramo clonati e impostarlo come principale. Per evitare di causare mal di testa agli sviluppatori, i ragazzi di CloudFlare hanno preparato uno script speciale – _dev/go.sh. Basta eseguirlo. Lo script insieme al makefile si occuperà di tutto. Per curiosità, potete dare un'occhiata dentro il makefile per maggiori dettagli.
Dopo che lo script ha terminato, durante la compilazione del progetto, dovremo specificare come GOROOT la directory locale preparata dallo script. Nel nostro caso, appare così:
GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" go build ….Successivamente, dobbiamo implementare nel tunneler la funzionalità di richiesta e parsing delle chiavi pubbliche eSNI per il dominio desiderato. In questo caso, saranno le chiavi pubbliche eSNI dai server frontend di CloudFlare. Per questo, creeremo tre funzioni:
func makeDoTQuery(dnsName string) ([]byte, error)
func parseTXTResponse(buf []byte, wantName string) (string, error)
func QueryESNIKeysForHost(hostname string) ([]byte, error)I nomi delle funzioni parlano da soli. Il contenuto sarà preso dal file esni_query.go, che fa parte di tls-tris. La prima funzione crea un pacchetto di rete con una richiesta al server DNS di CloudFlare, utilizzando il protocollo DoH (DNS-over-HTTPS), la seconda analizza i risultati della richiesta e ottiene i valori delle chiavi pubbliche del dominio, e la terza funge da contenitore per le prime due.
Successivamente, aggiungiamo nella nostra, appena creata, funzione di connessione WebSocket connectForWsSocks la funzionalità per richiedere le chiavi eSNI per il dominio. Dove funziona la parte server, impostiamo i parametri TLS e definiamo il nome del "dominio fittizio":

Vale la pena notare che inizialmente, il ramo tls-tris non è progettato per l'uso del domain fronting. Di conseguenza, non si è prestata attenzione al nome fittizio del server (nella richiesta client-hello, il campo serverName viene passato vuoto). Per correggerlo, dobbiamo aggiungere alla struttura TlsConfig il campo corrispondente FakeServerName. Non possiamo utilizzare il campo standard ServerName della struttura, poiché viene utilizzato dai meccanismi interni di tls e se fosse diverso dall’originale, il handshake tls finirebbe con un errore. La descrizione della struttura TlsConfig è contenuta nel file tls/common.go – e questo è il file che dobbiamo modificare:


Inoltre, dovremo apportare modifiche al file tls/handshake_client.go, per utilizzare il nostro campo FakeServerName durante la creazione dell handshake TLS:

È tutto! Puoi compilare il progetto e verificarne il funzionamento. Ma prima di avviare il test, è necessario configurare l'account CloudFlare. Bene, per essere chiari, è sufficiente creare un account su CloudFlare e collegare il proprio dominio. Tutte le funzionalità relative a DoH, WebSocket e ESNI sono abilitate di default in CloudFlare. Dopo l'aggiornamento dei record DNS, puoi verificare il funzionamento del dominio eseguendo la richiesta delle chiavi eSNI:
dig +short txt _esni.df13tester.info 
Se vedi qualcosa di simile per il tuo dominio, significa che tutto funziona e puoi procedere con il testing.
Avviamo un Ubuntu VPS, ad esempio, su DigitalOcean. P.S. Nel nostro caso, l'indirizzo IP del VPS appena assegnato dal provider era presente nelle blacklist dell'RKN. Quindi non sorprendersi se ti capita qualcosa di simile. Ho dovuto utilizzare una VPN per accedere al mio VPS.
Copiamo su VPS il rsockstun già compilato (questo, tra l'altro, è un altro vantaggio di Go: puoi compilare il progetto sul tuo sistema e avviarlo su qualsiasi Linux, rispettando solo l'architettura del sistema) e avviamo la parte server:

E poi la parte client:

Come vediamo, il client si è connesso con successo al server tramite il server frontend di CloudFlare utilizzando un websocket. Per verificare che il tunnel funzioni davvero come tale, puoi fare una richiesta curl attraverso il socks5 locale aperto sul server:

Ora vediamo cosa vede il DPI nel canale di comunicazione:

Inizialmente, il tunneler, utilizzando il meccanismo DoH, si rivolge al server DNS di Cloudflare per le chiavi eSNI per il dominio di destinazione (pacchetti n. 1-19), e poi si connette al server frontend stabilendo una connessione TLS, coprendosi con il dominio (questo è il valore predefinito quando non è stato specificato un dominio fasullo all'avvio del client). Per specificare il proprio dominio fasullo, è necessario utilizzare il parametro -frontDomain:
![]()

Ora un altro punto. Di default, nelle impostazioni dell'account di CloudFlare è attivata la modalità Flexible SSL. Questo significa che le richieste https ai server frontend di CloudFlare dai client saranno reindirizzate in forma non criptata (http) al nostro server. È per questo motivo che abbiamo avviato la parte server del tunneler in modalità non-ssl (-listen ws:0.0.0.0), invece di (-listen wss:0.0.0.0).

Per passare alla modalità di crittografia completa, è necessario selezionare Completo, o Full (strict) nel caso in cui sia presente un vero certificato sul server. Dopo aver cambiato la modalità, saremo in grado di accettare connessioni da CloudFlare tramite il protocollo https. Non dimenticare di generare un certificato self-signed per la parte server del tunneler.

Un lettore attento potrebbe chiedere: «E per il client su Windows? Non è vero che l'uso principale del tunneler è quello di stabilire un back-connect da macchine e server aziendali, dove di solito si utilizza Windows? Come posso compilare il tunneler per Windows, con uno stack TLS specifico?» Ora presenteremo un'altra caratteristica che dimostra quanto sia conveniente Go. Compiliamo per Windows direttamente da Kali, semplicemente aggiungendo il parametro GOOS=windows:
GOARCH=amd64 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Oppure la variante a 32 bit:
GOARCH=386 GOROOT="/opt/tls-tris/_dev/GOROOT/linux_amd64" GOOS=windows go build -ldflags="-s -w"Tutto! E non ci sono ulteriori complicazioni. Funziona davvero!

I flag del compilatore -w e -s servono per rimuovere il superfluo dall'eseguibile, riducendone le dimensioni di alcuni megabyte. Può anche essere impacchettato successivamente con UPX, per ulteriormente diminuire la dimensione.
In conclusion
In questo articolo, abbiamo mostrato con l'esempio del tunneler scritto in Go, l'applicazione della nuova tecnologia di domain fronting, realizzata su una caratteristica piuttosto interessante del protocollo TLS 1.3. Analogamente, è possibile adattare gli strumenti esistenti scritti in Go per funzionare attraverso i server CloudFlare, per esempio — un noto C2, o costringere CobaltStrike Beacon a utilizzare il domain fronting eSNI quando lavora con il Teamserver attraverso , implementato in Go, o in standard C++ utilizzando una versione patchata di OpenSSL, di cui abbiamo parlato nella parte precedente dell'articolo. In generale, le possibilità sono illimitate.
L'esempio del tunneler e CloudFlare è presentato come concetto e al momento è difficile dire sulle prospettive a lungo termine di questa forma di domain fronting. Attualmente, il supporto per eSNI è implementato solo da CloudFlare e, in teoria, nulla impedisce loro di disabilitare questo tipo di fronting e, ad esempio, interrompere le connessioni tls in caso di disallineamento tra SNI ed eSNI. In generale, il futuro lo dirà. Ma per ora, la prospettiva di lavorare sotto "le sembianze di kremlin.ru" sembra piuttosto allettante. Non è vero?
Il codice aggiornato del tunneler, così come gli eseguibili compilati exe, sono disponibili in un ramo separato del progetto su . Per eventuali problemi del tunneler, è meglio scrivere un issue sulla pagina del progetto su GitHub.
Fonte: habr.com
