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,没有就自己加一个

1

3、下载pom.xml源代码,如果很慢就配置maven代理。

4、配置Tomacat服务器,详细请看https://blog.csdn.net/qq_36389060/article/details/114895961

2

5、启动shiro-root-1.2.4/samples/web/src/main/webapp/index.jsp

3

大概就是这么个效果

shiro是如何处理登录的?

为了方便后面调试,先以调试模式运行项目。来到login.jsp

4

登录的时候记得选上Remember Me

我们直接来到AbstractShiroFilter类,断点打在doFilterInternal方法,登录的时候会进入这个方法。

5

一直到execueChain方法,跟进

6

中间过程省略,一直跟到AuthenticatingFilter类的executeLogin方法,这里是在进行处理登录,登录成功后触发onLoginSuccess方法。

7

一直来到DefaultSecurityManager类找到rememberMeSuccessfulLogin函数

8

看到RememberMeManager不为空后,进入AbstractRememberMeManager类的onSuccessfulLogin方法

9

跟进forgetIdentity方法,在CookieRememberMeManager类

10

继续跟进forgetIdentity方法,这里在获取cookie

11

继续跟进 getCookie().removeFrom

12

可以看到rememberMe和deleteMe字段,并且通过addCookieHeader写入cookie中

继续往下走,回到AbstractRememberMeManager类的onSuccessfulLogin方法中

13

isRememberMe方法检查我们登陆时是否选中了Remember Me选项,由于我们登录的时候选了,所以可以直接进去,接着单步步入rememberIdentity方法

14

15

16

convertPrincipalsToBytes方法是转化成bytes,就在这里还进行了serialize序列化。然后判断不为空后进入encrypt方法,单步步入encrypt函数,密码服务为AES/CBC/PKCS5Padding

17

接着进入getEncryptionCipherKey方法,发现CipherKey就是AbstractRememberMeManager类开头的kPH+bIxk5D2deZiIxcaaaA==

18

获取了CipherKey返回后进入cipherService.encrypt函数中,生成初始化向量ivBytes后,进入具体的加密函数,最后return。一步步return bytes后,回到之前的rememberIdentity函数,下面的rememberSerializedIdentity实现了记住序列化身份的功能,跟进

19

20

在这里把刚刚的序列化数据base64加密后,将信息加入到cookie中。然后一直返回,到AuthenticatingFilter类的executeLogin处理登录,返回成功登陆。

21

解密流程

测试页面:http://localhost:8080/samples_web_war/account/,带着刚刚登录后的rememberMe字段的cookie

在AbstractShiroFilter类的doFilterInternal方法下断点,单步进入DefaultSecurityManager类的createSubject方法

22

23

继续跟进resolvePrincipals方法,单步到getRememberedIdentity,RememberMeManager获取后进入getRememberedPrincipals方法

24

25

第一个函数getRememberedSerializedIdentity,可以看到先获取cookie中的值,然后base64解密,生成二进制数后返回

26

第二个函数convertBytesToPrincipals,先获取解密服务,解密服务不为空后,将二进制数据传入decrypt函数进行解密,之后return deserialize(bytes)

27

在deserialize方法中有readObject(),触发apache.commons利用链漏洞

28

整个过程就是:读取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的。

参考文章:

https://www.freebuf.com/articles/web/379451.html

https://juejin.cn/post/7133959651653058574


Shiro反序列化
http://example.com/2025/02/26/Shiro反序列化/
作者
Infernity
发布于
2025年2月26日
许可协议