Domínio front-end baseado no TLS 1.3. Parte 2

1. Introdução


Na primeira parte do artigo, fornecemos uma breve descrição do mecanismo SNI criptografado (eSNI). Eles mostraram como, em sua base, é possível evitar a detecção pelos modernos sistemas de DPI (usando o Beeline DPI e o rutracker de ILV proibido como exemplo), além de explorar uma nova versão do front-end do domínio com base nesse mecanismo.

Na segunda parte do artigo, passaremos para coisas mais práticas que o RedTeam será útil para especialistas em seu trabalho difícil. No final, nosso objetivo não é obter acesso a recursos bloqueados (para coisas comuns, temos uma boa e antiga VPN). Felizmente, existem muitos provedores de VPN, como se costuma dizer, para todos os gostos, cores e orçamentos.

Vamos tentar aplicar o mecanismo de domínio de domínio para as modernas ferramentas RedTeam, por exemplo, como Cobalt Strike, Empire, etc., e dar a eles oportunidades adicionais para imitar e fugir dos modernos sistemas de filtragem de conteúdo.

Na última vez, implementamos o mecanismo eSNI na biblioteca OpenSSL e o utilizamos com sucesso no familiar utilitário curl. Mas um cacho, como se costuma dizer, não estará cheio. Claro, quero implementar algo semelhante em linguagens de alto nível. Infelizmente, porém, uma pesquisa superficial da vastidão da rede nos decepciona, porque o suporte ao mecanismo eSNI é totalmente implementado apenas no GOLANG. Portanto, nossa escolha não é muito grande: ou escrevemos em C ou C ++ puro usando a biblioteca OpenSSL corrigida ou usamos um fork GOLANG separado do CloudFlare e tentamos portar nossas ferramentas para lá. Em princípio, há outra opção, mais clássica, mas também demorada - é implementar o suporte eSNI para python. Afinal, o Python também usa o OpenSSL para trabalhar com https. Mas deixaremos essa opção de desenvolvimento para outra pessoa e nos contentaremos com a implementação do Golang, especialmente porque nosso amado Cobalt Strike é perfeitamente capaz de trabalhar com um canal de comunicação criado por ferramentas de terceiros (canal externo C2) - falaremos sobre isso no final do artigo.

Tente mais ...


Uma das ferramentas implementadas no Go é o nosso desenvolvimento para rodar dentro da rede - o sintonizador rsockstun, que, aliás, agora é detectado pelas ferramentas da Microsoft e da Symantec como um software muito malicioso que viola a estabilidade do mundo ...



Seria ótimo usar o desenvolvimento anterior neste caso. Mas aqui surge um pequeno problema. O fato é que o rsockstun implica inicialmente no uso de um canal de comunicação SSL síncrono com o servidor. Isso significa que a conexão é estabelecida uma vez e existe por toda a duração da operação do túnel. E, como você entende, o protocolo https não foi projetado para esse modo de operação - ele funciona no modo de solicitação-resposta, onde cada nova solicitação http existe na estrutura de uma nova conexão tcp.

A principal desvantagem desse esquema é que o servidor não pode transferir dados para o cliente até que o cliente envie uma nova solicitação http. Mas, felizmente, existem muitas opções para solucionar esse problema: transmitir dados por meio do protocolo http (no final, conseguimos assistir de alguma forma aos nossos programas de TV favoritos e ouvir música de portais em https, e transmissão de vídeo e áudio não é isso. que não seja a transmissão de dados). Uma das tecnologias para emular a operação de uma conexão TCP de pleno direito através do protocolo http é a tecnologia WebSockets, cuja principal essência é a organização de uma conexão de rede completa entre o cliente e o servidor da Web.

Para nossa sorte (hooray hooray !!!), essa tecnologia está incluída por padrão em todos os planos tarifários do CloudFlare e funciona muito bem em combinação com o eSNI. É exatamente isso que usaremos para ensinar nosso túnel a usar frentes de domínio e ocultar do DPI moderno.

Um pouco sobre WebSockets


Antes de tudo, falaremos brevemente e em palavras simples sobre os soquetes da Web, para que todos tenham uma idéia do que trabalharemos.

A tecnologia de soquete da Web permite alternar temporariamente de uma conexão http para dados de streaming padrão em um soquete de rede sem interromper a conexão tcp estabelecida. Quando um cliente deseja alternar para um soquete da web, ele define vários cabeçalhos http em sua solicitação http. Dois cabeçalhos necessários são Conexão: Atualização e Atualização: websocket . Também pode forçar a versão do protocolo websocket ( Sec-Websockset-Version: 13 ) e algo como o identificador de soquete da Web base64 ( Sec-WebSocket-Key: DAGDJSiREI3 + KjDfwxm1FA == ). O servidor responde com o protocolo de comutação 101 com código http e também define os cabeçalhos de conexão, atualização e aceitação de Sec-WebSocket . O processo de troca é ilustrado na captura de tela abaixo:



