sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 3b121683f37f73fa09a7b7733de4189724449b91
parent 833ec8f70f99d109e91eead3cae791d524ff3b40
Author: Thomas WACHE <thomas@wache.fr>
Date:   Fri, 23 Mar 2018 08:37:19 +0100

I notice when an host has several IP addresses, it alerts because it sees
several times the same MAC address. The detection is now based on a MAC
address that changes. After a timeout, if it hasn't changed since, the new
MAC is learn. Otherwise it keeps alerting.
The new algorithm must be run in continuous because it track changes over time.

Diffstat:
dwm.suckless.org/dwmstatus/dwmstatus-mitm.c | 156++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
1 file changed, 110 insertions(+), 46 deletions(-)

diff --git a/dwm.suckless.org/dwmstatus/dwmstatus-mitm.c b/dwm.suckless.org/dwmstatus/dwmstatus-mitm.c @@ -2,18 +2,50 @@ * network traffic (i.e. a Man-In-The-Middle attack ran against you thanks * to ARP cache poisoning). * - * It checks the dump file of the kernel ARP table (/proc/net/arp) to see - * if there is more than one IP address associated with the same MAC - * address. If so, it shows an alert. If an error occurs during the - * check, it returns NULL. + * It must be called regularly because it monitors changes in the ARP table + * If a host got a new MAC address, it will alert during ALERT_TIMEOUT seconds. + * If the MAC address remains the same, it assumes it is just a new host. + * Otherwise, if it keep changing, it will keep on alerting. + * + * Returns true on success, false otherwise. * * Written by vladz (vladz AT devzero.fr). + * Updated by mephesto1337 ( dwm-status AT junk-mail.fr ) */ +#include <arpa/inet.h> +#include <linux/if_ether.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> - +#include <time.h> + + +// Some useful macros +#define CHK(expr, cond) \ + do { \ + if ( (expr) cond ) { \ + fprintf(stderr, "%s failed", #expr); \ + goto fail; \ + } \ + } while ( 0 ) + +#define CHK_NEG(expr) CHK((long)(expr), < 0L) +#define CHK_FALSE(expr) CHK(!!(expr), == false) +#define CHK_NULL(expr) CHK(expr, == NULL) +#define SAFE_FREE(func, ptr) \ + do { \ + if ( ptr != NULL ) { \ + func(ptr); \ + } \ + ptr = NULL; \ + } while ( 0 ) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define ALERT_TIMEOUT ((time_t)40L) // In seconds /* The hard maximum number of entries kept in the ARP cache is obtained via * "sysctl net.ipv4.neigh.default.gc_thresh3" (see arp(7)). Default value @@ -22,51 +54,83 @@ #define MAX_ARP_CACHE_ENTRIES 1024 -char *detect_arp_spoofing(void) { - - FILE *fp; - int i = 1, j; - char **ptr = NULL; - char buf[100], *mac[MAX_ARP_CACHE_ENTRIES]; - - if (!(fp = fopen("/proc/net/arp", "r"))) { - return NULL; - } - - ptr = mac; - - while (fgets(buf, sizeof(buf) - 1, fp)) { - - /* ignore the first line. */ - if (i == 1) { i = 0; continue; } - - if ((*ptr = malloc(18)) == NULL) { - return NULL; +struct ether_ip_s { + union { + uint8_t mac[ETH_ALEN]; + unsigned long lmac; + }; + time_t last_changed; + in_addr_t ip; +}; + +struct ether_ip_s table[MAX_ARP_CACHE_ENTRIES]; +size_t table_size = 0; + + +bool lookup_and_insert(const struct ether_ip_s *new); + +bool check_arp_table(char *message, size_t len) { + FILE *f; + struct ether_ip_s tmp; + char ip_address[32]; + char mac_address[32]; + + snprintf(message, len, "ARP table OK"); + CHK_NULL(f = fopen("/proc/net/arp", "r")); + time(&tmp.last_changed); + fscanf(f, "%*[^\n]\n"); + while ( !feof(f) ) { + CHK(fscanf(f, "%s%*[ ]0x%*x%*[ ]0x%*x%*[ ]%[a-f0-9:]%*[^\n]\n", ip_address, mac_address), != 2); + CHK_NEG(inet_pton(AF_INET, ip_address, &tmp.ip)); + + tmp.lmac = 0UL; + CHK(sscanf( + mac_address, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &tmp.mac[0], &tmp.mac[1], &tmp.mac[2], &tmp.mac[3], &tmp.mac[4], &tmp.mac[5] + ), != 6); + + if ( ! lookup_and_insert(&tmp) ) { + snprintf(message, len, "Possible MITM attack, please check %s", ip_address); + break; + } } + SAFE_FREE(fclose, f); + return true; - sscanf(buf, "%*s %*s %*s %s", *ptr); - ptr++; - } - - /* end table with a 0. */ - *ptr = 0; - - fclose(fp); - - for (i = 0; mac[i] != 0; i++) - for (j = i + 1; mac[j] != 0; j++) - if ((strcmp("00:00:00:00:00:00", mac[i]) != 0) && - (strcmp(mac[i], mac[j]) == 0)) { - - return "** MITM attack detected! Type \"arp -a\". **"; - } - - return "MITM detection: on"; + fail: + SAFE_FREE(fclose, f); + snprintf(message, len, "ARP table ???"); + return false; } -int main() { +bool lookup_and_insert(const struct ether_ip_s *new) { + for ( size_t i = 0; i < table_size; i++ ) { + if ( table[i].ip == new->ip ) { + if ( table[i].lmac != new->lmac ) { + if ( table[i].last_changed + ALERT_TIMEOUT > new->last_changed ) { + return false; + } else { + // Update the DB, it must be a new host + table[i].lmac = new->lmac; + table[i].last_changed = new->last_changed; + return true; + } + } else { + // Update last seen + table[i].last_changed = new->last_changed; + return true; + } + } + } - printf("%s\n", detect_arp_spoofing()); + if ( table_size < ARRAY_SIZE(table) ) { + memcpy(&table[table_size], new, sizeof(struct ether_ip_s)); + table_size++; + } else { + // To big, let's restart from the begining + table_size = 0; + return false; + } - return 0; + return true; }