声明:本公众号文章来自作者日常学习笔记或授权后的网络转载,切勿利用文章内的相关技术从事任何非法活动,因此产生的一切后果与文章作者和本公众号无关!
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) 。
那么当分组不为16byte时,就会进行填充,我们看着两个例子
-
卧卧卧卧卧卧卧卧槽槽槽槽槽槽槽槽(刚好分为两组,每组16byte)
-
卧卧卧卧卧卧卧卧槽槽槽槽槽(也可以分为两组,但第二组不满16byte)
若最后剩余的明文不够16byte,需要进行填充,通常采用PKCS5进行填充。比如最后缺3个字节,则填充3个字节的0x03;若最后缺10个字节,则填充10个字节的0x0a。
0x03 Padding Oracle Attack(填充Oracle攻击)
这里的Oracle可不是甲骨文的那个数据库哈,而是一种通过接收特定加密数据 , 解密并验证填充是否正确的方式。我们举例说明
-
用户经过站点认证后,产生经过AES-128-CBC与PKCS5填充加密后的Cookie
-
站点接收到用户的Cookie,进行AES解密
-
用户的密文与填充数据正确 ==> 200
-
用户的密文与填充数据都错误 ==> 500
-
用户的密文正确,填充数据错误 ==> 301
通过上上边的例子就可以得出结论:只需通过页面的响应或变化即可穷举出正确的填充数据,这也类似于SQL注入中的“盲注”。那么到底对哪个字段进行暴力破解呢? 我们结合加解密流程图来研究一下
名词解释:
-
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] 的值 , 让我们回到加密流程中.
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>
然后去下载Exp吧(https://github.com/feihong-cs/ShiroExploit-Deprecated),输入正确的rememberMe值后就可以等着了,实战中很难利用(这么打IP早没了)
这里我聊一嘴别的,之前有师傅问Shiro是不是只要有key跟Gadget就可以进行反序列化,我当时觉得,理论上应该没问题,今天正好记录一下吧,这里我用最新的Shiro1.9.0,因为从1.2.24后key就变成随机的了,每次重启服务都会生成一个新的key,所以我们把断点打在AbstractRememberMeManager#setEncryptionCipherKey()拿一下key
发现答案是可以的,虽然自带的Commons-Beanutils虽然也是最新的1.9.4,但丝毫不影响RCE
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漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论