Depois disso, a conexão WebSocket pode ser considerada concluída. Todos os dados do cliente e do servidor agora serão fornecidos não com http, mas com cabeçalhos WebSocket (eles começam com o byte 0x82). Agora, o servidor não precisa aguardar uma solicitação do cliente para transferir dados, como a conexão TCP não está interrompida.

Existem várias bibliotecas no grupo de soquetes da Web. Os mais populares são o Gorilla WebSocket e o WebSocket padrão. Vamos usar o último, porque é mais simples, menor e funciona, como se costuma dizer, um pouco mais rápido.

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





Queremos tornar o cliente parte do nosso túnel universal e capaz de trabalhar tanto por meio de uma conexão direta via SSL quanto pelo protocolo WebSockset. Para fazer isso, criaremos uma função separada func connectForWsSocks (cadeia de endereços, cadeia de proxy) erro {...} por analogia com connectForSocks () e a usaremos para trabalhar com soquetes da Web se o endereço do servidor especificado no início do cliente iniciar com ws: ou wss: (no caso do Secure WebSocket).

Para o lado do servidor do túnel, também criaremos uma função separada para trabalhar com soquetes da web. Uma instância da classe http será criada nela e um manipulador de conexão http (função wsHandler) será definido:



E colocaremos toda a lógica para processar a conexão (autorizando o cliente com uma senha, instalando e encerrando a sessão do yamux) no manipulador de conexões WebSocket:



Nós compilamos o projeto, iniciamos a parte do servidor:

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

E então a parte do cliente:

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

E verificamos o trabalho no host local:





Passamos à frente de domínio


Parece que resolvemos os soquetes da web. Agora vamos diretamente para o eSNI e as frentes de domínio. Como mencionado anteriormente, para trabalhar com o DoH e o eSNI, precisamos pegar um ramo especial do golang do CloudFlare . Precisamos de um ramo com suporte eSNI (pwu / esni).

Nós o clonamos localmente ou fazemos o download e expandimos 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 ramificação clonada e configurá-lo como o principal. Para salvar o desenvolvedor dessa dor de cabeça, os caras da CloudFlare prepararam um script especial - _dev / go.sh. Apenas corra. O script e o makefile farão tudo sozinhos. Por diversão - você pode olhar dentro do makefile para obter detalhes.

Após elaborar o script, ao compilar o projeto, precisaremos indicar como GOROOT o diretório local preparado pelo script. No nosso caso, fica assim:

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

Em seguida, precisamos implementar no túnel a funcionalidade de solicitar e analisar chaves eSNI públicas para o domínio desejado. No nosso caso, essas serão chaves públicas eSNI dos servidores front-end do CloudFlare. Para fazer 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, em princípio, falam por si. Tomaremos o preenchimento do arquivo esni_query.go, que faz parte do tls-tris. A primeira função cria um pacote de rede com uma solicitação ao servidor DNS CloudFlare usando o protocolo DoH (DNS sobre HTTPS), a segunda analisa os resultados da consulta e recebe os valores das chaves de domínio público e a terceira é um contêiner para as duas primeiras.

Em seguida, apresentamos a função de solicitar chaves eSNI para o domínio em nossa função de conexão recém-criada para o soquete da Web connectForWsSocks . Onde a parte do servidor funciona, defina os parâmetros TLS e também defina o nome do "domínio de cobertura" falso:



Deve-se notar aqui que, inicialmente, o ramo tls-tris não foi projetado para o uso da frente de domínio. Portanto, ele não presta atenção ao nome falso do servidor (um campo vazio serverName é transmitido como parte do pacote client-hello). Para corrigir isso, teremos que adicionar o campo FakeServerName correspondente à estrutura TlsConfig. Não podemos usar o campo ServerName padrão da estrutura, porque é usado por mecanismos internos de tls e, se for diferente do original, o tls-handshake terminará com um erro. A descrição da estrutura TlsConfig está contida no arquivo tls / common.go - precisamos corrigi-lo:





Além disso, teremos que fazer alterações no arquivo tls / handshake_client.go para usar nosso campo FakeServerName ao gerar o handshake TLS:



Isso é tudo! Você pode compilar o projeto e verificar o trabalho. Mas antes de executar o teste, você deve configurar sua conta do CloudFlare. Bem, como você diz para configurar - basta criar uma conta cloudflare e vincular seu domínio a ela. Todos os chips relacionados ao DoH, WebSocket e ESNI estão incluídos no CloudFlare por padrão. Após a atualização dos registros DNS - você pode verificar o domínio executando a solicitação de chave eSNI:

 dig +short txt _esni.df13tester.info 



