/* * Man in the Middle Packet Corruptor * Dino Dai Zovi , 20051026 * * COMPILING * * Requires libpcap and libnet 1.1.x, do the math. * * USAGE * * This tool requires three machines: the client, the corruptor, and * the server. The corruptor sniffs *not* in promiscuous mode and * with IP forwarding *disabled* corrupting and forwarding packets * received with the pcap filter string given on the command line. * Usually this will be "host or host ". * * The corruptor machine must be ARPed as a man-in-the-middle between * the client and server. This can be done by adding static ARP * entries on the client and server hosts with the corruptor's MAC * address for the server and client, respectively or by using a tool * like arpspoof. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __i386__ #define LIBNET_LIL_ENDIAN 1 #else #define LIBNET_BIG_ENDIAN 1 #endif #include /* * PCap and LibNet Globals */ static char* device = NULL; static pcap_t* pcap = NULL; static char pcap_errbuf[PCAP_ERRBUF_SIZE]; static libnet_t* libnet = NULL; /* * Randomly corrupt bytes in the given buffer */ void corrupt_buffer(unsigned char* buffer, size_t len) { if (len && rand() % 4 == 0) { // Corrupt 25% of the time size_t size = rand() % 8; // Number of bytes to corrupt size_t pos = rand() % (len - size + 1); // Where to start int i; if (pos > len) return; for (i = 0; i < size; i++) buffer[pos++] = rand(); } } void corrupt_ip_pkt(struct ip* ip_pkt, size_t ip_pkt_len) { size_t ip_hlen = ip_pkt->ip_hl * 4; size_t ip_len = ntohs(ip_pkt->ip_len); switch(ip_pkt->ip_p) { case IPPROTO_TCP: { struct tcphdr* th = (struct tcphdr*)((unsigned char*)ip_pkt + ip_hlen); size_t tcp_len = ip_len - ip_hlen; size_t tcp_hlen = th->th_off * 4; size_t tcp_dlen = tcp_len - tcp_hlen; unsigned char* tcp_data = ((unsigned char*)th + tcp_hlen); // Corrupt TCP data segment corrupt_buffer(tcp_data, tcp_dlen); // Workaround libnet's broken checksuming code in libnet <= 1.1.2 if (tcp_len % 2 == 1) { tcp_data[tcp_dlen] = 0; } // Fix TCP header+segment checksum if (libnet_do_checksum(libnet, (u_int8_t*)ip_pkt, IPPROTO_TCP, tcp_len) < 0) { fprintf(stderr, "libnet_do_checksum(TCP): %s\n", libnet_geterror(libnet)); exit(EXIT_FAILURE); } break; } case IPPROTO_UDP: { struct udphdr* uh = (struct udphdr*)((unsigned char*)ip_pkt + ip_hlen); size_t udp_hlen = sizeof(struct udphdr); unsigned char* udp_data = ((unsigned char*)uh + udp_hlen); size_t udp_len = ntohs(uh->uh_ulen); size_t udp_dlen = udp_len - udp_hlen; corrupt_buffer(udp_data, udp_dlen); // Workaround libnet's broken checksuming code in libnet <= 1.1.2 if (udp_len % 2 == 1) { udp_data[udp_dlen] = 0; } if (libnet_do_checksum(libnet, (u_int8_t*)ip_pkt, IPPROTO_UDP, ip_pkt_len - ip_hlen) < 0) { fprintf(stderr, "libnet_do_checksum(UDP): %s\n", libnet_geterror(libnet)); exit(EXIT_FAILURE); } break; } default: /* Do nothing */ break; } // Fix IP header checksum ip_pkt->ip_sum = 0; if (libnet_do_checksum(libnet, (u_int8_t*)ip_pkt, IPPROTO_IP, ip_hlen) < 0) { fprintf(stderr, "libnet_do_checksum(IP): %s\n", libnet_geterror(libnet)); exit(EXIT_FAILURE); } } void pkt_handler(u_char* user, const struct pcap_pkthdr* hdr, const u_char* data) { struct ip* ip_pkt = (struct ip*)(data + ETHER_HDR_LEN); size_t ip_pkt_len = ntohs(ip_pkt->ip_len); // We only do IPv4 if (ip_pkt->ip_v != 4) { return; } fprintf(stderr, "."); /* * Corrupt packet */ corrupt_ip_pkt(ip_pkt, ip_pkt_len); /* * Write IP packet to raw socket */ if (libnet_write_raw_ipv4(libnet, (u_int8_t*)ip_pkt, ip_pkt_len) < 0) { fprintf(stderr, "libnet_write_raw_ipv4: %s\n", libnet_geterror(libnet)); exit(EXIT_FAILURE); } } void usage(char* argv0) { fprintf(stderr, "usage: %s -d device [ pcap filter expression ]\n", argv0); } int main(int argc, char* argv[]) { int c; int pkts_read; bpf_u_int32 network, netmask; char filter[1024] = " "; struct bpf_program fp; struct libnet_ether_addr* ether_addr; char libnet_errbuf[LIBNET_ERRBUF_SIZE]; while ((c = getopt(argc, argv, "d:")) != EOF) { switch (c) { case 'd': device = strdup(optarg); break; default: usage(argv[0]); exit(EXIT_FAILURE); } } /* * Create libnet link interface to write packets to */ if (!(libnet = libnet_init(LIBNET_RAW4, device, libnet_errbuf))) { fprintf(stderr, "libnet_init: %s\n", libnet_errbuf); exit(EXIT_FAILURE); } if ((ether_addr = libnet_get_hwaddr(libnet)) == NULL) { fprintf(stderr, "libnet_get_hwaddr: %s\n", libnet_geterror(libnet)); exit(EXIT_FAILURE); } /* * Create pcap filter string: '(ether dst ) and ...' */ snprintf(filter, 1024, "(ether dst %.2x:%.2x:%.2x:%.2x:%.2x:%.2x) and", ether_addr->ether_addr_octet[0], ether_addr->ether_addr_octet[1], ether_addr->ether_addr_octet[2], ether_addr->ether_addr_octet[3], ether_addr->ether_addr_octet[4], ether_addr->ether_addr_octet[5]); while (argc > optind) { strlcat(filter, " ", 1024); strlcat(filter, argv[optind++], 1024); } /* * Create pcap to read packets from */ if (!device) { device = pcap_lookupdev(pcap_errbuf); if (device == NULL) { fprintf(stderr, "pcap_lookupdev: %s\n", pcap_errbuf); exit(EXIT_FAILURE); } } if (pcap_lookupnet(device, &network, &netmask, pcap_errbuf) < 0) { fprintf(stderr, "pcap_lookupnet: %s\n", pcap_errbuf); exit(EXIT_FAILURE); } if ((pcap = pcap_open_live(device, 2048, 0, 1, pcap_errbuf)) == NULL) { fprintf(stderr, "pcap_open_live: %s\n", pcap_errbuf); exit(EXIT_FAILURE); } if (pcap_compile(pcap, &fp, filter, 1, netmask) < 0) { pcap_perror(pcap, "pcap_compile"); exit(EXIT_FAILURE); } if (pcap_setfilter(pcap, &fp) < 0) { pcap_perror(pcap, "pcap_setfilter"); exit(EXIT_FAILURE); } pcap_freecode(&fp); printf("pcap filter: %s\n", filter); printf("Corrupting and forwarding matching packets...\n"); if ((pkts_read = pcap_loop(pcap, -1, pkt_handler, NULL)) < 0) { pcap_perror(pcap, "pcap_loop"); exit(EXIT_FAILURE); } pcap_close(pcap); libnet_destroy(libnet); return 0; }