黑名单机制必然会推动两种研究方向的发展:一是挖掘不在黑名单的新组件,是为绕过规则;二是发掘检查的盲区,是为绕过逻辑。
CVE-2020-14756
二次反序列化具有对抗检查逻辑的天生丽质,在CVE-2018-2893中就有利用字节数组和反射重建类两种方式。找找还有没有readObject
到Class.forName
的路子:

readUnsignedByte
读到的nType
为9或10时会进入readXmlSerializable
或readExternalizableLite
分支。

上述两个方法均通过自身loadClass
方法加载类,最终由Class.forName
获取类并返回。


readXmlSerializable
方法获取类后继续进行XML解析,是另一个XXE漏洞。
readExternalizableLite
方法获取类后继续调用readExternal
反序列化,不受黑名单限制。进而引出两个问题:
ExternalizableHelper
自身没有实现Serializable
接口,一定有什么地方调用它的readObject
loadClass
加载后强转为了ExternalizableLite
类型,它哪些满足readExternal
参数要求的子类可以被用作sink

找到PermissionInfo#readExternal
会调用ExternalizableHelper#readCollection
进而调用readObject
作为链首。

继续找到TopNAggregator$PartialResult
及其父类SortedBag
:

readExternal
方法会调用父类的instantiateInternalMap
方法将comparator
封装进TreeMap
,随后在add
方法中调用map.put
时就会触发compare
,进而连上以前的链尾。与PriorityQueue
的作用相同,只是绕这么一圈过掉了黑名单。

重写PermissionInfo#writeExternal
按照以前的套路一步步构造payload打出去就行。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| extract:95, MvelExtractor (com.tangosol.coherence.rest.util.extractor)
extract:112, ReflectionExtractor (com.tangosol.util.extractor) extract:105, ChainedExtractor (com.tangosol.util.extractor)
compare:143, AbstractExtractor (com.tangosol.util.extractor) compare:416, SortedBag$WrapperComparator (com.tangosol.util) compare:1295, TreeMap (java.util) put:538, TreeMap (java.util) add:152, SortedBag (com.tangosol.util) add:270, TopNAggregator$PartialResult (com.tangosol.util.aggregator) readExternal:299, TopNAggregator$PartialResult (com.tangosol.util.aggregator) readExternalizableLite:2345, ExternalizableHelper (com.tangosol.util) readObjectInternal:2661, ExternalizableHelper (com.tangosol.util) readObject:2606, ExternalizableHelper (com.tangosol.util) readCollection:2131, ExternalizableHelper (com.tangosol.util) readExternal:190, PermissionInfo (com.tangosol.net.security) readExternalData:2118, ObjectInputStream (java.io) readOrdinaryObject:2067, ObjectInputStream (java.io) readObject0:1573, ObjectInputStream (java.io)
|
漏洞作者用了AttributeHolder
作为链首,主要是writeExternal
的逻辑友好,不用重写构造起来更加方便。

CVE-2020-14644
按照同样的思路找ClassLoader.defineClass
的路子:

com.tangosol.internal.util.invoke.RemoteConstructor#readResolve
会触发newInstance
并调用com.tangosol.internal.util.invoke.RemotableSupport#realize
。

随后会经过多个方法处理后最终进入ClassLoader.defineClass
,可以看到关键是最开始传入RemoteConstructor
构造方法的ClassDefinition
对象。

对于字节码相关的处理函数就是获取构造方法,之后会被用于创建实例化对象,真正的关键变为了ClassDefinition
构造方法的ClassIdentity
对象。



看到ClassIdentity
构造函数会将包名、类名、以及md5哈希分别存入三个属性,上文中RemotableSupport
加载字节码时,会以这个getName
方法获取到的类名为准。

梳理一下整体逻辑:
将要加载的类喂给ClassIdentity
构造函数
ClassDefinition
构造函数接收第一步创建的ClassIdentity
对象、以及要加载的类字节码
RemoteConstructor
构造函数接收第二步创建的ClassDefinition
对象、以及要加载的类构造函数的参数类型数组
反序列化时就会触发类加载,要解决的核心问题是ClassIdentity
构造函数把传给ClassLoader.defineClass
的类名作了变化,我们也要对字节码中的类名作相应的格式变化,用asm或者javassist或者手动创建类对象都行。

1 2 3 4 5 6 7 8 9 10 11
| defineClass:181, RemotableSupport (com.tangosol.internal.util.invoke) realize:137, RemotableSupport (com.tangosol.internal.util.invoke) newInstance:120, RemoteConstructor (com.tangosol.internal.util.invoke) readResolve:231, RemoteConstructor (com.tangosol.internal.util.invoke) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invokeReadResolve:1260, ObjectStreamClass (java.io) readOrdinaryObject:2078, ObjectInputStream (java.io) readObject0:1573, ObjectInputStream (java.io)
|
CVE-2021-2135
早期对于14756的修复方法之一就是在ExternalizableHelper#readExternalizableLite
中loadClass
后,判断输入流属于ObjectInputStream
就进入检查流程。这就是把找一条非ObjectInputStream
输入流就能绕过写在脸上了;)

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
| extract:95, MvelExtractor (com.tangosol.coherence.rest.util.extractor)
extract:112, ReflectionExtractor (com.tangosol.util.extractor) extract:105, ChainedExtractor (com.tangosol.util.extractor)
compare:79, AbstractExtractor (com.tangosol.util.extractor) compare:416, SortedBag$WrapperComparator (com.tangosol.util) compare:1295, TreeMap (java.util) put:538, TreeMap (java.util) add:152, SortedBag (com.tangosol.util) add:268, TopNAggregator$PartialResult (com.tangosol.util.aggregator) readExternal:297, TopNAggregator$PartialResult (com.tangosol.util.aggregator) readExternalizableLite:2265, ExternalizableHelper (com.tangosol.util) readObjectInternal:2579, ExternalizableHelper (com.tangosol.util) deserializeInternal:3098, ExternalizableHelper (com.tangosol.util) fromBinary:334, ExternalizableHelper (com.tangosol.util) getKey:56, SimpleBinaryEntry (com.tangosol.internal.util) toString:153, SimpleBinaryEntry (com.tangosol.internal.util) equals:392, XString (com.sun.org.apache.xpath.internal.objects) equals:3415, Base (com.tangosol.util) put:213, LiteMap (com.tangosol.util) readMap:1900, ExternalizableHelper (com.tangosol.util) readExternal:190, ConditionalPutAll (com.tangosol.util.processor) readExternalizableLite:2265, ExternalizableHelper (com.tangosol.util) readObjectInternal:2579, ExternalizableHelper (com.tangosol.util) readObject:2524, ExternalizableHelper (com.tangosol.util) readObject:2502, ExternalizableHelper (com.tangosol.util) readExternal:406, AttributeHolder (com.tangosol.coherence.servlet) readExternal:371, AttributeHolder (com.tangosol.coherence.servlet) readExternalData:2118, ObjectInputStream (java.io) readOrdinaryObject:2067, ObjectInputStream (java.io) readObject0:1573, ObjectInputStream (java.io)
|
参考链接
CVE-2020-14644分析与gadget的一些思考
How Did I Find Weblogic T3 RCE