Shiro-721漏洞分析与复现

在上一篇文章中,我们分析了shiro 550的反序列化漏洞的原理以及应用,这次分析另一个shiro的高危漏洞shiro 721

shiro 721与550区别较大的是,550因为密钥为固定常量,导致可以伪造各种信息,而721修复了这一点,却又出现了新的问题

在shiro 721中,RememberMe Cookie 默认通过AES-128-CBC加密,而这种加密模式容易受到长度扩展攻击(在这里具体为Padding Oracle Attack( Oracle 填充攻击 )),攻击者可以使用有效的 RememberMe Cookie 作为Paddding Oracle Attack 的前缀,然后精心构造RememberMe Cookie 来实施反序列化攻击

  1. 使用任意账户登陆目标网站,以获取一个合法的RememberMe Cookie
  2. 将获取的值作为POA的前缀
  3. 加密反序列化的payload来构造恶意RememberMe Cookie
  4. 将构造好的恶意数据填充到 RememberMe Cookie字段中并发送

影响版本 1.2.5 <= Shiro <= 1.4.1

漏洞分析

首先,我们先得明确一点,这个漏洞能够被利用的核心,就是利用Padding Oracle Attack,那就先来分析下填充攻击

Padding Oracle Attack

AES-128-CBC加密,很好理解,使用的是AES加密算法,128bit为一个分组,而CBC指的是利用CBC加密模式

AES-128-CBC

CBC 加密模式:将明文切分成若干小段,然后每一段分别与上一段的密文进行异或运算,再与密钥进行加密,生成本段明文的密文,这段密文用于下一段明文的加密。第一段明文没有对应的密文,为了确保分组的唯一性,CBC加密模式使用了初始化向量(IV,Initialization Vector)。初始化向量是一个固定长度的随机数,该向量会作为密文第一个块,随密文一同传输,在CBC模式中,因为链接模式中的异或操作是等长操作,所以初始化向量(IV)的长度与分组大小相同,为16 Bytes(128 bits)
纯粹的文字还是比较抽象,这里放上一张CBC加密的流程图,就很好理解了
CBC加密
首先把明文按照128bit为一组分组,再由初始化的向量IV与明文的第一组进行异或,加密,得到密文的第一组。再用刚得到的异地组密文与第二组明文异或,加密,得到密文的第二组依次往后

这样就很好理解了,但我们也会发现一个问题,在给明文分组的时候,如果不是128bit的倍数,也就是最后一组不够128bit(16byte)的时候怎么办,例如:
0123456789ABCDEF FEDCBA9876543210
这一共是32byte,也就是256bit刚好可以分为两组
0123456789ABCDEF FEDCBA987654
这一共是28byte, 也就是224bit,第二组不满128bit
如果就这样进入CBC中加密,那么在异或这一步的时候,就会因为位数不同而无法进行下去,故当初的算法设计这就想到了,使用PKCS5进行长度填充,比如在第二个例子中,最后缺少4个byte,则填充4个0x04,若缺少15个byte,则填充15个0x0f

填充Oracle攻击

Padding Oracle填充攻击(Padding Oracle Attack)是比较早的一种漏洞利用方式了,早在2011年的Pwnie Rewards中被评为“最具有价值的服务器漏洞”。这个漏洞主要是由于设计使用的场景不当,导致可以利用密码算法通过“旁路攻击”被破解。值得强调的是,这个漏洞启示并不是对算法本身的破解,而是利用算法本身的某些padding特性以及系统中不必要的一些错误提示,进而分析出系统当前的漏洞利用路径。同时也需要强调下,这里的Oracle,其实与甲骨文公司关系并不大,这里的Oracle可以理解为“提示、暗示”的含义。
既然是服务器漏洞,那么肯定会涉及到与服务器进行的交互,而这里就是用到服务器给出的回显或者相应的不同,来判断填充的是否正确,例如:
服务器返回200 -> 密文与填充均正确
服务器返回301 -> 密文正确,填充错误
服务器返回500 -> 密文与填充均错误
这里根据服务器的相应变化,穷举填充的值,其实很类似于SQL中的盲注
我们再来具体看CBC的解密流程
CBC解密
解密时,与加密一样,先将密文按128bit分组,然后将第一组密文与密钥进行解密,再与初始向量IV异或,得到第一组明文,第二组密文与密钥进行解密,再与第一组密文异或,得到第二组密文,以此进行解密
因为Oracle的任务是提交数据让服务器解密,并验证解密后明文分组是否正确,所以我们不需要去解密明文,那我们只需要去遍历输入的值,根据服务器的返回信息,来判断正确,知道我们找到正确的值,也就这个padding符合规范

CBC解密

  1. 对于r的破解
    根据CBC解密的流程,从最后一个分组入手,穷举猜测r使其满足CBC最后一个或者多个分组的输出填充规则,例如最后一个字节满足注意,y的真实填充可能并非为0x01
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    for i in list:
    for _ in list:

    c2=c2[:30]+i+_
    # c1=c1[:30]+i+_
    # iv=iv[:30]+i+_

    iv_c=iv+c1+c2+c3
    # iv_c=iv+c1+c2
    # iv_c=iv+c1

    output = subp.call(['./dec_oracle.exe',iv_c])
    if output==200:
    print(i+_,'`````````````````````````')
  2. 进一步判断填充的长度
    接下来就是进一步判断填充的长度,从r1入手,逐个字修改与确认
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    for i in range(0,32,2):
    r=r1[:i]+'00'+r1[i+2:]

    iv_c_new=iv+c1+r+c3
    # iv_c_new=iv+r+c2
    #iv_c_new=r+c1
    output = subp.call(['./dec_oracle.exe',iv_c_new])
    if output==500:
    len=16-i/2
    print(len) #填充长度len=1,第二组填充长度len=1,第三组填充长度len=1

    print(hex(0x01^0x3d))
  3. 进一步得到aj-1
    通过调整r,将CBC解密后的填充修改为长度为b-j+2的形式,即让aj-1对应的字节也成为填充
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    for n in range(1,16):
    for i in list:
    for _ in list:
    r1=r1[:30-n*2]+i+_+r1[32-n*2:]

    iv_c_new=iv+c1+r1+c3
    # iv_c_new=iv+r1+c2
    # iv_c_new=r1+c1

    output = subp.call(['./dec_oracle.exe',iv_c_new])
    if output==200:
    print(i+_,'``````````````````````````````````succeed!````````````````````````````````````')
    num=(int(i+_,16)^(n+1))
    num=hex(num)
    if(len(num)<4):
    num='0x0'+num[2:]
    a.insert(0,num[2:])
    break
    r1=r1[:32-(n+1)*2]
    for q in range(n+1):
    if(len(hex(int(a[q],16)^(n+2)))<4):
    r1+='0'
    r1+=(hex(int(a[q],16)^(n+2))[2:])
  4. 得到其他分组
    利用上述方法可以恢复任意一个分组,的解密中间状态

    此时再根据截获的上一个分组密文y’,来获得真实的明文

在不知道密钥的情况下,完成数据的加密,绕过服务端的校验(解密成功+明文有效),达到攻击的目的,这就是填充Oracle攻击

漏洞利用

可以下载p神的环境
换一下pom.xml中shiro的版本即可
然后有exp也可以直接使用exp
我这里还是为了省事,直接用vulhub搭建的

然后还是工具一把梭


Shiro-721漏洞分析与复现
https://glacierrrr.online/2023/01/05/Shiro-721漏洞分析与复现/
作者
Glacier
发布于
2023年1月5日
许可协议