Domain Fronting baseado em TLS 1.3. Parte 2

Introdução

Na primeira parte artigo demos uma breve descrição do mecanismo encrypted SNI (eSNI). Mostramos de que forma, com base nele, é possível evitar a detecção por sistemas DPI modernos (com o exemplo do DPI da Beeline e o proibido RKN Rutracker) e também investigamos uma nova variante de domain fronting com base neste mecanismo.

Na segunda parte do artigo, abordaremos questões mais práticas que serão úteis para especialistas da RedTeam em seu trabalho desafiador. Afinal, nosso objetivo não é apenas obter acesso a recursos bloqueados (para essas coisas banais, temos o velho e bom VPN). Felizmente, existem muitos provedores de VPN, como se diz, para todos os gostos, cores e orçamentos.

Tentaremos aplicar o mecanismo de domain fronting a ferramentas modernas da RedTeam, como Cobalt Strike, Empire, etc., e oferecer a elas mais possibilidades de mimetização e de evitar sistemas contemporâneos de filtragem de conteúdo.

Na última vez, implementamos o mecanismo eSNI na biblioteca OpenSSL e o utilizamos com sucesso na conhecida ferramenta curl. Mas só com curl, como se diz, não se consegue se satisfazer. Claro que gostaríamos de implementar algo semelhante em linguagens de alto nível. Mas, infelizmente, uma pesquisa rápida pela rede nos decepciona, pois o suporte ao mecanismo eSNI está completamente implementado apenas no GOLANG. Assim, nossas opções não são muito amplas: ou escrevemos em C ou C++ puro usando a biblioteca OpenSSL patchada, ou utilizamos um fork separado do GOLANG da CloudFlare e tentamos portar nossas ferramentas para lá. Na verdade, há outra opção, mais clássica, mas também trabalhosa – implementar suporte ao eSNI para Python. Afinal, o Python também usa OpenSSL para trabalhar com https. Mas deixaremos essa opção para o desenvolvimento de outra pessoa, enquanto nós ficamos satisfeitos com a implementação em GOLANG, especialmente porque nosso querido Cobalt Strike lida muito bem com canais de comunicação estabelecidos por meios externos (External C2 channel) – isso será abordado no final do artigo.

Try Harder…

Uma das ferramentas implementadas em Go – nosso desenvolvimento para pivotar dentro da rede – tunnel rsockstun, que, aliás, agora é detectada por ferramentas da Microsoft e Symantec como um software malicioso muito prejudicial, destinado a desestabilizar o mundo…

Domain Fronting baseado em TLS 1.3. Parte 2

Seria ótimo usar o desenvolvimento anterior também neste caso. Mas aqui surge um pequeno problema. O fato é que o rsockstun originalmente pressupõe o uso de um canal de comunicação SSL síncrono com o servidor. Isso significa que a conexão é estabelecida uma vez e existe durante todo o tempo de funcionamento do tunelador. E, como você deve entender, o protocolo https não é exatamente projetado para esse modo de operação – ele funciona no modo de solicitação-resposta, onde cada nova solicitação http existe no âmbito de uma nova conexão tcp.

A principal desvantagem desse esquema é que o servidor não pode transmitir dados ao cliente até que o cliente envie uma nova solicitação http. Mas, felizmente, existem muitas opções para resolver esse problema – transmissão de dados via protocolo http (afinal, conseguimos assistir nossas séries favoritas e ouvir música em portais que funcionam com https, e a transmissão de vídeo e áudio nada mais é do que uma transmissão de dados). Uma das tecnologias para emular o funcionamento de uma conexão tcp completa sobre o protocolo http é a tecnologia de web sockets (WebSockets), cuja essência é organizar uma conexão de rede completa entre o cliente e o servidor web.

Por nossa sorte (ura-ura!!!), essa tecnologia está habilitada por padrão em todos os planos da CloudFlare e funciona muito bem em conjunto com eSNI. É exatamente isso que usaremos para ensinar nosso tunelador a utilizar domain fronting e se esconder de DPi modernos.

Um pouco sobre WebSockets

Antes de tudo, vamos explicar brevemente e de forma simples o que são web sockets, para que todos tenham uma ideia do que estaremos trabalhando.

A tecnologia de web sockets permite mudar temporariamente de uma conexão http para uma transmissão de dados padrão através de um socket de rede, sem quebrar a conexão tcp estabelecida. Quando o cliente deseja mudar para um web socket, ele inclui alguns cabeçalhos http em sua solicitação. Dois cabeçalhos obrigatórios são Connection: Upgrade e Upgrade: websocket. Ele também pode forçar a versão do protocolo websocket (Sec-Websocket-Version: 13) e algo parecido com um identificador base64 de websocket (Sec-WebSocket-Key: DAGDJSiREI3+KjDfwxm1FA==). O servidor responde com o código http 101 Switching Protocols e também define os cabeçalhos Connection, Upgrade e Sec-WebSocket-Accept. O processo de troca é ilustrado na tela abaixo:

Domain Fronting baseado em TLS 1.3. Parte 2

Após isso, a conexão WebSocket pode ser considerada estabelecida. Quaisquer dados, tanto do cliente quanto do servidor, agora serão fornecidos com cabeçalhos de WebSocket, não http (eles começam com o byte 0x82). Agora o servidor não precisa esperar um pedido do cliente para enviar dados, pois a conexão tcp não é interrompida.

Em Go, existem várias bibliotecas para trabalhar com websockets. As mais populares são Gorilla WebSocket e a padrão WebSocket. Nós utilizaremos a última, pois é mais simples, menor e, dizem, um pouco mais rápida.

No código do cliente rsockstun, precisamos substituir as chamadas net.dial ou tls.dial pelas respectivas chamadas de WebSocket:

Domain Fronting baseado em TLS 1.3. Parte 2

Domain Fronting baseado em TLS 1.3. Parte 2

Queremos tornar a parte cliente do nosso túnel universal e capaz de funcionar tanto através de uma conexão ssl direta quanto através do protocolo WebSockset. Para isso, criaremos uma função separada func connectForWsSocks(address string, proxy string) error {…} por analogia com connectForSocks() e a utilizaremos para trabalhar com websockets no caso de o endereço do servidor, definido ao iniciar o cliente, começar com ws: ou wss: (no caso de WebSocket Seguro).

Para a parte servidor do túnel, também faremos uma função separada para trabalhar com websockets. Nela, será criado um exemplo da classe http e definido um manipulador de conexão http (função wsHandler):

Domain Fronting baseado em TLS 1.3. Parte 2

E toda a lógica de tratamento da conexão (autenticação do cliente por senha, criação e finalização da sessão yamux) será colocada no manipulador de conexão WebSocket:

Domain Fronting baseado em TLS 1.3. Parte 2

Compilamos o projeto, iniciamos a parte do servidor:

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

E depois a parte cliente:

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

E verificamos o funcionamento no host local:

Domain Fronting baseado em TLS 1.3. Parte 2

Domain Fronting baseado em TLS 1.3. Parte 2

Vamos para o domain fronting

Com os websockets, parece que já entendemos. Agora vamos passar diretamente para eSNI e domain fronting. Como mencionado anteriormente, para trabalhar com DoH e eSNI, precisamos obter um ramo especial do Go da empresa CloudFlare. Precisamos da versão com suporte para eSNI (pwu/esni).

Clone-a localmente ou baixe e descompacte o zip correspondente:

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

Em seguida, precisamos copiar o diretório GOROOT, substituir os arquivos correspondentes da branch clonada e configurá-lo como o principal. Para livrar o desenvolvedor de dor de cabeça, a equipe da CloudFlare preparou um script especial – _dev/go.sh. É só executá-lo. O script, junto com o makefile, fará tudo automaticamente. Para curiosidade, você pode olhar dentro do makefile para mais detalhes.

