Categories:

Wird mein WLAN gehackt? WLAN-Angriffe erkennen mit Mikrocontroller für unter 5 Euro!

Veröffentlicht von empy
Lesezeit: 7 Minuten

WLANs können auf verschiedene Weise angegriffen werden. Für unter 5 Euro baue ich einen Detektor, der einen Angriffsvektor zuverlässig anzeigt, sodass ihr die sogenannten Deauth-Attacks zuverlässig erkennen könnt. Die LED des Detektors blinkt dann unübersehbar, sobald euer WLAN das Opfer einer solchen Attacke ist. Auch Anmeldeversuche im WLAN werden angezeigt.

Für das kleine Projekt verwende ich die Arduino-Plattform mit ESP 8266 Mikrocontroller. Kostenpunkt: Schlappe 5 Euro für ein Entwicklerboard mit LED, zum Beispiel dem NodeMCU Lua Lolin V3 ESP8266 ESP-12F. Das Board verfügt über einen Micro-USB-Anschluss zum einfachen flashen der Software und hat WLAN nach 802.11 b/g/n (WLAN 4) direkt mit eingebaut. Mit dieser Ausrüstung ist es möglich, sogenannte Deauthentication-Attacken im WLAN unter 2,4 GHz zu erkennen.

Neben dem ESP 8266 benötigt ihr ein Daten-Mico-USB-Kabel. Nur wenn ihr ein Datenkabel verwendet, dann wird das Board am Computer als serielles USB-Gerät erkannt. Das ist wichtig, um dem ESP8266 Programmcode überspielen zu können. Verwendet ihr ein simples Ladekabel, dann wird das Board nicht erkannt und erhält lediglich Strom per USB. Das Programmieren des ESP ist damit nicht möglich, aber wenn der Programmcode einmal aufgespielt wurde, genügt ein solches Kabel zum Betrieb.

Anmerkung: Mit dem ESP8266 können nur Angriffe im 2,4-GHz-Band erkannt werden. Das 5-GHz-Band unterstützt das Board leider nicht. Dafür ist es sehr stromsparend und sehr preiswert. Weitere Einschränkung: WLAN Data-Frames können nicht vollständig vom ESP8266 erfasst werden. Ich selbst musste dies in einer Nacht der Verzweiflung schmerzlich feststellen, als ich eine Erkennung von fehlerhaften Anmeldeversuchen im WLAN (falscher PSK) einbauen wollte.

Wollt ihr bei fehlerhaften Anmeldeversuchen in euer WLAN dennoch informiert werden, probiert am besten mein zuverlässiges Alarm-System mit Graylog oder Synology samt Push-Notification aus, welches auf Log-Meldungen eurer Access Points / WLAN-Router basiert. Oder ihr schaut euch meine erweiterte Version des Detektors für den ESP32 an, dem Nachfolger vom ESP8266. Dieser kann dann auch fehlerhafte WLAN-Anmeldeversuche recht zuverlässig erkennen. Ein ESP32 kostet knapp über 10 Euro.

Schritt 1: Arduino installieren

Damit es losgehen kann, müsst ihr zunächst die integrierte Entwicklungsumgebung für Arduino installieren. Mit Hilfe der Entwicklungsumgebung übertragt ihr später den Programmcode auf den ESP8266. Das Arduino IDE müsst ihr mit Admin-Rechten installieren, am besten für alle User, damit die Software im allgemeinen Programmordner abgelegt wird.

Startet dann die Anwendung. Beim ersten Start werden weitere erforderliche Pakete heruntergeladen und die Gerätetreiber für Arduino installiert.

Treiber für Arduino während dem ersten Start des IDE.

Schritt 2: ESP8266 SDK installieren

Nachdem ihr das Arduino IDE installiert habt, braucht ihr nun das ESP8266 SKD. Es sorgt dafür, dass der Programmcode für den ESP8266 verständlich kompiliert werden kann und dann auf dem Board reibungslos läuft. Es stellt dazu geläufige Arduino-Funktionsbibliotheken bereit, zum Beispiel um TCP über WLAN nutzen zu können.

In der Arduino IDE öffnet ihr dazu die Einstellungen und fügt den Boardmanager für den ESP8266 hinzu. Die Einstellungen findet ihr unter „File -> Preferences…“. Gebt dann unten im Feld für „Additional boards manager URLs“ folgende Quelle ein:

https://arduino.esp8266.com/stable/package_esp8266com_index.json

Anmerkung: Wenn ihr das Einstellungsfenster im Anschluss nicht mit „OK“ bestätigen könnt, dann überprüft bitte eure Sketchbook-Location. Der hier angegebene Ordner muss tatsächlich vorhanden sein. Wenn nicht, dann legt den Ordner an oder passt den Ordnerpfad an. Sollte der OK-Button noch immer nicht anklickbar sein, dann setzt den Haken bei „Show files inside sketches“ und entfernt ihn danach gleich wieder. Der OK-Button sollte nun aktiv sein.

