Fuzzing ist eine Methode, um Softwarefehler und Sicherheitslücken zu finden. Die aktuelle Entwicklung zeigt einen klaren Trend, Fuzzing in die Cloud zu verlagern, da Cloud-Fuzzing eine deutlich höhere Geschwindigkeit und große Flexibilität gegenüber klassischem Fuzzing bietet. In diesem Tutorial durchlaufen wir den gesamten Cloud-Fuzzing-Prozess (Amazon Cloud): Deployment, Fuzzing und das Einsammeln der Ergebnisse mit dem softScheck Cloud Fuzzing Framework (sCFF).
Wir identifizieren eine Schwachstelle in tcpdump Version 4.9 auf Ubuntu 16.04, analysieren den Fehler und schreiben einen Patch, der diese Schwachstelle schließt. Leser können sCFF herunterladen und das Tutorial Schritt für Schritt nachvollziehen.
Inhalt
1 Hintergrundwissen
Dieses Kapitel behandelt grundlegende Konzepte und Programme, die im Prozess verwendet werden. Wenn du bereits Erfahrung mit Cloud-Fuzzing hast oder dich die Details nicht interessieren, kannst du direkt zu Kapitel 2 springen. Andernfalls empfehlen wir dringend, den Artikel vollständig zu lesen.
1.1 Fuzzing
Fuzzing ist eine Technik zum Testen der Robustheit von Software. Ein Fuzzer ist ein Programm, das ein Zielprogramm mit automatisch generierten Eingabedaten füttert. Dabei überwacht er, ob diese Eingaben zu Abstürzen, Hängern oder anderem unerwartetem Verhalten führen. Dieser Prozess wird wiederholt, bis der Benutzer ihn beendet.
Viele dieser Anomalien sind tatsächlich Sicherheitslücken, die ein Angreifer ausnutzen könnte. Für Security-Experten ist Fuzzing ein Standardwerkzeug, da es automatisierbar ist und auf praktisch jede Software angewendet werden kann, die Daten verarbeitet – also fast jede Software.
Der Quellcode ist nicht zwingend erforderlich, kann aber den Prozess beschleunigen, wenn der Fuzzer z. B. instrumentiertes Fuzzing oder symbolische Ausführung nutzt. Früher liefen Fuzzer und Zielanwendung lokal auf demselben Rechner. Mit dem Aufkommen von Cloud-Infrastrukturen ist es jedoch naheliegend, Fuzzing in die Cloud zu verlagern. Große Unternehmen wie Microsoft und Google tun dies bereits. Microsoft bietet mit Project Springfield sogar Cloud-basiertes Fuzzing als Service an.
Warum also Cloud-Fuzzing statt klassischem Fuzzing? Man spart sich den Kauf, den Betrieb und die Wartung eigener Hardware. Der größte Vorteil ist jedoch die Flexibilität: Man kann schnell verschiedene Betriebssysteme testen, Instanzen mit viel RAM oder SSD-RAID0 wählen oder hunderte günstige Maschinen für Netzwerk- oder Web-Fuzzing starten.
Nachteile gibt es aber auch: Man muss dem Cloud-Anbieter vertrauen, und bei langer Laufzeit können die Kosten höher sein als bei eigener Hardware.
1.2 Amazon AWS
Amazon Web Services (AWS) ist Amazons Cloud-Plattform und der größte Anbieter im Cloud-Computing-Markt. Eine zentrale Komponente ist EC2 (Elastic Compute Cloud), mit der virtuelle Maschinen gestartet werden können.
Eine „Instance“ ist ein virtueller Server mit Betriebssystem, Software und zugewiesenen Ressourcen. Man kann aus vielen Amazon Machine Images (AMI) wählen sowie aus rund 100 verschiedenen Maschinentypen – von sehr kleinen Systemen bis hin zu Maschinen mit 256 CPU-Kernen und 2 TB RAM. Abgerechnet wird pro Stunde Laufzeit.
1.3 softScheck Cloud Fuzzer Framework
Um den Fuzzing-Prozess möglichst einfach zu machen, haben wir bei softScheck das sCFF – softScheck Cloud Fuzzer Framework entwickelt. sCFF ist in Python 3 geschrieben und verwendet die Boto3-API zur Kommunikation mit AWS. Es ist in mehrere Subprogramme aufgeteilt, gemäß der Unix-Philosophie „ein Tool, eine Aufgabe“.

Für Details zur Architektur siehe das sCFF-Paper.
1.4 American Fuzzy Lop
American Fuzzy Lop (afl) ist der von sCFF verwendete Fuzzer. AFL ist bekannt für seine Geschwindigkeit, Stabilität und seine Retro-Oberfläche. Wenn der Quellcode verfügbar ist, kann AFL ihn instrumentieren, um während des Fuzzings bessere Eingaben zu erzeugen und so die Code-Coverage zu erhöhen – was die Wahrscheinlichkeit erhöht, Sicherheitslücken zu finden.

