commit 3b121683f37f73fa09a7b7733de4189724449b91
Author: Thomas WACHE <[email protected]>
Date:   Fri Mar 23 08:37:19 2018 +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.

diff --git a/dwm.suckless.org/dwmstatus/dwmstatus-mitm.c 
b/dwm.suckless.org/dwmstatus/dwmstatus-mitm.c
index ebb21571..41ff52e2 100644
--- 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, "%*[^
]
");
+    while ( !feof(f) ) {
+        CHK(fscanf(f, "%s%*[ ]0x%*x%*[ ]0x%*x%*[ ]%[a-f0-9:]%*[^
]
", 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
", 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;
 }


Reply via email to