Se você vir algo semelhante para o seu domínio, tudo funcionará para você e você poderá prosseguir com os testes.

Inicie o Ubuntu VPS, por exemplo, no DigitalOcean. PS No nosso caso, o endereço IP do VPS recém-emitido pelo provedor estava nas listas negras do ILV. Portanto, não se surpreenda se algo semelhante acontecer com você. Eu tive que usar uma VPN para acessar meu VPS.

Copiamos o rsockstun compilado para o VPS (a propósito, esse é outro charme do golang - você pode compilar o projeto por conta própria e executá-lo em qualquer Linux, observando apenas a capacidade de bits do sistema) e iniciar a parte do servidor:



E então a parte do cliente:



Como podemos ver, o cliente se conectou com êxito ao servidor através do servidor front-end CloudFlare usando um soquete da web. Para verificar se o túnel funciona como um túnel, você pode fazer uma solicitação de curl através das meias locais5 abertas no servidor:



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



Primeiro, o sintonizador de túnel, usando o mecanismo DoH, acessa o servidor DNS do Cloudflare para obter as chaves eSNI do domínio de destino (pacotes nº 1 a 19), depois acessa o servidor front-end e estabelece uma conexão TLS, oculta no domínio www.google.com (esse valor por padrão, quando um domínio falso não está definido na inicialização do cliente). Para especificar seu domínio falso, você deve usar o parâmetro -fronfDomain:





Agora mais uma coisa. Por padrão, as configurações da conta do CloudFalre são definidas como SSL flexível. Isso significa que solicitações https para clientes front-end do Cloudflare de clientes serão redirecionadas no formato não criptografado (http) para o nosso servidor. É por isso que lançamos a parte do servidor do túnel no modo não-ssl (-listen ws: 0.0.0.0) e não (-listen wss: 0.0.0.0).



Para alternar para o modo de criptografia completa, você deve selecionar Completo ou Completo (estrito) no caso da presença deste certificado no servidor. Depois de mudar o modo, poderemos aceitar conexões do CloudFlare através do protocolo https. Lembre-se de gerar um certificado autoassinado para o lado do servidor do túnel.



Um leitor irritante perguntará: “E a conta do cliente no Windows? De fato, com certeza, a principal aplicação do túnel é aumentar a conexão traseira de máquinas e servidores corporativos e, por regra, sempre é o Windows. Como posso compilar um túnel para Windows e mesmo com uma pilha TLS específica? ”E agora apresentaremos outro chip que mostra como o golang é conveniente. Compilamos para janelas diretamente do Kali, simplesmente adicionando o parâmetro GOOS = windows:

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

Ou uma opção de 32 bits:

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

Isso é tudo! E não são necessários mais problemas. Isso realmente funciona!



Os sinalizadores do compilador –w e –s são necessários para remover o excesso de lixo do arquivo executável, diminuindo em alguns megabytes. Além disso, ele pode ser empacotado usando o UPX para reduzir ainda mais o tamanho.

Em vez de uma conclusão


No artigo, nós, usando o exemplo de um sintonizador escrito em um golang, demonstramos claramente o uso da nova tecnologia de domínio de domínio, implementada em um recurso bastante interessante do protocolo TLS 1.3. Da mesma forma, você pode adaptar o kit de ferramentas existente escrito no golang para funcionar através do servidor CloudFlare, por exemplo, Merlin , o conhecido C2 ou forçar o CobaltStrike Beacon a usar as frentes de domínio eSNI ao trabalhar com o Teamserver por meio do canal externo C2 , implementado no golang ou no C ++ padrão usando a versão corrigida do OpenSSL, sobre a qual falamos na última parte do artigo. Em geral, a fantasia não tem limites.

O exemplo do túnel e CloudFlare é apresentado na forma de um conceito e ainda é difícil dizer sobre as perspectivas distantes desse tipo de domínio voltado para a frente. No momento, o suporte ao eSNI está disponível apenas no CloudFlare e, em princípio, nada os impede de desativar esse tipo de front-end e, por exemplo, interromper as conexões tls quando o SNI e o eSNI não coincidem. Em geral, o futuro será mostrado. Mas, por enquanto, a perspectiva de trabalhar sob a cobertura do kremlin.ru parece bastante atraente. Certo?

O código de encapsulamento atualizado, bem como os arquivos executáveis ​​executáveis ​​compilados, estão localizados em uma ramificação separada do projeto no github . É melhor escrever um problema sobre todos os possíveis problemas de túnel na página do projeto no GitHub.

Source: https://habr.com/ru/post/pt477696/


All Articles