Shiro反序列化:一场持续多年的序列“加密与破解”攻防战 2016年,安全研究员Larry Spiro在分析Apache Shiro的序列“记住我”功能时,发现了一个要命的序列逻辑漏洞——Shiro将用户身份序列化后,用AES密钥加密,序列再存入Cookie。序列问题在于,序列这个AES密钥是序列硬编码在源码里的,默认值“kPH+bIxk5D2deZiIxcaaaA==”在GitHub上一搜一大把。序列也就是序列说,任何人拿到这个公开密钥,序列就能自己构造一个恶意序列化对象,序列加密成Cookie发给服务器,序列服务器解密后反序列化,序列攻击者的序列代码就在目标机器上跑起来了。这不算什么高深技巧,序列纯粹是“你锁门的钥匙就插在锁孔里”的蠢事。 但真正让这个漏洞火遍全球的,是它引爆的连锁反应。Shiro是Java生态里最流行的权限框架,几乎每个后台管理系统都挂着它。攻击者不需要猜密码,不需要打SQL注入,只要往Cookie里塞一个精心构造的payload,就能直接拿到服务器权限。我见过最骚的操作是,某甲方把Shiro的密钥换成了自定义的随机字符串,以为万事大吉——结果开发为了方便调试,把密钥写在了JVM启动参数里,而那个参数又被监控脚本抓进了日志文件。你看,漏洞永远不只是代码的问题,是人性的问题。 修复方案其实不复杂:升级到1.2.5以上版本,强制更换密钥,或者干脆关闭“记住我”功能。但现实是,直到2023年,我还能在众测平台上看到大量Shiro反序列化的报告。为什么?因为很多老系统跑了好几年,没人敢动认证模块,怕一升级就出兼容问题。更讽刺的是,有些企业把Shiro的密钥设置成了“shiro123”这种弱口令,觉得“反正没人知道”。结果扫描器一跑就中招,内网直接被横向穿透,数据库被拖走,勒索信贴在服务器桌面上。 这漏洞还有个变种叫“Shiro-550”和“Shiro-721”,后者需要先拿到一个合法的会话Cookie才能利用,但原理一样——都是AES密钥泄露导致的反序列化攻击。你看,攻击者不需要懂什么高深的二进制利用,只需要会跑现成的工具,比如ysoserial,生成一个CommonsCollections的payload,再加密成Cookie发过去。整个过程五分钟搞定,比点外卖还快。 聊点实际的检测思路。如果你要排查自家系统,先抓个登录后的Cookie,看value开头是不是“rememberMe=deleteMe”——如果是,恭喜你,Shiro跑不掉了。然后试着用默认密钥解一下,能解出来就说明密钥没改。更狠的做法是,直接发一个畸形的反序列化对象,观察服务器返回的报错信息里有没有“ClassNotFoundException”或者“java.io.StreamCorruptedException”,有的话基本实锤。当然,最稳妥的方式还是代码审计,搜一下“setCipherKey”这个函数,看看密钥从哪来。 说实话,Shiro反序列化这个洞,技术含量不算顶尖,但它完美诠释了“安全漏洞的本质是信任错位”——你信任了硬编码的密钥,信任了用户传来的数据,信任了序列化机制的“安全性”。这些信任一旦被戳破,就像多米诺骨牌一样哗啦啦全倒。我见过最离谱的案例,是某甲方把密钥写在了docker-compose.yml里,然后整个仓库被公开推到GitHub上,还带着“生产环境”的标签。你说这怪谁?怪Shiro吗?它早就提醒过你要换密钥。怪开发吗?他只是想快点上线。 所以,下次你看到“Shiro反序列化”这几个字,别只想到CVE编号和漏洞库描述。它背后是一个个熬夜上线、懒得改配置的工程师,和一群拿着扫描器批量收割的脚本小子。技术对抗从来不是代码层面的博弈,而是懒惰与警觉之间的拉锯。 |