服务攻防-shiro框架原理分析到不出网环境利用
字数 1399 2025-08-09 18:44:09
Apache Shiro框架安全分析与不出网环境利用
1. Shiro框架概述
Apache Shiro是一个强大且易用的Java安全框架,提供以下核心功能:
- 身份验证(Authentication)
- 授权(Authorization)
- 密码加密
- 会话管理
2. Shiro反序列化漏洞原理
2.1 漏洞成因
Shiro框架在rememberMe功能中存在反序列化漏洞,关键点:
- 使用AES加密的Cookie值存储在
rememberMe字段中 - 默认使用硬编码的AES加密密钥(CBC模式,PKCS5Padding)
- 攻击者获取密钥后可构造恶意序列化对象
2.2 影响版本
- 理论上所有版本的Shiro都可能受影响
- 只要rememberMe的AES加密密钥泄露,无论Shiro版本如何都存在风险
3. 漏洞利用条件
3.1 必要条件
- 目标系统使用了Shiro框架
- 目标系统开启了rememberMe功能
- 攻击者获取了AES加密密钥
3.2 密钥获取途径
- 默认密钥:
kPH+bIxk5D2deZiIxcaaaA== - 代码泄露
- 配置文件泄露
- 其他信息泄露途径
4. 漏洞利用流程
4.1 标准利用步骤
- 识别Shiro应用(特征:Cookie中包含rememberMe字段)
- 收集可能的AES密钥
- 构造恶意序列化payload
- 使用正确的密钥加密payload
- 将加密后的payload作为rememberMe Cookie发送
4.2 不出网环境利用技术
在目标服务器无法出网的情况下,需要特殊技术:
4.2.1 利用链选择
- 优先使用不依赖外部连接的利用链
- 常见可用链:
- CommonsBeanutils
- CB链
- 其他本地执行链
4.2.2 内存马注入
- 注入Servlet型内存马
- 注入Filter型内存马
- 注入Listener型内存马
4.2.3 本地命令执行
- 通过Runtime.exec执行本地命令
- 写入webshell到可访问目录
- 添加计划任务
- 修改系统配置
5. 防御措施
5.1 临时缓解方案
- 关闭rememberMe功能
- 升级Shiro版本
5.2 根本解决方案
-
更换默认AES密钥
- 生成强随机密钥(至少128位)
- 修改shiro.ini或配置类中的
cipherKey配置
-
反序列化防护
- 实现ObjectInputFilter
- 限制反序列化类白名单
-
其他安全配置
- 禁用危险的Shiro功能
- 定期审计密钥管理
6. 检测与验证
6.1 漏洞检测方法
- 发送带有rememberMe Cookie的请求
- 观察响应特征(如UID乱码、错误信息等)
- 使用已知密钥测试加密/解密
6.2 验证工具
- ShiroExploit
- ShiroAttack2
- Burp插件(如ShiroScan)
7. 高级利用技巧
7.1 密钥爆破
- 基于常见密钥字典爆破
- 结合其他信息推测密钥
7.2 绕过技巧
- 特殊字符处理
- 编码转换问题利用
- 填充Oracle攻击
8. 不出网环境下的后渗透
8.1 信息收集
- 通过反射调用获取系统信息
- 读取配置文件
- 枚举环境变量
8.2 权限维持
- 写入内存马
- 修改现有Filter/Servlet
- 注册MBean
8.3 横向移动
- 读取本地凭据
- 利用本地服务漏洞
- 访问内网服务
附录:常用命令与工具
- 密钥生成:
keytool -genseckey -keystore shiro.keystore -storetype jceks -alias shiro -keyalg AES -keysize 128 -storepass 123456
- 检测脚本示例(Python):
import base64
import uuid
from Crypto.Cipher import AES
def encrypt(key, payload):
iv = uuid.uuid4().bytes
cipher = AES.new(base64.b64decode(key), AES.MODE_CBC, iv)
return base64.b64encode(iv + cipher.encrypt(pad(payload)))
- 内存马注入代码片段(Java):
// 通过反射注册Filter内存马
ServletContext servletContext = request.getSession().getServletContext();
Field field = servletContext.getClass().getDeclaredField("context");
field.setAccessible(true);
// ... 省略具体实现 ...