Javassist动态构造和分块传输字节码绕过Shiro Cookie长度限制分析
字数 1619 2025-08-29 08:30:30

Javassist动态构造和分块传输字节码绕过Shiro Cookie长度限制分析

1. Shiro反序列化漏洞背景

Apache Shiro框架提供了"记住我"(RememberMe)功能,用户登录成功后生成经过加密并编码的cookie。攻击流程如下:

客户端流程

命令 => 序列化 => AES加密 => Base64编码 => RememberMe Cookie值

服务端流程

RememberMe Cookie值 => Base64解码 => AES解密 => 反序列化

2. 实战中的限制与挑战

  1. Cookie长度限制:许多WAF会对rememberMe长度进行限制(通常小于3500字符)
  2. 解密payload检查:部分WAF会解密payload检查反序列化class
  3. 依赖版本问题
    • Shiro自带commons-beanutils版本可能与本地不一致
    • Shiro中的commons-beanutils包含部分但不全的commons-collections类

3. Payload缩短技术

3.1 序列化数据本身缩小

  1. 使用最小化的序列化数据结构
  2. 移除不必要的类信息和元数据

3.2 TemplatesImpl中的_bytecodes字节码缩小

  1. 优化生成的恶意类字节码
  2. 使用精简的类结构

3.3 静态代码块优化

  1. 将执行逻辑放在静态代码块中
  2. 减少类成员和方法数量

4. Javassist动态构造技术

4.1 为什么选择Javassist

  1. 相比ASM更易用
  2. 可以直接用Java代码生成字节码
  3. 动态构造类结构,避免写死恶意类

4.2 动态构造示例

传统恶意类(如Clac.java)的问题:

  1. 需要实现AbstractTranslet中定义的抽象方法
  2. 需要引入大量其他包中的类
  3. 导致payload长度增加

使用Javassist动态构造:

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("EvilClass");
// 动态添加构造函数和执行逻辑
cc.addConstructor(CtNewConstructor.make("public EvilClass() { Runtime.getRuntime().exec(\"calc\"); }", cc));
byte[] bytecode = cc.toBytecode();

5. 分块传输技术(Block Transmission)

5.1 基本思路

  1. 将完整字节码分成多个小块
  2. 通过多个请求分别传输
  3. 在服务端重组

5.2 实现步骤

  1. 分块写入

    • 使用Javassist动态生成写入每一分块的Payload
    • 以追加方式将所有字节码的Base64写入某文件
  2. 最终组装

    • 在最后一个包中将字节码进行Base64Decode
    • 写入完整class文件
    • 也可以直接写字节码二进制数据(Base64更易分割处理)

6. 利用方式

6.1 出网利用

使用不依赖CC链的CB链POC:

  1. 构造特定的序列化对象
  2. 利用Shiro自带的commons-beanutils进行反序列化

6.2 不出网利用

使用DynamicClassGenerator生成恶意class:

  1. 针对不同系统使用不同方法
  2. 生成缩短后的payload

7. 实战利用步骤

  1. 指纹识别

    • 检测rememberme字段
    • 确认Shiro框架
  2. 密钥爆破

    • 使用工具爆破Shiro的AES加密key
    • 常见默认key如"kPH+bIxk5D2deZiIxcaaaA=="
  3. 利用链测试

    • 测试可用的反序列化链
    • 注意依赖版本匹配问题
  4. Payload构造

    • 使用Javassist动态生成恶意类
    • 应用分块传输技术绕过长度限制
  5. 内存马注入

    • 在缩短的payload中实现内存马
    • 避免直接使用大型payload

8. 关键注意事项

  1. 版本兼容性

    • 确保commons-beanutils版本与Shiro自带版本匹配
    • 注意serialVersionUID问题
  2. 依赖管理

    • Shiro可能不包含完整的commons-collections
    • 可能需要额外添加依赖
  3. Payload优化

    • 优先使用静态代码块
    • 减少类成员和方法数量
    • 使用最小化的执行逻辑
  4. WAF绕过

    • 结合多种缩短技术
    • 测试不同长度限制
    • 考虑分时发送payload

