关于程序员:Exchange-攻击链-CVE202126855CVE202127065-分析

41次阅读

共计 5526 个字符,预计需要花费 14 分钟才能阅读完成。

Exchange 攻打链 CVE-2021-26855&CVE-2021-27065 剖析

近期 Exchange 爆出了已被在朝利用的高危利用链,CVE-2021-26855 和 CVE-2021-27065 就是其中波及到的两个破绽。

CVE-2021-26855 是一个 SSRF 破绽,问题呈现在将客户端申请代理到服务端时,该破绽能够获取用户的 sid,实现了无交互攻打链中最重要的第一步。

CVE-2021-27065 则是一个写文件破绽,尽管不能齐全管制要写入的内容,然而文件名与门路能够任意设置。当咱们以 .aspx 为后缀创立文件,并在文件中插入一句话木马时,能够实现近程管制。

CVE-2021-26855

该破绽尽管只是个绕过平安验证的 ssrf,但堪称近 Exchange 近两年比拟重要的破绽之一。因为与以往那些隔靴搔痒的 RCE 相比(须要根底用户权限),该破绽提供了批量攻打的机会,这也是本次破绽影响这么大的起因。

上图为利用该破绽时收回的 post 申请包,其中有两处要害:

url: /ecp/target.js
Cookie: X-BEResource=name]@WIN-PDEIT81MJNQ.server.cd:444/autodiscover/autodiscover.xml?#~1941962753

咱们带着以下几个问题去剖析:

  1. /ecp/target.js 的 url 代表着什么?是否非此不可?
  2. Cookie 中的 X-BEResource 代表什么?其值是如何结构的?为什么要这样结构?

要想答复下面的问题,咱们必须找到 target.js 是如何解决申请的。通过搜寻,咱们能够判定 target.js 不是一个实体文件,而是一个虚构门路,那么解决该申请的代码位于哪里?

通过调试,咱们能够得出以下调用过程:

Microsoft.Exchange.FrontEndHttpProxy.dll!Microsoft.Exchange.HttpProxy.ProxyModule.OnPostAuthorizeRequest()

首先进入了 ProxyModule 的 OnPostAuthorizeRequest() 函数,从名字来看,该函数用于对 post 申请进行平安验证。该函数又调用了 ProxyModule.OnPostAuthorizeInternal() 函数。

在 OnPostAuthorizeInternal() 函数里,调用了 ProxyModule.SelectHandlerForUnauthenticatedRequest() 办法,该办法用于对 未平安验证的申请 抉择 Handler (即处理函数),后续会调用该 Handler 类的 BeginProcessRequest 函数进一步解决申请。咱们跟进去看他是依据什么抉择 Hanlder 的。

能够看到,对于不同的 ProtocolType,应用不同的 RequestPathParser 函数进行判断,并生成了不同的 httpHandler。这个 ProtocolType 不分明来自哪里,应该是与咱们申请的应用程序集无关,以 /ecp/target.js 进行申请,则 ProtocolType 就是 Ecp。

在 ProtocolType 为 Ecp 的状况下,又进行了屡次判断,其中最要害的就是 BEResourceRequestHandler.CanHandle() 函数。如果该函数判断为真,则应用 BEResourceRequestHandler 作为申请的 Handler。咱们跟入 CanHandler 看一下。

能够看到,判断因素有两个,Cookies 中要有 X-BEResource 字段,且申请门路须要以 .axc 或 .css 或 .js 或 其余一些后缀进行结尾。只有这两个条件满足,才会抉择 BEResourceRequestHandler 作为申请 Handler。

该 Handler 会被设置给 context.RemapHandlerInstance 属性,并最终被赋值给了 context.Handler。

而后便会调用 Handler.BeginProcessRequest() 函数来对申请进行进一步解决。因为 BEResourceRequestHandler 继承与 ProxyRequestHandler,所以最终调用了 ProxyRequestHandler.BeginProcessRequest() 函数。

ProxyRequestHandler 类的作用应是将指向 FrontEnd 的 Http 申请,转发给 BackEnd。

在 ProxyRequestHandler.BeginProcessRequest() 函数里,创立新线程调用了 ProxyRequestHandler.BeginCalculateTargetBackEnd() 函数,其作用是依据 Cookie 中的 X-BEResource 字段来判断与生成指向 BackEnd 的指标 url。

BeginCalculateTargetBackEnd() 调用了 ProxyRequestHandler.InternalBeginCalculateTargetBackEnd() 函数。

InternalBeginCalculateTargetBackEnd() 调用了 BEResourceRequestHandler.ResolveAnchorMailbox() 函数。

如下:

能够看到,该函数间接提取出 Cookie 中的 X-BEResource 字段,并用其生成 BackEndServe 实例。查看 BackEndServer.FromString() 函数,会发现它间接 根据 ‘\~’ 符号切割 beresourceCookie 字符串,前半段作为 fqdn,后半段作为 version。所谓 fqdn 既是 “ 全限定域名 ”,而邮件服务器中的 fqdn 相似于这样: tom@404.com。这个 version 指的是 BackEndServer Version。

之后:

InternalBeginCalculateTargetBackEnd() 创立新线程调用了 ProxyRequestHandler.OnCalculateTargetBackEndCompleted() 函数。

OnCalculateTargetBackEndCompleted() 函数又调用了 BEResourceRequestHandler.InternalOnCalculateTargetBackEndCompleted() 函数。

。。。(省略好几个调用)

进入了 ProxyRequestHandler.BeginProxyRequest() 函数,该函数开始解决代理申请。

BeginProxyRequest 调用 GetTargetBackEndServerUrl() 来获取 之前给你 BackEnd 的 url。

