HessianServlet、HessianServiceExporter场景下的Payload
字数 2555 2025-08-29 22:41:10

HessianServlet与HessianServiceExporter漏洞利用分析

1. 背景介绍

Hessian是一种轻量级的RPC框架,在Java开发中广泛使用。目前常见的Hessian服务暴露方式主要有两种:

  1. HessianServletcom.caucho.hessian.server.HessianServlet
  2. HessianServiceExporterorg.springframework.remoting.caucho.HessianServiceExporter

现有的漏洞利用工具生成的payload往往针对原生Hessian反序列化场景,无法直接用于这两种服务暴露方式,因为它们在解析Hessian序列化数据时会进行额外的协议头判断。

2. 常见错误分析

当使用现有工具生成的payload攻击这两种服务时,通常会遇到以下错误:

  1. 协议头不匹配错误:服务端期望特定的协议头格式
  2. 版本校验错误:Hessian2.0服务需要正确的版本标识

3. 组件访问特征

3.1 HessianServiceExporter

典型服务暴露代码:

@Bean(name = "/remoting/AccountService")
public HessianServiceExporter accountService() {
    HessianServiceExporter exporter = new HessianServiceExporter();
    exporter.setService(new AccountServiceImpl());
    exporter.setServiceInterface(AccountService.class);
    return exporter;
}

访问特征:

  • 通过Spring MVC暴露的HTTP接口
  • 通常以/remoting/*等形式出现在应用路由中

3.2 HessianServlet

访问特征:

  • 直接继承HessianServlet的HTTP服务
  • 通常配置在web.xml中

4. 协议头分析

4.1 HessianServiceExporter协议头要求

POST请求进入invoke方法后,会首先检查输入流的第一个字节:

  1. 第一位必须是以下三种之一

    • 'H' (0x48)
    • 'C' (0x43)
    • 'c' (0x63)
  2. 不同协议头的处理逻辑

'H' (Hessian2.0)

  • 检查第二位是否为0x02(版本号)
  • 检查第四位是否为'C' (0x43)
  • 有效payload格式:\x48\x02\x00\x43

'C' (Hessian2.0简化)

  • 只需第一位为'C'
  • 有效payload格式:\x43

'c' (Hessian1.0)

  • 检查第二位和第三位
  • 需要额外添加头部:\x48\x00\x03abc
  • 有效payload格式:\x63\x02\x00\x48\x00\x03abc

4.2 HessianServlet协议头要求

与HessianServiceExporter类似,但有细微差别:

  1. 第一位必须是以下两种之一

    • 'r' (0x72)
    • 'H' (0x48)
    • 'c' (0x63)
  2. 不同协议头的处理逻辑

'H' (Hessian2.0)

  • 检查第二位是否为0x02(版本号)
  • 检查第四位是否为'C' (0x43)
  • 有效payload格式:\x48\x02\x00\x43

'c' (Hessian1.0)

  • 检查第二位和第三位
  • 需要额外添加头部:\x48\x00\x03abc
  • 有效payload格式:\x63\x02\x00\x48\x00\x03abc

5. 反序列化流程分析

5.1 HessianServiceExporter流程

  1. 请求进入HessianServiceExporter.invoke()
  2. 读取输入流第一个字节进行协议头判断
  3. 根据协议头创建对应的输入流:
    • 'H'/'C':Hessian2Input
    • 'c':HessianInput
  4. 进入readCall方法,检查第四位是否为'C'
  5. 调用skeleton.invoke(in, out)进行反序列化

5.2 HessianServlet流程

  1. 请求进入HessianServlet.service()
  2. 读取输入流前三个字节进行协议头判断
  3. 根据协议头创建对应的输入流
  4. 后续流程与HessianServiceExporter类似

6. 有效payload构造

6.1 针对HessianServiceExporter

  1. Hessian2.0

    \x48\x02\x00\x43 + [原生Hessian2.0 payload]
    
  2. 简化Hessian2.0

    \x43 + [原生Hessian2.0 payload]
    
  3. Hessian1.0

    \x63\x02\x00\x48\x00\x03abc + [原生Hessian1.0 payload]
    

6.2 针对HessianServlet

  1. Hessian2.0

    \x48\x02\x00\x43 + [原生Hessian2.0 payload]
    
  2. Hessian1.0

    \x63\x02\x00\x48\x00\x03abc + [原生Hessian1.0 payload]
    

7. 原生Hessian与封装Hessian的区别

原生Hessian反序列化(如xxl-job):

HessianInput input = new HessianInput(stream);
input.readObject();
  • 无协议头检查
  • 直接接受原生Hessian序列化流

封装Hessian(HessianServlet/HessianServiceExporter):

  • 有严格的协议头检查
  • 需要添加特定前缀才能进入反序列化流程

8. 协议头总结表

协议头 ASCII HEX 版本 输入流 输出流 适用场景 备注
'H' 72 \x48\x02\x00\x43 Hessian 2.0 Hessian2Input Hessian2Output 两者通用 Hessian2.0稳定利用
'C' 67 \x43 Hessian 2.0 Hessian2Input Hessian2Output 仅HessianServiceExporter 简化版
'c' 99 \x63\x02\x00\x48\x00\x03abc Hessian 1.0 HessianInput Hessian(2)Output 两者通用 根据版本选择输出流

9. 实际利用建议

  1. 优先使用'H'开头的Hessian2.0 payload,兼容性最好
  2. 对于HessianServiceExporter,可以尝试简化版的'C'开头payload
  3. Hessian1.0利用限制较多,不建议优先考虑
  4. 在实际测试中,可以通过修改现有工具的payload生成逻辑,添加相应的协议头前缀

10. 防御建议

  1. 升级Hessian到最新版本
  2. 对Hessian服务进行访问控制
  3. 考虑使用白名单机制限制可反序列化的类
  4. 监控异常的Hessian协议头请求
HessianServlet与HessianServiceExporter漏洞利用分析 1. 背景介绍 Hessian是一种轻量级的RPC框架,在Java开发中广泛使用。目前常见的Hessian服务暴露方式主要有两种: HessianServlet : com.caucho.hessian.server.HessianServlet HessianServiceExporter : org.springframework.remoting.caucho.HessianServiceExporter 现有的漏洞利用工具生成的payload往往针对原生Hessian反序列化场景,无法直接用于这两种服务暴露方式,因为它们在解析Hessian序列化数据时会进行额外的协议头判断。 2. 常见错误分析 当使用现有工具生成的payload攻击这两种服务时,通常会遇到以下错误: 协议头不匹配错误 :服务端期望特定的协议头格式 版本校验错误 :Hessian2.0服务需要正确的版本标识 3. 组件访问特征 3.1 HessianServiceExporter 典型服务暴露代码: 访问特征: 通过Spring MVC暴露的HTTP接口 通常以 /remoting/* 等形式出现在应用路由中 3.2 HessianServlet 访问特征: 直接继承 HessianServlet 的HTTP服务 通常配置在web.xml中 4. 协议头分析 4.1 HessianServiceExporter协议头要求 POST请求进入 invoke 方法后,会首先检查输入流的第一个字节: 第一位必须是以下三种之一 : 'H' (0x48) 'C' (0x43) 'c' (0x63) 不同协议头的处理逻辑 : 'H' (Hessian2.0) 检查第二位是否为0x02(版本号) 检查第四位是否为'C' (0x43) 有效payload格式: \x48\x02\x00\x43 'C' (Hessian2.0简化) 只需第一位为'C' 有效payload格式: \x43 'c' (Hessian1.0) 检查第二位和第三位 需要额外添加头部: \x48\x00\x03abc 有效payload格式: \x63\x02\x00\x48\x00\x03abc 4.2 HessianServlet协议头要求 与HessianServiceExporter类似,但有细微差别: 第一位必须是以下两种之一 : 'r' (0x72) 'H' (0x48) 'c' (0x63) 不同协议头的处理逻辑 : 'H' (Hessian2.0) 检查第二位是否为0x02(版本号) 检查第四位是否为'C' (0x43) 有效payload格式: \x48\x02\x00\x43 'c' (Hessian1.0) 检查第二位和第三位 需要额外添加头部: \x48\x00\x03abc 有效payload格式: \x63\x02\x00\x48\x00\x03abc 5. 反序列化流程分析 5.1 HessianServiceExporter流程 请求进入 HessianServiceExporter.invoke() 读取输入流第一个字节进行协议头判断 根据协议头创建对应的输入流: 'H'/'C': Hessian2Input 'c': HessianInput 进入 readCall 方法,检查第四位是否为'C' 调用 skeleton.invoke(in, out) 进行反序列化 5.2 HessianServlet流程 请求进入 HessianServlet.service() 读取输入流前三个字节进行协议头判断 根据协议头创建对应的输入流 后续流程与HessianServiceExporter类似 6. 有效payload构造 6.1 针对HessianServiceExporter Hessian2.0 : 简化Hessian2.0 : Hessian1.0 : 6.2 针对HessianServlet Hessian2.0 : Hessian1.0 : 7. 原生Hessian与封装Hessian的区别 原生Hessian反序列化(如xxl-job): 无协议头检查 直接接受原生Hessian序列化流 封装Hessian(HessianServlet/HessianServiceExporter): 有严格的协议头检查 需要添加特定前缀才能进入反序列化流程 8. 协议头总结表 | 协议头 | ASCII | HEX | 版本 | 输入流 | 输出流 | 适用场景 | 备注 | |-------|-------|-----|-----|-------|-------|---------|-----| | 'H' | 72 | \x48\x02\x00\x43 | Hessian 2.0 | Hessian2Input | Hessian2Output | 两者通用 | Hessian2.0稳定利用 | | 'C' | 67 | \x43 | Hessian 2.0 | Hessian2Input | Hessian2Output | 仅HessianServiceExporter | 简化版 | | 'c' | 99 | \x63\x02\x00\x48\x00\x03abc | Hessian 1.0 | HessianInput | Hessian(2)Output | 两者通用 | 根据版本选择输出流 | 9. 实际利用建议 优先使用'H'开头的Hessian2.0 payload,兼容性最好 对于HessianServiceExporter,可以尝试简化版的'C'开头payload Hessian1.0利用限制较多,不建议优先考虑 在实际测试中,可以通过修改现有工具的payload生成逻辑,添加相应的协议头前缀 10. 防御建议 升级Hessian到最新版本 对Hessian服务进行访问控制 考虑使用白名单机制限制可反序列化的类 监控异常的Hessian协议头请求