惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
Cisco Talos Blog
Cisco Talos Blog
Y
Y Combinator Blog
罗磊的独立博客
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
GbyAI
GbyAI
The GitHub Blog
The GitHub Blog
S
SegmentFault 最新的问题
J
Java Code Geeks
C
CXSECURITY Database RSS Feed - CXSecurity.com
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 聂微东
B
Blog RSS Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
P
Privacy & Cybersecurity Law Blog
L
LangChain Blog
L
LINUX DO - 热门话题
Hugging Face - Blog
Hugging Face - Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Privacy International News Feed
F
Full Disclosure
S
Schneier on Security
T
Tenable Blog
量子位
NISL@THU
NISL@THU
Latest news
Latest news
V
Visual Studio Blog
C
Check Point Blog
aimingoo的专栏
aimingoo的专栏
博客园_首页
K
Kaspersky official blog
L
Lohrmann on Cybersecurity
美团技术团队
P
Proofpoint News Feed
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Vercel News
Vercel News
人人都是产品经理
人人都是产品经理
The Hacker News
The Hacker News
AWS News Blog
AWS News Blog
S
Securelist
The Register - Security
The Register - Security
G
GRAHAM CLULEY
Project Zero
Project Zero
T
The Blog of Author Tim Ferriss
Recent Announcements
Recent Announcements
Engineering at Meta
Engineering at Meta
C
Cisco Blogs

SumSec's Blog

