shiro反序列化漏洞分析

分析源码

我们去源码里面去找找,搜索rememberMe:

发现有一个 CookieRememberMeManager 这个类,看名字就知道他多半就是处理 RememberMe的逻辑,所以根据该类查看它干了什么

这里继承 AbstractRememberMeManager 类,AbstractRememberMeManager提供了创建和验证这些令牌的方法,以及配置cookie属性的方法。

这是CookieRememberMeManager 类的构造函数。首先创建一个名为"rememberMe "的SimpleCookie 对象,并将其设置为HttpOnly 。然后,将cookie的最大生存期设置为一年,以确保用户在一年内不需要重新登录。最后,将cookie对象赋值给类中的cookie属性。这个构造函数的作用是创建一个CookieRememberMeManager对象,并设置默认的"记住我"cookie配置。如果需要更改cookie的配置,可以使用其他构造函数或通过setter方法来更改属性。

继续往下看,感觉这里是重点有没有.......

看类名可以知道此类用于将序列化后的用户身份信息存储到cookie中。

在方法中,首先通过WebUtils.isHttp(subject)方法判断Subject对象是否为HTTP-aware实例。如果不是,则返回并忽略记住我操作。如果Subject对象是HTTP-aware实例,就可以获取到HttpServletRequest和HttpServletResponse对象,从而可以设置cookie。

接下来是对这些对象执行的操作:

  1. 通过WebUtils.getHttpRequest(subject)方法获取HttpServletRequest对象。
  2. 通过WebUtils.getHttpResponse(subject)方法获取HttpServletResponse对象。
  3. 将序列化后的用户身份信息进行Base64编码。
  4. 创建一个新的SimpleCookie对象,并将其值设置为Base64编码后的身份信息。
  5. 将新的cookie对象保存到HttpServletRequest和HttpServletResponse对象中。

所以这个方法的作用是将序列化后的用户身份信息存储到cookie中,并将其保存到HTTP响应中,以便在用户下一次访问网站时自动登录。

方法调用的跟踪

接下来去查看一下该方法在什么地方被调用(快捷键:Ctrl+Alt+Shift+F7)

左下角如果有弹框的话,将所以选项勾上,下拉范围选项框选择所有。

在这可以看到该类继承的AbstractRememberMeManager 类调用了该方法。

发现这个方法被 rememberIdentity 方法给调用了。

在这里会发现 rememberIdentity 方法会被 onSuccessfulLogin 方法给调用,跟踪到这一步,就看到了 onSuccessfulLogin登录成功的方法。

当登录成功后会调用AbstractRememberMeManage.onSuccessfulLogin 方法,该方法主要实现了生成加密的RememberMe Cookie, 然后将RememberMe Cookie 设置为用户的Cookie值。在rememberSerializedIdentity方法里面去实现了。

回到 onSuccessfulLogin 这个地方,这里看到调用了isRememberMe 很显而易见得发现这个就是一个判断用户是否选择了Remember Me选项。

另一方面 rememberIdentity 方法会调用 rememberSerializedIdentity方法,到这里为止,我们已经接触到序列化了。

继续跟踪,有一个叫getRememberedPrincipals 的方法调用getRememberedSerializedIdentity 。看名字就知道 getRememberedPrincipals是一个取得Remember验证的方法。

这里我们再跟进getRememberedPrincipals方法,(快捷键:Ctrl+Alt+h)

我们继续跟踪convertBytesToPrincipals方法

因为convertBytesToPrincipals 方法就是处理getRememberedPrincipals方法 的东西,看名字应该是进行字节转换的。

该方法首先检查是否存在加密服务,如果存在,则使用密钥对字节数组进行解密。接着,该方法使用"deserialize"方法将字节数组序列化。

我们可以先看反序列化

发现有一个反序列化入口 **readObject()**这里就是要利用的点

然后看解码那个地方他的逻辑是如何的

该方法名为"decrypt",有一个参数:"encrypted",代表需要解密的字节数组。

该方法首先将需要解密的字节数组赋值给一个名为"serialized"的新的字节数组。然后,该方法通过调用"getCipherService"方法获取加密服务,如果加密服务存在,则用"getDecryptionCipherKey"方法获取解密密钥,并使用加密服务对字节数组进行解密。解密后,将解密后的结果赋值给"serialized"字节数组。最后,该方法返回"serialized"字节数组,即解密后的结果。

在这里我们需要关注俩个点,接口的抽象方法,有两个参数

第一个是要解密的数据

第二个参数就是解密的key了,这个是我们十分关心的,所以我们跟进第二个参数

跟踪返回 decryptionCipherKey

跟踪 setDecryptionCipherKey方法

跟踪 setCipherKey方法

相关推荐
冷静 包容11 分钟前
C语言学习之 没有重复项数字的全排列
c语言·开发语言·学习
碳苯16 分钟前
【rCore OS 开源操作系统】Rust 枚举与模式匹配
开发语言·人工智能·后端·rust·操作系统·os
结衣结衣.29 分钟前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
学习使我变快乐30 分钟前
C++:静态成员
开发语言·c++
TJKFYY31 分钟前
Java.数据结构.HashSet
java·开发语言·数据结构
杰哥在此40 分钟前
Python知识点:如何使用Multiprocessing进行并行任务管理
linux·开发语言·python·面试·编程
小白学大数据40 分钟前
User-Agent在WebMagic爬虫中的重要性
开发语言·爬虫·http
汪子熙41 分钟前
Angular 服务器端应用 ng-state tag 的作用介绍
前端·javascript·angular.js
ROBIN__dyc1 小时前
C语言基本概念
c语言·开发语言
学习使我变快乐3 小时前
C++:const成员
开发语言·c++