Exchange ProxyLogon漏洞分析

  • A+
所属分类:安全文章

编者注:本文仅供学习研究,严禁从事非法活动,任何后果由使用者本人负责。


组件架构


Exchange不同版本的组件架构并不相同,但总体上可以将其分为核心的邮箱服务器角色(Mailbox Role)和可选的边缘传输角色(Edge Transport Role)。


●Exchange作为边缘传输角色时部署在内外网交界处,充当邮件安全网关


Exchange ProxyLogon漏洞分析


Exchange作为邮箱服务器角色时分为客户端访问服务(Client Access Services)和后端服务(BackendServices)部分,CAS负责校验用户身份并将请求反代至具体的后端服务。


Exchange ProxyLogon漏洞分析


CAS对应IIS中的Default Web Site监听在80和443端口,BS对应IIS中的Exchange Back End 监听在81和444端口。


Exchange ProxyLogon漏洞分析


出于解耦和兼容考虑,各个功能被封装为多个模块,有如下常用功能(缩写名对应URL访问路径): 

●OWA(Outlook Web App) 

●ECP(Exchange Control Panel) 

●EWS(Exchange Web Service) 

●AutodiscoverMAPI(Messaging Application Programming Interface) 

●EAS(Exchange ActiveSync) 

OAB(Offline Address Books) 

●PowerShell


影响版本


●Exchange Server 2013 < Mar21SU 

●Exchange Server 2016 < Mar21SU < CU20

●Exchange Server 2019 < Mar21SU < CU9


补丁分析


根据微软官方通告可以知道ProxyLogon漏洞的补丁编号为KB5000871,也可以看到此补丁的前置补丁编号为KB4602269,将两个msp补丁文件下载下来并通过7z解压得到多个dll。


