Exchange ProxyShell 漏洞分析

前言:Exchange ProxyShell 漏洞分析笔记

参考文章:https://www.4hou.com/posts/AO03
参考文章:https://3gstudent.github.io/ProxyShell利用分析1-CVE-2021-34473
参考文章:https://3gstudent.github.io/ProxyShell利用分析2-CVE-2021-34523
参考文章:https://3gstudent.github.io/ProxyShell利用分析3-添加用户和文件写入
参考文章:https://blog.riskivy.com/exchange-proxyshell漏洞复现及分析/
参考文章:https://www.blackhat.com/us-21/briefings/schedule/index.html#proxylogon-is-just-the-tip-of-the-iceberg-a-new-attack-surface-on-microsoft-exchange-server-23442

ProxyShell

ProxyShell是利用了Exchange服务器对于路径的不准确过滤导致的路径混淆生成的SSRF,进而使攻击者通过访问PowerShell端点。而在PowerShell端点可以利用Remote PowerShell来将邮件信息打包到外部文件,而攻击者可以通过构造恶意邮件内容,利用文件写入写出webshell,从而达成命令执行。

  • CVE-2021-34473 - Pre-auth Path Confusion leads to ACL Bypass

  • CVE-2021-34523 - Elevation of Privilege on Exchange PowerShell Backend

  • CVE-2021-31207 - Post-auth Arbitrary-File-Write leads to RCE

影响版本

Microsoft Exchange Server 2019 Cumulative Update 9
Microsoft Exchange Server 2019 Cumulative Update 8
Microsoft Exchange Server 2016 Cumulative Update 20
Microsoft Exchange Server 2016 Cumulative Update 19
Microsoft Exchange Server 2016 Cumulative Update 18
Microsoft Exchange Server 2013 Cumulative Update 23

路径混淆导致的SSRF漏洞

这边测试访问,结果如下所示 https://192.168.75.155/autodiscover/autodiscover.json?@foo.com/mapi/nspi/?&Email=autodiscover/autodiscover.json%3F@foo.com

这边来看来到Microsoft.Exchange.FrontEndHttpProxy.dll中进行观察

一般实现了ProxyRequestHandler对象的入口点方法ResolveAnchorMailbox,这边的EwsAutodiscoverProxyRequestHandler类也是一样,入口点也是ResolveAnchorMailbox

接着这边可以看到如果请求的路径是/autodiscover.json结尾的话,那么取请求路径中的Email参数,但是这边的话需要满足IsAutodiscoverV2PreviewRequest为true的条件下

if (RequestPathParser.IsAutodiscoverV2PreviewRequest(base.ClientRequest.Url.AbsolutePath))
{
  text = base.ClientRequest.Params["Email"];
}

这边跟进IsAutodiscoverV2PreviewRequest,可以看到先进行判断IsAutodiscoverV2PreviewRequest是否以/autodiscover.json结尾

然后就是通过 base.ClientRequest.Params["Email"] 获取Email参数

这里还可以注意下,这里是通过Params获取的,而这里的Params包含了Get请求参数和表单请求参数以及Cookie字段参数,ServerVariables参数如下所示,如果有waf还可以从这方面入手来进行尝试绕过

explicitLogonAddress变量拿到的就是autodiscover/autodiscover.json?@foo.com

关于proxyshell中ssrf的漏洞位置还是在GetTargetBackEndServerUrl这个地方,但是已经不再是ProxyRequestHandler中的GetClientUrlForProxy,而是EwsAutodiscoverProxyRequestHandler中的GetClientUrlForProxy方法

这边还是一样的发起请求,先来到ProxyRequestHandler的GetTargetBackEndServerUrl方法,其中可以看到会调用GetClientUrlForProxy

但是这边是EwsAutodiscoverProxyRequestHandler(ProxyRequestHandler的子类),所以会回到EwsAutodiscoverProxyRequestHandler中的GetClientUrlForProxy中

在GetClientUrlForProxy中不会直接返回对应的请求地址,而是还会做一次路径归一化RemoveExplicitLogonFromUrlAbsoluteUri操作,如下图所示

这边跟进到RemoveExplicitLogonFromUrlAbsoluteUri进行观察,这里比较有意思的点就是int num = absoluteUri.IndexOf(text);,而这边的text是 /autodiscover/autodiscover.json?@foo.com

public static string RemoveExplicitLogonFromUrlAbsoluteUri(string absoluteUri, string explicitLogonAddress)
{
	ArgumentValidator.ThrowIfNull("absoluteUri", absoluteUri);
	ArgumentValidator.ThrowIfNull("explicitLogonAddress", explicitLogonAddress);
	string text = "/" + explicitLogonAddress;
	int num = absoluteUri.IndexOf(text);
	if (num != -1)
	{
		return absoluteUri.Substring(0, num) + absoluteUri.Substring(num + text.Length);
	}
	return absoluteUri;
}

下面的代码最终得到的结果值是23,那么最终通过substring截取到的就是 https://192.168.75.155//mapi/nspi/?&Email=autodiscover/autodiscover.json%3F@foo.com 这一部分

"https://192.168.75.155/autodiscover/autodiscover.json?@foo.com/mapi/nspi/?&Email=autodiscover/autodiscover.json%3f@foo.com".IndexOf("autodiscover/autodiscover.json?@foo.com")

下面如下图所示,请求转发之后就来到了444端口的后端处理模块的/mapi/nspi/接口的请求,最终导致权限绕过

CVE-2021-34523 Exchange PowerShell Remoting命令执行攻击链

这边大家需要知道的,调试Exchange Powershell的接口的时候在w3wp中下的MSExchangePowerShellAppPool