Nachdem der Pfad zum ESP8266 Boardmanager eingerichtet wurde, muss die ESP8266-Plattform noch installiert werden. Öffnet dazu den „Boards Manager“ über „Tools -> Board -> Boards Manager…“ und wählt dann die Plattform „esp8266 by ESP8266 Community“ aus. Installiert von dort aus das ESP8266 SDK über den entsprechenden Knopf.

Schritt 3: Gerätetreiber installieren

Fehlt noch der USB-Treiber für das Board. Beim NodeMCU Lua Lolin V3 ESP8266 ESP-12F ist der CH340-USB-Chipsatz verbaut. Daher benötigt ihr hier noch den passenden Treiber. Ohne Treiber wird der ESP 8266 nicht vom PC erkannt. Installiert den Gerätetreiber und schließt dann den ESP8266 per USB am Computer an.

Taucht der ESP trotz USB-Treiber nicht im Gerätemanager auf, dann prüft, ob ihr das korrekte USB-Kabel verwendet. Wie eingangs erwähnt benötigt ihr ein Micro-USB-Daten-Kabel. Ein Ladekabel genügt nicht.

Schritt 4: Board auswählen

Damit ihr mit der Programmierung beginnen bzw. den Programmcode einspielen könnt, müsst ihr das Board noch entsprechend einstellen und den ESP8266 entsprechend auswählen. Daher unter „Tools -> Board -> esp8266 -> NodeMCU 1.0 (ESP-12E Module)“ das Board auswählen. Wählt dann noch den passenden Port aus, an dem ihr das USB-Kabel am Computer angeschlossen habt („Tools -> Port -> COM1“). Die genaue Portbezeichnung könnt ihr im Gerätemanager einsehen.

Schritt 5: Programmcode auf ESP8266 hochladen

Nun kann es richtig losgehen und ihr könnt folgenden Programmcode auf euren ESP8266 übertragen. Der Code basiert auf dem Deauth Detector von Stefan Kremser aka Spacehuhn.

Den Code habe ich für meine Zwecke angepasst und die Erkennungssensibilität etwas erhöht. Zudem wollte ich, dass mein ESP 8266 dauerhaft seine Betriebsbereitschaft per LED anzeigt. Wenn die LED blinkt, dann wird ein Angriff erkannt. Die Taktung des Blinkens deutet die Anzahl der Deauth-Pakete beim Angriff an.

Meine angepasste Version erkennt zudem jede Anmeldung im WLAN. Sobald eine Anmeldung im WLAN (egal ob erfolgreich oder ein fehlgeschlagener Versuch) erkannt wird, blinkt die LED ebenfalls. So könnt ihr ableiten, ob mit eurem WLAN alles in Ordnung ist.

Kopiert den folgenden Code nun in euren Arduino-Sketch und passt das Skript auf diejenigen Kanäle an, die ihr in eurem heimischen WLAN verwendet. Je präziser ihr die WLAN-Kanäle eingrenzen könnt, desto besser ist die Erkennungsrate des ESP8266. Der Mikrocontroller kann immer nur einen WLAN-Kanal zur gleichen Zeit auf verdächtige Frames überprüfen.

const short channels[] = { 1, 6, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }; // channels Europe

Wenn ihr mehr als nur einen WLAN-Kanal definiert, dann wechselt der ESP alle 120 Millisekunden den WLAN-Kanal und scannt dort auf verdächtige Pakete. Allerdings bekommt er verdächtige Pakete, die auf anderen als dem gegenwärtig überwachten Kanal gesendet werden, dann leider nicht mehr mit. Wenn ihr euch unsicher seid, dann erlaubt einfach alle Kanäle. Ihr müsst in diesem Fall aber mit etwas schlechteren Erkennungsraten leben.

Natürlich steht euch frei, für jeden WLAN-Kanal einen eigenen ESP8266 in Betrieb nehmen. Preiswert genug sind die Boards schließlich. Dann verpasst ihr keinen Angriffsversuch. Bei einer üblichen Deauthentication-Attacke schlägt der Detektor aber in allen Varianten ausreichend Alarm.

Nachdem ihr die Kanäle angepasst habt, könnt ihr den Code auf den verbundenen ESP8266 hochladen. Nutzt dazu den entsprechenden Button in der Arduino IDE. Der ESP startet dann neu, im Anschluss sollte die blaue LED dauerhaft leuchten. Das Gerät ist nun betriebsbereit und prüft auf die oben beschriebene Vektoren.

