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
this.category == 'wohnen'.
Um zu testen, ob dies Angreifbar ist können die folgenden Strings genutzt werden:
'"`{
;$Foo}
$Foo \xYZ
'\"`{\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!='
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.