shiro721(padding oracle)

    渗透测试 lz520520 2年前 (2020-07-27) 576次浏览

    Padding Oracle

    padding oracle的全称是填充提示,和SQL盲注类似,不过攻击的目标是算法,AES/DES这种算法。
    实际上攻击的是算法模式CBC,CBC分组加密
    明文:M
    密文:E
    密钥:K

    E[i] = k(M[i] ^ E[i-1])

    即第i组密文等于第i-1组密文 XOR 第i组明文,然后使用算法加密。
    因为每组长度一致,所以最后一组会填充数据满足分组长度。
    填充的方式有很多种,比如填充零,比如pkcs5Padding,即填充的每字节值和长度一致,比如缺5个字节则填充0x0505050505

    但如果字节值和长度不匹配则算法会报错,此时程序逻辑处理不当,就会出现padding oracle攻击,意思就是填充对和不对的数回显不一样,则可二值枚举。
    比如0x05050502,最后是02,则算法里最后两个字节必须都为02,而此时只有最后一个字节为02则会报错。

    padding oracle攻击,通过伪造IV,枚举使得明文最后一个字节为0x01,这样只有一个IV和固定的中间值异或才能得到0x01,然后通过IV和0x01异或反推中间值,中间值知道了,就可以和真实的IV异或得到真实的明文。

    先假设只有一个分组(因为padding oracle攻击的是最后一个分组,只有最后一个分组需要padding),就取密文的第一个分组,根据上面所述,可获取明文,然后将第一个密文作为第二个密文的IV,从而推导出所有的明文。

    伪造任意长的明文,我的想法是,也是现假设只有一个分组,这个明文是我们需要伪造的真实payload,然后反推密文。
    具体可以看CBC翻转攻击。
    公式如下
    E'[i-1] = E[i-1] ^ M[i] ^ 'AAAAAAAAAAAAAAAA'

    如果是第一组,伪造明文'AAAAAAAAAAAAAAAA', 则是
    IV' = IV ^ M[1] ^ 'AAAAAAAAAAAAAAAA'

    https://www.freebuf.com/articles/web/15504.html

     

    CBC翻转攻击

    CBC加密方式:明文异或前一组的密文,得到中间值,然后中间值使用key进行加密
    解密方式:密文使用key解密得到中间值,中间值和前一组异或即可得到明文
    总结:加密,先异或在加密,解密是先解密再异或

    明文:M
    密文:E
    密钥:K
    解密算法:de

    M[i] = de(E[i], k) ^ E[i-1]
    解密第一组时:
    M[1] = de(E[1], k) ^ IV

    现需要修改明文M[i]为X
    那么设置E'[i-1] = E[i-1] ^ M[i] ^ X
    那么解密公式就变为
    M'[i] = de(E[i], k) ^ E'[i-1] = de(E[i], k) ^ E[i-1] ^ M[i] ^ X
    M'[i] = (de(E[i], k) ^ E[i-1]) ^ M[i] ^ X
    M'[i] = M[i] ^ M[i] ^ X
    M'[i] = X
    所以只要将E'[i-1] = E[i-1] ^ M[i] ^ X,则可让M'[i] = X
    E'[i-1] = E[i-1] ^ M[i] ^ X = E'[i-1] = E[i-1] ^ intermediate_i ^ E[i-1] ^ X = intermediate_i ^ X
    即修改第i-1组的密文内容,就可以使得明文变为任意值,并且不需要知道key和算法。
    反过来也就是通过第i组中间值和伪造的明文异或,即可得到需要伪造的第i-1组密文了。

    PS:在shiro中,第i组密文可设置成全0,获取到第i组中间值后,可伪造i-1组密文,然后将i-1组作为最后一组,即可获取到中间值,即可伪造i-1组明文,推导伪造i-2组密文。

    如果是修改第1组,让明文为X,则是修改IV
    令IV' = IV ^ M[1] ^ X
    则可得出M'[1] = X

    总结来说
    在不知道key的情况下,知道明文和密文,IV,通过修改密文和IV,可达到篡改明文,让目标获取伪造的密文,解密结果为我们设定的明文。
    修改公式为,如AES算法中,第i组明文需要修改成16个A,那么公式为
    E'[i-1] = E[i-1] ^ M[i] ^ 'AAAAAAAAAAAAAAAA'
    其中E[i-1]、M[i]都是已知的

    这种攻击其实是一种伪造攻击。

    https://www.freebuf.com/articles/system/163756.html

    shiro721 padding oracle利用

    漏洞形成原因就不细讲了,可以参考最后的链接,总而言之,在1.2.5以后,shiro使用动态密钥,防止了反序列化攻击,但使用padding oracle的方式,可以在不知道aes密钥的情况下,伪造任意长度payload的AES密文。
    这里对padding oracle的利用,不是用来获取原数据的明文或中间值,他是被利用来获取伪造密文的中间值,算一种变相利用吧。
    我结合部分代码讲解
    1. 用yso生成payload,然后会用pcks5进行padding,满足16的整数倍,接着会在后面补上16字节的0,当做最后一段密文。

    shiro721(padding oracle)

     

    2. 接着将块分为16字节一组,从最后一组开始爆破,因为只有最后一组才需要padding,最后一组爆破成功后得到中间值,继而得到一个伪造的n-1组密文,接着将n-1组作为最后一组,继续爆破,得到伪造的n-2组密文,以此类推。
    这里就用到上述所说的E'[i-1] = intermediate_i ^ X,第i组payload和第i组中间值异或,可获取需要伪造的i-1组密文。

    shiro721(padding oracle)

    最终就可以获取一个任意长度的伪造密文encrypted

    3. 最后就是padding oracle的细节了,每次bust方法爆破,针对每字节发256个http请求包,一般用不到256个包,那么每一组就需要发送16*256=4096个包。
    可以看到bust传入的是当前的block,即当前组需要伪造的密文,他会和16字节的0拼接,bytes(0)*16 + block,然后对前16字节的每一位(bytes0 * 16)进行256个数的遍历填充,最后将这32字节放到shiro的AES COOKIE尾部

    shiro721(padding oracle)

    这里为啥放在尾部呢,涉及到shiro和java的反序列化。
    关于shiro,因为无论是填充错误还是反序列化失败都会返回deleteMe,所以要使填充成功的时候不返回deleteMe,首要条件就是有效的cookie值,这就是为什么该漏洞利用需要先有用户登录成功的,其次用到java反序列化中的一个小trick。java中的ObjectOutputStream是一个Stream,他会按格式以队列方式读下去,后面拼接无关内容,不会影响反序列化。通过这种方式,在抓到的rememberMe之后加新的iv和value,就既能反序列化成功,又能验证padding是否正确了,从而满足了padding oracle所需要的bool条件。

    最后
    针对网上的exp做了一些优化调整,之前测试原有的exp,根据之前的计算,每16字节需要发4096个包,而URLDNS的payload算很短了,大概在320字节左右,也就是需要发81920个包,每个包算你50ms,也需要68分钟构造一个payload,你验证一个漏洞都需要这么久,更别谈利用了,就显得很鸡肋,所以要优化得在并发上做处理,也很简单,python用async,golang用goroutine,协程的方式可以很有效的提升效率,我优化后,测试20并发,大概4分钟左右,在还是网络不稳定的情况下,如果目标系统性能好的话,可进一步提升并发,提高测试效率,因为我测试环境100并发,目标系统就容易中断超时,速度提升不明显。

     

    shiro721(padding oracle)

     

    https://xz.aliyun.com/t/7026


    Security , 版权所有丨如未注明 , 均为原创丨
    转载请注明原文链接:shiro721(padding oracle)
    喜欢 (1)