dotNet 反序列化ISerializable系列链分析
字数 1345 2025-08-24 23:51:18

.NET 反序列化 ISerializable 系列链分析

ISerializable 接口介绍

ISerializable 接口允许对象控制其自己的序列化和反序列化过程。与普通序列化不同,实现 ISerializable 的类可以完全控制序列化和反序列化的行为。

关键特点

  1. 必须实现 GetObjectData 方法用于序列化
  2. 必须实现特殊构造函数("序列化构造函数")用于反序列化
  3. 构造函数参数必须与 GetObjectData 相同:(SerializationInfo info, StreamingContext context)

常见 ISerializable 反序列化链

以下类均实现了 ISerializable 接口,可作为反序列化链的入口点:

  1. System.Web.Security.RolePrincipal
  2. System.Security.Principal.WindowsIdentity
  3. System.Security.Principal.WindowsPrincipal
  4. Microsoft.IdentityModel.Claims.WindowsClaimsIdentity
  5. System.IdentityModel.Tokens.SessionSecurityToken

攻击链分析模式

所有实现 ISerializable 且继承自 System.Security.Claims.ClaimsIdentity 的类都可以作为入口链使用。

通用分析步骤

  1. 查找实现了 ISerializable 的类
  2. 检查是否存在 (SerializationInfo info, StreamingContext context) 构造函数
  3. 跟踪构造函数中的反序列化操作
  4. 寻找可利用的属性设置点

WindowsIdentity 链详细分析

类定义

public class WindowsIdentity : ClaimsIdentity, ISerializable, IDeserializationCallback, IDisposable

关键构造函数

public WindowsIdentity(SerializationInfo info, StreamingContext context) : this(info)
{
}

private WindowsIdentity(SerializationInfo info) : base(info)
{
    m_claimsInitialized = false;
    IntPtr intPtr = (IntPtr)info.GetValue("m_userToken", typeof(IntPtr));
    if (intPtr != IntPtr.Zero)
    {
        CreateFromToken(intPtr);
    }
}

反序列化流程

  1. 调用基类 ClaimsIdentity 的构造函数
  2. 基类构造函数调用 Deserialize 方法
  3. Deserialize 方法处理多个关键属性:
// 处理 Actor 属性
case "System.Security.ClaimsIdentity.actor":
{
    using (MemoryStream serializationStream2 = new MemoryStream(Convert.FromBase64String(info.GetString("System.Security.ClaimsIdentity.actor"))))
    {
        m_actor = (ClaimsIdentity)binaryFormatter.Deserialize(serializationStream2, null, fCheck: false);
    }
    break;
}

// 处理 Claims 属性
case "System.Security.ClaimsIdentity.claims":
    DeserializeClaims(info.GetString("System.Security.ClaimsIdentity.claims"));
    break;

// 处理 BootstrapContext 属性
case "System.Security.ClaimsIdentity.bootstrapContext":
{
    using (MemoryStream serializationStream = new MemoryStream(Convert.FromBase64String(info.GetString("System.Security.ClaimsIdentity.bootstrapContext"))))
    {
        m_bootstrapContext = binaryFormatter.Deserialize(serializationStream, null, fCheck: false);
    }
    break;
}

可利用属性

  1. Actor 属性

    public ClaimsIdentity Actor {
        get { return m_actor; }
        set {
            if (value != null && IsCircular(value)) {
                throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
            }
            m_actor = value;
        }
    }
    
  2. BootstrapContext 属性

    public object BootstrapContext {
        get { return m_bootstrapContext; }
        [SecurityCritical]
        set { m_bootstrapContext = value; }
    }
    

注意:Claims 属性没有 set 方法,无法直接利用。

RolePrincipal 链分析

关键构造函数

protected RolePrincipal(SerializationInfo info, StreamingContext context) : base(info, context)
{
    // ...
}

反序列化流程

  1. 调用基类 ClaimsPrincipal 的构造函数
  2. 基类构造函数调用 Deserialize 方法
  3. 处理 Identities 属性:
public virtual IEnumerable<ClaimsIdentity> Identities => m_identities.AsReadOnly();
  1. 调用 DeserializeIdentities 函数处理身份信息

防御建议

  1. 避免反序列化不可信数据
  2. 使用安全的序列化格式(如 JSON 而非 BinaryFormatter)
  3. 实现自定义序列化时进行严格验证
  4. 使用类型限制或白名单机制

总结

ISerializable 接口的反序列化漏洞模式相对固定,一旦理解了一个链的分析方法,其他类似链的分析就会变得简单。关键在于:

  1. 识别 ISerializable 实现
  2. 跟踪反序列化构造函数
  3. 寻找可控的反序列化点
  4. 构造利用链实现代码执行

这种类型的漏洞在 .NET 反序列化中较为常见,开发人员和安全研究人员都应给予足够重视。

.NET 反序列化 ISerializable 系列链分析 ISerializable 接口介绍 ISerializable 接口允许对象控制其自己的序列化和反序列化过程。与普通序列化不同,实现 ISerializable 的类可以完全控制序列化和反序列化的行为。 关键特点 必须实现 GetObjectData 方法用于序列化 必须实现特殊构造函数("序列化构造函数")用于反序列化 构造函数参数必须与 GetObjectData 相同: (SerializationInfo info, StreamingContext context) 常见 ISerializable 反序列化链 以下类均实现了 ISerializable 接口,可作为反序列化链的入口点: System.Web.Security.RolePrincipal System.Security.Principal.WindowsIdentity System.Security.Principal.WindowsPrincipal Microsoft.IdentityModel.Claims.WindowsClaimsIdentity System.IdentityModel.Tokens.SessionSecurityToken 攻击链分析模式 所有实现 ISerializable 且继承自 System.Security.Claims.ClaimsIdentity 的类都可以作为入口链使用。 通用分析步骤 查找实现了 ISerializable 的类 检查是否存在 (SerializationInfo info, StreamingContext context) 构造函数 跟踪构造函数中的反序列化操作 寻找可利用的属性设置点 WindowsIdentity 链详细分析 类定义 关键构造函数 反序列化流程 调用基类 ClaimsIdentity 的构造函数 基类构造函数调用 Deserialize 方法 Deserialize 方法处理多个关键属性: 可利用属性 Actor 属性 BootstrapContext 属性 注意:Claims 属性没有 set 方法,无法直接利用。 RolePrincipal 链分析 关键构造函数 反序列化流程 调用基类 ClaimsPrincipal 的构造函数 基类构造函数调用 Deserialize 方法 处理 Identities 属性: 调用 DeserializeIdentities 函数处理身份信息 防御建议 避免反序列化不可信数据 使用安全的序列化格式(如 JSON 而非 BinaryFormatter) 实现自定义序列化时进行严格验证 使用类型限制或白名单机制 总结 ISerializable 接口的反序列化漏洞模式相对固定,一旦理解了一个链的分析方法,其他类似链的分析就会变得简单。关键在于: 识别 ISerializable 实现 跟踪反序列化构造函数 寻找可控的反序列化点 构造利用链实现代码执行 这种类型的漏洞在 .NET 反序列化中较为常见,开发人员和安全研究人员都应给予足够重视。