概述
Apache Shiro 是一个开源安全框架,提供身份验证、授权、密码学和会话管理。在 Apache Shiro <= 1.2.4 版本中存在反序列化漏洞,而整体的思路就是 Shiro的“记住我”功能是设置 cookie中的 rememberMe 值来实现。当后端接收到来自未经身份验证的用户的请求时,处理 Cookie 的流程是
-
检索 cookie 中 RememberMe 的值
-
Base64 解码
-
使用 AES 解密
-
反序列化
这里的每一步我们都能构造(AES加解密的密钥是写死在代码中的),所以我们可以构造 RememberMe 的值,然后让其反序列化执行。
环境搭建
所需环境
https://www.oracle.com/java/technologies/javase/javase7-archive-downloads.html
https://tomcat.apache.org/download-80.cgi
https://github.com/apache/shiro/tree/shiro-root-1.2.4
搭建
下载完 shiro 后手动增加 version 代码
<version>1.2</version>
随后导入进来,如果有已经打开的项目先 close
导入过程不要动,静静等它下载完!ok后点击锤子后面的 Edit Configurations(下图最上面红色方框那个位置),按下图配置
选出 samples-web:war 后ok,运行
漏洞分析
加密过程
我们要研究的是开启RememberMe后登录的过程,所以从 org.apache.shiro.mgt.AbstractRememberMeManager#onSuccessfulLogin 处开始即可,这里是判断是否开启 RememberMe 然后开始操作的地方,所以从这里下个断点,然后开始 debug
这里先清除认证信息,然后通过 isRememberMe() 判断是否有认证信息,也就是是否开启了 RememberMe 选项框,跟进 rememberIdentity() 方法
首先调用 getIdentityToRemember 函数来获取用户身份(用户 root),接着跟进 rememberIdentity 构造方法
其实 convertPrincipalsToBytes 方法将 accountPrincipals(也就是用户 root)转换为字节形式,我们可以跟进研究一下
这里将 Principals (用户 root)序列化后进行 encrypt 加密,跟进 encrypt 函数
这里我们可以在 debug 中看到加密方式,而 encrypt 函数其实就是调用 AES 方法对序列化后的用户 root 进行加密,加密的密钥由 getEncryptionCipherKey() 得到,然而这里跟进 getEncryptionCipherKey() 函数会发现其值为常量
这一步加密完成后我们继续返回跟进 rememberSerializedIdentity
base64编码后,设置到 cookie 中,其实到这里就是存入的全部流程:
序列化--->AES加密--->base64编码--->设置到 cookie 中的 rememberme 字段
解密过程
从收到认证开始分析,我们从 org.apache.shiro.mgt.DefaultSecurityManager#getRememberedIdentity 开始断点分析
跟进到 getRememberedPrincipals
再跟进到 getRememberedSerializedIdentity 与 convertBytesToPrincipals,看一下分别是用来干嘛的。
首先在 getRememberedSerializedIdentity 我们发现它读取了 cookie 并进行 base64 解码,这里进到 convertBytesToPrincipals 函数
发现在 convertBytesToPrincipals 函数中,对 cookie 进行 decrypt 并反序列化,而 decrypt 明显就是 AES 解密了。
解密的流程到这就结束了。
读取 cookie 中的 rememberMe--->base64 解码--->AES 解密--->反序列化
参考文档
https://saucer-man.com/information_security/396.html#cl-2
https://blog.csdn.net/god_zzZ/article/details/108391075
原文始发于微信公众号(NS Demon团队):shiro 550 反序列化漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论