Sobald ihr ein überdurchschnittlich häufiges Flackern der LED wahrnehmt, solltet ihr euer WLAN überprüfen. Denn möglicherweise ist es gerade Opfer einer Deauth-Attacke oder jemand versucht sich anzumelden.

// Based on Spacehuhn's DeauthDetector. For details visit github.com/spacehuhn/DeauthDetector
// changed LED behaviour, added assoc detection


// include necessary libraries
#include <ESP8266WiFi.h>


// include ESP8266 Non-OS SDK functions
extern "C" {
#include "user_interface.h"
}


// ===== SETTINGS ===== //
const short channels[] = { 1,6,11 };    // Channels to scan


// ===== Runtime variables ===== //
int ch_index{ 0 };                      // Current index of channel array
int packet_rate_deauth{ 0 };            // Deauth packet counter (resets with each update)
int attack_counter_deauth{ 0 };         // Deauth attack counter
int packet_rate_auth{ 0 };              // Auth packet counter (resets with each update)
int attack_counter_auth{ 0 };           // Auth attack counter
unsigned long update_time_deauth{ 0 };  // Last update time deauth detection
unsigned long update_time_auth{ 0 };    // Last update time auth detection
unsigned long ch_time{ 0 };             // Last channel hop time


// ===== Sniffer function ===== //
void sniffer(uint8_t *buf, uint16_t len) {
  if (!buf || len < 28) return;   // Drop packets without MAC header
  byte pkt_type = buf[12];        // where frame subtype is defined in packet

  // If captured packet is a deauthentication or dissassociaten frame
  if (pkt_type == 0xA0 || pkt_type == 0xC0) {
    ++packet_rate_deauth;
  }

  // If captured packet is a authentication frame
  if (pkt_type == 0x10) {
    ++packet_rate_auth;
  }
}


// ===== Setup ===== //
void setup() {
  Serial.begin(115200);                 // Start serial communication

  pinMode(2, OUTPUT);                   // Onboard LED
  digitalWrite(2, !true);               // Enable LED pin

  WiFi.disconnect();                    // Disconnect from any saved or active WiFi connections
  wifi_set_opmode(STATION_MODE);        // Set device to client/station mode
  wifi_set_promiscuous_rx_cb(sniffer);  // Set sniffer function
  wifi_set_channel(channels[0]);        // Set channel
  wifi_promiscuous_enable(true);        // Enable sniffer  
}


// ===== Loop ===== //
void loop() {
  unsigned long current_time = millis();          // Get current time (in ms)
    
  if (current_time - update_time_deauth >= 60) {  // Run every 60ms
    update_time_deauth = current_time;            // Reset 60ms interval counter

    // When detected deauth packets exceed the minimum allowed number
    if (packet_rate_deauth >= 1) {                // If one or more deauthentication packets within 60ms
      ++attack_counter_deauth;                    // Then increment attack counter
    } else {
      if (attack_counter_deauth >= 1) {
        digitalWrite(2, !true);                   // turn LED on
        Serial.println("DEAUTH STOPPED");
        attack_counter_deauth = 0;                // Reset attack counter
      }
    }

    // When attack exceeds minimum allowed time
    if (attack_counter_deauth == 1) {             // If attack conditions hit
      digitalWrite(2, true);                      // turn LED off
      Serial.println("DEAUTH DETECTED");
    }

    //Serial.print("Deauth Packets/s: ");
    //Serial.println(packet_rate_deauth);

    packet_rate_deauth = 0;                       // Reset packet rate
  }

  if (current_time - update_time_auth >= 120) {   // Run every 120ms
    update_time_auth = current_time;              // Reset 120ms interval counter

    // When detected auth packets exceed the minimum allowed number
    if (packet_rate_auth >= 1) {                  // If one or more deauthentication packets within 120ms
      ++attack_counter_auth;                      // Then increment attack counter
    } else {
      if (attack_counter_auth >= 1) {
        digitalWrite(2, !true);                   // turn LED on
        Serial.println("AUTH STOPPED");
        attack_counter_auth = 0;                  // Reset attack counter
      }
    }

    // When attack exceeds minimum allowed time
    if (attack_counter_auth == 1) {               // If attack conditions hit
      digitalWrite(2, true);                      // turn LED off
      Serial.println("AUTH DETECTED");
    }

    //Serial.print("Auth Packets/s: ");
    //Serial.println(packet_rate_auth);

    packet_rate_auth = 0;                         // Reset packet rate
  }

  // Channel hopping
  if (sizeof(channels) > 1 && current_time - ch_time >= 120) {
    ch_time = current_time;   // Run every 120ms

    // Get next channel
    ch_index = (ch_index + 1) % (sizeof(channels) / sizeof(channels[0]));
    short ch = channels[ch_index];

    wifi_set_channel(ch);
  }
}