0%

shiro-550 反序列化

前言

学习shiro反序列化

是什么

Apache Shiro是一种功能强大且易于使用的Java安全框架,它执行身份验证、授权、加密和会话管理,可用于保护任何应用程序的安全。

Shiro提供了应用程序安全性API来执行以下方面:

  • 身份验证:证明用户身份,通常称为用户登录

  • 授权:访问控制

  • 密码术:保护或隐藏数据以防窥视;

  • 会话管理:每个用户的时间敏感状态。

Shiro还支持一些辅助功能,例如Web应用程序安全性,单元测试和多线程支持,它们的存在也是为了加强上述四个方面。

简介

当 shiro 版本 <1.2.5时,主要是由shiro 的 rememberMe 内容 反序列化 导致的命令执行漏洞,造成的原因是 AES密钥被硬编码在shiro源码中,这就导致了可以通过在cookie的rememberMe字段插入payload实现任意代码执行

在我们勾选rememberme登陆后,刷新,抓包,将其 JSESSIONID 删除,使 shiro 验证cookie中rememberme的值是否正确(如果不删除JSESSIONID,shiro则直接以JSESSIONID为登陆凭证了,就不会验证rememberme中的值了)

环境准备

JavaThings/shirodemo at master · phith0n/JavaThings (github.com),p神的shiro的demo 然后导入idea

添加tomcat

image-20220225153128673

部署war包

image-20220225153150758

这样就行了

image-20220225153302200

漏洞利用

admin :secret 选择remember me登录,当访问其他资源时cookie中会携带rememberme,shiro会进行反序列化导致漏洞发生

image-20220225154200301

利用工具,爆破内置密钥

image-20220225153415417

分析

加密过程

账号密码登录,选择 remerberMe,入口是在 AbstractRememberMeManager.java 中的 onSuccessfulLogin 方法中,判断token是否为true,然后调用 rememberIdentity

image-20220228192058505

getIdentityToRemember 作用是获取 用户名 赋值给 principals,然后再次调用重构的 rememberIdentity 方法

image-20220228192813101

发现调用了 convertPrincipalsToBytes

image-20220228193027909

先是将用户名序列化处理 然后如果存在 getCipherService 看名字应该是获取某种加密方式 ,就进行加密操作 encrypt

image-20220228193222032

看一下 getCipherService 确实是返回了一种aes的加密方式

image-20220228193509942

看一下 encrypt ,看注释就知道,通过用设置好的加密方式,对传进来的已经序列化过的byte数组进行加密,然后返回加密的value

这里这个地方 getEncryptionCipherKey()

image-20220228193736549

看到getEncryptionCipherKey() 直接返回一个常量,向上看看 encryptionCipherKey产生的过程

image-20220228203944931

AbstractRememberMeManager()
setCipherKey()
setEncryptionCipherKey()
encryptionCipherKey

看到最后 用到了 DEFAULT_CIPHER_KEY_BYTES

image-20220228204320106

DEFAULT_CIPHER_KEY_BYTES 是一个特定的值

image-20220228204400367

接下来就进入到了 cipherService.encrypt(),最后的encrypt就是具体的加密过程

image-20220228204618717

一路向上返回到重构的 rememberIdentity 方法,然后进入到 rememberSerializedIdentity 方法

image-20220228204826221

在 CookieRememberMeManager.java 中 有一个 CookieRememberMeManager 类,看一下里面的方法,把刚才加密的byte进行base64加密,然后放到cookie中

image-20220225162643258

此时,经过server返回登录响应,就可以看到rememberMe的值了

image-20220228205603747

解密过程

了解了加密过程,如果使用了特定的key进行加密,如果在解密过程中有危险的点,就可以伪造cookie进行触发危险方法

getRememberedPrincipals 下断点

image-20220228210252805

getRememberedSerializedIdentity 方法从名称来看是获取remember中的序列化的值,往下看看还有什么其他的操作

image-20220225162737876

通过getCookie读取cookie的值,判断符不符合base64格式,最后解码后返回

image-20220225163217983

返回到 getRememberedPrincipals 中 调用 convertBytesToPrincipals

image-20220228210659776

可以看到就进行了两个操作 decryptdeserialize

image-20220228210805161

decrypt 就不跟进了,就是获取key解密,重点看一下反序列化的操作

image-20220228211002005

最后走到 DefaultSerializer.java 中的 deserialize 最后 readObject() 这里打CC依赖

image-20220228211217692

细节

onSuccessfulLogin -> rememberMeSuccessfulLogin -> onSuccessfulLogin -> 

当存在JSESSIONID时,会忽略rememberMe,所以在攻击时需要将JSESSIONID删掉

Shiro反序列化漏洞目前为止有两个,Shiro-550(Apache Shiro < 1.2.5)和 Shiro-721( Apache Shiro < 1.4.2 )

主要区别在于

  • Shiro550使用已知密钥撞

  • Shiro721是使用登录后rememberMe={value}去爆破正确的key值进而反序列化

对比Shiro550条件只要有足够密钥库(条件比较低)、Shiro721需要登录(要求比较高鸡肋)

  • Apache Shiro < 1.4.2默认使用AES/CBC/PKCS5Padding模式

  • Apache Shiro >= 1.4.2默认使用AES/GCM/PKCS5Padding模式