Skip to content

Bei NoSQL handelt es sich um Abfragesprachen, welche keinen universellen Standard nutzen. Zudem haben sie viel weniger Relationale Abhängigkeiten.![[nosql-injection-graphic 1.svg]]

Typen der Injektionen

Bei den NoSQL Datenbanken gibt es zwei verschiedene Möglichkeiten für Injektionen. - Zum einen gibt es die Syntax injection diese tritt auf, wenn die NoSQL Abfrage so verändert wird, dass die Syntax falsch ist. Dadurch ist es möglich einen eigenen Payload zu injizieren. Dabei ist diese Attacke ähnlich zu der normalen SQL Injektion. Allerdings unterscheidet sie sich Aufgrund der großen Unterschiedlichkeiten der verschiedenen Datenbanken natürlich stark. - Daneben gibt es noch die Operator Injection diese treten auf, wenn NoSQL Abfrage Operatoren manipuliert werden können.

NoSQL syntax Injektion

Um NoSQL Injektionen zu erkennen muss die syntax der Abfrage kaputt gemacht werden. Um dies systematisch zu machen, sollte jede Eingabe durch fuzzing geprüft werden. Diese Eingaben sollten dabei Sonderzeichen oder andere Zeichen enthalten, welche einen Datenbankfehler werfen, oder ein anderes Verhalten, welches beobachtet werden kann.

Syntax Injektionen bei MongoDB erkennen

Eine Shopping Webseite macht beispielsweise um die Produkte der Kategorie Wohnen anzuzeigen die folgende Anfrage:

https://insecure-website.com/product/lookup?category=wohnen
Die Applikation sendet dann eine JSON Anfrage zu der MongoDB Datenbank this.category == 'wohnen'. Um zu testen, ob dies Angreifbar ist können die folgenden Strings genutzt werden:
'"`{
;$Foo}
$Foo \xYZ
Manchmal ist es auch nötig den fuzz String nicht url enkodiert zu schicken, sondern als JSON: '\"`{\r;$Foo}\n$Foo\\xYZ\u0000

Erkennen, welche Zeichen verarbeitet werden

Um zu prüfen, welche Zeichen verarbeitet werden, können diese einzeln eingegeben werden. Ist die Antwort anders als bei den legitimen Antworten wurde die syntax kaputt gemacht. Der nächste Schritt ist es zu prüfen, ob es möglich ist Boolean Konditionen mit der NoSQL syntax zu beeinflussen. Dazu muss ein Statement gesendet werden, mit einer falschen Kondition ' && 0 && 'x und eins mit einer wahren Kondition && 1 &&'x. Auch kann dafür gesorgt werden, dass ein Ergebnis immer wahr ist, wie '||1||'. Dies wird in der MongoDB Abfrage zu this.catogory == 'wohnen'||'1'=='1'. Die Abfragesprache kann beendet werden mit anhängen eines Null Zeichens: category=wohnen'%00.

NoSQL Datenbanken nutzen oft Abfrageoperatoren, welche Wege zur Verfügung stellen zu spezifizieren, welche Daten in dem Ergebnis enthalten sein sollen. Sie können auch dazu führen, dass die Syntax verändert oder zerstört wird. - $where matcht daten, welche eine bestimmte Javascript Bedingung erfüllen - $ne Alle Werte, die nicht gleich eines spezifizierten Wertes sind. - $in Matcht alle Werte die in einem Array enthalten sind. - $regex Matcht Daten, welche einem regulären Ausdruck passen.

In JSON Nachrichten ist es möglich Abfrage Operatoren als verschachtelte Objekte einzusetzen. Beispielsweise {"username":"wiener"} wird zu {"username":{"$ne":"invalid"}}. Für URL basierte Eingaben können Abfrageoperatoren als URL parameter eingefügt werden. Beispielsweise username=wiener wird zu username[$ne]=invalid. Falls das nicht klappt gibt es die folgenden Möglichkeiten: 1. Ändern der Methode von GET zu POST 2. Ändern des Content-Type headers zu application/json 3. Hinzufügen von JSON zu dem Nachrichten Körper 4. Injizieren von Abfrageoperatoren im JSON Es kann auch helfen das Format der geschickten Daten zu ändern. Hierbei kann auch das Content Type Converter Plugin helfen.

Erkennen der Operator Injektion

Mit dem $in Operator ist es beispielsweise möglich mehrere Usernamen auszuprobieren: {"username":{"$in":["admin","administrator","superadmin"]},"password":{"$ne":""}} Auch kann an einen gültigen Request ein weiterer Parameter hinzugefügt werden. Beispielsweise der POST Request: {"username":"wiener", "$where":"0"}.

Extrahieren von Daten

Wird der $where Operator benutzt kann hier javascript injiziert werden. Beispielsweise wird die Abfrage {"$where":"this.username == 'admin'"} benutzt, ist es hier möglich admin' && this.password[0] == 'a' || 'a'=='b. Es kann auch der match() Operator benutzt werden. Das folgende kann zum beispiel genutzt werden, ob das Passwort Zahlen enthält: admin' && this.password.match(/\d/) || 'a'=='b. Es gibt auch Operatoren, wie den Regex Operator $regex, welcher kein Javascript zur ausführung benötigt. Auch timing basierte Angriffe sind möglich. So kann mit dem Payload {"$where": "sleep(5000)"} ein Timing Angriff durchgeführt werden.

admin'+function(x){var waitTill = new Date(new Date().getTime() + 5000);while((x.password[0]==="a") && waitTill > new Date()){};}(this)+'

`'``admin'+function(x){if(x.password[0]==="a"){sleep(5000)};}(this)+'`

Identifizieren von Feld namen

Da MongoDB semi strukturierte Daten verarbeitet, kann es nötig sein Valide Felt namen zu identifizieren. Beispielsweise zum feststellen, ob es in der MongoDB ein Passwort Feld gibt kann der folgende Payload genutzt werden:

admin' && this.password!='
Mit der keys() Funktion ist es ebenfalls möglich Feldnamen zu extrahieren, falls ein Operator injiziert wurde, mit dem es möglich ist Javascript auszuführen:
"$where":"Object.keys(this)[0].match('^.{0}a.*')"

NoSQL Basics

Diese Datenbanken sind Designt, um große Datenmengen zu handhaben, welche unstrukturiert oder semi strukturiert sind. Dadurch haben sie weniger relationale Beziehungen und Konsistenz Checks, als SQL. Dadurch haben sie auch einige Vorteile, wie Skalierbarkeit, Flexibilität und Performanz. Doch jede NoSQL Datenbank nutzt eine andere Abfragesprache. Dabei kann dies eine völlig frei entwickelte sein, oder eine Standardsprache wie XML oder JSON. Es gibt verschiedene Modelle die von NoSQL Sprachen benutzt werden. Einige der häufig benutzten Modelle sind: - Dokumenten Speicher: Speichern Daten flexibel in einem semi strukturierten Dokument. Dabei werden typischerweise Formate wie JSON, BSON und XML genutzt. Zur Abfrage wird eine API oder Abfragesprache benutzt. Beispiele hierfür sind MongoDB und Couchbase. - Key-value stores: Jedes Datenelement wird mit einem einzigartigen Schlüssel gespeichert. Beispiele hierfür sind Redis und Amazon DynamoDB.