作者:青藤实验室
原文链接:https://mp.weixin.qq.com/s/ZLSFXUoNNAFxqeiD9RpYZg

SharePoint Rce 系列分析(一) 里我简单介绍了 SP 的沙箱机制,并且通过 CVE-2020-0974 展示了 bypass 原理:

VerifyControlOnSafeList(..., false)

这类漏洞原理简单,正则特征明显,可以借助自动化手段检测,除非找到新的 bypass 点,之后再出现同类型的漏洞概率不大。

本文介绍的是议题中的另一个漏洞:CVE-2020-1444。它在所有安全机制开启,没有方法参数使用不当情况下,通过 TOCTOU 实现了 rce;这种漏洞很难通过有效的漏洞模型去自动化检测,所以后续出现这种漏洞的可能性仍然存在,比如 CVE-2020-16951。

调试环境

Server2016
SP2016
dnSpy

通过 proexp 找到管理 web 的 pid -w981 dnSpy attach 进程后在触发 url 被处理的地方下断点 -w1433

背景知识

ObjectDataSource

通过 ObjectDataSource 定义 知道在 asp.netObjectDataSource 可以调用任意运行时方法,类似 ObjectDataProvider

Asp.Net 的内联表达式

<%-- ... -- %> 表示注释
<%@ ... %> 表示指令

以上都是 asp.net 的内联表达式,更具体点,比如下面 Register 指令实例:

<%@ Register TagPrefix="asp3" Namespace="System.Web.UI.WebControls" Assembly="System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" %>

<asp3:ObjectDataSource ID="ODS1" runat="server" SelectMethod="Start" TypeName="System.Diagnostics.Process" >
    <SelectParameters>
        <asp3:Parameter Direction="input" Type="string" Name="fileName" DefaultValue="calc"/>
    </SelectParameters>
</asp3:ObjectDataSource>
<asp3:ListBox ID="LB1" runat="server" DataSourceID = "ODS1" />

第1行注册了对 System.Web.UI.WebControls 命名空间的引用,类似 python 中的 import,别名是 asp3。

之后的 <asp3:ObjectDataSource...> 表示 System.Web.UI.WebControls 下必有一个名为 ObjectDataSource 的类。

CVE-2020-1444

借用议题中的原图来概括漏洞过程: -w642 用户输入在经过服务端校验后,被服务端修改后再使用,这个顺序显然是有问题的,也是漏洞成因,具体到代码里: -w931 为了方便理解,我把 "change and use" 指向了 "check" 下一行,实际上这里只是 change 的开始,也是漏洞成因的一个关键地方,不是最终解析的地方。

check

SP 的沙箱机制在议题中有详细介绍,上篇文章也说过,下面直接说漏洞相关的部分。

在 design mode 下,Check 通过 VerifyControlOnSafeList 完成。它位于: -w748 -w919 请求发生时的调用栈 -w1408 通过 call stack 可以看到 check 始于 ConvertWebPartMarkup

暂时不考虑具体的校验逻辑,试想一下,假如上传的整个网页文件是个注释,比如: -w622

这时 Check 肯定能通过。

Change

Change 发生在 ConvertMarkupToTree。通过 OnLoad 前几行代码可以看出有两个必须提供的参数:WebPartUrlUrl

Url 参数暂时不管,在后面漏洞利用章节会去讨论。通过对代码 trace 可以发现 WebPartUrl 指向的文件必须是一个 xml,这个 xml 还有其他要求,暂且不提。从服务端取参到 ConvertMarkupToTree 的处理步骤是:

  1. 取参(url of xml)
  2. 通过 web 获取 xml 的字符串流(GetWebPartMarkup)
  3. 对字符串流做一些预处理,包括校验(ConvertWebPartMarkup)
  4. 将字符串流转成 xml 树(ConvertMarkupToTree)
  5. 经过各种处理...,将 xml 树 转回字符串流(xelement2.ToString)
  6. 网页解析(ParseControl)

从上面可以看出 string -> xml tree 发生在校验之后,看看具体做了哪些事 -w761 下面是正则定义: -w764 这个正则匹配的是内联表达式中的 Register 指令,有两个命名捕获:TagPrefix 和 DllInfo。

TagPrefix 用正则捕获,DllInfo 是 TagPrefix 之后的所有内容。比如下面的例子: -w718 再来看 ConvertMarkupToTree:首先把所有 Register 指令从字符串流中取出来做特殊处理(比如全部放到 xml 首尾这样的特殊位置),把剩下的内容简单处理一下,比如去掉转义、重新添加一个 root 就转成 xml tree 了: -w677 回忆一下刚才通过 Check 的 demo: -w622 由于正则匹配到了其中的 Register 指令,取出 Register 指令之后:

webPartMarkup = webPartMarkup.Replace(match.Value, "")

demo 就变成了: -w627 之后会继续构造 xml tree,在最终网页解析之前肯定还要转回字符串流,那么这里本应该是注释的内容({unsafe ...})就在变成了一个 ASPX 标签,实现了沙箱逃逸。

