Einleitung

Fuzzing ist eine automatisierte Technik zur Entdeckung von Sicherheitslücken. Ziel des Fuzzings ist es, Programmeingaben zu erzeugen, die unerwartetes Verhalten im Untersuchungsobjekt auslösen. Dies können unlogische oder fehlerhafte Ausgaben oder ein Programmabsturz sein.

Der Ablauf beim Fuzzing ist folgendermaßen:

  1. Der Fuzzer generiert eine Eingabe für das Zielprogramm. Diese Eingabe muss nicht zwingend der Struktur und Syntax des Zielprogramms entsprechen. Auch strukturell fehlerhafte Daten können potenziell Sicherheitslücken aufdecken.
  2. Die Eingabe wird an das Zielprogramm weitergeleitet, welches sie genauso verarbeitet, wie es bei einer normalen Programmeingabe der Fall wäre.
  3. Führt die Eingabe zu einer Ausgabe, wird diese an den Fuzzer zurückgegeben. Der Fuzzer kann die Programmausgabe entweder lediglich protokollieren oder sie als Grundlage für die Generierung der nächsten Eingabe verwenden.
Fuzzing
Fuzzing eines Programms

Grundsätzlich ist Fuzzing also eine vergleichsweise einfache Technik, die vollständig automatisiert ablaufen kann, sodass kein Eingreifen eines Testers erforderlich ist. Je mehr Eingaben dabei generiert und getestet werden, desto mehr potenzielle Fehler können aufgedeckt werden. Die eigentliche Herausforderung liegt jedoch in der Generierung der Eingaben, wofür verschiedene Verfahren zur Verfügung stehen. Die wichtigsten davon sind:

  • Dumb Fuzzing: Bei dieser einfachsten Form des Fuzzings werden zufällige Eingaben an das Zielprogramm gesendet, ohne auf die Struktur oder das Verhalten des Programms Rücksicht zu nehmen. Diese Methode kann zufällig Schwachstellen aufdecken, ist jedoch wenig effizient.

  • Mutation-based Fuzzing: Hierbei wird eine gültige Eingabedatei genommen und die Eingaben zufällig verändert. Der Fuzzer testet dann die mutierten Eingaben, um Fehler im Zielprogramm zu entdecken. Diese Methode ist gezielter als Dumb Fuzzing, da es strukturell korrekte Eingaben verwendet und nur bestimmte Felder verändert.

  • Generation-based Fuzzing: Bei dieser Technik gibt der Tester anhand von Regeln die Struktur gültiger Eingaben vor. Darauf basierend wird die Eingabe vollständig vom Fuzzer generiert. Dies erfordert Wissen über das Zielprogramm und die Eingabestruktur. Diese Methode ist präzise, da die Eingaben spezifisch auf die Anforderungen des Programms zugeschnitten sind.

Da beim Fuzzing kontinuierlich neue Eingaben erzeugt und getestet werden, kann der Prozess unbegrenzt weiterlaufen. Es stellt sich also die Frage, wann es sinnvoll ist, das Fuzzing zu beenden. Verschiedene Faktoren können den Zeitpunkt des Abbruchs beeinflussen:

  • Erreichen eines Zeitlimits oder einer maximalen Anzahl an durchgeführten Tests
  • Erreichen aller relevanten oder möglichen Eingaben
  • Erreichen einer festgelegten Testabdeckung (Grad, zu dem der Quellcode durch Tests ausgeführt wird)

Open-Source-Fuzzer sind beispielsweise AFL++, randamsa oder boofuzz.

Fuzzing in Aktion

Nun kommen wir zu einem Beispiel. Bei der aktiven Netzwerkanalyse hattest du festgestellt, dass der UDP-Port 161 offen oder gefiltert ist. Den Dienst hinter diesem offenen Port sollst du nun mittels Fuzzing untersuchen.

Dieser Pentestleitfaden enthält das Python-Skript dumb_fuzzing.py, das zufällige Eingaben generiert und als UDP-Datagramm an ein Ziel sendet. Wenn das Ziel eine Antwort sendet, wird diese vom Skript im Terminal angezeigt. Aufgerufen wird das Skript über

python3 dumb_fuzzing.py

Es fordert den Benutzer auf, folgende Informationen einzugeben:

Parameter Beschreibung
target_ip IP-Adresse des Zielhosts
target_port Zielport
data_type Datentyp, der erzeugt werden soll (ASCII-Zeichenfolge oder hexadezimale Bytes)
num_data Maximale Anzahl der zu erzeugenden ASCII-Zeichen oder Byte-Werte in einem Datagramm
num_packets Anzahl der zu erzeugenden Datagramme

Es können beliebig lange Sequenzen aus ASCII-Zeichen oder hexadezimalen Byte-Werten erzeugt werden. Die ASCII-Zeichen sind ausschließlich druckbar und können als lesbarer Text angezeigt werden, während Steuerzeichen ausgeschlossen sind.

Der Umstand, dass der Dienst das Protokoll UDP nutzt, erschwert das Fuzzing. Zur Erinnerung: UDP ist ein verbindungsloses Protokoll. Pakete werden einfach an das Ziel gesendet, ohne dass es deren Erhalt bestätigt. Daher erkennt der Absender nicht, wenn der Dienst in einen Fehlerzustand gerät und nicht mehr erreichbar ist.

Mit etwas Glück sendet der getestete Dienst eine Antwort zurück, aus der du schließen kannst, dass der Dienst aktiv ist und Anfragen verarbeitet. Die Antwort besteht hauptsächlich aus hexadezimalen Bytes und ist daher nicht menschenlesbar. Ob dies ein unerwartetes Verhalten ist, lässt sich jedoch nicht ohne Weiteres sagen. Es kann sein, dass die Daten einfach nicht für den Menschen bestimmt sind, sondern für ein anderes System.

Nun kannst du prüfen, ob das Fuzzing zu einem Fehlerzustand des Dienstes führt und vorübergehend nicht mehr erreichbar ist. Dies solltest du mit folgenden Einstellungen erreichen:

Parameter Wert
data_type hex
num_data 2
num_packets 100

Hinweis: Der Dienst hinter dem Port 161 wird nach spätestens 5 Minuten neu gestartet.

Grundsätzlich ist es so, dass Fuzzing auch dabei helfen kann, den Dienst besser zu verstehen. Durch das Erzeugen zufälliger Nachrichten werden Antworten vom Dienst provoziert, die anschließend ausgewertet werden können. Allerdings benötigt man Glück oder viel Geduld, um tatsächlich gültige Nachrichten zu erzeugen, die eine sinnvolle Antwort des Dienstes hervorrufen.

Es gibt jedoch eine weitere Möglichkeit, ein Programm zu verstehen, die du gleich kennenlernen wirst: Das Debugging.