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方法

相关推荐
前端百草阁14 分钟前
【TS简单上手,快速入门教程】————适合零基础
javascript·typescript
彭世瑜14 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
Backstroke fish15 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
zwjapple15 分钟前
typescript里面正则的使用
开发语言·javascript·正则表达式
小五Five17 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
临枫54117 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
酷酷的威朗普18 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省19 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_98519 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript
契机再现20 分钟前
babel与AST
javascript·webpack·typescript