Java反序列化漏洞简单理解 反序列化原理
关于反序列化的原理不在多说,和php类似,序列化的数据是方便存储的,而存储的状态信息想要再次调用就需要反序列化
Java反序列化的API实现
实现方法
序列化:ObjectOutputStream类
–> writeObject()
注:该方法对参数指定的obj对象进行序列化,把字节序列写到一个目标输出流中,输出的文件为二进制
注:该方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回
对象可序列化的要求
序列化实例 下面给出一个序列化的实例,首先是实现Serializable
接口待序列化对象
1 2 3 4 5 6 7 8 public class Employee implements java .io .Serializable { public String name; public String identify; public void mailCheck () { System.out.println("This is the " +this .identify+" of our company" ); } }
序列化类代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import java.io.*;public class sdemo { public static void main (String [] args) { Employee e = new Employee(); e.name = "admin" ; e.identify = "admin" ; try { FileOutputStream fileOut = new FileOutputStream("E:\\test\\test.db" ); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(e); out.close(); fileOut.close(); System.out.printf("数据保存在 E:\\test\\test.db文件中" ); }catch (IOException i) { i.printStackTrace(); } } }
执行函数,如下
文件内容如下,也不是很好看懂,毕竟是二进制文件,直接打开会乱码
而文件的二进制形态是什么样呢?
java序列化的数据库一般都是aced0005开头,当然严格来说应该是aced开头,0005有时候会不太一样,我昨天的序列化数据就是2005,查询某些资料说是跟什么版本有关
下面我们对 test.db 文件进行反序列化,代码如下
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 import java.io.*;public class UnSDemo { public static void main (String [] args) { Employee e = null ; try { FileInputStream fileIn = new FileInputStream("E:\\test\\test.db" ); ObjectInputStream in = new ObjectInputStream(fileIn); e = (Employee) in.readObject(); in.close(); fileIn.close(); }catch (IOException i) { i.printStackTrace(); return ; }catch (ClassNotFoundException c) { System.out.println("未发现test.db文件" ); c.printStackTrace(); return ; } System.out.println("反序列化成功..." ); System.out.println("Name: " + e.name); System.out.println("identify: " +e.identify); } }
执行结果如下
反序列化漏洞 与php
饭学列化漏洞类似,要想产生漏洞,必要的条件就是参数可控啊
而我们在利用反序列化漏洞的时候肯定是想getshell
或者命令执行啊,但是默认的readobject
方法是无法帮我们实现这些要求的,下面说一下漏洞的两大成因
开发失误
开发人员对反序列化完全没有进行安全审查,在被序列化的对象类中重写了readobject
方法,那么在反序列的过程中,会使用被反序列化类的readObejct
方法
如下代码会成功弹出计算器
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 package com.test;import java.io.*;public class test { public static void main (String args[]) throws Exception { UnsafeClass Unsafe = new UnsafeClass(); Unsafe.name = "弹出计算器" ; FileOutputStream fos = new FileOutputStream("object" ); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(Unsafe); os.close(); FileInputStream fis = new FileInputStream("object" ); ObjectInputStream ois = new ObjectInputStream(fis); UnsafeClass objectFromDisk = (UnsafeClass)ois.readObject(); System.out.println(objectFromDisk.name); ois.close(); } } class UnsafeClass implements Serializable { public String name; private void readObject (java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); Runtime.getRuntime().exec("calc.exe" ); } }
如上图我们在UnsafeClass类中定义了name属性,并且重写了readobject方法,在原有的基础上添加了执行命令的代码,最中弹出计算器
而在实际环境中,有些常识的开发者都不会直接将命令写在readObject中,因此此处就需要通过反射链来进行任意代码执行了
反射链
end 下章分析Commons Collections
链