CVE-2021-26855 分析学习

 

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

img

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()函数。

image-20211011214501352

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

image-20211011214650297

BEResourceRequestHandler类用于处理向后端进行资源型请求,直接分析CanHandle()函数:

internal static bool CanHandle(HttpRequest httpRequest)
{
	return !string.IsNullOrEmpty(BEResourceRequestHandler.GetBEResouceCookie(httpRequest)) && BEResourceRequestHandler.IsResourceRequest(httpRequest.Url.LocalPath);
}

发现需要满足两个条件可以使CanHandle返回True

  1. BEResourceRequestHandler.GetBEResouceCookie()

  2. 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()

image-20210816135145058

发现如果请求的文件是这些类型的话就满足,换句话说请求的文件为资源型就可以。

​ 再回到OnPostAuthorizeInternal()函数,现在已经完成了Handler的分配,下一步由于BEResourceRequestHandler继承于ProxyRequestHandler因此执行((ProxyRequestHandler)httpHandler).Run(context)

image-20210816140437617

该Handler被设置为context.RemapHandlerInstance属性

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

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

BeginProcessRequest()函数内创建新线程调用ProxyRequestHandler.BeginCalculateTargetBackEnd()函数

其作用是根据 Cookie 中的 X-BEResource 字段来判断与生成指向 BackEnd 的目标url

image-20210818141338125

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

image-20210818141555159

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

image-20211011220003213

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

image-20210816170450165image-20210816170619948

回到InternalBeginCalculateTargetBackEnd()中,又创建线程调用了OnCalculateTargetBackEndCompleted()

ThreadPool.QueueUserWorkItem(new WaitCallback(this.OnCalculateTargetBackEndCompleted), new TargetCalculationCallbackBeacon(this.AnchoredRoutingTarget));

接着调用InternalOnCalculateTargetBackEndCompleted()

image-20211011220457049

image-20211011220621415

最终到BeginProxyRequestOrRecalculate(),最后调用ProxyRequestHandler.BeginProxyRequest()

ThreadPool.QueueUserWorkItem(new WaitCallback(this.BeginProxyRequest));

其中调用了GetTargetBackEndServerUrl()

image-20211011220958841

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

image-20211011221148480

1.设置HTTPS

2.Host即FQDN,xxxx.com

3.如果端口小于Server.E15MinVersion的值,端口会被设置为443

image-20211011221311037

获取完后端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()