今天“忙里偷闲”,一直在慢慢学习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()