The last few days I had been working on a script to block malicious ARP packets in order to stop ARP Poisoning attacks on a user’s PC. It’s the first big project I’ve written in C, and while C has its drawbacks, like making a simple program vulnerable to buffer overflow or format string attacks, I managed to write a reliable script that should have no problem running in the background. 2 days ago, I released it on Github: https://github.com/Prodicode/ARPPD
How it works
The program sniffs for every incoming ARP (Address Resolution Protocol) packets, using the pcap C library.
</pre> dev = pcap_lookupdev(errbuf); if(dev == NULL) { fprintf(stderr, "%s\n", errbuf); exit(1); } pcap_lookupnet(dev, &netp, &maskp, errbuf); descr = pcap_open_live(dev, BUFSIZ, 1,-1, errbuf); if(descr == NULL) { printf("pcap_open_live(): %s\n", errbuf); exit(1); } if(pcap_compile(descr, &fp, "arp", 0, netp) == -1) { fprintf(stderr, "Error calling pcap_compile\n"); exit(1); } if(pcap_setfilter(descr, &fp) == -1) { fprintf(stderr, "Error setting filter\n"); exit(1); } pcap_loop(descr, -1, my_callback, NULL); return 0; <pre>
The funnction my_callback() is called when the program receives an ARP packet. The function my_callback() looks like this:
</pre> void my_callback(u_char *args, const struct pcap_pkthdr* pkthdr, const u_char* packet) { int i=0; static int count=0; char p_source[128] = ""; char arp_source[128] = ""; char arp_ip_source[128] = ""; for(i=0;i<pkthdr->len;i++) { if(i >= 6 && i<= 11){ //Daca el vrea sa si schimbe ip ul lui si exista deja in arp, blocheaza! char j[8]; snprintf(j, sizeof(j), "%02x", packet[i]); strcat(p_source, j); if (i != 11) strcat(p_source, ":"); } if(i >= 22 && i<= 27){ //Daca el vrea sa si schimbe ip ul lui si exista deja in arp, blocheaza! char f[8]; snprintf(f, sizeof(f), "%02x", packet[i]); strcat(arp_source, f); if(i != 27) strcat(arp_source, ":"); } if(i >= 28 && i<= 31){ //Daca el vrea sa si schimbe ip ul lui si exista deja in arp, blocheaza! char h[8]; snprintf(h, sizeof(h), "%d", packet[i]); strcat(arp_ip_source, h); if(i != 31) strcat(arp_ip_source, "."); } } char attacker_ip[128] = ""; if(strncmp(arp_source, gtwy, 20) != 0){ //Check if ARP Source IP is GATEWAY if(strncmp(arp_ip_source, gateway_ip, 20) == 0){ //MALICIOUS ARP PACKET! printf("\x1B[31m MALICIOUS ARP PACKET DETECTED FROM %s.\x1B[37m\n", p_source); blockARPPackets(arp_source); rearpGateway(gtwy, gateway_ip); } } } <pre>
The way my_callback() decides if the ARP packet is “malicious” will be explained later in this blog post. If the packet is malicious, the functions blockARPPackets() and rearpGateway() will be called.
The function blockARPPackets() will block all ARP packets coming from that MAC Address using arptables and the function rearpGateway() will re-arp the Gateway’s MAC which was saved when starting the script to its original IP address, in case the malicious ARP packet was acknowledged. The functions look like this:
void blockARPPackets(char* mac_address){ FILE *fp; char path[1035]; int exists = 0; char command[1000]; snprintf(command, sizeof(command), "arptables -A INPUT --source-mac %s -j DROP", mac_address); fp = popen(command, "r"); if (fp == NULL) { printf("Failed to block packets\n" ); exit(1); } printf("%sBLOCKING PACKETS FROM %s%s\n", KGRN, mac_address, KWHT); } void rearpGateway(char* gateway_mac, char* gateway_ip){ //arp -s 10.0.0.2 00:0c:29:c0:94:bf FILE *fp; char path[1035]; int exists = 0; char command[1000]; snprintf(command, sizeof(command), "arp -s %s %s", gateway_ip, gateway_mac); fp = popen(command, "r"); if (fp == NULL) { printf("Failed to block packets\n" ); exit(1); } printf("%sREARPED %s TO %s%s\n", KGRN, gateway_ip, gateway_mac, KWHT); }
How can ARP Packets be “malicious”?
The ARP Packet’s payload, without the Ethernet header looks like this:
Source: Wikipedia
Basically, the script will check if the Sender protocol address is equal to the gateway’s original IP Address and if the Sender hardware address is not equal to the gateway’s original MAC Address (if they were equal, it would have been just a usual ARP packet coming from the router, otherwise it’s an attempted Man-In-The-Middle Attack). Here’s an example:In the photo, you can see that the IP Address of the gateway is 192.168.1.1 and its MAC Address 70:72:3c:1b:d9:66 and the attacker’s MAC Address is 00:a0:d1:dc:79:80.
Here’s how a packet generated by arpspoof (ARP Poisoning software) looks like:
Exit Cleanup
When the program exits a cleanup function is activated. It allows all connections and flushes the arp table:
</pre> FILE *fp; char path[1035]; int exists = 0; char j[1024] = ""; snprintf(j, sizeof(j), "arp -d %s && arptables -P INPUT ACCEPT && arptables --flush && ip -s neighbour flush all && echo done", gateway_ip); fp = popen(j, "r"); if (fp == NULL) { printf("Failed to run command\n" ); exit(1); } while (fgets(path, sizeof(path)-1, fp) != NULL) { printf("%s", path); } printf("Successfully exited. Flushed ARP table and enabled all ARP connections!\n"); exit(0); <pre>
Conclusion
In the end, I can say it really was fun writing this program, and I’ve learned a lot about programming in C, a lower-level language.
This script is obviously safer than other scripts which poll the arp table, therefore there would be a 5 second delay in stopping the attack. In this case, my script blocks the malicious ARP packets immediately.
You can download the source on the Github page.