Shiro反序列化
在shiro版本小于1.2.5时,被称作shiro-550
,shiro反序列化的产生原因主要是因为rememberMe内容,原因是AES密钥被硬编码在shiro源码中,导致在cookie中的rememberMe可以被插入恶意代码造成代码执行。在1.2.5之后,shiro使用了随机密钥,又因为padding oracle attack导致反序列化,被称作shiro-721
。
环境搭建
jdk版本这里是1.8_66
(下载pom源码下了好久,配maven代理一直有问题,环境是最麻烦的事情)
1、下载shiro1.2.4:
https://codeload.github.com/apache/shiro/zip/shiro-root-1.2.4
2、IDEA打开shiro-root-1.2.4修改shiro-root-1.2.4/samples/web/pom.xml中的jstl的依赖版本为1.2,没有就自己加一个
3、下载pom.xml源代码,如果很慢就配置maven代理。
4、配置Tomacat服务器,详细请看https://blog.csdn.net/qq_36389060/article/details/114895961
5、启动shiro-root-1.2.4/samples/web/src/main/webapp/index.jsp
大概就是这么个效果
shiro是如何处理登录的?
为了方便后面调试,先以调试模式运行项目。来到login.jsp
登录的时候记得选上Remember Me
我们直接来到AbstractShiroFilter类,断点打在doFilterInternal方法,登录的时候会进入这个方法。
一直到execueChain方法,跟进
中间过程省略,一直跟到AuthenticatingFilter类的executeLogin方法,这里是在进行处理登录,登录成功后触发onLoginSuccess方法。
一直来到DefaultSecurityManager类找到rememberMeSuccessfulLogin函数
看到RememberMeManager不为空后,进入AbstractRememberMeManager类的onSuccessfulLogin方法
跟进forgetIdentity方法,在CookieRememberMeManager类
继续跟进forgetIdentity方法,这里在获取cookie
继续跟进 getCookie().removeFrom
可以看到rememberMe和deleteMe字段,并且通过addCookieHeader写入cookie中
继续往下走,回到AbstractRememberMeManager类的onSuccessfulLogin方法中
isRememberMe方法检查我们登陆时是否选中了Remember Me
选项,由于我们登录的时候选了,所以可以直接进去,接着单步步入rememberIdentity方法
convertPrincipalsToBytes方法是转化成bytes,就在这里还进行了serialize序列化。然后判断不为空后进入encrypt方法,单步步入encrypt函数,密码服务为AES/CBC/PKCS5Padding
接着进入getEncryptionCipherKey方法,发现CipherKey就是AbstractRememberMeManager类开头的kPH+bIxk5D2deZiIxcaaaA==
获取了CipherKey返回后进入cipherService.encrypt函数中,生成初始化向量ivBytes后,进入具体的加密函数,最后return。一步步return bytes后,回到之前的rememberIdentity函数,下面的rememberSerializedIdentity实现了记住序列化身份的功能,跟进
在这里把刚刚的序列化数据base64加密后,将信息加入到cookie中。然后一直返回,到AuthenticatingFilter类的executeLogin处理登录,返回成功登陆。
解密流程
测试页面:http://localhost:8080/samples_web_war/account/,带着刚刚登录后的rememberMe字段的cookie
在AbstractShiroFilter类的doFilterInternal方法下断点,单步进入DefaultSecurityManager类的createSubject方法
继续跟进resolvePrincipals方法,单步到getRememberedIdentity,RememberMeManager获取后进入getRememberedPrincipals方法
第一个函数getRememberedSerializedIdentity,可以看到先获取cookie中的值,然后base64解密,生成二进制数后返回
第二个函数convertBytesToPrincipals,先获取解密服务,解密服务不为空后,将二进制数据传入decrypt函数进行解密,之后return deserialize(bytes)
在deserialize方法中有readObject(),触发apache.commons利用链漏洞
整个过程就是:读取cookie中rememberMe值->base64解码->AES解密->反序列化
只要获取到密钥,就可以进行反序列化操作。在1.2.5之后,shiro采取了随机密钥,虽然阻止了shiro550的利用方式。但由于padding oracle attack也导致了反序列化。
Shiro-721
Apache Shiro RememberMe Cookie默认通过AES-128-CBC
模式加密,这种加密方式容易受到Padding Oracle Attack(Oracle填充攻击)
,利用有效的RememberMe Cookie作为Padding Oracle Attack的前缀,然后精心构造 RememberMe Cookie 值来实现反序列化漏洞攻击。
shiro721用到的加密方式是AES-CBC,而且其中的ase加密的key基本猜不到了,是系统随机生成的。而cookie解析过程跟cookie的解析过程一样,也就意味着如果能伪造恶意的rememberMe字段的值且目标含有可利用的攻击链的话,还是能够进行RCE的。
参考文章: