入坑Java安全之Shiro-721漏洞分析

admin 2025年2月15日23:08:14评论12 views字数 5787阅读19分17秒阅读模式

声明:本公众号文章来自作者日常学习笔记或授权后的网络转载,切勿利用文章内的相关技术从事任何非法活动,因此产生的一切后果与文章作者和本公众号无关!

0x00 前言

上一篇文章介绍了Shiro550的原理以及利用,本文主将从漏洞原理,以及实战中可能会遇到的问题这两大模块继续分析Shiro的另一个高危漏洞——shiro721,对密码学不感兴趣的同学可以直接移步到0x05

0x01 漏洞原理

Apapche Shiro RememberMe Cookie 默认通过 AES-128-CBC 模式加密 ,这种加密模式容易受到 Padding Oracle Attack( Oracle 填充攻击 ),攻击者可以使用有效的 RememberMe Cookie 作为 Paddding Oracle Attack 的前缀,然后精心构造 RememberMe Cookie 来实施反序列化攻击。

攻击流程:

  • 使用任意账户登陆目标网站,以获取一个合法的 RememberMe Cookie

  • 将获取的值作为POA的前缀

  • 加密反序列化的payload来构造恶意RememberMe Cookie

  • 将构造好的恶意数据填充到 RememberMe Cookie 字段中并发送

必要条件:

  • 合法的RememberMe Cookie(需认证一次)

0x02 AES-128-CBC加密模式

首先,我们按照密钥特征分为以下两种:

  • 对称加密(比如AES,也就是使用同一把钥匙上锁、开锁)

  • 非对称加密(比如RSA,使用同一把钥匙上锁,各自的钥匙开锁)

其次,从加密方式,也可分为两类:

  • 分组密码,也称块密码(将明文消息编码表示后的数字序列,划分成长度相等的组,暂且定义为n,n>0,每组分别在密钥的控制下变换成等长的数组数字序列)

  • 流密码,也称序列密码(明文和密钥的长度一致,一一进行异或运算,可以得出密文,比如100m的明文就有100m的密钥)

而Shiro721中就使用了AES-128-CBC加密,也就是分组对称加密,这里为什么是128呢?因为加密标准是128bit为一组密文,那CBC又是什么?这是分组加密算法中的一种加密模式,即Cipher Block Chaining , 密码分组链接模式

CBC 加密模式:将明文切分成若干小段,然后每一段分别与上一段的密文进行异或运算,再与密钥进行加密,生成本段明文的密文,这段密文用于下一段明文的加密。第一段明文没有对应的密文,为了确保分组的唯一性,CBC加密模式使用了初始化向量(IV,Initialization Vector)。初始化向量是一个固定长度的随机数,该向量会作为密文第一个块,随密文一同传输,在CBC模式中,因为链接模式中的异或操作是等长操作,所以初始化向量(IV)的长度与分组大小相同,为16 Bytes(128 bits) 。

入坑Java安全之Shiro-721漏洞分析

入坑Java安全之Shiro-721漏洞分析

那么当分组不为16byte时,就会进行填充,我们看着两个例子

  • 卧卧卧卧卧卧卧卧槽槽槽槽槽槽槽槽(刚好分为两组,每组16byte)

  • 卧卧卧卧卧卧卧卧槽槽槽槽槽(也可以分为两组,但第二组不满16byte)

若最后剩余的明文不够16byte,需要进行填充,通常采用PKCS5进行填充。比如最后缺3个字节,则填充3个字节的0x03;若最后缺10个字节,则填充10个字节的0x0a。

入坑Java安全之Shiro-721漏洞分析

0x03 Padding Oracle Attack(填充Oracle攻击)

这里的Oracle可不是甲骨文的那个数据库哈,而是一种通过接收特定加密数据 , 解密并验证填充是否正确的方式。我们举例说明

  • 用户经过站点认证后,产生经过AES-128-CBC与PKCS5填充加密后的Cookie

  • 站点接收到用户的Cookie,进行AES解密

    • 用户的密文与填充数据正确 ==> 200

    • 用户的密文与填充数据都错误 ==> 500

    • 用户的密文正确,填充数据错误 ==> 301

通过上上边的例子就可以得出结论:只需通过页面的响应或变化即可穷举出正确的填充数据,这也类似于SQL注入中的“盲注”。那么到底对哪个字段进行暴力破解呢? 我们结合加解密流程图来研究一下

入坑Java安全之Shiro-721漏洞分析

名词解释:

  • RawIV:原始的IV , 解密时即为前一个密文分组

  • FuzzIV:枚举的IV , 下文会通过枚举 IV 的方式来计算出明文的值

  • Key:密钥

  • PlainText:明文分组

  • CipherText:密文分组

  • MediumValue:我们把 CipherText 和 Key 进行 Block_Cipher_Decryption 运算后的值称为 MediumValue(中间值)     

在解密时,CipherText 会被密钥 Key 解密为 MediumValue,然后 MediumValue 会与 RawIV 进行异或运算 , 得到该分组对应的 PlainText 。

但是我们不需要去解密(如前文所说:Oracle 的核心是提交数据让服务端解密 , 并验证解密后明文分组的 Padding 是否符合规范),因此我们创建一个新的 IV ( 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 ) 作为 FuzzIV,并结合 CipherText 提交给服务端 。

服务端会将接收到的 FuzzIV 与 解密后的 MediumValue 进行异或运算,得到一个明文分组,然后去验证这个明文分组的 Padding 是否有效。

注意!上面的“明文分组”不是指PlainText,它只是用于验证异或计算结果是否满足PKCS5Padding规范。根据PKCS5Padding规范,Padding值的范围在 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 到 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 之间

在某一轮解密中 , 服务端异或运算后的明文分组为 0x29 0x34 0x5A 0x6B 0x07 0xA3 0xB2 0x3C , 这个分组的 Padding 不满足 PKCS5Padding 规范 , 因此这轮解密中提交的 FuzzIV 是无效的 , 服务端会返回 "密文有效 , 填充无效" 的报错信息.

不断修正 FuzzIV 的值( 0x00 - 0xFF , 最多修正 255 次 ) . 直到某一轮解密中 , 服务端异或运算后的明文分组为 0x39 0x73 0x23 0x32 0x5A 0x7B 0x9C 0x01 , 这个 Padding 符合 PKCS5Padding 规范 , 因此这轮提交的 FuzzIV 是有效的 . 服务端会返回 "密文有效 , 填充有效" 的信息.

当获取了有效的 FuzzIV 后我们能做什么呢 ? 我们能够根据等价代换得到如下公式 :

∵   FuzzIV[8]   ^   MediumValue[8]   =   0x01∴   MediumValue[8]   =   FuzzIV[8]   ^   0x01

现在我们得到了 MediumValue[8] 的值 , 让我们回到加密流程中.

入坑Java安全之Shiro-721漏洞分析

MediumValue 与 RawIV 的异或运算结果就是真正的明文 ! 存在如下公式 :

∵   MediumValue[8]   ^   RawIV[8]   =   PlainText[8]∴   PlainText[8]   =   RawIV[8]   ^   FuzzIV[8]   ^   0x01

FuzzIV[8] , RawIV[8] 都是已知的 , 因此我们可以直接计算出 plainText[8] 的值 . 这就是 Padding Oracle Attack 的攻击原理 .

按照上面的步骤 , 我们可以依次计算其他位置的 PlainText.

当计算 PlainText[7] 时, 我们希望异或运算后得到的明文分组的最后两个字节为 0x02 0x02 . 这样 Padding 就是有效的了 .

由于我们已经计算了出了 PlainText[8] 的值 , 因此这轮解密中我们可以直接确定 FuzzIV[8] 的值 . 来看下面的公式 :

∵   MediumValue[8]   =   RawIV[8]   ^   PlainText[8]∵   FuzzIV[8]   ^   MediumValue[8]   =   0x02∴   FuzzIV[8]   =   RawIV[8]   ^   PlainText[8]   ^   0x02

根据上述公式我们能够确定 FuzzIV[8] 的值 , 然后固定FuzzIV[8] , 去暴力破解 FuzzIV[7] 的值.
当 FuzzIV[7] 和 MediumValue[7] 异或运算为 0x02 时 , 根据如下公式可以得到 PlainText[7] 的值

PlainText[7]   =   Raw[7]   ^   FuzzIV[7]   ^   0x02

后面的过程都是类似的 , 我们可以通过这种 Padding Oracle 的方法获取每一位明文的值,至此 , Padding Oracle Attack 的攻击原理与攻击流程就已经介绍完了 , 整个过程比较繁琐枯燥 , 看不明白可以多看几遍 , 看明白后其实非常好玩的 .

0x04 CBC Byte-Flipping Attack(CBC翻转攻击)

CBC翻转攻击的目的就是通过破坏密文中的字节来更改明文中的字节。回顾 CBC 加密模式的解密过程 , 我们不难发现如下特性:

  • IV 向量影响第一个明文分组( IV 一般被放在密文开头 , 单独作为一个分组 )

  • 第 n 个密文分组可以影响第 n+1 个明文分组 .

设第 n 个明文分组为 P(n) , 第 n 个密文分组为 C(n) , 则存在以下公式 :

P(n+1)   =   C(n)   ^   Block_Cipher_Decryption(C(n+1))

如果明文和密文已知 , 那么我们可以手动修改 C(n) 的值 , 使得 P(n+1) 变成任何值.

令   C(n)   =   C(n)   ^   P(n+1)   ^   Payload∵   Block_Cipher_Decryption(C(n+1))   =   P(n+1)   ^   C(n)∴   P(n+1)   =   C(n)   ^   P(n+1)   ^   Payload   ^   Block_Cipher_Decryption(C(n+1))∴   P(n+1)   =   Block_Cipher_Decryption(C(n+1))   ^   Block_Cipher_Decryption(C(n+1))   ^   Payload∴   P(n+1)   =   0   ^   Payload∴   P(n+1)   =   Payload

大概就是上面这个意思 , 我们能够通过修改密文字节( C(n) = C(n) ^ P(n+1) ^ Payload ) , 实现篡改明文字节( P(n+1) = Payload ).

总结:

  • AES 是指 "高级加密标准" , 是一种对称加密的分组加密算法 , 128是指密钥长度 , CBC 是指 "密码分组链接" 加密模式 , PKCS5Padding 是 Apache Shiro 中默认填充方式 , 最后一个明文分组缺少 N 个字节 , 则填充 N 个 0x0N .

  • 在 Apache Shiro 中默认使用 CBC 加密模式与 PKCS5Padding 填充方式 . CBC 加密模式容易遭到 Padding Oracle Attack , 攻击者可以通过枚举 IV 的方式计算出全部明文 , 并且可以通过 CBC Byte-Flipping Attack 篡改某一段的明文 .

  • Padding Oracle Attack 利用前提 :

    • 攻击者能够获得密文( CipherText )与附带在密文前面的初始化向量( IV ).

    • 服务端对密文解密后会判断 Padding 是否有效 . 并根据不同的判定结果返回不同的响应信息 .

  • CBC Byte-Flipping Attack 利用前提 :

    • 明文和密文已知

0x05 漏洞复现

依然用P神的环境,换一下pom.xml中shiro的版本即可,我这里换成shiro1.2.5,下载地址(https://github.com/phith0n/JavaThings/tree/master/shirodemo

    <dependency>      <groupId>org.apache.shiro</groupId>      <artifactId>shiro-core</artifactId>      <version>1.2.5</version>    </dependency>    <dependency>      <groupId>org.apache.shiro</groupId>      <artifactId>shiro-web</artifactId>      <version>1.2.5</version>    </dependency>

入坑Java安全之Shiro-721漏洞分析

然后去下载Exp吧(https://github.com/feihong-cs/ShiroExploit-Deprecated),输入正确的rememberMe值后就可以等着了,实战中很难利用(这么打IP早没了)

入坑Java安全之Shiro-721漏洞分析

这里我聊一嘴别的,之前有师傅问Shiro是不是只要有key跟Gadget就可以进行反序列化,我当时觉得,理论上应该没问题,今天正好记录一下吧,这里我用最新的Shiro1.9.0,因为从1.2.24后key就变成随机的了,每次重启服务都会生成一个新的key,所以我们把断点打在AbstractRememberMeManager#setEncryptionCipherKey()拿一下key

入坑Java安全之Shiro-721漏洞分析

发现答案是可以的,虽然自带的Commons-Beanutils虽然也是最新的1.9.4,但丝毫不影响RCE

入坑Java安全之Shiro-721漏洞分析

入坑Java安全之Shiro-721漏洞分析

0x06 Reference

  • https://blog.csdn.net/qq_28205153/article/details/55798628

  • https://media.blackhat.com/bh-eu-10/whitepapers/Duong_Rizzo/BlackHat-EU-2010-Duong-Rizzo-Padding-Oracle-wp.pdf

  • https://blog.skullsecurity.org/2016/going-the-other-way-with-padding-oracles-encrypting-arbitrary-data

  • https://www.guildhab.top/2020/11/cve-2019-12422-shiro721-apache-shiro-rememberme-padding-oracle-1-4-1-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E-%E5%88%86%E6%9E%90-%E4%B8%8A/

  • https://resources.infosecinstitute.com/topic/padding-oracle-attack-2/

  • https://resources.infosecinstitute.com/topic/cbc-byte-flipping-attack-101-approach/

  • https://www.anquanke.com/post/id/193165#h3-1

原文始发于微信公众号(安全日记):入坑Java安全之Shiro-721漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月15日23:08:14
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   入坑Java安全之Shiro-721漏洞分析https://cn-sec.com/archives/1023179.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息