当前位置: 首页 > news >正文

什么是 Shiro-550 漏洞?——从原理到实践的完整指南

前言

Shiro-550反序列化漏洞大约在2016年就被披露了,但感觉直到近一两年,在各种攻防演练中这个漏洞才真正走进了大家的视野,Shiro-550反序列化应该可以算是这一两年最好用的RCE漏洞之一,原因有很多:Shiro框架使用广泛,漏洞影响范围广;攻击payload经过AES加密,很多安全防护设备无法识别/拦截攻击...

最初碰到Shiro反序列化漏洞应该是在2017或者2018年的一个CTF线下赛。一直到现在,这个漏洞不断有新的知识出现,因此打算开个系列笔记重新记录漏洞学习过程。

环境搭建

下载源码https://github.com/apache/shiro.git
编辑 shiro/samples/web 目录下的 pom.xml, 将 jstl 的版本修改为 1.2

<dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> <scope>runtime</scope> </dependency>

漏洞概述

Shiro-550(CVE-2016-4437) 是 Apache Shiro 框架中的一个 高危反序列化远程代码执行漏洞 。攻击者可以通过构造恶意的 rememberMe cookie,在目标服务器上执行任意 Java 代码。

漏洞原理

在 Shiro 1.2.4 及之前版本 中, AbstractRememberMeManager 使用了 硬编码的默认加密密钥

private static final byte[] DEFAULT_CIPHER_KEY = Base64.decode("kPH+bIxk5D2deZiIxcaaaA==");

这个密钥是公开的!攻击者可以:

1. 使用该密钥加密恶意序列化对象(包含 Gadget 链,如 CommonsCollections)

2. 将加密结果作为 rememberMe cookie 发送给目标服务器

3. 服务器解密后反序列化恶意对象 → 执行任意代码

RememberMe 机制流程

在 CookieRememberMeManager 中,身份信息的处理流程如下:

序列化(加密)流程 :

用户登录(勾选记住我) ↓ PrincipalCollection(用户身份) ↓ serialize() → Java 序列化对象 ↓ encrypt() → AES 加密 ↓ Base64.encodeToString() → Base64 编码 ↓ 写入 rememberMe cookie

反序列化(解密)流程 :

读取 rememberMe cookie ↓ Base64.decode() → Base64 解码 ↓ decrypt() → AES 解密 ↓ deserialize() → Java 反序列化 ↓ 获取 PrincipalCollection → 自动登录

漏洞分析(代码层面)

代码分析思路:如何找到关键方法

从功能入口找线索

当我们要分析 Shiro 的 RememberMe 功能时,首先要问: 这个功能是怎么触发的?

1. 用户登录时勾选"记住我" → 服务端写入 rememberMe cookie

2. 用户下次访问 → 服务端读取 rememberMe cookie → 自动登录

所以我们应该从 cookie 的读写 入手。搜索 rememberMe 关键字找到 CookieRememberMeManager.java 。

加密流程详解(用户登录时)
入口: rememberSerializedIdentity 方法

这个方法接收的 serialized 参数已经是加密后的字节数组了。

追溯:谁调用了 rememberSerializedIdentity

查看父类 AbstractRememberMeManager.java

核心加密: convertPrincipalsToBytes 方法

这里就是漏洞的关键位置! 先序列化,再加密。

序列化: serialize 方法

这里实际调用的是 DefaultSerializer.java#L45-L65

这是标准的 Java 序列化,任何实现了 Serializable 接口的对象都能被序列化。

加密: encrypt 方法

AbstractRememberMeManager.java#L523-L531

AES 加密核心: AesCipherService.encrypt

实际调用的是父类 JcaCipherService.java#L306-L317

继续看 JcaCipherService.java#L319-L348

最终加密: crypt 方法

底层调用 cipher.doFinal(bytes) 完成 AES 加密。

解密流程详解(用户下次访问时)
入口: getRememberedSerializedIdentity 方法

CookieRememberMeManager.java#L196-L249

protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) { if (!WebUtils.isHttp(subjectContext)) { if (LOGGER.isDebugEnabled()) { String msg = "SubjectContext argument is not an HTTP-aware instance. This is required to obtain a " + "servlet request and response in order to retrieve the rememberMe cookie. Returning " + "immediately and ignoring rememberMe operation."; LOGGER.debug(msg); } return null; } WebSubjectContext wsc = (WebSubjectContext) subjectContext; if (isIdentityRemoved(wsc)) { return null; } HttpServletRequest request = WebUtils.getHttpRequest(wsc); HttpServletResponse response = WebUtils.getHttpResponse(wsc); String base64 = getCookie().readValue(request, response); // Browsers do not always remove cookies immediately (SHIRO-183) // ignore cookies that are scheduled for removal if (Cookie.DELETED_COOKIE_VALUE.equals(base64)) { return null; } if (base64 != null) { base64 = ensurePadding(base64); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Acquired Base64 encoded identity [" + base64 + "]"); } byte[] decoded; try { decoded = Base64.decode(base64); } catch (RuntimeException rtEx) { /* * https://issues.apache.org/jira/browse/SHIRO-766: * If the base64 string cannot be decoded, just assume there is no valid cookie value. * */ getCookie().removeFrom(request, response); LOGGER.warn("Unable to decode existing base64 encoded entity: [" + base64 + "].", rtEx); return null; } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Base64 decoded byte array length: " + decoded.length + " bytes."); } return decoded; } else { //no cookie set - new site visitor? return null; } }
追溯:谁调用了 getRememberedSerializedIdentity

查看父类 AbstractRememberMeManager.java#L416-L432

核心解密: convertBytesToPrincipals 方法

这里就是漏洞利用的关键! 如果攻击者能控制解密后的数据,就能执行任意代码。

解密: decrypt 方法

AbstractRememberMeManager.java#L539-L547

AES 解密核心: JcaCipherService.decryptInternal

反序列化: deserialize 方法

AbstractRememberMeManager.java#L567-L569

实际调用的是 DefaultSerializer.java#L75-L92

危险就在这里! ois.readObject() 会执行对象的反序列化,如果数据是恶意构造的,就会触发 Gadget 链执行任意代码。

代码流程图总结

加密流程(登录时)

用户身份 (PrincipalCollection) ↓ AbstractRememberMeManager.rememberIdentity() ↓ convertPrincipalsToBytes() ↓ serialize() → DefaultSerializer.serialize() → ObjectOutputStream.writeObject() ↓ encrypt() → JcaCipherService.encrypt() ↓ AES/GCM/NoPadding 加密(带随机 IV) ↓ IV + 密文 拼接 ↓ Base64.encodeToString() ↓ CookieRememberMeManager.rememberSerializedIdentity() ↓ 写入 rememberMe cookie

解密流程(访问时)

读取 rememberMe cookie ↓ CookieRememberMeManager.getRememberedSerializedIdentity() ↓ Base64.decode() ↓ AbstractRememberMeManager.getRememberedPrincipals() ↓ convertBytesToPrincipals() ↓ decrypt() → JcaCipherService.decryptInternal() ↓ 提取 IV → AES/GCM/NoPadding 解密 ↓ deserialize() → DefaultSerializer.deserialize() → ObjectInputStream.readObject() ↓ 获取 PrincipalCollection → 自动登录

漏洞利用

这里的话我直接用工具就进行测试了

这里的话也可以伪造cookie去进行命令执行,测试时候记得删除cookie里面的jsessionid

利用流程
攻击者构造恶意序列化对象(含 Gadget 链) ↓ 使用默认密钥 AES 加密 ↓ Base64 编码 ↓ 发送 rememberMe cookie 给目标服务器 ↓ 服务器读取 cookie → Base64 解码 ↓ 使用默认密钥 AES 解密 ↓ Java 反序列化 → 触发 Gadget 链 ↓ 执行任意代码(如反弹 Shell)

防御建议

措施

优先级

说明

升级 Shiro 版本

🔴 高

使用 1.2.5+ 版本,默认使用随机密钥

自定义密钥

🔴 高

通过配置文件设置自己的 cipherKey

禁用 RememberMe

🟡 中

如果业务不需要,直接禁用此功能

审计依赖

🟡 中

移除不必要的包含 Gadget 的库

使用安全序列化

🟢 低

考虑使用白名单机制或替换 Java 原生序列化

http://www.jsqmd.com/news/1133192/

相关文章:

  • 专业的叉车车轮清洁地刷制造厂哪家强
  • 【JAVA毕设源码分享】基于springboot咖啡共赏平台的设计与实现(程序+文档+代码讲解+一条龙定制)
  • 2026年网文圈卷疯了!顶配 AI 创作工具实测:除了炼字工坊,全是陪跑?
  • LP5812与PIC18LF25K80实现RGB LED灯光控制方案
  • TPS65263三路降压转换器与PIC18F45K42的嵌入式电源设计
  • LTC6904与TM4C1299NCZAD构建高精度方波发生器
  • 2026年邯郸地区AI搜索优化服务商系统,究竟有何独特之处?
  • 4-20mA电流环原理与INA196检测方案设计
  • 1. JS函数
  • IS31FL3731 LED驱动芯片与PIC18F47K40微控制器的应用指南
  • AMD Ryzen调试神器SMUDebugTool:免费开源硬件性能调优终极指南
  • 快递批量查询的底层逻辑:从规则识别到并发架构的技术演进
  • 告别手动翻资料:用 Agent Plan 搞定销售档案与问答
  • WarcraftHelper:让经典魔兽争霸III在现代电脑上重获新生的终极指南
  • 工业4-20mA电流环技术与XTR116芯片应用指南
  • PCF8591与PIC18F55K42的工业级信号处理方案
  • 5四路视频接入国产软硬件兼容步态布控预警与人体抑郁指数分析成套技术白皮书
  • MC6470与PIC18LF45K22的6DOF姿态控制系统设计
  • MIC1557与MKV42F128VLH16构建高精度定时系统方案
  • 工业4-20mA电流环与XTR116变送器设计指南
  • 工业4-20mA电流环技术优化与DAC161S997应用
  • 【电赛/毕设降维打击】抛弃玩具直流电机!大疆 Robomaster (M3508/M2006) CAN 总线组网与多闭环硬核避坑指南
  • STM32F746VG与AD5593R混合信号IO开发实战
  • 汽车电子智能散热方案:DRV8213与模糊PID温控实践
  • AI漫剧制作全流程:从文生图到视频合成的开源方案实践
  • KMR221数字电位器与PIC18F2553实现精密电压控制方案
  • Sunshine完全卸载指南:从游戏流媒体主机到系统净化的技术探险
  • 操作系统页面置换算法:FIFO、LRU、OPT 3种算法缺页率对比与Python模拟
  • 题解:P10647 [NordicOI 2023] Ice Cream Machines
  • AMD Ryzen终极调试指南:SMUDebugTool从入门到精通