From d0009856814c13d13770db5aadd7b2fabf947776 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 13 May 2013 13:53:27 +0200 Subject: staging: add mausezahn staging directory After some back and forth, we decided that it is easier to maintain mausezahn in a staging directory until it is fully reworked and cleaned up to be ready to be fully integrated. This way, it is better than having it in a separate branch, and we can also accept patches from outside more easily. Also, while at it, fix up some function mismatches with libcli. Signed-off-by: Daniel Borkmann Signed-off-by: Tobias Klauser --- staging/mopsrx_arp.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 staging/mopsrx_arp.c (limited to 'staging/mopsrx_arp.c') diff --git a/staging/mopsrx_arp.c b/staging/mopsrx_arp.c new file mode 100644 index 0000000..0aac152 --- /dev/null +++ b/staging/mopsrx_arp.c @@ -0,0 +1,301 @@ +/* + * Mausezahn - A fast versatile traffic generator + * Copyright (C) 2008-2010 Herbert Haas + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License version 2 as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see http://www.gnu.org/licenses/gpl-2.0.html + * +*/ + +#include "mz.h" +#include "mops.h" +#include "cli.h" + +// Starts an ARP RX thread for *every* device in the device_list. +// (Except for the loopback interface) +// +// RETURN VALUE: 0 upon success, +// 1 upon error. +// +int mops_rx_arp () +{ + int i; + + for (i=0; idev, + 100, // max num of bytes to read + 1, // 1 if promiscuous mode + PCAP_READ_TIMEOUT_MSEC, // read timeout 'until error' (-1 = indefinitely) + errbuf); + + if (p_arp == NULL) { + fprintf(stderr," rx_arp: [ERROR] %s\n",errbuf); + return NULL; // TODO: Should return pointer to error message or something similar + } + + dev->p_arp = p_arp; // also assign pointer to a global which is needed for clean_up + + if ( pcap_compile(p_arp, + &filter, // the compiled version of the filter + filter_str, // text version of filter + 0, // 1 = optimize + 0) // netmask + == -1) { + fprintf(stderr," rx_arp: [ERROR] Error calling pcap_compile\n"); + return NULL; + } + + if ( pcap_setfilter(p_arp, &filter) == -1) { + fprintf(stderr," rx_arp: [ERROR] Error setting pcap filter\n"); + pcap_perror(p_arp, " rx_arp: "); + return NULL; + } + + if (pcap_setdirection(p_arp, PCAP_D_IN) == -1) { + pcap_perror(p_arp, " rx_arp: "); + return NULL; + } + + again: + pcap_loop (p_arp, + 1, // number of packets to wait + got_arp_packet, // name of callback function + (u_char*) dev); // optional additional arguments for callback function + goto again; + + pthread_exit(NULL); // destroy thread + return NULL; +} + + +void got_arp_packet (u_char *args, + const struct pcap_pkthdr *header, // statistics about the packet (see 'struct pcap_pkthdr') + const u_char *packet) // the bytestring sniffed +{ + const struct struct_ethernet *ethernet; + const struct struct_arp *arp; + int size_ethernet = sizeof(struct struct_ethernet); + struct device_struct *dev = (struct device_struct*) args; + + u_int8_t + da[6], // eth da + sa[6], // eth sa + smac[6], // source hw address + sip[4], // source protocol address + tmac[6], // target hw address + tip[4]; // target protocol address + u_int16_t op; // operation + u_int32_t sec, nsec; + u_int8_t *x; + + // These are the most important lines here: + ethernet = (struct struct_ethernet*)(packet); + arp = (struct struct_arp*)(packet+size_ethernet); + sec = (u_int32_t) header->ts.tv_sec; + nsec = (u_int32_t) ((header->ts.tv_usec) * 1000); + + op = arp->arp_op; // note that we don't have network byte order anymore! + // tmact is: + // 100 instead of 00:01 (request) + // 200 instead of 00:02 (response) + + memcpy((void*) da, (void*) ethernet->eth_da, 6); + memcpy((void*) sa, (void*) ethernet->eth_sa, 6); + memcpy((void*) smac, (void*) arp->arp_smac, 6); + memcpy((void*) sip, (void*) arp->arp_sip, 4); + memcpy((void*) tmac, (void*) arp->arp_tmac, 6); + memcpy((void*) tip, (void*) arp->arp_tip, 4); + + // Only handle the packet if it is really an ARP response! + ////AND if it is not sent by THIS host! (not possible, we only scan inbound!) + x = (u_int8_t*) & op; + if (*(x+1) == 0x02) { + // ARP RESPONSE: Update ARP table + arptable_add(dev, sa, da, smac, sip, sec, nsec); + } else if (*(x+1) == 0x01) { + // ARP REQUEST: Detect poisoning attacks + arpwatch(dev, sa, da, smac, sip, tmac, tip, sec, nsec); + } + + + + + // ARP binding consists of: sip (IP) - smac (MAC) + // + // User alert, 2 possibilities: + // + // 1. Learned new binding: does smac belong to sip? + // + // 2. Alert: Mismatch of stored versus announced sip-to-smac binding + // + // In both cases user action: [Learn] [Ignore] [Attack] [Amok Attack] + // Countermeasures: Mausezahn him! + // + // ALSO correct ARP tables of other hosts, especially on the default gateway + // that is, send arp replies with true binding + // + // Finally: Create logging message + +} + + + +// Add new entry in device-specific ARP table +// but first check if already existing or change. +// +// RETURN VALUE: 0 upon success +// 1 upon error +// +int arptable_add(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int32_t sec, + u_int32_t nsec) +{ + struct arp_table_struct *prev=NULL, *cur = dev->arp_table; + int i=0, alert=0; + + // If SA and SMAC are different this might be a MITM !!! + if (compare_mac(smac, sa)) alert=1; + + // Check if IP (sip) is already existing in arp table: + while (cur!=NULL) { + if (compare_ip(sip, cur->sip)==0) { // IP found! + timestamp_hms(cur->when); + if (da[0]==0xff) cur->bc_resp++; + else cur->uni_resp++; + if (compare_mac(smac, cur->smac)==0) { + // entry identical ! + cur->sec=sec; + cur->nsec=nsec; + return 0; + } else { + // entry with other MAC address found ! + if (cur->locked==0) { + cur->changed++; + memcpy((void*) cur->smac_prev, (void*) cur->smac, 6); + memcpy((void*) cur->smac, (void*) smac, 6); + cur->sec_prev=cur->sec; + cur->nsec_prev=cur->nsec; + cur->sec=sec; + cur->nsec=nsec; + if (alert) cur->flags|=0x02; + } + return 0; + } + } + prev = cur; + cur = cur->next; + i++; + } + + // If we get here, then there was no entry for that IP yet! + // Create new arp_table entry: + cur = (struct arp_table_struct *) malloc(sizeof(struct arp_table_struct)); + if (cur==NULL) return 1; + + // Append element: + if (dev->arp_table==NULL) dev->arp_table = cur; + else prev->next = cur; + + memcpy((void*) cur->sa, (void*) sa, 6); + memcpy((void*) cur->smac, (void*) smac, 6); + cur->smac_prev[0]=0x00; + cur->smac_prev[1]=0x00; + cur->smac_prev[2]=0x00; + cur->smac_prev[3]=0x00; + cur->smac_prev[4]=0x00; + cur->smac_prev[5]=0x00; + memcpy((void*) cur->sip, (void*) sip, 4); + if (da[0]==0xff) { + cur->bc_resp=1; + cur->uni_resp=0; + } else { + cur->bc_resp=0; + cur->uni_resp=1; + } + cur->changed=1; + cur->locked=0; + cur->dynamic=1; + cur->flags=0; + cur->sec=sec; + cur->nsec=nsec; + cur->sec_prev=0; + cur->nsec_prev=0; + cur->index=i+1; // I assume users prefer to count from 1. + timestamp_hms(cur->when); + if (alert) cur->flags|=0x02; + cur->next=NULL; + return 0; +} + + + +// Validate ARP requests +int arpwatch(struct device_struct *dev, + u_int8_t *sa, + u_int8_t *da, + u_int8_t *smac, + u_int8_t *sip, + u_int8_t *tmac, + u_int8_t *tip, + u_int32_t sec, + u_int32_t nsec) +{ + // Unicast requests are considered as anomaly + + if ((da[0]&0x01)==0) { // broadcast bit NOT set? + fprintf(stderr, "NOTE: Non-broadcast ARP request from %02x:%02x:%02x:%02x:%02x:%02x\n", + sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); + } + + return 0; +} + -- cgit v1.2.3-54-g00ecf