Code-Assisted Pentests

Zahlreiche Unternehmen setzen auf regelmäßige Penetrationstests, um ihre Anwendungen und IT-Infrastrukturen auf Schwachstellen zu prüfen. Meist wird hier aus der Sicht eines externen Angreifers mit einem Black-Box Ansatz, also ohne Informationen über die Anwendung oder Infrastruktur, getestet. Dies ist in vielen Situationen ein Kompromiss aus Testtiefe und der verfügbaren Testzeit. Ein beauftragter Penetrationstest hat das Ziel, in einer definierten Zeit möglichst viele potenzielle Schwachstellen aufzufinden.

Bei einem Code-Assisted Pentest steht dem Pentester der Quellcode oder zumindest ein Auszug relevanter Quellcode-Teile während der Analyse zur Verfügung. Diese Analysemethode bringt wesentliche Vorteile hinsichtlich der Effektivität und der Prüftiefe eines Pentests mit sich.

Im Folgenden betrachten wir die Vorteile aus Unternehmenssicht sowie konkrete Probleme, die sich für einen Tester ergeben und wie diese mit einem Code-Assisted Pentest gelöst werden können.

Code-Assisted Pentesting bedeutet Effizienz

Viele Penetrationstest gehen von der Annahme aus, dass ein möglicher Angriff durch einen externen Hacker nachgestellt werden soll. Die Frage, die oft hinter der Beauftragung steht, lautet: Kann ein externer Angreifer meine Anwendung kompromittieren?

Dementsprechend wird die Vorgehensweise eines Angriffs von außen oftmals gewählt und entspricht üblicherweise einem Black-Box-Ansatz. Der Pentester hat also keinerlei Informationen über die Anwendung, wie vermeintlich ein echter Hacker. Allerdings muss berücksichtigt werden, dass ein Pentester auch nur eine endliche Zeit für seinen Pentest zur Verfügung hat. Dies trifft auf einen realen Angreifer nicht unbedingt in gleichem Maße zu. Daher ist der Pentester darauf angewiesen, möglichst effektiv in der vorgegebenen Zeit zu testen.

Durch einen Code-Assisted Pentest kann sich der Testfokus hauptsächlich auf das Auffinden von Schwachstellen konzentrieren und ist beispielsweise nicht davon abhängig, eine zeitraubende, initiale Informationsgewinnungsphase (Enumeration) durchgeführt zu haben. Die folgenden Beispiele veranschaulichen die Probleme bei einem Black-Box-Ansatz.

Problematik 1: Enumerieren von Verzeichnissen und Endpunkten

Zu Beginn eines jeden Penetrationstests ist es notwendig, die Struktur der Applikation kennenzulernen und so viele Endpunkte wie möglich ausfindig zu machen. Die einzelnen Endpunkte interagieren mit dem Nutzer und können potenziell Schwachstellen beinhalten.

Da der Tester von außen auf den Applikationsserver zugreift, hat er nicht die Möglichkeit sich die Verzeichnis- oder Routenstruktur auf dem Server selbst anzusehen. Er muss somit eine Wörterliste mit möglichen Endpunkten nutzen und jeden einzelnen Eintrag vom Applikationsserver anfragen, um herauszufinden welche Endpunkte existieren. Die folgende Abbildung zeigt diesen Vorgang:

Abbildung 1: Enumerieren von Applikationsendpunkten mit Wortlisten

Die Wortliste enthält meist mehrere 100.000 Einträge.  Oft sind Scan-Wiederholungen erforderlich, um die Scankonfiguration optimal auf das Antwortverhalten des Servers anzupassen.

Enthält die Wortliste den Namen eines verwundbaren Endpunktes nicht, so bleibt dieser meist unangetastet und wir nicht entdeckt. Gerade die zeitliche Begrenzung eines Penetrationstests limitiert den Tester in seinen Möglichkeiten zum Auffinden von Endpunkten oder Routen. Sollen Produktnamen des Kunden in die Wortliste aufgenommen werden, um so potenzielle Endpunkte zu finden? Soll die Webpräsenz des Kunden herunterladen und alle auf ihr enthaltenen Wörter in einem Scan inkludieren? Dies sind Entscheidungen mit meist unbekannten Folgen – auf jeden Fall erfordern sie zusätzliche Zeit, ohne die Trefferquote zum Auffinden eines Endpunkts sicher zu erhöhen.

Ohne einen Code-Assisted Pentest können Endpunkte und damit mögliche Schwachstellen unentdeckt bleiben.

Problematik 2: Eingabevalidierung

Nachdem eine Übersicht der Endpunkte erstellt wurde, identifiziert der Tester, welche Schnittstellen Nutzereingaben verarbeiten und prüft diese auf gängige Schwachstellentypen wie Cross-Site Scripting (XSS), Command Injection oder SQL Injection (SQLi). Hierbei tritt oft das Problem auf, dass der Anwendungsserver die Eingaben filtert und bösartige Zeichen entfernt, dem Pentester im Black-Box-Ansatz jedoch unbekannt ist, wie diese Filterung implementiert wurde.

Im Folgenden ist ein Codebeispiel (download.php) zu sehen, dass den Download von Systembackups in einer Webanwendung implementiert.

<?php
$file = str_replace('../', '', $_GET['file']);
if(isset($file)){ 
   downloadBackup("backups/$file");
}

Die Applikation liest den file Parameter einer Nutzeranfrage und gibt die angegebene Datei als Download zurück. Um das Ausbrechen aus dem aktuellen Verzeichnis zu verhindern, wird die Verzeichniswechselsequenz “../” mit einem Aufruf von str_replace() gefiltert. Ein Angreifer kann somit keinen unmittelbaren Angriff auf die Applikation ausführen, indem er mit einer Anfrage wie GET /download.php?file=../../../../../etc/passwd die Passwortdatei des Systems herunterlädt (sogenanntes Path Traversal). Als externer Tester ist jedoch ebenso unklar, wie der Server die Eingabe validiert.

Durch die zeitliche Begrenzung des Penetrationstests beginnt ein Spiel, bei dem der Tester abwägen muss, wie viele Eingaben er an den Server schickt, um mit hoher Wahrscheinlichkeit eine korrekte Aussage treffen zu können, dass die Komponente sicher implementiert wurde. In unserem Beispiel könnte er ein weiteres “../” an seine bösartige Anfrage anhängen – vielleicht waren es zu wenige Wechselsequenzen um in das Stammverzeichnis des Servers zu wechseln?

Nach einigen weiteren Tests kommt er möglicherweise zu der Entscheidung, dass die Komponente kein ausnutzbares Verhalten zeigt. Mit Sicht auf den Code wäre die Schwachstelle eindeutig: Die str_replace() Funktion ersetzt bösartige Sequenzen nicht rekursiv, sondern lediglich einmalig. Ein Angreifer könnte folgende Anfrage an den Server stellen, um die Passwortdatei auszulesen:

GET /download.php?file=….//….//….//….//….//etc/passwd

Die str_replace() Funktion ersetzt im file Parameter nun alle bösartigen “../” Sequenzen – was bleibt ist der Parameterwert: ../../../../../etc/passwd – dieser enthält nun die Wechselsequenz, die ein Ausbrechen aus dem backup Verzeichnis ermöglicht!

Bei einem Black-Box-Pentest ist es praktisch unmöglich alle denkbaren Varianten durchzuprobieren. Ein Code-Assisted Pentest würde diese Schwachstelle schnell und effizient identifizieren.

Code-Assisted Pentesting als Ansatz zur Lösung

Wie in den Beispielen erläutert, weitet sich diese Problematik auf viele Testabschnitte eines Penetrationstests aus. Folgende Fragen stehen beispielhaft für weitere Themen, die in einem Black-Box-Verfahren nicht oder nur unzureichend getestet werden können:

  • Wie sind die Passwörter in der Applikation gespeichert? Sind Kundenpasswörter bei einer Kompromittierung unmittelbar auslesbar oder als „Salted Hash“ gesichert?
  • Sind zufällige Tokens (z.B. zum Zurücksetzen eines Nutzeraccounts) tatsächlich zufällig? Existiert genügend Entropie, um einen Brute-Force Angriff zu verhindern?
  • Wurde ein Endpunkt in der Implementierung der Zugriffskontrolle vergessen?

Um diese Fragen beantworten zu können und kritische Applikationen mit der notwendigen Detailtiefe zu testen, bietet sich ein Code-Assisted Pentest an. Gemeinsam mit dem Penetrationstester werden kritische Applikationskomponenten identifiziert und der Quellcode selektiert. Dadurch kann sichergestellt werden, dass der Quellcode nicht vollständig beim Pentester vorliegen muss, sondern nur die Teile, die für die Analyse zwingend notwendig sind.

Der Aufwand eines Code-Assisted Pentests ist nur unwesentlich höher als bei einem Pentest nach dem Black-Box-Ansatz. Dieser Aufwand wird durch einen Gewinn an Effektivität und Prüftiefe problemlos ausgeglichen. Das Ergebnis ist ein Pentest, der deutlich mehr Schwachstellen abdeckt und in der vorgegebenen Zeit daher wesentlich genauere Ergebnisse liefert.