Apache Shiro源码浅析之从远古洞到最新PaddingOracle CBC
字数 1357 2025-08-25 22:59:03
Apache Shiro安全框架深度解析:从远古反序列化漏洞到Padding Oracle CBC攻击
0x00 前言
Apache Shiro是一个功能强大且易于使用的Java安全框架,提供以下核心功能模块:
- Authentication:身份认证
- Authorization:授权验证
- Session Manager:会话管理
- Cryptography:加密
- Web Support:Web支持
- Caching:缓存
- Concurrency:多线程
- Testing:测试模块
- Run As:用户伪装
- Remember Me:记住我功能
0x01 Shiro源码深度解析
1. Shiro基础配置与使用
1.1 依赖配置(pom.xml)
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
<!-- 其他相关依赖 -->
1.2 Web配置(web.xml)
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
1.3 Shiro核心配置(spring-shiro.xml)
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login.jsp"/>
<property name="filterChainDefinitions">
<value>
/admin/**=authc,roles[admin]
/user/**=user,roles[user]
</value>
</property>
</bean>
2. Shiro核心运行机制
2.1 过滤器链初始化流程
DelegatingFilterProxy代理ShiroFilterFactoryBeanShiroFilterFactoryBean创建AbstractShiroFilter实例- 初始化默认过滤器(11个):
- anon:匿名访问
- authc:需要认证
- logout:注销
- perms:权限验证
- roles:角色验证
- user:RememberMe功能
2.2 请求处理流程
- 请求到达
AbstractShiroFilter.doFilterInternal() - 创建Subject对象:
Subject subject = createSubject(request, response); - 执行过滤器链:
executeChain(request, response, chain);
2.3 RememberMe机制实现
关键类:CookieRememberMeManager
- 从Cookie读取rememberMe值
- Base64解码
- AES解密
- 反序列化为PrincipalCollection对象
0x02 反序列化远古漏洞(Shiro <= 1.2.4)
漏洞成因
AbstractRememberMeManager使用固定AES密钥:private static final byte[] DEFAULT_CIPHER_KEY_BYTES = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");- 攻击者可构造恶意序列化数据,加密后作为rememberMe Cookie发送
- 服务端解密后反序列化导致RCE
漏洞利用
利用ysoserial生成payload并加密:
byte[] payload = generatePayload("CommonsCollections6", "calc");
byte[] encrypted = encryptWithAES(payload, DEFAULT_KEY);
String rememberMe = Base64.encode(encrypted);
修复方案
Shiro 1.2.5+改为启动时随机生成密钥:
setCipherKey(cipherService.generateNewKey().getEncoded());
0x03 Padding Oracle CBC攻击(Shiro <= 1.4.1)
攻击原理
- Shiro对解密失败的响应特征:
- Padding错误:返回
rememberMe=deleteMeCookie - Padding正确:正常响应
- Padding错误:返回
- 利用特征进行Padding Oracle攻击
攻击条件
- 需要至少一个配置了
user过滤器的端点 - 服务端对padding错误有明确响应
攻击步骤
- 构造恶意序列化数据
- 分段进行CBC翻转攻击
- 通过响应判断padding是否正确
- 最终构造出有效的攻击payload
Java实现示例
public void attack(byte[] payload) {
byte[] originCookie = Base64.decode(rememberMe);
CBCResult result = PaddingOracleCBCForShiro.paddingOracleCBC(payload, data -> {
byte[] newCookie = combineArrays(originCookie, data);
return sendRequest(newCookie);
});
System.out.println("Exploit Cookie: " + Base64.encode(result.getFullCookie()));
}
参考资源
防御建议
- 及时升级到最新版本
- 自定义RememberMe加密密钥
- 限制反序列化类白名单
- 监控异常rememberMe请求