Exchange PowerShell远程处理是一个命令行接口,用于实现Exchange任务的自动化,就比如导出用户邮件的信息等等,如下图所示

这里先看下为什么说可以利用Exchange PowerShell Remoting,这边可以跟到如下文件中,如下图所示

Microsoft.Exchange.Configuration.RemotePowershellBackendCmdletProxyModule.dll

-> Microsoft.Exchange.Configuration.RemotePowershellBackendCmdletProxy
-> RemotePowershellBackendCmdletProxyModule
-> CommonAccessToken CommonAccessTokenFromUrl

请求到后端的Exchange PowerShell的接口的时候会先进行身份验证,这边身份验证就是通过X-Rps-CAT参数来进行获取的

获取完X-Rps-CAT参数值之后就会将先通过Uri.UnescapeDataString进行url解码,然后将其作为参数传入到CommonAccessToken.Deserialize中

这边跟进到CommonAccessToken.Deserialize中进行观察,先通过Convert.FromBase64String进行base64解码一次

然后接着就是这边调用new CommonAccessToken(memoryStream),将其作为CommonAccessToken的构造函数

CommonAccessToken中调用了Deserialize,继续跟进

先是以读取VTC三种形式进行读取,ReadAndValidateFieldType是用来判断是不是对应的V或者C或者T的类型,如果是的话则通过BinaryRead进行读取数据

  • V对应的是AuthorizationStrings.MissingVersion

  • T对应的是 AuthorizationStrings.MissingTokenType

  • C对应的是AuthorizationStrings.MissingIsCompressed

然后下面的就是对该剩下的字节数组进行操作

这边的话跟进WindowsAccessToken的DeserializeFromToken方法中继续观察,同样的读取了ALU三个类型

后面的大家可以继续跟下,这边就不继续了,这边来一张这些VTC格式下内存中的结构,如下图所示,可以看到对应的VTC以及ALU等字符的存在

通过观察都可以一一对照

我这里就拿三好学生师傅中的结论来说明,最终拿到的数据的格式是如下所示

V + 版本(\x01\x00) + T + 类型长度(\x07) + 类型(Windows) + C + \x00 + A + 认证类型长度(\x08) + 认证类型(kerberos) + L + 用户名长度 + 用户名 + U + sid长度 + sid + G + 组个数(4字节,little endian,\x06\x00\x00\x00) + 分隔符\x07\x00\x00\x00 + 组1长度 + 组1 + 分隔符\x07\x00\x00\x00 + 组2长度 + 组2 + 分隔符\x07\x00\x00\x00 + 组3长度 + 组3 + 分隔符\x07\x00\x00\x00 + 组4长度 + 组4 + 分隔符\x07\x00\x00\x00 + 组5长度 + 组5 + 分隔符\x07\x00\x00\x00 + 组6长度 + 组6 + E + \x00\x00\x00\x00

接着这边X-Rps-CAT伪造CommonAccessToken实现任意用户,构造流程参考三好学生师傅的,如下图所示

问题1

为什么不能直接用ssrf直接进行攻击?

默认权限是system,如果使用system用户的身份使用此服务的话就会失败,原因是system用户是没有邮箱的,而想要使用exchange的服务的话就需要授权用户才可以

问题2

为什么这边是利用的是Exchange PowerShell Remoting 其他后端处理模块就不行吗?

首先要知道下exchange有个ShouldCopyHeaderToServerRequest,在前端每次请求完之后在进行转发给后端的时候都会进行处理一次

如果前端此时header头就已经带有X-CommonAccessToken或者图中的所标识的字段,那么此时这个请求将默认不会被转发到后端

对应的还有一个是AddProtocolSpecificHeadersToServerRequest,用来对专属X-CommonAccessToken字段进行处理

  • BackendRehydrationModule后端接受请求的入口模块,入口点是OnAuthenticateRequest方法,其中会调用ProcessRequest,然后来后端手动来解析X-CommonAccessToken

最终的话我们就知道如果想要用ssrf进行成功利用的话,想要通过认证必须传递正确的CommonAccessToken给Backend。

但是现在的情况就是无法通过设置X-CommonAccessToken间接设置CommonAccessToken来伪造自己的身份,因为有黑名单(无法直接传递X-CommonAccessToken这些字段)的存在,因此exchange无法通过ssrf将X-CommonAccessToken的值带给backend,因此无法通过这种方法进行身份验证。

而在这里RemotePowershellBackendCmdletProxyModule这个后端端点就满足我们的需要,它会自己在后端解析X-Rps-CAT字段(我们自己可控)来生成X-CommonAccessToken,从而进行相关的授权用户的操作

CVE-2021-31207 认证后任意文件写入/用户添加

这边可以来看exchange powershell中提供的功能,这里输入get-command,exchange中提供了很多功能

其中有一个功能可以将用户的邮箱导出到指定路径,这个功能是New-MailboxExportRequest

但是New-MailboxExportRequest导出的前提是当前是以高权限来请求调用才可以

其中还有一个用户添加,这个功能是New-Mailbox,同样前提当前是以高权限来请求调用请求才可以

漏洞修复

补丁下载地址:https://www.catalog.update.microsoft.com/Search.aspx?q=Security Update For Exchange Server 2013

从上面的漏洞分析可以知道,如果我们的请求是IsAutodiscoverV2Request(),它将从URI中删除explicitLogonAddress字段并构建URI,这样的操作最终造成SSRF漏洞,所以这边官方修复的方法就是直接把IsAutodiscoverV2Request的代码片段删除,可以看到下面的补丁diff情况

posted @ 2023-04-01 11:32  zpchcbd  阅读(740)  评论(0编辑  收藏  举报