AI Agent 工程的必然演进:CLI、Skills、Harne… 从安全角度谈Java反射机制--前章 · SUMSEC 从安全角度谈Java反射机制--终章 · SUMSEC 逆向学习fastjson反序列化始 · SUMSEC 2020年研究回顾总结 · SUMSEC Abstract syntax tree classes for … Analyzing data flow in Java · SUM… Annotations in Java · SUMSEC Basic query for Java code · SUMSEC BypassSuper使用介绍说明 · SUMSEC CodeQL Create OpenJdk/Jdk8 Databa… CodeQL library for Java · SUMSEC Navigating the call graph · SUMSEC Overflow-prone comparisons in Jav… Types in Java · SUMSEC Working with source locations · S… Aliases · SUMSEC Expression · SUMSEC Formulas · SUMSEC Javadoc · SUMSEC Modules · SUMSEC Predicates · SUMSEC Queries · SUMSEC Type · SUMSEC Variables · SUMSEC 一道shiro反序列化题目引发的思考 · SUMSEC 修改ysoserial使其支持任意代码执行 · SUMSEC 自定义 ClassLoader 隔离运行不同版本jar包的方式 ·… 2020网鼎杯---Java文件上传wp · SUMSEC CNVD-2020-10487(CVE-2020-1938)tom… JDSRC安全课笔记 · SUMSEC Java反序列化链回显解决方案 · SUMSEC Skipped breakpoint because it hap… Windows Terminal 配置文件 · SUMSEC bypass 学习笔记之绕安全狗bypass safedog · … 一次意外的代码审计----JfinalCMS审计 · SUMSEC 一篇文章读懂Java代码审计之XXE · SUMSEC 从安全角度谈Java反射机制--序章 · SUMSEC 小楼昨夜又春风,你知ysoserial-Gadget-URLDNS… 春眠不觉晓,RCE知多少? · SUMSEC 漫谈Commons-Collections反序列化 · SUMSEC 白头搔更短,SSTI惹人心! · SUMSEC 记一次面试题 · SUMSEC About Me 关于我 · SUMSEC Apache Flink任意Jar包上传导致远程代码执行 · SU… CVE-2019-1388 UAC提权复现 · SUMSEC CVE-2019-16097 || Harbor任意管理员注册漏洞… Python加密shellcode免杀 · SUMSEC Telegram机器人作为渗透测试框架 · SUMSEC VM虚拟机无法安装vmtools解决|本程序需要您将此虚拟机上安装… 谁能想到,电视遥控器竟成了 Vibe Coding 神器 · SU… 从手改 Skill 到自动进化:评测结果和执行轨迹如何让 Agen… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的SKIL… 模型人人都能用,什么才是你能带走的?我的答案是一个可进化的Skil… AI 时代 ShiroAttack2 5.x:修改了什么 · SU… 在 AGI 降临前,先给 AI 开一条”脑内弹幕”通道 · SUM… 一篇博文,三种时间:网页幻灯与 Remotion 动效的交付逻辑 … 🔍 别让大模型”想太多”:SKILL开发中的语义陷阱与抗幻觉设计 … 2022 年年度总结 · SUMSEC Java Swing To RCE 漏洞分析 · SUMSEC SpringBoot GatewayEL表达式漏洞分析 · SUM… Sensitive keys in codebases · SUM… 论如何优雅注入 Java 内存马 · SUMSEC SUMSEC 知识点 · SUMSEC VMWare Workspace ONE Access Auth … Spring Framework RCE CVE-2022-229… 相似度算法调研 · SUMSEC CVE-2022-33891 Apache Spark shell… 正则匹配配置不当 · SUMSEC Spring Data MongoDB SpEL CVE-2022… CodeQl Usage Tricks · SUMSEC Spring Boot RCE到内存马探索 · SUMSEC Shiro后渗透拓展面 · SUMSEC shiro反序列化漏洞攻击拓展面–修改key · SUMSEC GitHub Java CodeQL CTF · SUMSEC Hack-Tools 转化成Web · SUMSEC CodeQL与Shiro550碰撞 · SUMSEC CodeQL初见Shiro550 · SUMSEC CodeQL与AST之间联系 · SUMSEC Java加载动态链接库 · SUMSEC Log4j2 漏洞分析 · SUMSEC Interprocedural-Analysis 过程间分析 · … Data Analysis Foundation 数据分析基础 ·… Data Flow Analysis · SUMSEC Intermediate Representation 中间代表(… PII泄露–用CodeQL识别日志中的PII数据 · SUMSEC CodeQL workshop for Java: Unsafe … 前言 · SUMSEC 漏洞环境的搭建 · SUMSEC Fastjson回显 · SUMSEC Tomcat通用回显学习笔记 · SUMSEC 从Java反序列化漏洞题看CodeQL数据流 · SUMSEC 概述 · SUMSEC 记一次Log4j失败的Gadget挖掘记录 · SUMSEC Ysoserial改造记录 · SUMSEC JNDI注入 · SUMSEC shiro JRMP gadget · SUMSEC Fastjson MySQL gadget复现 · SUMSEC 2021年度总结 · SUMSEC
漫谈Java反序列化 · SUMSEC
2026-07-17 · via SumSec's Blog

漫谈Java反序列化

前言

   Java的反序列化漏洞探索出了Java安全的新纪元。开发人员为什么要反序列化呢?众所周知,用户和服务器进行交互时,会传输一下数据,数据传输前需要格式化,将数据转化成服务器认可的格式。比例:JSONXML
   JSONXML的优点是兼容性比较强,是通用的数据交互格式。缺点是不支持复杂的数据类型。故开发人员面对需要复杂的数据类型是将数据反序列化,以来达数据交互的目的。
   Java程序在运行时,会产生大量的数据。有些时候,我们需要将内存中的对象信息存储到磁盘或者通过网络发送给第三者,此时,就需要对对象进行序列化操作。当我们需要从磁盘或网络读取存储的信息时,即为反序列化。简单理解,序列化即将内存中的对象信息转换为字节流并存储在磁盘或通过网络发送。反序列化,即从磁盘或网络读取信息,直接转换为内存对象。

PS: 为避免代码太长而导致的阅读效果,故将完整的实验代码全部已经上传至 https://github.com/SummerSec/JavaLearnVulnerability


反序列化demo

知识补充

反序列化漏洞基本条件

  1. Java反序列化类一定要实现Serializabe接口
  2. 所有的Java反序列化漏洞都是用通过readObject()实现
  3. 所有反序列化数据都是要通过writeObject()函数实现
    在这里插入图片描述

SerialVersionUID
   Java的序列化的机制通过判断serialVersionUID来验证版本的一致性。在反序列化的时候与本地的类的serialVersionUID进行比较,一致则可以进行反序列化,不一致则会抛出异常InvalidCastException。IDEA是可以自动生成一个serialVersionUID,需要设置如下。
在这里插入图片描述
在这里插入图片描述


案例DEMO

javaSerializableDemo1源码

public class javaSerializableDemo1 implements Serializable {
	// 序列版本ID
    private static final long serialVersionUID = -1877568378649280904L;
    private String username;
    private String password;
    private Integer age;
    private Integer IdCard;
    private Date time;
    

    public javaSerializableDemo1(String username, String password, Integer age, Integer idCard, Date time) {
        this.username = username;
        this.password = password;
        this.age = age;
        IdCard = idCard;
        this.time = time;
    }

   // 省略一部分set、get方法。
   
    @Override
    public String toString() {
        return "javaSerializableDemo{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", age=" + age +
                ", IdCard=" + IdCard +
                ", time=" + time +
                '}';
    }
}

javaUnSerizableDemo1源码,一般情况下对象写入流writerObject()对象的输出流readObject是分开实现的。

public class javaUnSerializableDemo1 {
    public static void main(String[] args) {
        javaSerializableDemo1 demo = new javaSerializableDemo1("Summer","6666888",18,666666,new Date());

        System.out.println("serializable: " + demo);

        // 将对象写入文件中
        ObjectOutputStream oos = null;
        try {
            FileOutputStream fileOutputStream = new FileOutputStream("tempFile.txt");
            oos = new ObjectOutputStream(fileOutputStream);

            // 序列化
            oos.writeObject(demo);

            oos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 读文件
        File file = new File("tempFile.txt");
        ObjectInputStream ois = null;
        try {

            FileInputStream fileInputStream = new FileInputStream(file);
            ois = new ObjectInputStream(fileInputStream);

            // 反序列化
            javaSerializableDemo1 newdemo = (javaSerializableDemo1) ois.readObject();
            System.out.println("unserializable: " + newdemo);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述
由于是字节码,直接打开是乱码。
在这里插入图片描述
用vscode插件hexdump查看生成的文件。
在这里插入图片描述
或者使用SerializationDumper.jar工具,效果如下部分截图。
下载地址:https://github.com/NickstaDB/SerializationDumper
在这里插入图片描述

   这个DEMO中实现了笔者前文所提及到的三要素,但似乎你还看不出来漏洞的存在的地方。


漏洞DEMO

   下面会以一个存在的漏洞demo,带你更进一步理解Java反序列化的危害。
漏洞源码

public class VulnerabilityClass implements summer.serializable.Serializable {
    private static final long serialVersionUID = 5550839108669505813L;
    private String username;
    private String password;
    private Date date;

   
    private void readObject(java.io.ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // 加入执行命令代码
        Runtime.getRuntime().exec("calc");
    }

    public VulnerabilityClass() {
    }

    // 省略set、get方法
    @Override
    public String toString() {
        return "VulnerabilityClass{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", date=" + date +
                ", id=" + id +
                '}';
    }

    public VulnerabilityClass(String username, String password, Date date, Integer id) {
        this.username = username;
        this.password = password;
        this.date = date;
        this.id = id;
    }

漏洞利用

public static void main(String[] args) {
       // 调用序列化方法
        Serilizable();
        // 反序列化方法
        UnSerializable();

    }
    public static void Serilizable(){
        VulnerabilityClass clazz = new VulnerabilityClass();
        clazz.setDate(new Date());
        clazz.setId(1314520);
        clazz.setPassword("summer6666");
        clazz.setUsername("summer");

        // 写文件
        File file = new File("tempFile3");
        try {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
            oos.writeObject(clazz);
            System.out.println("serilizable: " + clazz);
            oos.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static Object UnSerializable(){

        File file = new File("tempFile3");
        try {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
            VulnerabilityClass clazz = (VulnerabilityClass) ois.readObject();
            System.out.println("unserializable: " + clazz);
            ois.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        return VulnerabilityClass;
    }

}

在这里插入图片描述

漏洞成因分析

   在漏洞源码中的readObject()方法,第一行是默认的反序列化方法defaultReadObject(),但是下面一行是添加了Runtime.getRuntime().exec("calc"),虽然这里简单粗暴的将执行命令的代码写入了方法。实际的情况下,开发人员是不会这么做,笔者这里简单展示一下漏洞原理。实际情况都是攻击者通过各种伪造方法、修改、重定义等方法最后到达执行命令。

private void readObject(java.io.ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // 加入执行命令代码
        Runtime.getRuntime().exec("calc");
    }

总结

   反序列化漏洞三要素实现Serializabe接口、readObject()writeObject()方法,缺一不可。但试想一下,如果用户控制了readObject亦或者是writeObject方法,那么是不是可以造成反序列化漏洞呢?其实也有一个问题控制不了writeObject方法,因为其在服务器内,或者是Java应用内,我们不可能去修改内部代码。所以说只能通过控制readObject方法,这里的控制得打双引号。问题来了,前人大佬们已经研究出来许多控制方法,经典AnnotationInvocationHandlerBadAttributeValueExpException类均满足条件,下篇文章带你分析。
   如果要深入理解反序列化漏洞可以去学习反序列化利用工具ysoserial。网上很多反序列化文章基本上都是研究commons-collection反序列化,但其实commons-colection反序列化链是很复杂,不建议新手小白学习。ysoserial-Gadget-URLDNS这条反序列化链建议新手小白学习,比较简单。推荐文章小楼昨夜又春风,你知ysoserial-Gadget-URLDNS多少?,这篇文章全方位的解释URLDNS这条链的利用、成因,相对其他作者写的文章分析更加全面,基本上你知道或者不知道都在文章里面。