#序列化和反序列化
序列化 (Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。
反序列化:从存储区中读取该数据,并将其还原为对象的过程,称为反序列化。
- Java反序列化及命令执行代码测试
- 在IDE创建一个package包,包名为SerialTest
- 包中创建三个类文件 Main Person SerializableTest
Main文件代码
// 执行DOS命令
package SerialTest;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.nio.charset.Charset;public class Main { public static void main(String args[]) throws IOException, InterruptedException { Process p = Runtime.getRuntime().exec("ipconfig"); java.io.InputStream is = p.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("GBK"))); p.waitFor(); if (p.exitValue() != 0){ // 命令执行失败 进入错误处理步骤中 } String s = null; while ((s = reader.readLine()) != null){ System.out.println(s); } }}
Person 文件
// 待调用类
package SerialTest;import com.sun.xml.internal.ws.server.ServerRtException;import java.io.Serializable;class Person implements Serializable { private String name; private int age; private String sex; private int height; public Person(){ this.name = null; this.age = 0; this.sex = null; this.height = 0; } public Person(String name,int age,String sex,int height){ this.name = name; this.age = age; this.sex = sex; this.height = height; } public String toString(){ return "Persion{" + "name=" + name + ", age=" + age + ", sex=" + sex + ", height=" + height + "}"; }}
SerializableTest 文件
// 序列化
package SerialTest;import java.io.*;public class SerializableTest { public static void main(String[] args) throws Exception { serialPerson(); Person person = deserialPerson(); System.out.println(person); } /** * Person 对象序列化 * @throws IOException */ private static void serialPerson() throws IOException{ Person person = new Person("阿杰",20,"男",175); ObjectOutputStream oos = new ObjectOutputStream( new FileOutputStream(new File("F:/person.txt")) ); oos.writeObject(person); System.out.println("person 对象序列化成功!"); oos.close(); } /** * Person 对象反序列化 * @return * @throws Exception */ private static Person deserialPerson() throws Exception { ObjectInputStream ois = new ObjectInputStream( new FileInputStream(new File("F:/person.txt")) ); Person person = (Person)ois.readObject(); System.out.println("person 对象反序列化成功!"); return person; }}
代码分析
java中 在ObjectOutputStream类中 有一个writeObject()方法 把指定的对象进行序列化后把字节序列写到一个目标输出流中ObjectInputStream类中 readObject()方法 从一目标输入流中读取字节序列进行反序化成一个对象 再把结果返回代码中定义了一个person类 类中含有成员属性 使用writeObject()方法 把对象序列化写进一个文本里 然后在使用readObject() 方法 从文件中读取字节序列 在还原成源对象
目前没有此文件 运行后
写入文件中的是字节序列 通过readObject()方法 再将对象还原。
如果对象不是字符串 而是一些命令 则反序列化后 利用java exec命令函数进行执行 就会产生命令执行漏洞。
在Web应用中有时候程序员为了考虑灵活性、简洁性,会在代码调用代码或命令执行函数去处理。比如当应用在调用一些能将字符串转化成代码的函数时,没有考虑用户是否能控制这个字符串,将造成代码执行漏洞。同样调用系统命令处理,将造成命令执行漏洞。php(system()、shell_exec()、exec()、eval())JAVA中的命令执行漏洞(struts2/ElasticsearchGroovy等)ThinkPHP命令执行
比如下图:
执行了ipconfig系统命令
- WebGoat_Javaweb靶场反序列化测试
cmd 输入 java -jar webgoat-server-8.0.1.jar
注:JDK8 无法运行 版本较低 安装11以上的版本
浏览器输入 127.0.0.1:8080/WebGoat
注册一个账号即可
部分源码示例
public static Object fromString(String s) throws IOException, ClassNotFoundException{ byte[] data = Base64.getDecoder().decode(s); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data)); Object o = ois.readObject(); ois.close(); return o; } public static String toString(Serializable o) throws IOException{ ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(o); oos.close(); return Base64.getEncoder().encodeToString(baos.toByteArray()); }
将对象序列化后 进行base64加密 变成了图片中的一堆字符
rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l
字符串 ——> 字节序列 序列化 writeObject()
字节序列 ——> 字符串 反序列化 readObject()
如何构造payload?
calc ----> 序列化 ----> base64加密=rO0AB格式开头的字符串
java -Dhibernate5 -cp hibernate-core-5.4.9.Final.jar;ysoserial-master-30099844c6-1.jar ysoserial.GeneratePayload Hibernate1 calc.exe > payload.bin