1.5 tcpdump
tcpdump ist ein weit verbreiteter Netzwerk-Paketanalysator. Er kann Netzwerkpakete erfassen, anzeigen und im pcap-Format speichern. Im Gegensatz zu Wireshark ist tcpdump ein reines Kommandozeilenprogramm – ideal zum Fuzzing.
1.6 GNU Debugger
Mit dem GNU Debugger (GDB) kann man Programme Schritt für Schritt ausführen und analysieren, was zu einem Absturz führt. Mit Debug-Symbolen sieht man sogar die exakte Zeile im Quellcode. Variablen können zur Laufzeit verändert werden, um Hypothesen über Fehlerursachen schnell zu testen.
2 Fuzzing von tcpdump mit sCFF
Nachdem wir die Grundlagen behandelt haben, suchen wir nun eine Schwachstelle in tcpdump 4.9.
2.1 Vorbereitungen
Um den gleichen Weg wie wir zu gehen, müssen zuerst AWS und sCFF eingerichtet werden:
Erforderlich
- AWS-Account erstellen
- AWS-Key und Secret exportieren
.aws/configund.aws/credentialskorrekt setzen- SSH-Security-Group anlegen
- Key-Pair erstellen und herunterladen
- sCFF herunterladen und installieren
Optional
- AFL lokal für instrumentiertes Fuzzing
- afl-collect, GDB und exploitable-Plugin
2.2 Pre-Fuzzing-Phase
Wenn die Vorarbeiten abgeschlossen sind, muss der Quellcode von tcpdump Version 4.9 heruntergeladen werden. Du kannst auch die aktuelle Git-Version verwenden; allerdings ist die in diesem Tutorial gezeigte Sicherheitslücke in neueren Versionen bereits behoben – es könnten dort aber noch andere Schwachstellen existieren 😉. Nach dem Download des Quellcodes wird tcpdump mit afl-gcc kompiliert, um vom instrumentierten Fuzzing von AFL zu profitieren:
CC=afl-gcc ./configure && make
Wenn die Kompilierung erfolgreich war, kannst du mit scff-mkconfig eine sCFF-Projektdatei erstellen. Achte darauf, als Target tcpdump zu setzen und als Argumente -e -r @@ anzugeben.
-e und -r sind tcpdump-Parameter, wobei -e für „extended header anzeigen“ und -r für „Datei lesen“ steht. Die beiden At-Zeichen @@ werden später von AFL durch die jeweils generierte, gefuzzte Datei ersetzt.
Beachte außerdem: Wenn du neu bei AWS bist, sind t2-Instanzen kostenlos, solange deine Laufzeit unter 750 Stunden bleibt. Daher empfiehlt es sich, für den Fuzzing-Prozess bei t2-Instanzen zu bleiben.
Für deutlich schnellere Ergebnisse wird außerdem empfohlen, ein Template zu verwenden. Wir haben dafür eine kleine (170 Byte große) pcap-Datei mit IPv4-Traffic genutzt. Als Referenz folgt hier die von scff-mkconfig erzeugte Konfigurationsdatei, die wir bei softScheck verwendet haben:
[INSTANCES]
ami = ami-0963b466
gid = tcpdump49
instancetype = t2.micro
name = auto
numberofmachines = 4
platform = linux
[FUZZING]
dependencies = none
fuzzer = afl
fuzzdir = fuzzing
inputdir = fuzzing/input
outputdir = fuzzing/output
template = ipv4.pcap
target = tcpdump
args = -e -r @@
Die EC2-Instanzen können nun mit scff-create-instances erstellt werden.
Du solltest dich anschließend mit deinem Key-Pair per SSH mit ihnen verbinden können.
2.3 Fuzzing-Phase
Als Nächstes müssen die neu erstellten Maschine(n) für den Fuzzing-Prozess eingerichtet werden. Das geschieht mit dem Befehl:
scff-ctrl . bootstrap
Sobald das Bootstrapping abgeschlossen ist, kann das eigentliche Fuzzing beginnen. sCFF erlaubt die Wahl zwischen Single-Mode-Fuzzing und verteiltem (Distributed) Fuzzing.
Im Single-Mode führt jede Instanz ihren eigenen Fuzzer unabhängig aus. Beim verteilten Fuzzing läuft ebenfalls auf jeder Instanz ein Fuzzer, allerdings werden die Fuzzing-Daten zwischen den Instanzen geteilt, was zu deutlich schnelleren Ergebnissen führt. Wenn du mehr als zwei Instanzen verwendest, wird der Distributed Mode empfohlen.
Der verteilte Modus wird mit folgendem Befehl gestartet:
scff-ctrl . distributed
Um den Status des Fuzzings zu sehen, öffne in deinem Webbrowser die IP-Adresse und den Port des Roving Servers.

Nach einiger Zeit solltest du sehen, dass der Crash-Zähler ansteigt. Die Dateien, die diese Abstürze verursachen, kannst du jederzeit mit folgendem Befehl herunterladen:
scff-ctrl . grab-findings
2.4 Post-Fuzzing-Phase
Durch das Ausführen von scff-exploitcheck werden diese Funde analysiert. False Positives und Duplikate werden herausgefiltert. Die verbleibenden Ergebnisse werden anschließend auf Exploitierbarkeit geprüft.