漏洞利用

BH 上作者给出了一部分 poc: -w632 我参考了 CVE-2020-16951 的 poc,另一个 SP 的 TOCTOU 漏洞,结合上述分析,明白上述 xml 是这个漏洞利用的第一步,这个漏洞是分两步实现:

// put xml
PUT /poc.xml HTTP/1.1
...
// trigger rce
GET /_layouts/15/WebPartEditingSurface.aspx?WebPartUrl=http://.../poc.xml&Url=? HTTP/1.1

问题就出在 GET 请求的 URL 参数上,作者对这个参数的要求进行了简单说明: -w660 我自然就想到了 Master Page Gallery 里的几个 master 文件 -w852 CVE-2020-16951 的 poc 就用了其中的 seattle.master -w1051 我尝试也用这个文件作为参数响应报错,calc 进程也没启动 -w1439 在 SMP 中开启了启用详细日志:

Set-SPLogLevel –TraceSeverity VerboseEx
New-SPLogFile
Get-SPDiagnosticConfig | select LogLocation
Clear-SPLogLevel

通过重新错误的报错信息得到了报错时的 call stack:

Application error when access /_layouts/15/WebPartEditingSurface.aspx, Error=The field b510aac1-bba3-4652-ab70-2d756c29540f does not exist in the list item _catalogs/masterpage/seattle.master.   
at Microsoft.SharePoint.Publishing.Internal.WebControls.ComponentRibbonHelper.GetContentTypeFieldValue(String pathToPageLayout)     
at Microsoft.SharePoint.Publishing.Internal.WebControls.ComponentRibbonHelper.IsPageLayout(String pathToPageLayout, ContentTypeIdFieldValue& associatedContentTypeFieldValue)     
at Microsoft.SharePoint.Publishing.Internal.WebControls.ComponentRibbonHelper.OnPreRender(EventArgs e)     
at System.Web.UI.Control.PreRenderRecursiveInternal()     
at System.Web.UI.Control.PreRenderRecursiveInternal()     
at System.Web.UI.Control.PreRenderRecursiveInternal()     
at System.Web....   2c6c909f-e9be-00ce-823a-6efbede23872
...

进而确定了出错位置: -w1009 跟进后发现 b510aac1-bba3-4652-ab70-2d756c29540fFieldId.AssociatedContentType 的 GUID: -w802 正好和漏洞作者说的一致

It should contain the relative address of any file from the SharePoint 
DataBase with the FieldId.AssociatedContentType field

我怎么判断 seattle.master 是否存在某个指定的 GUID 比如 b5..0f 呢。

通过了解 Onet.xml 文件这篇文章我知道了 Onet.xml 定义了所有的 BaseTypes,我在所有 Onet.xml 里搜索这个 GUID 并没有找到,之后我扩大了搜索范围在

C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE

最终在

C:\Program Files\Common Files\microsoft shared\Web Server Extensions\16\TEMPLATE\FEATURES\PublishingResources

下找到了包含这个 GUID 的两个 xml -w1436 参考 CVE-2020-16951 的 poc,url 如果是个 master 文件肯定没问题,不管 master 如何和 xml 关联,此时我想直接把PublishingMasterTemplate.master 作为 url,如果这样我必须通过 web 能访问到这个 master 文件。 -w1093 google 了一下 PublishingMasterTemplate.master 得到一个别人报错信息中的 url:

/_catalogs/masterpage/Forms/Publishing Master Page/PublishingMasterTemplate.master

尝试直接访问这个 path 得到 404,进一步研究发现这个东西似乎和 publishingresources 这个 feature 有关,默认关闭

我用 SMP 打开了这个 feature -w959 之后再尝试那个 path 仍然 404,但是尝试原请求不再报 GUID 找不到的错误了 -w1439 也不再进入 catch 分支 -w1008 在 list 中能看到 FieldId.AssociatedContentType-w1007 calc 进程启动 -w1025

我确定 publishingresources 这个 feature 在 SP2016 默认安装条件下不会开启,看漏洞作者和 @mr_me 用的也是 SP2016 测试但没提到这个,查了下官方漏洞公告也没提到。所以我不确定这个是否是漏洞利用的条件之一,但是我确实是在开启了这个功能后复现成功,感兴趣的朋友可以研究一下。

参考

https://i.blackhat.com/USA-20/Wednesday/us-20-Munoz-Room-For-Escape-Scribbling-Outside-The-Lines-Of-Template-Security-wp.pdf

https://srcincite.io/pocs/cve-2020-16951.py.txt

https://msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-1444

https://docs.microsoft.com/en-us/dotnet/api/system.web.ui.webcontrols.objectdatasource?view=netframework-4.8

https://docs.microsoft.com/zh-cn/previous-versions/office/developer/sharepoint-2010/ms474369(v=office.14)


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1430/