这段代码实例化了一个 UrlBuilder 类,波及三个要害属性,Scheme、Host 和 Port。

Schema 被设置为 https;

Host 取自于 BackEndServer.Fqdn,看一下 Host.Set() :

如果 fqdn 中蕴含 ‘:’ 且不是以 ‘[‘ 结尾,则应用 ‘[]’ 来包住 fqdn。’:’ 用于批示指标 BackEnd Server 端口。

Port 根据 BackEndServer.Version。如果该 Version 小于 E15MinVersion(即 1941962752),则指定 this.ProxyToDownLevel 为 true, 且将 port 将指定为 443。

还记得咱们第一张图里的 X-BeResource 吗:

name]@WIN-PDEIT81MJNQ.server.cd:444/autodiscover/autodiscover.xml?#~1941962753

其对应的三个字段下图:

但在给 Post 字段赋值完后会主动进行从新解析,变成上面这样:

我没有找到这个主动从新解析的原理。

在将下面三个属性赋值后,该函数就返回了 clientUrlForProxy.Uri,查看 Uri 的 get 办法:

调用了 UriBuilder.ToString() 办法来获得最终的 指向 BackEnd 的指标 url。

在 ToString() 中对各个参数进行拼接,造成 url。

至此咱们就取得了一个指向 BackEnd 的 url,相似上面这样:

https://[name]@win-pdeit81mjnq.server.cd:444/autodiscover/autodiscover.xml?#]:443/ecp/target.js

通过下面的剖析,咱们答复了一开始的问题:

/ecp/target.js 不是必须的,它能够是其余的门路 /ecp/xxxxxxxx.png

X-BEResource 用于代理申请,其本来格局应该是 [fqdn]\~BackEndServerVersion

BackEndServerVersion 应该大于 1941962752,‘#’用于在有 url 申请参数时分隔参数。

而且咱们晓得了 X -BEResource 实际上齐全不须要 ‘]’ 去闭合中括号,咱们齐全能够间接用‘[]’来将 name 括起来,比方上面这样:

[name]@WIN-PDEIT81MJNQ.server.cd:444/autodiscover/autodiscover.xml?#~1941962754
[name]@WIN-PDEIT81MJNQ.server.cd:444/autodiscover/autodiscover.xml?&~1941962755

甚至如果你不须要特地指定端口号,你还能够应用上面的值:

WIN-PDEIT81MJNQ.server.cd/autodiscover/autodiscover.xml?#~1941962753

这样会导致以 443 端口拜访 https://win-pdeit81mjnq.serve…:444/ecp/target.js

不过大多数的 BackEnd 站点须要用 444 端口拜访,不指定端口可能会导致拜访失败。

之后 ProxyRequestHandler 就会向指标 BackEnd Url 发动申请,将来自客户端的申请代理给服务端。

须要留神的是,两头代理在 BeginProxyRequest() 函数中 调用 CreateServerRequest() 来创立指向服务端的申请,而该函数会间接调用 GenerateKerberosAuthHeader() 函数来 创立 Kerberos 认证头部。这也是两头代理可能拜访 BackEnd Server 的一个重要起因。

不过还有一点咱们没有解决,就是对于 BackEndServer Version 的问题。后面咱们将其设置为 1941962753 时,说是要大于 E15MinVersion。而且我看其余的剖析中也说到,如果不 设置大于,会导致前面进行平安验证,进而导致失败。但我在 Exchange 2016 上察看,AddDownLevelProxyHeaders() 函数的作用大抵是面对低版本的 Server , 增加额定的申请头部,如果没有平安验证,则不增加。并没有呈现报错的状况。

起初我在 Exchange 2013 上测试,发现了端倪:

在 Exchange2013 上代码稍有不同,这里没有间接返回,而是会进入 if 分支,调用 GetSecurityIdentifier()。而咱们的申请是未平安验证的,identity 中 User 属性为 Null。所以当 GetSecurityIdentifier() 返回 identity.User,并调用其 ToString() 办法时,会间接报错:” 未将对象援用设置到对象的实例 ”。

CVE-2021-27065

该破绽是一个写文件破绽,其原理很简略。

在 ecp 治理界面找到 对于虚拟目录的配置窗口:

能够看到,在 OAB VirtualDirectory 的配置界面有多个字段能够配置,我么将其中的 ExternUrl 中设置为一句话木马。

而后抉择重置 虚拟目录:

能够看到,页面中说明会将以后设置存储在文件中,门路由咱们本人设置,尽管示例给的文件名后缀是 .txt,但实际上咱们能够将其设置为 .aspx。咱们抉择将设置保留在 C:inetpubwwwrootmyshell.aspx,这样做会导致当咱们通过网络申请拜访该文件时,服务器会以 aspx 的格局对其进行解析。

留神:如果是从网页进行重置,须要应用 unc 门路,须要提前将指标文件夹进行网络共享。然而 如果通过 /ecp/DDI/DDIService.svc/SetObject 接口进行重置,则能够间接应用物理门路,会主动将对应文件夹进行网络共享。

抉择重置后,咱们查看对应地位:

能够看到,OAB VirtualDirectory 的配置信息曾经被写入到指标地位,这其中藏着一句话木马,而且以 .aspx 为文件后缀。

该破绽的官网补丁也很简略,间接强制要求重置配置文件时,新建的文件必须以 .txt 为后缀。

附录

https://www.praetorian.com/blog/reproducing-proxylogon-exploit/

https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/

https://www.volexity.com/blog/2021/03/02/active-exploitation-of-microsoft-exchange-zero-day-vulnerabilities/

本文由 Seebug Paper 公布,如需转载请注明起源。本文地址:https://paper.seebug.org/1501/

正文完
 0