今天“忙里偷闲”,一直在慢慢学习Exchange,把很早之前写了一半的东西补充好发出来 -0-

CVE-2021-26855,是一个SSRF漏洞,问题出在前后端解析Cookie时,可以成功访问后端
这个漏洞常与CVE-2021-27065一起使用,称为ProxyLogon
CVE-2021-26855分析
关于这个SSRF漏洞的触发请求大致如下
POST /ecp/3.jpg HTTP/1.1
Host: 10.0.19.20
Cookie: X-BEResource=EXCHANGE2016.pig.com:444/autodiscover/autodiscover.xml?a=~194206252
首先进入Microsoft.Exchange.FrontEndHttpProxy.dll中,命名空间为Microsoft.Exchange.HttpProxy内的ProxyModule类中的OnPostAuthorizeRequest()函数,接着此函数又会调用OnPostAuthorizeInternal()函数。

OnPostAuthorizeInternal()函数里调用SelectHandlerForUnauthenticatedRequest()函数,此方法为未经验证的请求决定其处理Handler,当请求类型为Ecp时,并且BEResourceRequestHandler.CanHandle()函数为True时,将会创建BEResourceRequestHandler类的实例做为Handler。

BEResourceRequestHandler类用于处理向后端进行资源型请求,直接分析CanHandle()函数:
internal static bool CanHandle(HttpRequest httpRequest)
{
return !string.IsNullOrEmpty(BEResourceRequestHandler.GetBEResouceCookie(httpRequest)) && BEResourceRequestHandler.IsResourceRequest(httpRequest.Url.LocalPath);
}
发现需要满足两个条件可以使CanHandle返回True
-
BEResourceRequestHandler.GetBEResouceCookie()
-
BEResourceRequestHandler.IsResourceRequest()
首先看第一个:
private static string GetBEResouceCookie(HttpRequest **httpRequest**)
{
string result = null;
HttpCookie httpCookie = **httpRequest**.Cookies[Constants.BEResource];
if (httpCookie != null)
{
result = httpCookie.Value;
}
return result;
}
//public static readonly string BEResource = "X-BEResource";
如果Cookie中存在的X-BEResource字段不为空的话,就满足。
第二个:
internal static bool IsResourceRequest(string **localPath**)
{
return RequestPathParser.IsResourceRequest(**localPath**);
}
继续跟进RequestPathParser.IsResourceRequest()

发现如果请求的文件是这些类型的话就满足,换句话说请求的文件为资源型就可以。
再回到OnPostAuthorizeInternal()函数,现在已经完成了Handler的分配,下一步由于BEResourceRequestHandler继承于ProxyRequestHandler因此执行((ProxyRequestHandler)httpHandler).Run(context)

该Handler被设置为context.RemapHandlerInstance属性
然后就会调用Handler.BeginProcessRequest()函数对申请进行进一步解决,因为BEResourceRequestHandler 继承与 ProxyRequestHandler,所以最终调用了 ProxyRequestHandler.BeginProcessRequest() 函数
ProxyRequestHandler 类的作用应是将指向 FrontEnd 的Http 申请,转发给 BackEnd
BeginProcessRequest()函数内创建新线程调用ProxyRequestHandler.BeginCalculateTargetBackEnd()函数
其作用是根据 Cookie 中的 X-BEResource 字段来判断与生成指向 BackEnd 的目标url

接着上述函数又调用了ProxyRequestHandler.InternalBeginCalculateTargetBackEnd()函数

InternalBeginCalculateTargetBackEnd()又调用了ProxyRequestHandler.ResolveAnchorMailbox()

ResolveAnchorMailbox()函数中,BackEndServer.FromString()函数会对X-BEResource字段内的Cookie做处理,以”~”号做为分隔,提取两段字符串分别赋值给FQDN和Version变量


回到InternalBeginCalculateTargetBackEnd()中,又创建线程调用了OnCalculateTargetBackEndCompleted()
ThreadPool.QueueUserWorkItem(new WaitCallback(this.OnCalculateTargetBackEndCompleted), new TargetCalculationCallbackBeacon(this.AnchoredRoutingTarget));
接着调用InternalOnCalculateTargetBackEndCompleted()


最终到BeginProxyRequestOrRecalculate(),最后调用ProxyRequestHandler.BeginProxyRequest()
ThreadPool.QueueUserWorkItem(new WaitCallback(this.BeginProxyRequest));
其中调用了GetTargetBackEndServerUrl()

GetTargetBackEndServerUrl()中获取了发送给后端的URL:

1.设置HTTPS
2.Host即FQDN,xxxx.com
3.如果端口小于Server.E15MinVersion的值,端口会被设置为443

获取完后端URL后最后赋值给uri就执行CreateServerRequest()
需要注意的是,中间代理在 BeginProxyRequest() 函数中 调用 CreateServerRequest() 来创建指向服务端的请求,而该函数会间接调用 GenerateKerberosAuthHeader() 函数来 创建Kerberos 认证头部。这也是中间代理能够访问BackEnd Server 的一个重要原因。
上述这个坑待填…
调用堆栈
Microsoft.Exchange.FrontEndHttpProxy.dll!
1.
ProxyModule.OnPostAuthorizeRequest()
-->FbaModule.OnPostAuthorizeInternal()
-->ProxyModule.SelectHandlerForUnauthenticatedRequest()
-->BEResourceRequestHandler.BEResourceRequestHandler()
2.
BeginProcessRequest()
-->BeginCalculateTargetBackEnd()
-->InternalBeginCalculateTargetBackEnd()
-->OnCalculateTargetBackEndCompleted()
-->InternalOnCalculateTargetBackEndCompleted()
-->BeginValidateBackendServerCacheOrProxyOrRecalculate()
-->BeginProxyRequestOrRecalculate()
-->BeginProxyRequest()
-->CreateServerRequest()