JAVA反序列化的学习
反序列化漏洞的概念
首先了解什么是序列化和反序列化
序列化:将实体对象按照一定的格式写入到有序字节流的过程
反序列化:将字节流恢复为对象的过程称为对象的反序列化
为什么会需要序列化
当互联网上的两个进程进行远程通信的时候,是可以发送各种类型的数据,如视频、图片、音频等,这些数据都是通过二进制序列的形式在网络中进行传送的。所以当两个Java进程进行通信时,就需
要通过Java的序列化将发送的Java对象转化为字节流,传输完成后,再通过反序列化从字节流中恢复成Java对象
对象序列化的用途
- 将对象的字节序列永久保存到硬盘中
- 在网络中传送对象的字节序列
在Web服务器中的Session对象中,如果用户量大的话,Session的数量将可能导致内存崩溃,Web容器会将一些session先序列化到硬盘中,如果要用,在讲硬盘中的对象还原
JAVA 中的序列化和反序列化
- 在Java中,只有实现了Serializable和Externalizable的对象才可以被序列化和反序列化
- java.io.ObjectOutputStream代表对象输出流,调用它的writeObject()方法可以对指定的对象进行序列化,将得到的字节序列写入到目标输出流
- java.io.ObjectOutputStream代表对象输入流,调用它的readObject()方法从输入流中读取字节序列,反序列化得到对象
- 如果要自定义类的反序列化方式,需要在被反序列化的类中重写readObject()方法
序列化的简单实现
- 创建一个对象输出流,用文件输出流将其实例化
- 调用对象输出流的writeObject()方法,将对象序列化
- 调用对象输入流将序列化的字节还原成对象
代码如下
package java_fanxuliehau;
import java.io.*;
class Name implements Serializable{ // 创建一个用于序列化和反序列化的对象
private String zxcv;
public String getName() {
return zxcv;
}
public void setName(String zxcv) {
this.zxcv = zxcv;
}
}
// xlh()函数序列化过程;fxlh()是反序列过程
class XLH{
public void fxlh() throws IOException{
FileOutputStream fos = new FileOutputStream("xxx.txt"); // 创建一个文件输出流
ObjectOutputStream oos = new ObjectOutputStream(fos); // 实例化对象输出流的对象
Name name = new Name();
name.setName("zxcv");
oos.writeObject(name); // 将name对象进行序列化,保存到xxx.txt中
oos.close();
System.out.println("Name对象已经序列化成功了");
}
public void xlh() throws IOException, ClassNotFoundException{
FileInputStream fis = new FileInputStream("xxx.txt"); // 创建一个文件输入流
ObjectInputStream ois = new ObjectInputStream(fis); // 实例化文件输入流对象
Name name = (Name)ois.readObject(); // 从xxx.txt中将字节序列转换成Name对象
System.out.println("Name对象反序列化成功了!");
System.out.println(name.getName());
ois.close();
}
}
public class object{
public static void main(String[] argv) throws IOException, ClassNotFoundException {
XLH xlh = new XLH();
xlh.xlh();
xlh.fxlh();
}
}
通过运行结果,可以看到,反序列化的输出了Name对象序列化之前的被赋予的zxcv的值。证明了序列化到反序列化的过程,数据是不变的。在序列化中理论上会生成xxx.txt文件保存Name对象的字节序列,但是在测试的时候,并没有生成xxx.txt文件,还不清楚原因
在Java中执行系统命令的函数是Runtime.getRuntime().exec()
重写Name类的readObject()方法,实现调用计算机的功能
class Name implements Serializable{
private String zxcv;
public String getName() {
return zxcv;
}
public void setName(String zxcv) {
this.zxcv = zxcv;
}
private void readObject(ObjectInputStream in) throws InterruptedException, IOException, ClassNotFoundException {
//先调用默认的readObject()方法
in.defaultReadObject();
//重写,执行系统命令calc.exe
Runtime.getRuntime().exec("calc.exe");
}
}
执行后,会调用windows中的计算机程序
Apache Commons Collection反序列化的思路
-
Apache Commons Collections中的InvokerTransformer接口实现了反射链(大意应该是一些列的连锁反应),通过反射机制来执行任意命令
-
Apache Commons Collections也实现了一个TransformedMap类,实例化需要Map和Transformer作为参数传入
-
TransformMap中的任意项的Key或者Value被修改,相应的Transformer的transform()方法就会被调用
-
AnnotationInvocationHandler这个类可序列化,且该类有一个成员变量是Map类型,并且在重写readObjetc()方法中,会修改成员变量的值
现在整理一下思路:在对AnnotationInvocationHandler 类进行实例化的时候,传入TransformedMap对象作为这个类的成员变量。AnnotationInvocationHandler 这个类反序列化的时候,触发了readObject()函数导致修改了成员变量TransformedMap的值,然后TransformMap中的值被修改后会触发InvokerTransformer()中的transform()方法,然后这个方法通过反射链再去执行例如调用计算器之类的函数Runtime.getRuntime().exec(“calc.exe”);
参考文章:https://xie1997.blog.csdn.net/article/details/84504405
https://blog.csdn.net/xlgen157387/article/details/79840134