当前位置: 首页 > news >正文

从“Hello World”到漏洞利用:用Java写一个自己的简易版ysoserial(理解Gadget链)

从零构建Java反序列化漏洞:手写简易版Gadget链

当你第一次听说Java反序列化漏洞时,是否对那些神奇的"Gadget链"感到困惑?为什么简单的对象反序列化就能触发命令执行?本文将带你从最基础的Java序列化机制开始,逐步构建一个能弹出计算器的简易漏洞链。不同于直接使用ysoserial工具,我们会从创造者的角度,用不到200行代码还原漏洞本质。适合已经掌握Java基础语法,想深入理解安全原理的开发者。

1. Java序列化机制基础

Java序列化就像把一个对象"拍扁"成字节流,而反序列化则是将这些字节"还原"成活的对象。这个机制在日常开发中常用于网络传输或持久化存储。让我们先看一个最简单的可序列化类:

import java.io.Serializable; public class Person implements Serializable { private String name; public Person(String name) { this.name = name; } // 标准的getter/setter省略... }

要使类可序列化,只需实现Serializable标记接口。序列化和反序列化的基本操作如下:

// 序列化 Person alice = new Person("Alice"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.bin")); oos.writeObject(alice); oos.close(); // 反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.bin")); Person person = (Person) ois.readObject(); ois.close();

关键点:当对象被反序列化时,JVM会调用该类的readObject()方法。如果类中没有自定义这个方法,就会使用默认实现。但如果我们重写它...

2. 危险的readObject重写

让我们修改Person类,加入自定义的readObject方法:

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); // 先调用默认反序列化 System.out.println("[!] 反序列化触发: " + this.name); }

现在当我们反序列化这个对象时,控制台会打印出警告信息。这本身无害,但设想如果这里的代码不是打印日志,而是执行系统命令...

3. 构造第一条Gadget链

真正的漏洞利用需要将多个类的操作"链式"组合起来。让我们创建一个包含Runtime.exec的简单链:

public class ExploitObject implements Serializable { private String command; public ExploitObject(String cmd) { this.command = cmd; } private void readObject(ObjectInputStream ois) throws Exception { ois.defaultReadObject(); Runtime.getRuntime().exec(this.command); } }

测试这个漏洞链:

// 生成恶意序列化数据 ExploitObject exploit = new ExploitObject("calc"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("exploit.bin")); oos.writeObject(exploit); oos.close(); // 受害者反序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("exploit.bin")); ois.readObject(); // 计算器弹出!

这已经是一个完整的漏洞利用,但现实中很少有这么直接的案例。更常见的是通过多个类的组合间接触发命令执行。

4. 多级Gadget链构造

让我们构建一个更接近真实场景的两级调用链。首先定义两个类:

public class Gadget1 implements Serializable { private Runnable action; public void setAction(Runnable action) { this.action = action; } private void readObject(ObjectInputStream ois) throws Exception { ois.defaultReadObject(); this.action.run(); // 关键点:反序列化时自动执行 } } public class Gadget2 implements Serializable, Runnable { private String command; public Gadget2(String cmd) { this.command = cmd; } @Override public void run() { try { Runtime.getRuntime().exec(this.command); } catch (IOException e) { e.printStackTrace(); } } }

利用链的组装方式:

Gadget2 g2 = new Gadget2("calc"); Gadget1 g1 = new Gadget1(); g1.setAction(g2); // 序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("gadget.bin")); oos.writeObject(g1); oos.close(); // 反序列化触发 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("gadget.bin")); ois.readObject(); // 依然弹出计算器

这种间接调用模式正是ysoserial中各种payload的核心思路。通过精心设计的对象关系,让反序列化过程像多米诺骨牌一样触发一连串操作。

5. 防御措施与最佳实践

理解了攻击原理后,我们才能更好地防御。以下是几种常见防护方案:

白名单验证

public class SafeObjectInputStream extends ObjectInputStream { private static final Set<String> ALLOWED_CLASSES = Set.of("java.lang.String", "com.example.SafeClass"); protected SafeObjectInputStream(InputStream in) throws IOException { super(in); } protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { if (!ALLOWED_CLASSES.contains(desc.getName())) { throw new InvalidClassException("Unauthorized deserialization attempt"); } return super.resolveClass(desc); } }

其他防御手段

  • 使用第三方安全库如Apache Commons IO的ValidatingObjectInputStream
  • 对序列化数据添加数字签名
  • 完全禁用不受信任源的序列化功能

6. 从原理看真实漏洞

理解了基础原理后,再看Shiro等框架的反序列化漏洞就更容易理解了。以Shiro RememberMe为例:

  1. 攻击者构造恶意序列化对象
  2. 使用已知AES密钥加密后作为Cookie发送
  3. Shiro解密后自动反序列化触发漏洞

整个过程与我们手写的Demo本质相同,只是多了加密层和框架自动处理的环节。

http://www.jsqmd.com/news/965978/

相关文章:

  • Delphi轻量级网卡实时流量监控工具,支持上传下载吞吐量精确统计
  • Python 并发性能调优:深入 CPython 解释器 GIL 锁(Global Interpreter Lock)物理限制与多进程、多线程、协程异步 I/O 混合高并发底座实战
  • 2026产品宣传动画服务商评测:香港安全警示动画、上海事故还原动画、上海工业3D动画、事故还原动画、北京3D动画选择指南 - 优质品牌商家
  • Switch游戏文件管理难题?5个核心功能让NSC_BUILDER成为你的瑞士军刀
  • 保姆级教程:用Docker 2.0.0镜像5分钟搞定RocketMQ Dashboard部署与监控
  • 2026年智能体开发平台服务实力排行:Agent平台、agent开发、无代码、智能体搭建、智能问数、私有化AI低代码选择指南 - 优质品牌商家
  • 生成式 AI 驱动钓鱼攻防成本异化与智能代理防御体系研究
  • 终极小说下载指南:100+网站一键永久保存,打造你的私人数字图书馆
  • 2026医疗健康数据治理技术解析与优质服务商参考:企业数据治理方案/企业数智融合方案/全链路数据治理库/医疗健康数据治理/选择指南 - 优质品牌商家
  • 大模型评估指标全解析:困惑度、BLEU、ROUGE、BERTScore怎么用?
  • 零代码AI工具实战指南:6款真正免编程的智能应用方案
  • Flowable实战:如何精准获取当前任务的下一个节点(含会签与网关处理)
  • MCP协议实战:用gpt-oss统一调用多LLM的兼容性压测
  • NLP文本预处理与EDA实战指南:从SMS分类看数据清洗核心步骤
  • 【LangChain-AI】聊天模型--流式传输
  • YOLO11部署优化:ONNX精简 | 使用ONNX GraphSurgeon剔除冗余节点,配合算子融合,推理延迟再降20%
  • Python速通实战课:90分钟掌握文件处理与错误调试
  • MinIO文件分享与权限管理实战:mc share/policy命令生成临时链接与设置桶策略
  • PDFBox实战:批量清理上百份带斜体水印的PDF文档,我是如何用Java自动化搞定的
  • Web Speech API语音识别实战:从‘玩具Demo’到‘可用产品’的避坑指南
  • 2026年6月国内口碑好的纸箱包装袋生产厂家推荐,成都PE平口袋/油脂纸箱包装袋,纸箱包装袋直销厂家哪家靠谱 - 品牌推荐师
  • DsHidMini终极指南:如何在Windows 10/11上完美使用PS3手柄
  • DP2232H的MPSSE双引擎怎么玩?一个USB口同时调试JTAG和UART的实战配置
  • 2026万向导缆器选型全攻略:船用掣链器/单点式系泊导缆孔/卷车/导缆滚轮/托架/滚柱导缆器/系缆桩/羊角单滚轮导缆器/选择指南 - 优质品牌商家
  • RAPTOR检索框架:多粒度分层融合的工程化实践
  • 超越提示词工程:构建下一代智能 AI Agent 的技术架构与实践指南
  • AI测试入门:如何设计LLM的Prompt?这份提示词工程指南请收好
  • 程序员读《不速之客》:从间谍故事里学到的3个系统安全设计原则
  • ICC实战笔记:Chip Finishing阶段这6个坑,新手最容易踩(附详细命令与避坑指南)
  • Flowable实战:如何动态获取流程当前节点与候选人信息(附完整Java代码)