Após a execução do script, ao compilar o projeto, precisaremos indicar como GOROOT o diretório local preparado pelo script. No nosso caso, isso fica assim:

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

Em seguida, precisamos implementar no túnel a funcionalidade de solicitação e parsing de chaves eSNI públicas para o domínio desejado. No nosso caso, essas serão as chaves eSNI públicas dos servidores front-end da CloudFlare. Para isso, criaremos três funções:

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

Os nomes das funções falam por si só. O conteúdo, por sua vez, pegaremos do arquivo esni_query.go, que faz parte do tls-tris. A primeira função cria um pacote de rede com a solicitação ao servidor DNS da CloudFlare, utilizando o protocolo DoH (DNS-over-HTTPS), a segunda faz o parsing dos resultados da solicitação e obtém os valores das chaves públicas do domínio, enquanto a terceira é um contêiner para as duas primeiras.

Em seguida, incorporamos em nossa nova função de conexão WebSocket connectForWsSocks a funcionalidade de solicitação de chaves eSNI para o domínio. Onde a parte do servidor funciona, definimos os parâmetros TLS, além de especificar um nome de "domínio de encobrimento" falso:

Domain Fronting baseado em TLS 1.3. Parte 2

Aqui vale a pena notar que, inicialmente, a branch tls-tris não foi projetada para uso de domain-fronting. Portanto, não foi dada atenção ao nome falso do servidor (no pacote client-hello um campo serverName vazio é transmitido). Para corrigir isso, teremos que adicionar o campo correspondente FakeServerName à estrutura TlsConfig. Não podemos usar o campo padrão ServerName da estrutura, pois ele é utilizado por mecanismos internos do TLS e, se diferir do original, a handshake TLS terminará com erro. A descrição da estrutura TlsConfig está contida no arquivo tls/common.go – e é neste arquivo que teremos que fazer ajustes:

Domain Fronting baseado em TLS 1.3. Parte 2

Domain Fronting baseado em TLS 1.3. Parte 2

Além disso, teremos que fazer mudanças no arquivo tls/handshake_client.go, para usar nosso campo FakeServerName ao formar o handshake TLS:

Domain Fronting baseado em TLS 1.3. Parte 2

É isso! Você pode compilar o projeto e verificar se tudo está funcionando. Mas antes de iniciar o teste, é necessário configurar a conta do CloudFlare. Bem, não é bem configurar – é só criar uma conta no CloudFlare e vincular seu domínio a ela. Todos os recursos relacionados ao DoH, WebSocket e ESNI estão ativados no CloudFlare por padrão. Depois que os registros DNS forem atualizados – você pode testar o funcionamento do domínio fazendo a solicitação das chaves eSNI:

dig +short txt _esni.df13tester.info 

Domain Fronting baseado em TLS 1.3. Parte 2

Se você ver algo semelhante para seu domínio – significa que tudo está funcionando e você pode passar para os testes.

Iniciamos um VPS Ubuntu, por exemplo, na DigitalOcean. P.S. No nosso caso, o endereço IP do VPS fornecido recentemente estava em listas negras do RKN. Então, não se surpreenda se algo semelhante acontecer com você. Tive que usar um VPN para acessar meu VPS.

Copiamos o rsockstun já compilado para o VPS (a propósito, essa é mais uma vantagem do Go – você pode compilar o projeto localmente e executá-lo em qualquer Linux, respeitando apenas a arquitetura do sistema) e iniciamos a parte do servidor:

Domain Fronting baseado em TLS 1.3. Parte 2

E então a parte do cliente:

Domain Fronting baseado em TLS 1.3. Parte 2

Como podemos ver, o cliente conseguiu se conectar ao servidor através do servidor frontend do CloudFlare usando WebSocket. Para verificar se o túnel funciona realmente como um túnel, você pode fazer uma solicitação curl através do socks5 local, aberto no servidor:

Domain Fronting baseado em TLS 1.3. Parte 2

Agora vamos ver o que o DPI vê no canal de comunicação:

Domain Fronting baseado em TLS 1.3. Parte 2

Primeiro, o tunnel, usando o mecanismo DoH, consulta o servidor DNS do Cloudflare em busca das chaves eSNI para o domínio de destino (pacotes nº 1-19), e depois se conecta ao servidor frontend e estabelece uma conexão TLS, ocultando-se sob o domínio www.google.com (esse é o valor padrão, quando ao iniciar o cliente não foi especificado um domínio falso). Para especificar seu próprio domínio falso é necessário usar o parâmetro -fronfDomain:

Domain Fronting baseado em TLS 1.3. Parte 2

Domain Fronting baseado em TLS 1.3. Parte 2

Agora, mais um ponto. Por padrão, nas configurações da conta do CloudFlare, o modo de operação SSL Flexível está ativado. Isso significa que as solicitações https aos servidores frontend do Cloudflare por clientes serão redirecionadas em formato não criptografado (http) para nosso servidor. É por isso que iniciamos a parte do servidor do tunnel em modo non-ssl (-listen ws:0.0.0.0), e não (-listen wss:0.0.0.0).

Domain Fronting baseado em TLS 1.3. Parte 2

Para mudar para o modo de criptografia total, é necessário selecionar Completo, ou Full (strict) em caso de um certificado real no servidor. Após mudar o modo, poderemos aceitar conexões da CloudFlare pelo protocolo https. Não se esqueça de gerar um certificado autoassinado para a parte do servidor do tunelador.

Domain Fronting baseado em TLS 1.3. Parte 2

Um leitor perspicaz perguntará: "E quanto ao cliente para Windows? Afinal, a principal aplicação do tunelador é estabelecer uma conexão de retorno com máquinas e servidores corporativos, e lá, como regra, sempre há Windows. Como posso compilar o tunelador para Windows, ainda mais com uma pilha de TLS específica?" E agora, vamos apresentar mais uma funcionalidade que mostra o quão conveniente é o Go. Compilamos para Windows diretamente do Kali, apenas adicionando o parâmetro GOOS=windows:

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

Ou a versão de 32 bits:

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

É isso! E mais nenhuma complicação é necessária. Isso realmente funciona!

Domain Fronting baseado em TLS 1.3. Parte 2

As flags do compilador –w e –s são necessárias para remover lixo desnecessário do arquivo executável, tornando-o alguns megabytes menor. Além disso, pode ser empacotado com o UPX para ainda reduzir o tamanho.

Em conclusão

Neste artigo, demonstramos, com o exemplo do tunelador escrito em Go, a aplicação da nova tecnologia de domain fronting, implementada em uma característica bastante interessante do protocolo TLS 1.3. Da mesma forma, é possível adaptar ferramentas existentes escritas em Go para funcionar através de servidores CloudFlare, por exemplo, Merlin — um conhecido C2, ou fazer o CobaltStrike Beacon usar o domain fronting eSNI ao operar com o Teamserver através de External C2 Channel, implementado em Go, ou mesmo em C++ padrão usando uma versão patchada do OpenSSL, sobre a qual falamos na parte anterior do artigo. Em geral, a criatividade não tem limites.

O exemplo com o tunelador e CloudFlare é apresentado como um conceito e, por enquanto, é difícil dizer sobre as perspectivas futuras desse tipo de domain fronting. No momento, o suporte ao eSNI é oferecido apenas pela CloudFlare e, em teoria, nada impede que eles desativem esse tipo de fronting e, por exemplo, interrompam as conexões tls em caso de discrepância entre SNI e eSNI. Em geral, o futuro dirá. Mas, por enquanto, a perspectiva de operar sob a "cobertura de kremlin.ru" parece bastante atraente. Não parece?

O código atualizado do tunelador, assim como os arquivos executáveis compilados, estão disponíveis em um ramo separado do projeto em github. Para quaisquer problemas relacionados ao tunelador, é melhor registrar uma issue na página do projeto no GitHub.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster