Michael Brooks beschreibt in seinem kürzlich erschienenen Paper Bypassing PHPIDS 0.6.5 wie man das Intrusion Detection System (kurz IDS) PHPIDS in der Version 0.6.5 umgeht. Er hat dieses Produkt unter anderem mit dem statischen Analysetool RIPS PHP untersucht und einige gravierende Schwachstellen gefunden.
Firewalls und IDS verfolgen generell zwei unterschiedliche Ansätze: Firewalls reduzieren die Angriffsfläche indem sie den eingehenden Netzwerkverkehr analysieren und nur das durchlassen, was explizit erlaubt ist. IDS hingegen überprüfen den Netzwerkverkehr auf Anzeichen eines Angriffs. Haben sie einen solchen erkannt, generieren sie eine Warnmeldung oder blockieren die Anfrage. Anders ausgedrückt könnte man bei einer Firewall von einem White-List-Filter sprechen, der nur das durchlässt, was ausdrücklich erlaubt ist. IDS hingegen stellen einen Black-List-Filter dar, es wird also alles durchgelassen bis auf das, was als Angriff erkannt wird. Bei Web-IDS und -Firewalls verfolgen üblicherweise beide Ansätze, weshalb diese Begriffe häufig synonym gebraucht werden, daher teilen diese Techniken auch dieselben Schwächen.
Unzureichender Schutz vor ReDoS-Angriffen kann dazu ausgenutzt werden, die Angriffserkennung komplett zu umgehen
Die erste Schwachstelle ergibt sich aus einem Mechanismus, der das IDS vor ReDoS-Angriffen schützen soll.
/** * Make sure the value to normalize and monitor doesn't contain * possibilities for a regex DoS. * * @param string $value the value to pre-sanitize * * @static * @return string */ public static function convertFromRepetition($value) { // remove obvios repetition patterns $value = preg_replace( '/(?:(.{2,})\1{32,})|(?:[+=|\-@\s]{128,})/', 'x', $value ); return $value; }
Der regulärte Ausdruck überprüft dabei einfach, ob sich irgendein Textmuster, das zumindest zwei Zeichen enthält, mindestens 33 mal wiederholt oder Ein Zeichen der Zeichenklasse [+=|\-@\s]
zumindest 128 mal wiederholt. Ist dies der Fall, wird der ganze Ausdruck durch x
ersetzt, bevor die eigentliche Angriffserkennung erfolgt.
Ein Ausdruck der Form:
abababababababababababababababababababababababababababababababababa!
Wird also schlicht in folgenden umgewandelt:
x!
Das Problem dabei ist, dass diese Funktion einerseits keinen Schutz gegen ReDoS-Schwachstellen darstellt (siehe meine Artikelserie darüber) und andererseits dazu missbraucht werden kann, die gesammte nachgeschaltete Angriffserkennung zu umgehen. Man brauch ja nur die Angriffssignatur mindestens 33 mal zu wiederholen, also beispielsweise folgende Anfrage absetzten:
<script>alert(1);</script><script>alert(1);</script><script>alert(1);</script> ... (insgesamt 33 mal wiederholen)
Diese Anfrage wird nicht auf Angriffe überprüft, da sie von der Funktion convertFromRepetition
einfach in x
umgewandelt wird, bevor sie auf Angriffe untersucht wird. So kann das IDS natürlich keinen Angriff erkennen und die Anfrage wird unverändert an die Anwendung weitergeleitet und damit eine eventuell in der Anwendung vorhandene Cross-Site-Scripting-Schwachstelle ausgenutzt!
Selbverständlich kann man so auch SQL-Injection-Schwachstelle ausnutzen. Man braucht lediglich dafür zu sorgen, dass die Wiederholungen in einem Kommentar münden. Dann wird nur der erste Teil des Musters ausgewertet und die Wiederholungen von der Datenbank ignoriert, da sie sich ja innerhalb eines Kommentars befiden, also beispielsweise:
' union select password from mySQL.user limit 1 /*' union select password from mySQL.user limit 1 /* ... (insgesamt 33 mal wiederholen)
Auch Directory-Traversal-Angiffe funktionieren, sofern die Eingabe irgendwo in einem Nullterminierten String landet.
../../../etc/passwd%00../../../etc/passwd%00... (insgesamt 33 mal wiederholen)
Das erste Null-Teuchen sorgt dann dafür, dass alles, was dahinter kommt, ignoriert wird (siehe auch HTTP-Parameterverunreinigung, Teil 2).
Die Annahme, man könne keine Angriffe ausschließlich mit alphanumerischen Zeichen durchführen, ist falsch!
Folgende regel filtert alle Parameter aus, die keine Sonderzeichen enhalten:
// define the pre-filter $prefilter = '/[^\w\s\/@!?\.]+|(?:\.\/)|(?:@@\w+)/'; // to increase performance, only start detection if value // isn't alphanumeric
Die Annahme ist, dass ein erfolgreicher Angriff nicht ohne Sonderzeichen funktioniert, was prinzipiell falsch ist. Um eine SQL-Injection-Schwachstelle wie die folgende auszunutzen braucht man keine Sonderzeichen, wie eine Schwachstelle im IG-Shop beweist:
"select type_id from catalog_product where product_id = "$HTTP_GET_VARS['id']
Hier genügt die Anfrage…
http://localhost/compare_product.php?id=0 union select password from users limit 1
…um das erste Passwort auszulesen. Im Übrigens ist der Filter nicht ganz „dicht“, denn folgende Zeichen /@!?.\r\n
werden problemlos durchgelassen.
Die Annahme, Benutzerdaten können nur mittels $_GET, $_POST ,$_COOKIE und $_REQUEST übergeben werden, ist ebenfalls falsch!
Neben den genannten Quellen vergaßen die Schöpfer von PHPIDS die Quelle $_SERVER
zu berücksichtigen. Schließlich wird der Eintrag $_SERVER[‚PHP_SELF‘] gerne für Cross-Site-Scripting-Angriffe ausgenutzt, denn dieser Eintrga wird vom Browser gesetzt!
Gibt beispielsweise ein Skript diesen Wert ohne weitere Kodierung aus, beispielsweise um das aktuelle Skript als Formular-Aktion anzugeben…
<form action="$_SERVER['PHP_SELF']" method ="post">
…kann der Pfad, den der Angreifer übergibt, ohne weiters wie folgt aussehen…
http://localhost/contact/myform.php/"><script>alert(1);</script>
Das Skript übernimmt dann diesen Pfad und baut ihn ins Formular ein und das ohne dass PHPIDS etwas davon bemerkt.
Die Directory-Traversal-Schwachstelle in PHPIDS, die in dem Paper beschrieben wird, ist der aktuellen Version offenbar bereits behoben. Das Projekt hat offenbar kreative Tester als Mitglieder (siehe Projektforum), wodurch PHPIDS in Zukunft hoffentlich sicherer und Treffsicherer im Aufspüren und Abblocken von Angriffen wird. Leider liegt es in der Natur der Sache, dass man unsichere Webanwendungen mit dieser Technik nicht in allen Fällen zuverlässig schützen kann.