ProxyShell漏洞分析
文章目录
影响版本
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漏洞分析中,我们梳理了几个关键的方法调用栈,知道可以利用EcpProxyRequestHandler
类GetClientUrlForProxy
方法调用的UriBuilder
类特性造成SSRF。而AutodiscoverProxyRequestHandler
类的父类EwsAutodiscoverProxyRequestHandler
也实现了GetClientUrlForProxy
方法:
RemoveExplicitLogonFromUrlAbsoluteUri
方法用于剔除absoluteUri
中的this.explicitLogonAddress
:
所以也许能够构造一个满足所有前置判断的URL,让它在经过剔除后变成其它敏感路径?首先跟进this.isExplicitLogonRequest
属性,看到满足if的条件时会置为true,并将text
变量赋值给会被剔除的this.explicitLogonAddress
属性:
1 | public static bool TryGetNormalizedExplicitLogonAddress(string explicitLogonAddressout string normalizedAddress) |
TryGetNormalizedExplicitLogonAddress
和IsValidSmtpAddress
传入合法邮箱格式就能满足。经过RequestPathParser.IsAutodiscoverV2PreviewRequest
的判断后,text
变量就会从GPCS(GET|POST|Cookie|Server)中取Email
参数的值,继续跟进:
存在指定路径/autodiscover.json
就可以满足判断,至此想要被剔除的部分就可控了。之后的流程就跟ProxyLogon类似,会由CAS向后端发起经过Kerberos认证的请求:
CVE-2021-34523
在ProxyLogon漏洞分析中,我们已经知道ShouldCopyHeaderToServerRequest
方法会过滤一些自定义请求头,其中就包括用于校验用户身份的X-CommonAccessToken
。
在调用BackendRehydrationModule
校验身份前,会先由RemotePowershellBackendCmdletProxyModule
的OnAuthenticateRequest
方法对commonAccessToken
进行判断和处理:
当不存在X-CommonAccessToken
头时会进一步调用CommonAccessTokenFromUrl
方法(屏幕不够大没截到,就是上图第二个断点那里):
可见X-CommonAccessToken
头可由X-Rps-CAT
参数替代且不在过滤范围内,当能获取/构造所需的数据时,就可以管理员身份访问受限的后端接口。史辛泽师傅分析了Microsoft.Exchange.Net
中Authorization
与WindowsAccessToken
的相关代码,由Python实现了通过用户名与SID生成Token的函数(详见参考链接)。
将ProxyLogon利用流程123步套用在CVE-2021-34473上,获取SID后本地生成CommonAccessToken
,实现以管理员身份访问后端接口:
CVE-2021-31207
Microsoft.Exchange.Management.Migration
中有一处补丁变动:
1 | private static readonly string[] AllowedPSTFileExtensions = new string[] |
看起来是限制了从邮箱导出时的文件后缀名,搜索官方文档基本确定对应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 | New-ManagementRoleAssignment -Role "Mailbox Import Export" -User "administrator" |
除了写WebShell,也可以考虑利用其它写文件GetShell的方式,毕竟现在不像27065一样有长度和格式限制。也不要局限于写文件上,ExchangePowerShell还有相当多的cmdlet可用:D