影响版本

CVE-2021-34473 & CVE-2021-34523

  • Exchange Server 2013 < Apr21SU
  • Exchange Server 2016 < Apr21SU < CU21
  • Exchange Server 2019 < Apr21SU < CU10

CVE-2021-31207

  • Exchange Server 2013 < May21SU
  • Exchange Server 2016 < May21SU < CU21
  • Exchange Server 2019 < May21SU < CU10

漏洞分析

CVE-2021-34473

The specific flaw exists within the Autodiscover service. The issue results from the lack of proper validation of URI prior to accessing resources. An attacker can leverage this in conjunction with other vulnerabilities to execute arbitrary code in the context of SYSTEM.

ZDI通告说Autodiscover服务没有验证好URL,结合其它漏洞可以SYSTEM身份任意代码执行。看到在CAS中也继承自ProxyRequestHandler类,且会由SelectHandlerForUnauthenticatedRequest方法调用:

与CVE-2021-26855很相似,猜测CAS的/autodiscover接口存在SSRF。之前ProxyLogon漏洞分析中,我们梳理了几个关键的方法调用栈,知道可以利用EcpProxyRequestHandlerGetClientUrlForProxy方法调用的UriBuilder类特性造成SSRF。而AutodiscoverProxyRequestHandler类的父类EwsAutodiscoverProxyRequestHandler也实现了GetClientUrlForProxy方法:

RemoveExplicitLogonFromUrlAbsoluteUri方法用于剔除absoluteUri中的this.explicitLogonAddress

所以也许能够构造一个满足所有前置判断的URL,让它在经过剔除后变成其它敏感路径?首先跟进this.isExplicitLogonRequest属性,看到满足if的条件时会置为true,并将text变量赋值给会被剔除的this.explicitLogonAddress属性:

1
2
3
4
5
6
7
8
9
10
public static bool TryGetNormalizedExplicitLogonAddress(string explicitLogonAddressout string normalizedAddress)
{
normalizedAddress = null;
if (string.IsNullOrEmpty(explicitLogonAddress))
{
return false;
}
normalizedAddress = explicitLogonAddress.Replace("...", ".@").Replace("..", "@");
return true;
}

TryGetNormalizedExplicitLogonAddressIsValidSmtpAddress传入合法邮箱格式就能满足。经过RequestPathParser.IsAutodiscoverV2PreviewRequest的判断后,text变量就会从GPCS(GET|POST|Cookie|Server)中取Email参数的值,继续跟进:

存在指定路径/autodiscover.json就可以满足判断,至此想要被剔除的部分就可控了。之后的流程就跟ProxyLogon类似,会由CAS向后端发起经过Kerberos认证的请求:

CVE-2021-34523

在ProxyLogon漏洞分析中,我们已经知道ShouldCopyHeaderToServerRequest方法会过滤一些自定义请求头,其中就包括用于校验用户身份的X-CommonAccessToken

在调用BackendRehydrationModule校验身份前,会先由RemotePowershellBackendCmdletProxyModuleOnAuthenticateRequest方法对commonAccessToken进行判断和处理:

当不存在X-CommonAccessToken头时会进一步调用CommonAccessTokenFromUrl方法(屏幕不够大没截到,就是上图第二个断点那里):

可见X-CommonAccessToken头可由X-Rps-CAT参数替代且不在过滤范围内,当能获取/构造所需的数据时,就可以管理员身份访问受限的后端接口。史辛泽师傅分析了Microsoft.Exchange.NetAuthorizationWindowsAccessToken的相关代码,由Python实现了通过用户名与SID生成Token的函数(详见参考链接)。

将ProxyLogon利用流程123步套用在CVE-2021-34473上,获取SID后本地生成CommonAccessToken,实现以管理员身份访问后端接口:

CVE-2021-31207

Microsoft.Exchange.Management.Migration中有一处补丁变动:

1
2
3
4
5
6
private static readonly string[] AllowedPSTFileExtensions = new string[]
{
".pst",
".eml",
".ost"
};

看起来是限制了从邮箱导出时的文件后缀名,搜索官方文档基本确定对应New-MailboxExportRequest这个cmdlet,用于将邮箱内容导出为pst文件。

PST编码算法和转换表是一套字节置换规则。转换表看似是一张表,其实是以每256个字节为分隔的三张表。导出邮件进行编码时,由相应字节(0x00-0xff)对应到数组表中偏移(0-255),将字节替换为表中偏移对应的值。

想要导出时内容为“原始数据”而非PST编码时,可以根据第一张表(mpbbCrypt)的字节置换规则,查询“原始数据”的字节在表中对应值的偏移,逆推得到导出前要构造的邮件内容。但微软本身也要解码PST,所以不用这么麻烦去逆推,因为第三张表(mpbbCrypt + 512)其实就是这个逆推的转换表。只不过我们此时变成了用解码表规则编码,这样Exchange在导出邮件对数据进行编码时,实际就变成了PST解码(妙啊,有点异或那味)

对于写WebShell而言,接下来可以通过SMTP发送邮件或是EWS接口操作某个账户邮箱,将构造好的Payload作为附件引入目标邮箱。随后基于WinRM协议与/powershell接口通信,赋予administrator用户邮箱导出权限,并利用UNC路径将目标邮件导出至Web服务目录:

1
2
3
New-ManagementRoleAssignment -Role "Mailbox Import Export" -User "administrator"

New-MailboxExportRequest -Mailbox SomeUser -IncludeFolders "#Inbox#" -FilePath \\127.0.0.1\c$\somepath\api.aspx

除了写WebShell,也可以考虑利用其它写文件GetShell的方式,毕竟现在不像27065一样有长度和格式限制。也不要局限于写文件上,ExchangePowerShell还有相当多的cmdlet可用:D

参考链接

(Pwn2Own) Microsoft Exchange Server Autodiscover Server Side Request Forgery Authentication Bypass Vulnerability

Reproducing The ProxyShell Pwn2Own Exploit

Exchange ProxyShell漏洞复现及分析