.NET高级代码审计(第九课) BinaryFormatter反序列化漏洞
字数 1663 2025-08-18 11:38:28
.NET BinaryFormatter 反序列化漏洞深度解析
一、BinaryFormatter 概述
BinaryFormatter 是 .NET 框架中的一个类,位于 System.Runtime.Serialization.Formatters.Binary 命名空间,用于将对象序列化为二进制格式或从二进制格式反序列化对象。
主要特点:
- 使用二进制格式进行序列化
- 序列化速度快
- 跨 .NET 版本兼容性好
- 支持泛型等数据类型
二、BinaryFormatter 序列化机制
2.1 基本序列化方法
要使类可序列化,需要使用 [Serializable] 特性标记。如果某些成员不需要序列化,可以使用 [NoSerialized] 特性。
[Serializable]
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
[NonSerialized]
public string Secret; // 不会被序列化
public static void ClassMethod(string process)
{
Process.Start(process);
}
}
2.2 序列化过程
TestClass test = new TestClass { Name = "Ivan1ee", Age = 18 };
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
formatter.Serialize(ms, test);
byte[] serializedData = ms.ToArray();
// 保存或传输 serializedData
}
序列化后的二进制数据格式包含类型信息和对象状态数据。
三、BinaryFormatter 反序列化机制
3.1 反序列化方法
BinaryFormatter 提供了四个主要的反序列化方法:
Deserialize(Stream)DeserializeMethodResponse(Stream, HeaderHandler, IFormatterConverter)UnsafeDeserialize(Stream, HeaderHandler)UnsafeDeserializeMethodResponse(Stream, HeaderHandler, IFormatterConverter)
3.2 基本反序列化示例
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(serializedData))
{
TestClass obj = (TestClass)formatter.Deserialize(ms);
Console.WriteLine(obj.Name); // 输出反序列化后的Name值
}
四、BinaryFormatter 反序列化漏洞
4.1 漏洞原理
BinaryFormatter 反序列化时会完全重建对象图,包括执行对象的构造函数、属性设置器等。攻击者可以构造恶意序列化数据,在反序列化过程中执行任意代码。
4.2 主要攻击向量
4.2.1 ActivitySurrogateSelector 攻击
通过重写 ISerializationSurrogate 接口,可以在反序列化过程中执行自定义代码:
// 恶意SurrogateSelector实现
var surrogateSelector = new SurrogateSelector();
var surrogate = new MySurrogate();
surrogateSelector.AddSurrogate(typeof(Process),
new StreamingContext(StreamingContextStates.All),
surrogate);
var binaryFormatter = new BinaryFormatter
{
SurrogateSelector = surrogateSelector
};
// 序列化恶意对象
byte[] payload = Serialize(binaryFormatter, new object());
// 反序列化触发漏洞
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(payload))
{
formatter.Deserialize(ms); // 触发RCE
}
4.2.2 WindowsIdentity 攻击
利用 WindowsIdentity 解析 Base64 编码数据的特性,构造恶意数据触发代码执行:
// 构造恶意WindowsIdentity数据
byte[] payload = GenerateMaliciousWindowsIdentityData();
// 反序列化触发漏洞
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(payload))
{
formatter.Deserialize(ms); // 触发RCE
}
五、漏洞代码审计要点
5.1 危险方法识别
审计时应重点关注以下 BinaryFormatter 方法的使用:
5.1.1 UnsafeDeserialize
// 不安全的代码示例
public object DeserializeData(string path)
{
using (FileStream fs = new FileStream(path, FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.UnsafeDeserialize(fs, null); // 危险!
}
}
5.1.2 UnsafeDeserializeMethodResponse
// 不安全的代码示例
public object DeserializeResponse(Stream stream)
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.UnsafeDeserializeMethodResponse(stream, null, null); // 危险!
}
5.1.3 Deserialize
// 不安全的代码示例
public object DeserializeData(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data))
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.Deserialize(ms); // 危险!
}
}
5.1.4 DeserializeMethodResponse
// 不安全的代码示例
public object DeserializeResponse(Stream stream)
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.DeserializeMethodResponse(stream, null, null); // 危险!
}
5.2 安全建议
- 避免反序列化不受信任的数据:永远不要反序列化来自不可信源的二进制数据
- 使用安全替代方案:考虑使用 JSON 或 XML 序列化器,如
System.Text.Json或XmlSerializer - 实现类型检查:如果必须使用 BinaryFormatter,实现
SerializationBinder限制可反序列化的类型 - 签名验证:对序列化数据进行数字签名,确保数据完整性
- 更新 .NET 版本:较新版本的 .NET 已对 BinaryFormatter 进行了安全限制
六、防御措施
6.1 使用 SerializationBinder 限制类型
public class SafeBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
// 只允许反序列化已知安全的类型
if (typeName == typeof(TestClass).FullName)
{
return typeof(TestClass);
}
throw new SerializationException("Unexpected type: " + typeName);
}
}
// 使用方式
BinaryFormatter formatter = new BinaryFormatter
{
Binder = new SafeBinder()
};
6.2 完全禁用 BinaryFormatter
在最新 .NET 版本中,可以通过配置完全禁用 BinaryFormatter:
<configuration>
<runtime>
<enableUnsafeBinaryFormatterSerialization enabled="false"/>
</runtime>
</configuration>
七、总结
BinaryFormatter 虽然提供了高效的序列化能力,但其反序列化过程存在严重安全风险。开发人员应当:
- 充分了解 BinaryFormatter 的安全风险
- 避免反序列化不可信数据
- 使用更安全的替代方案
- 如果必须使用,实施严格的安全控制措施
- 保持 .NET 运行时的最新更新
安全开发实践是防止此类漏洞的关键,在设计和实现阶段就应考虑数据序列化的安全性问题。