Wenn ein Fund mit einem roten EXPLOITABLE-Label markiert ist, ist die Wahrscheinlichkeit hoch, dass du eine echte Sicherheitslücke gefunden hast. Diese Funde solltest du anschließend mit gdb untersuchen. Wie man im Bild unten sehen kann, besitzt tcpdump 4.9 eine ausnutzbare Schwachstelle in der Datei print-sl.c.

Mit etwas weiterem Debugging sehen wir, dass der Wert von dir 255 ist. dir wird jedoch auch als Index für lastlen verwendet, das als lastlen[2][255] definiert ist. Damit greifen wir außerhalb des erlaubten Bereichs zu (Out-of-Bounds), was zum Absturz führt.
Um diesen Fehler zu beheben, müssen wir entweder den Wert von dir korrigieren oder sicherstellen, dass dir zwischen 0 und 2 liegt.
Setze dazu einen Breakpoint nach dir = p[DIR_SLX] und ändere dann in gdb den Wert von dir auf einen gültigen Wert (z. B. 0) mit set p = 0.
Versuche nun, einen Patch für dieses Problem zu schreiben!
Wenn du dir die Lösung vorwegnehmen willst, klicke hier.
--- a/print-sl.c
+++ b/print-sl.c
@@ -131,8 +131,21 @@ sliplink_print(netdissect_options *ndo,
u_int hlen;
dir = p[SLX_DIR];
- ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
+ switch (dir) {
+ case SLIPDIR_IN:
+ ND_PRINT((ndo, "I "));
+ break;
+
+ case SLIPDIR_OUT:
+ ND_PRINT((ndo, "O "));
+ break;
+
+ default:
+ ND_PRINT((ndo, "Invalid direction %d ", dir));
+ dir = -1;
+ break;
+ }
if (ndo->ndo_nflag) {
/* XXX just dump the header */
register int i;
@@ -155,13 +168,21 @@ sliplink_print(netdissect_options *ndo,
* has restored the IP header copy to IPPROTO_TCP.
*/
lastconn = ((const struct ip *)&p[SLX_CHDR])->ip_p;
+ ND_PRINT((ndo, "utcp %d: ", lastconn));
+ if (dir == -1) {
+ /* Direction is bogus, don't use it */
+ return;
+ }
hlen = IP_HL(ip);
hlen += TH_OFF((const struct tcphdr *)&((const int *)ip)[hlen]);
lastlen[dir][lastconn] = length - (hlen << 2);
- ND_PRINT((ndo, "utcp %d: ", lastconn));
break;
default:
+ if (dir == -1) {
+ /* Direction is bogus, don't use it */
+ return;
+ }
if (p[SLX_CHDR] & TYPE_COMPRESSED_TCP) {
compressed_sl_print(ndo, &p[SLX_CHDR], ip,
length, dir);
Kompiliere das Programm anschließend erneut und überprüfe, ob es weiterhin abstürzt.

3 Fazit
Die Sicherheitslücke ist keine besonders große, da ein Angreifer das Opfer zunächst dazu bringen muss, eine manipulierte pcap-Datei mit dem Parameter -e zu öffnen. Selbst dann muss es dem Angreifer noch gelingen, Daten an der Speicheradresse zu platzieren, auf die das Out-of-Bounds-Array zugreift, damit das Programm nicht einfach nur abstürzt, sondern tatsächlich schädliche Aktionen ausführt.
Als wir das tcpdump-Sicherheitsteam über das Problem informiert haben, reagierten sie schnell und sehr ausführlich. Die potenzielle Schwachstelle wird in der nächsten tcpdump-Version (4.10) behoben.
Trotzdem zeigt dieser Beitrag sehr gut, wie schnell sich Fehler und Sicherheitslücken mit Cloud-Fuzzing und einer guten Toolchain finden lassen. Bei softScheck benötigten wir etwa 5 Stunden, um die Schwachstelle zu identifizieren und zu beheben. Hier ist die Aufschlüsselung:
| Phase | Zeit [Minuten] |
|---|---|
| Herunterladen und Kompilieren von tcpdump | 10 |
| Pre-Fuzzing-Phase + Template-Erstellung | 10 |
| Fuzzing-Phase | 110 |
| Post-Fuzzing-Phase | 60 |
| Patch schreiben und erneut testen | 90 |
| Gesamt | 300 |
Die gesamten EC2-Kosten für diesen Test hätten etwa 25 Cent betragen – wenn er nicht innerhalb der ersten 750 kostenlosen Stunden gelaufen wäre.
Bei Fragen zu diesem Artikel oder zum softScheck Cloud Fuzzing Framework kannst du uns gerne eine E-Mail schreiben.
Update (September 2017)
Die am 3. September veröffentlichte Version 4.9.2 behebt diese Sicherheitslücke. GitHub-Commit, der die Schwachstelle schließt.
Lies weitere interessante Themen in unserem Blog.