Apache shiro一个开源框架,提供身份验证、授权、密码学和会话管理。
Apache shiro<1.2.4 版本存在反序列化漏洞
原理
shiro的记住我的功能,是通过cookie的rememberMe值来实现,当后端接收到来自未经身份验证的用户请求时,后端将执行以下操作来寻找记住的身份
- 检索cookie中的rememberMe的值
- base64解码
- 使用AES加密
- 反序列化
漏洞主要是存在AES采用的是CBC加密模式,密钥是写死在代码里的也就是说,我们只要拿到了密钥,就能够构造恶意代码来触发反序列化漏洞。
搭建
环境搭建 git clone https://github.com/apache/shiro 加载maven的pom.xml文件 然后编辑shiro\samples\web的pom.xml中的pom.xml文件:
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<!-- 这里需要将jstl设置为1.2 -->
<version>1.2</version>
<scope>runtime</scope>
</dependency>
配置好本地tomcat即搭建完成。
分析
搭建好tomcat后,将断点下在org/apache/shiro/mgt/RememberMeManager.java#onSuccessfulLogin,开始debug。
输入密码进行登陆,勾选上RememberMe选项,程序会暂停在断点处:
首先调用forgetIdentity构造方法处理request和response请求,包括在response中加入cookie信息,然后调用rememberIdentity函数,来处理cookie中的rememberme字段。我们f7跟进rememberIdentity函数:
rememberIdentity方法首先会调用getIdentityToRemember函数来获取用户的身份,这里是“root”,接着进入rememberIdentity的构造方法;
跟进#convertPrincipalsToBytes:
跟进#encrypt:
encrypt函数就是调用AES加密对序列化后的”root”进行加密,加密的密钥由getEncryptionCipherKey()得到,跟进getEncryptionCipherKey()函数会发现其值为常量:
回到#rememberIdentity函数进入#rememberSerializedIdentity方法中:
对其进行base64编码,且设置到cookie中。
总结
所以我们在勾选rememberMe时后端进行如下操作
- 序列化用户的身份“root”
- 对root进行AES加密,密钥为常量
- base64进行编码
- 设置cookie的rememberMe的值