●也可以在Microso Update Catalog [https://www.catalog.update.microsoft.com/home.aspx]中搜索补丁编号下载cab文件并解压


下载dnSpy [https://github.com/dnSpy/dnSpy/releases/download/v6.1.8/dnSpy-net-win64.zip]用于反编译和调试C#的dll文件。由于我们并不是要调试二进制洞,为了避免干扰需要取消勾选View->Options->Decompiler->ILSpy->Show tokens, RVAs and file offsets 。将解压出的dll拖入dnSpy并选中高亮,通过 File->Export to Project 就可以得到反编译后的工程文件。


拿到补丁前后两份反编译的源码后,在低版本源码文件夹建立git目录,再将高版本源码文件覆盖过来:


cd kb4602269git initgit add .git commit -m 'init'
# Bypass Alias cp='cp -i'/usr/bin/cp -r ../kb5000871/* ./


这样就能在任意支持git管理的IDE中方便地进行补丁对比了(比如VSCode),小缺点就是有的整个文件就一点无关紧要的字符变化而已(之前对比vCenter时也是),而我们显然只是想关注一些函数和流程的变动,所以之后也许可以结合页面相似度之类的算法再筛一遍,现阶段可以用批量替换的办法凑合。


CVE-2021-26855


Microsoft.Exchange.FrontEndHttpProxy未有效校验Cookie中用户可控的X-BEResource值,后续处理中结合.NET的UriBuilder类特性造成SSRF。


漏洞分析


CVE-2021-26855 is a server-side request forgery (SSRF) vulnerability in Exchange which allowed the attackerto send arbitrary HTTP requests and authenticate as the Exchange server.


微软通告说这是一个以 Exchange服务器 作为身份认证的SSRF漏洞,说明肯定涉及到了NTLM/Kerberos认证,再结合Volexity捕获到的相关访问路径来看,定位到 Microsoft.Exchange.FrontEndHttpProxy相关的代码变动:


Exchange ProxyLogon漏洞分析


ProxyRequestHandler 类是CAS反代过程中,负责处理用户请求与后端响应的一个承前启后的组件。因为函数调用关系比较复杂,为了避免看上去一团乱麻,所以在具体分析某个方法作用前,先从广度上列出从收到用户请求开始几个主线的方法调用栈。


// Microsoft.Exchange.FrontEndHttpProxypublic class ProxyModule : IHttpModule    public void Init(HttpApplication application)        private void OnPostAuthorizeRequest(object sender, EventArgs e)            protected virtual void OnPostAuthorizeInternal(HttpApplication httpApplication)
private IHttpHandler SelectHandlerForUnauthenticatedRequest(HttpContext httpContext)
HttpContext context = httpApplication.Context; context.RemapHandler(httpHandler);


Exchange ProxyLogon漏洞分析


当请求路径为 /ecp/ 时,会通过 IsResourceRequest 方法判断文件后缀名:


// Microsoft.Exchange.FrontEndHttpProxyinternal class BEResourceRequestHandler : ProxyRequestHandler    internal static bool CanHandle(HttpRequest httpRequest)        private static string GetBEResouceCookie(HttpRequest httpRequest)        internal static bool IsResourceRequest(string localPath)


Exchange ProxyLogon漏洞分析


Exchange ProxyLogon漏洞分析


通过判断后由 BeginProcessRequest 方法继续处理后续流程:


public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)    private void InternalBeginCalculateTargetBackEnd(out AnchorMailbox anchorMailbox)
protected override AnchorMailbox ResolveAnchorMailbox() public ServerInfoAnchorMailbox(BackEndServer backendServer, IRequestContext requestContext) public static BackEndServer FromString(string input)
private void OnCalculateTargetBackEndCompleted(object extraData) private void InternalOnCalculateTargetBackEndCompleted(TargetCalculationCallbackBeacon beacon) private void BeginValidateBackendServerCacheOrProxyOrRecalculate() protected void BeginProxyRequestOrRecalculate() protected void BeginProxyRequest(object extraData)
protected virtual Uri GetTargetBackEndServerUrl()
protected HttpWebRequest CreateServerRequest(Uri targetUrl) protected void PrepareServerRequest(HttpWebRequest serverRequest)
internal static string KerberosUtilities.GenerateKerberosAuthHeader(...)
private void CopyHeadersToServerRequest(HttpWebRequest destination) protected virtual bool ShouldCopyHeaderToServerRequest(string headerName)
private void CopyCookiesToServerRequest(HttpWebRequest serverRequest)
protected virtual void SetProtocolSpecificServerRequestParameters(HttpWebRequest serverRequest) protected virtual void AddProtocolSpecificHeadersToServerRequest(WebHeaderCollection headers)
private void BeginGetServerResponse() private static void ResponseReadyCallback(IAsyncResult result) private void OnResponseReady(object extraData) private void ProcessResponse(WebException exception) private void CopyHeadersToClientResponse() private void CopyCookiesToClientResponse()


BackEndServer.FromString 方法获取Cookie的 X-BEResource 值中,以 波浪线分隔开的FQDN和version,而且涉及一处补丁变更:


Exchange ProxyLogon漏洞分析


这里的值可以由Cookie控制,调用 FromString ResolveAnchorMailbox 方法也有补丁变更,基本可以说明漏洞点就在这附近了。果然随后的 GetTargetBackEndServerUrl 方法就把Fqdn赋值给了UriBuilder对象Host属性:


Exchange ProxyLogon漏洞分析


protected virtual UriBuilder GetClientUrlForProxy(){  return new UriBuilder(this.ClientRequest.Url);}


UriBuilder是一个.NET类,在微软的Reference Source找到源码。如果传入的Host中存在 : 冒号,并且不是 [ 开头,就用一对中括号将值包裹起来。:


public string Host {    get {        return m_host;    }    set {        if (value == null) {            value = String.Empty;        }        m_host = value;        //probable ipv6 address -         if (m_host.IndexOf(':') >= 0) {            //set brackets            if (m_host[0] != '[')                m_host = "[" + m_host + "]";        }        m_changed = true;    }}


根据传入的version是否大于Server.E15MinVersion(1941962752),将Port赋值为444或443。最后由Uri属性的get访问器(accessor)调用ToString将各部分拼接还原:


public Uri Uri {    get {        if (m_changed) {            m_uri = new Uri(ToString());            SetFieldsFromUri(m_uri);            m_changed = false;        }        return m_uri;    }}


得到后端URL之后继续处理请求头,将 GenerateKerberosAuthHeader 方法生成的Kerberos票据放入Authorization请求头。CopyHeadersToServerRequest 方法会筛选出后端需要的请求头,其中ShouldCopyHeaderToServerRequest 方法用来过滤一些自定义请求头:


Exchange ProxyLogon漏洞分析


最后 AddProtocolSpecificHeadersToServerRequest 方法会将序列化得到的用于标识用户身份的Token,放入 X-CommonAccessToken 请求头中:


Exchange ProxyLogon漏洞分析


相应的,后端模块会由 AllowsTokenSerializationBy 方法校验通常机器用户才有的 ms-Exch-EPI-TokenSerialization 扩展权限(验证请求由CAS发出),随后反序列化还原 X-CommonAccessToken 请求头的身份标识。


// Microsoft.Exchange.Security.Authenticationpublic class BackendRehydrationModule : IHttpModule    public void Init(HttpApplication application)        private void OnAuthenticateRequest(object source, EventArgs args)            private void ProcessRequest(HttpContext httpContext)                private bool TryGetCommonAccessToken(HttpContext httpContext, Stopwatch stopwatch, out CommonAccessToken token)
private bool IsTokenSerializationAllowed(WindowsIdentity windowsIdentity) using (ClientSecurityContext clientSecurityContext = new ClientSecurityContext(windowsIdentity)){ flag2 = LocalServer.AllowsTokenSerializationBy(clientSecurityContext); }
token = CommonAccessToken.Deserialize(text); httpContext.Items["Item-CommonAccessToken"] = token;


小结一下,Cookie的 X-BEResource 值可以控制CAS请求的Host,结合UriBuilder类特性可以构造出可控的完整URL,因为采用Kerberos认证所以不能向任意站点发起请求:


Exchange ProxyLogon漏洞分析


X-FEServer 响应头的值就是计算机名,可以用它构造URL请求后端服务:


Exchange ProxyLogon漏洞分析


●Exchange2013需要将Version设置为大于1941962752的值


CVE-2021-27065


Microsoft.Exchange.Management.DDIService.WriteFileActivity 未校验写文件后缀,可由文件内容部分可控的相关功能写入WebShell。


利用流程


Microsoft.Exchange.Management.DDIService.WriteFileActivity 中有一处明显的补丁变动,使得文件后缀名只能为txt。


Exchange ProxyLogon漏洞分析


private static readonly string textExtension = ".txt";


ResetOABVirtualDirectory 触发点为例,利用流程如下(均通过SSRF发起):

 1. 请求EWS,从 X-CalculatedBETarget 响应头获取后端域名


Exchange ProxyLogon漏洞分析


2. 爆破邮箱用户名,请求Autodiscover获取配置中的LegacyDN


Exchange ProxyLogon漏洞分析


3. 由 MAPI over HTTP 请求引发 Microsoft.Exchange.RpcClientAccess.Server.LoginPermException ,获取SID


Exchange ProxyLogon漏洞分析


4. 替换尾部RID为500,伪造管理员SID,由ProxyLogonHandler获取管理员身份 ASP.NET_SessionIdmsExchEcpCanary


Exchange ProxyLogon漏洞分析


5. 通过DDI组件Getlist接口获取RawIdentity(GetObject接口有时候返回NULL)


Exchange ProxyLogon漏洞分析


6. 利用外部URL虚拟路径属性引入WebShell


Exchange ProxyLogon漏洞分析


7. 最后触发重置时的备份功能,将文件写入指定的UNC目录


Exchange ProxyLogon漏洞分析


●WebShell的内容需要规避会被URL编码的特殊字符,且字符长度不能超过255


下一篇我们将一起讨论ProxyOracle~


参考内容

Exchange architecture


Web services reference for Exchange


[HAFNIUM targeting Exchange Servers with 0-day exploits


Description of the security update for Microsoft Exchange Server 2019, 2016, and 2013: March 2, 2021 (KB5000871)


Operation Exchange Marauder: Active Exploitation of Multiple Zero-Day Microsoft Exchange Vulnerabilities


Reproducing the Microsoft Exchange Proxylogon Exploit Chain


Phân tích lỗ hổng ProxyLogon — Mail Exchange RCE (Sự kết hợp hoàn hảo CVE-2021–26855 + CVE-2021–27065)


A New Attack Surface on MS Exchange Part 1 - ProxyLogon!



#CVE-2021-26855

#CVE-2021-27065


Exchange ProxyLogon漏洞分析
Exchange ProxyLogon漏洞分析

默安科技安全研究院旗下团队,“逐日”寓意为追逐技术永不停歇,专注于安全攻防研究,包括漏洞挖掘、逆向工程、红蓝对抗、代码审计、产品赋能等方向。


本文始发于微信公众号(默安逐日实验室):Exchange ProxyLogon漏洞分析

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: