shiro721漏洞分析
0x01 前言
来填坑了,拖了很久的shiro721漏洞,涉及的加密解密挺让人头疼的。注:水文,写给自己看的
漏洞影响版本是 1.2.5 <= Apache Shiro <= 1.4.1
以往版本中,AES加密的key是已知固定的,但是在1.2.5版本之后,key就是随机的了,我们不可能猜到随机的key值,但是可以绕过key构造任意密文执行反序列化
0x02 漏洞分析
环境搭建就不多说了,网上有很多的教程。
Apache Shiro Padding Oracle Attack 的漏洞利用必须满足如下前提条件:
- 开启 rememberMe 功能;
- rememberMe 值使用 AES-CBC 模式解密;
- 能获取到正常 Cookie,即用户正常登录的 Cookie 值;
- 密文可控;
Padding Oracle Attack
以下思路来自此文章:https://goodapple.top/archives/217,并基于此文章做一些简单的说明
Padding 填充,AES是分组加密,消息明文会分为特定长度的分组,比如8字节或者16字节,CBC是分组模式,在分组密码中,有两种常见的填充算法,分别是Pkcs5Padding和Pkcs7Padding。而在shiro框架中采用的是 Pkcs5Padding
举一个例子:明文为abcdefghijk,则分组与填充为
a,b,c,d,e,f,g,h |
缺多少位,填充多少,如果明文恰好能分组,则填充下一分组全为8个0x08,无论如何都是要有填充的
加密过程与解密过程如下
网上搜的大多都是如何利用 Padding Oracle Attack 来破解明文,而在shiro框架的利用上,我们只需要构造恶意序列化串的合理密文,发送到服务端能顺利解密并且反序列化
以下摘抄与此片文章:浅析Shiro Padding Oracle Attack)
这里简单说下 Padding Oracle Attack 加密数据整体过程:
- 选择一个明文
P
,用来生成你想要的密文C
; - 使用适当的 Padding 将字符串填充为块大小的倍数,然后将其拆分为从 1 到 N 的块;
- 生成一个随机数据块(
C[n]
表示最后一个密文块); - 对于每一个明文块,从最后一块开始:
- 创建一个包括两块的密文C’,其是通过一个空块(00000…)与最近生成的密文块
C[n+1]
(如果是第一轮则是随机块)组合成的; - 这步容易理解,就是Padding Oracle的基本攻击原理:修改空块的最后一个字节直至Padding Oracle没有出现错误为止,然后继续将最后一个字节设置为2并修改最后第二个字节直至Padding Oracle没有出现错误为止,依次类推,继续计算出倒数第3、4…个直至最后一个数据为止;
- 在计算完整个块之后,将它与明文块
P[n]
进行XOR一起创建C[n]
; - 对后续的每个块重复上述过程(在新的密文块前添加一个空块,然后进行Padding Oracle爆破计算);
- 创建一个包括两块的密文C’,其是通过一个空块(00000…)与最近生成的密文块
简单地说,每一个密文块解密为一个未知值,然后与前一个密文块进行XOR。通过仔细选择前一个块,我们可以控制下一个块解密来得到什么。即使下一个块解密为一堆无用数据,但仍然能被XOR化为我们控制的值,因此可以设置为任何我们想要的值。
实际上利用 Padding Oracle Attack 攻击获取明文的原理是理解了,就是不太理解如何合理加密的
0x03 漏洞代码分析
shiro 的 Padding Oracle Attack 攻击就类似与布尔盲注,需要有报错回显
在Apache Shiro的场景中,这个服务端的两个不同的响应特征为:
- Padding Oracle错误时,服务端响应报文的Set-Cookie头字段返回
rememberMe=deleteMe
; - Padding Oracle正确时,服务端返回正常的响应报文内容;
下面分析代码
key的生成
在 AbstractRememberMeManager 类的构造方法中
函数返回值,并不是硬编码了,跟进到 generateNewKey 方法中
init方法主要是对AES算法进行初始化,跟进这个方法
Padding 的错误处理
断点下在解密函数 org.apache.shiro.mgt.AbstractRememberMeManager#decrypt() 中
获取密文,利用decrypt()函数解密,一路跟进crypt
来到doFinal函数,而在这个函数里面,做了一些错误处理,IllegalBlockSizeException 和 BadPaddingException 这两个异常,分别用于捕获块大小异常和填充错误异常,抛出的异常最终由 getRememberedPrincipals 方法捕获
捕获到异常后会执行 onRememberedPrincipalFailure 方法,跟进,
实际上会走到 forgetIdentity 方法
这时候就开始处理请求与响应了,跟进 getCookie().removeFrom
开始设置响应包了,其中 DELETED_COOKIE_VALUE 就是 deleteMe
0x04 漏洞复现
因为要爆破,所以攻击的时间非常长,网上也有那种exp脚本,使用工具ShiroExploit
0x05 结语
水文一篇,主要写一些大致的知识点(写给自己看的)
以下是详细的分析文章: