漏洞说明
shiro采用了AES-128-CBC的模式对cookie进行加密,这种加密方式可以使用Padding Oracle Attack( Oracle 填充攻击 ) ,用户可以通过这种方式构造恶意的RememberMe字段,并重新请求网站,进行反序列化攻击,从而达到任意代码执行.
影响版本: Apache Shiro 1.2.5, 1.2.6, 1.3.0, 1.3.1, 1.3.2, 1.4.0-RC2, 1.4.0, 1.4.1版本
AES加密
AES/128/CBC/PKCS5Padding 加密
AES是一款对称加密 ,分组加密算法,这个书上有介绍过,不过最近书没在旁边,参考了下师傅们博客
https://blog.csdn.net/qq_28205153/article/details/55798628
https://www.cxyxiaowu.com/3239.html
基本结构
AES的加密标准中,每个分组的 长度固定为128bits(16Bytes),密钥长度为128bits,192bits,256bits,不同的密钥长度有不同的加密轮数,根据长度不同可以分为AES-128,AES-192,AES-256.
AES加密 | 密钥长度 | 分组长度 | 加密轮数 |
---|---|---|---|
AES-128 | 128bits | 128bits | 10 |
AES-192 | 192bits | 128bits | 12 |
AES-256 | 256bits | 128bits | 14 |
加密步骤
初始轮
- 轮密钥加(AddRoundKey)
普通轮
- 字节替换 (SubBytes)
- 行位移(ShiftRows)
- 列混淆(MixColumns)
- 轮密钥加(AddRoundKey)
最终轮
- 字节替换 (SubBytes)
- 行位移 (ShiftRows)
- 轮密钥加(AddRoundKey)
具体加密步骤详解如下
字节替换(SubBytes)
AES 字节替换的过程可以理解为一个查表的过程,AES提供了S盒和逆S盒,
字节的高四位作为行,字节底四位为列值,然后根据s盒中的对应的值作为输出,图中给出例如,加密时输出的字节S1为0x12,查S盒的第1行 ,第2列,得到的值为 0xc9
0xAB,查S盒第A行,第B列,得到值为0x62
行位移(ShiftRows)
行位移,可以理解为左循环移位的操作
行位移的逆变换相反,为左循环位移
列混淆(MixColumns)
列混合变换 就是矩阵相乘,行位移后的状态矩阵盒固定矩阵相乘
轮密钥加(AddRoundKey)
轮密钥加时i将128为的轮密钥Ki和相同状态的矩阵中的数据逐位进行异或运算
a[i,j]与k[i,j]异或,输出b[i,j]
加密模式
AES分组加密的加密模式,共五种可选加密模式
1 | ECS ( Electronic Codebook Book , 电话本模式 ) |
在shiro中使用的为CBC的加密模式,
CBC加密模式: 将明文切分成若干小段 , 然后每一段分别与上一段的密文进行异或运算 , 再与密钥进行加密 , 生成本段明文的密文 , 这段密文用于下一段明文的加密
第一段明文没有对应的密文,保证分组的唯一性,CBC加密模式使用了初始化向量(IV , Initialization Vector)初始化向量是一个固定长度的随机数 , 该向量会作为密文第一个块 , 随密文一同传输 .
在 CBC 模式中 , 初始化向量( IV ) 的长度与分组大小相同 , 为 16 Bytes( 128 bits ) , 因为链接模式中的异或操作是等长操作
加密模式对应的填充方式
填充有六种:NoPadding, PKCS#5, PKCS#7, ISO 10126, ANSI X9.23和ZerosPadding
Nopadding
不进行填充
PKCS#7 & PKCS#5
缺少几个字节就填充几个字节,值为填充的字节数
例如,AAAAAAAA BBBBBBBB CCCCCCCC 刚好分成3个分组 AAAAAAAA BBBBBBBB CCCCC 缺少3个字节 所以使用 0x03进行填充 (这里例子的A B C 均视为一个字节)
不同在于PKCS5限定了块大小为8bytes而PKCS7没有限定,在AES加密当中严格来说是不能使用pkcs5的,因为AES的块大小是16bytes而pkcs5只能用于8bytes,通常我们在AES加密中所说的pkcs5指的就是pkcs7! ----在知乎上看到的
ISO 10126
最后一个字节是填充的字节数(包括最后一字节),其他全部填随机数
直接引用师傅的例子
... | DD DD DD DD DD DD DD DD | DD DD DD DD 81 A6 23 04 | ...
ANSI X9.23
和ISO 10126差不多,最后一个字节是填充的字节数,区别在 这种方法填充的其他字节都是0,而不是随机数
ZerosPadding
缺少的字节全部使用
0x00
填充
漏洞分析
AES加密大体上有所了解了,再回到shiro上来,看一下 AES/CBC/PKCS5Padding 即可
Padding Oracle Attack( 填充 Oracle 攻击 )
是指使用密文的填充验证信息来进行解密的攻击方法,也就是通过加密数据解密后验证填充是否正确的方式
例如
用户去请求 http://www.example.com?id=xxxxxxxxxxx
服务端接收客户端发送的数据后,会对参数值进行AES解密处理:
- 密文无效 => 用户不存在。返回http 500
- 密文有效,填充有效 => 用户存在,登陆成功。返回htto 200
- 密文有效,填充无效 => 自定义报错。返回http 30X
所以可以通过对填充进行爆破来确定有效的填充值
Ciphertext通过Key解密为Midvalue,然后midvalue和IV进行异或处理得到Plaintext
那可以去创建一个FuzzIV 让它与Midvalue进行异或运算,得到Plaintext后再去验证填充是否有效
PKCS5Padding 中 padding的有效范围为0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 到 0x08 0x08 0x08 0x08 0x08 0x08 0x08 0x08 之间
1 | FuzzIV[i] ^ Midvalue[i] = 0x01 => Midvalue[i] = FuzzIV[i] ^ 0x01 |
最终得到 Plaintext[i] = IV[i] ^ FuzzIV[i] ^ 0x01
,
而IV和FuzzIV是已知的,所以可以得到Plaintext的值,这就是 Padding Oracle
Attack 的攻击原理
漏洞复现
漏洞搭建
获取dockfile
git clone https://github.com/3ndz/Shiro-721.git
cd Shiro-721/Docker
构建并运行
docker build -t shiro-721 .
docker run -p 8080:8080 -d shiro-721
尝试登录 root/secret,勾选RememberMe
可以看到 Set-Cookie: rememberMe=deleteMe
漏洞利用
使用burp抓取rememberMe
需要爆破很长时间... ...用了半个多小时...
最终成功反弹shell
漏洞防御
- 升级 Shiro版本
- 使用waf拦截过于频繁访问的ip
- 使用waf拦截Cookie中长度过大的RememberMe值
- 升级jdk版本
Reference
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://www.cnblogs.com/backlion/p/14077791.html
https://www.yuque.com/m1tang/gqsay7/qwegln
https://www.bilibili.com/video/BV1yX4y1V75c
https://www.cxyxiaowu.com/3239.html
https://blog.csdn.net/qq_28205153/article/details/55798628
https://zhuanlan.zhihu.com/p/131324301