dotNet 反序列化ISerializable系列链分析
字数 1345 2025-08-24 23:51:18
.NET 反序列化 ISerializable 系列链分析
ISerializable 接口介绍
ISerializable 接口允许对象控制其自己的序列化和反序列化过程。与普通序列化不同,实现 ISerializable 的类可以完全控制序列化和反序列化的行为。
关键特点
- 必须实现
GetObjectData方法用于序列化 - 必须实现特殊构造函数("序列化构造函数")用于反序列化
- 构造函数参数必须与
GetObjectData相同:(SerializationInfo info, StreamingContext context)
常见 ISerializable 反序列化链
以下类均实现了 ISerializable 接口,可作为反序列化链的入口点:
System.Web.Security.RolePrincipalSystem.Security.Principal.WindowsIdentitySystem.Security.Principal.WindowsPrincipalMicrosoft.IdentityModel.Claims.WindowsClaimsIdentitySystem.IdentityModel.Tokens.SessionSecurityToken
攻击链分析模式
所有实现 ISerializable 且继承自 System.Security.Claims.ClaimsIdentity 的类都可以作为入口链使用。
通用分析步骤
- 查找实现了 ISerializable 的类
- 检查是否存在
(SerializationInfo info, StreamingContext context)构造函数 - 跟踪构造函数中的反序列化操作
- 寻找可利用的属性设置点
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);
}
}
反序列化流程
- 调用基类
ClaimsIdentity的构造函数 - 基类构造函数调用
Deserialize方法 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;
}
可利用属性
-
Actor 属性
public ClaimsIdentity Actor { get { return m_actor; } set { if (value != null && IsCircular(value)) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular")); } m_actor = value; } } -
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)
{
// ...
}
反序列化流程
- 调用基类
ClaimsPrincipal的构造函数 - 基类构造函数调用
Deserialize方法 - 处理
Identities属性:
public virtual IEnumerable<ClaimsIdentity> Identities => m_identities.AsReadOnly();
- 调用
DeserializeIdentities函数处理身份信息
防御建议
- 避免反序列化不可信数据
- 使用安全的序列化格式(如 JSON 而非 BinaryFormatter)
- 实现自定义序列化时进行严格验证
- 使用类型限制或白名单机制
总结
ISerializable 接口的反序列化漏洞模式相对固定,一旦理解了一个链的分析方法,其他类似链的分析就会变得简单。关键在于:
- 识别 ISerializable 实现
- 跟踪反序列化构造函数
- 寻找可控的反序列化点
- 构造利用链实现代码执行
这种类型的漏洞在 .NET 反序列化中较为常见,开发人员和安全研究人员都应给予足够重视。