9. 防御建议

  1. 升级Shiro到最新版本
  2. 修改默认加密密钥
  3. 限制RememberMe Cookie长度
  4. 监控异常的反序列化操作
  5. 使用安全的反序列化机制

通过以上技术组合,可以有效绕过Shiro的Cookie长度限制,实现反序列化漏洞的利用。关键在于动态构造字节码和分块传输技术的结合应用。

Javassist动态构造和分块传输字节码绕过Shiro Cookie长度限制分析 1. Shiro反序列化漏洞背景 Apache Shiro框架提供了"记住我"(RememberMe)功能,用户登录成功后生成经过加密并编码的cookie。攻击流程如下: 客户端流程 : 服务端流程 : 2. 实战中的限制与挑战 Cookie长度限制 :许多WAF会对rememberMe长度进行限制(通常小于3500字符) 解密payload检查 :部分WAF会解密payload检查反序列化class 依赖版本问题 : Shiro自带commons-beanutils版本可能与本地不一致 Shiro中的commons-beanutils包含部分但不全的commons-collections类 3. Payload缩短技术 3.1 序列化数据本身缩小 使用最小化的序列化数据结构 移除不必要的类信息和元数据 3.2 TemplatesImpl中的_ bytecodes字节码缩小 优化生成的恶意类字节码 使用精简的类结构 3.3 静态代码块优化 将执行逻辑放在静态代码块中 减少类成员和方法数量 4. Javassist动态构造技术 4.1 为什么选择Javassist 相比ASM更易用 可以直接用Java代码生成字节码 动态构造类结构,避免写死恶意类 4.2 动态构造示例 传统恶意类(如Clac.java)的问题: 需要实现AbstractTranslet中定义的抽象方法 需要引入大量其他包中的类 导致payload长度增加 使用Javassist动态构造: 5. 分块传输技术(Block Transmission) 5.1 基本思路 将完整字节码分成多个小块 通过多个请求分别传输 在服务端重组 5.2 实现步骤 分块写入 : 使用Javassist动态生成写入每一分块的Payload 以追加方式将所有字节码的Base64写入某文件 最终组装 : 在最后一个包中将字节码进行Base64Decode 写入完整class文件 也可以直接写字节码二进制数据(Base64更易分割处理) 6. 利用方式 6.1 出网利用 使用不依赖CC链的CB链POC: 构造特定的序列化对象 利用Shiro自带的commons-beanutils进行反序列化 6.2 不出网利用 使用DynamicClassGenerator生成恶意class: 针对不同系统使用不同方法 生成缩短后的payload 7. 实战利用步骤 指纹识别 : 检测rememberme字段 确认Shiro框架 密钥爆破 : 使用工具爆破Shiro的AES加密key 常见默认key如"kPH+bIxk5D2deZiIxcaaaA==" 利用链测试 : 测试可用的反序列化链 注意依赖版本匹配问题 Payload构造 : 使用Javassist动态生成恶意类 应用分块传输技术绕过长度限制 内存马注入 : 在缩短的payload中实现内存马 避免直接使用大型payload 8. 关键注意事项 版本兼容性 : 确保commons-beanutils版本与Shiro自带版本匹配 注意serialVersionUID问题 依赖管理 : Shiro可能不包含完整的commons-collections 可能需要额外添加依赖 Payload优化 : 优先使用静态代码块 减少类成员和方法数量 使用最小化的执行逻辑 WAF绕过 : 结合多种缩短技术 测试不同长度限制 考虑分时发送payload 9. 防御建议 升级Shiro到最新版本 修改默认加密密钥 限制RememberMe Cookie长度 监控异常的反序列化操作 使用安全的反序列化机制 通过以上技术组合,可以有效绕过Shiro的Cookie长度限制,实现反序列化漏洞的利用。关键在于动态构造字节码和分块传输技术的结合应用。