HessianServlet、HessianServiceExporter场景下的Payload

admin 2025年4月23日21:46:40评论5 views字数 3009阅读10分1秒阅读模式

HessianServlet、HessianServiceExporter场景下的Payload

首发先知社区

Hession漏洞利用现状

目前 Java 开发框架里使用 Hessian 去做面向对象的 RPC 传输,大部分都是使用 HessianServlet 以及 spring-web 组件里的 HessianServiceExporter 去暴露 hessian 服务,但是使用现有工具生成 payload 去打就报各种错,实际上 HessianServletHessianServiceExporter 在解析 Hessian 序列化数据时会进行一些协议头判断,而 web-chiasn 生成的都是针对自封装调用的 Hessian 直接打的,并没有针对 HessianServlet、HessianServiceExporter 场景做 payload 封装,导致无法攻击成功。

报错如下

HessianServlet、HessianServiceExporter场景下的PayloadHessianServlet、HessianServiceExporter场景下的Payload

组件代码分析

访问特征

针对 HessianServiceExporter发布的 Hessian 服务,一般都是这么写的

<bean name="/HessianService"        class="org.springframework.remoting.caucho.HessianServiceExporter">        <property name="service" ref="HessianService" /></bean>

HessianServlet、HessianServiceExporter场景下的Payload

访问特征如下

HessianServlet、HessianServiceExporter场景下的Payload
HessianServlet、HessianServiceExporter场景下的Payload

针对 Servlet 暴漏的服务

    <servlet>        <servlet-name>hessian</servlet-name>        <servlet-class>com.servletserver.ServletBasicService</servlet-class>    </servlet>    <servlet-mapping>        <servlet-name>hessian</servlet-name>        <url-pattern>/hessian</url-pattern>    </servlet-mapping>package com.servletserver;import com.caucho.hessian.server.HessianServlet;import java.util.HashMap;publicclassServletBasicServiceextendsHessianServletimplementsServletBasic {@Overridepublic String SayHello(HashMap o) {return"123"+ o.toString();    }}

访问特征如下

HessianServlet、HessianServiceExporter场景下的Payload

HessianServiceExporter 组件代码分析

• POST 请求会进入到 invoke,对整个请求体(传入的序列化 payload)和响应体转发,跟进后赋值
  • HessianServlet、HessianServiceExporter场景下的Payload
    拿到输入流的第一位

    HessianServlet、HessianServiceExporter场景下的Payload
  • • 首先进行第一位字符判断,是否是如下三种,H、C、c,若不是,直接产生第一种报错。
  • HessianServlet、HessianServiceExporter场景下的Payload
    • 倘若协议头为 H,还需要进行判断第二位是不是 0x02,也就是校验版本号是不是 Hessian2.0 是的话才会进行反序列化。这里因为正常生成的 payload 只是满足原生 Hessian 反序列化的要求,所以会报第二种错。
  • HessianServlet、HessianServiceExporter场景下的Payload
    进到 readCall 方法后,要满足第四位为 C (x43)
  • HessianServlet、HessianServiceExporter场景下的Payload
    这样才能正常进入到 skeleton.invoke(in, out);
  • HessianServlet、HessianServiceExporter场景下的Payload
    然后后面就是正常的反序列化流程了,这里不再赘述反序列化流程,可以看下调用栈,有需要的自己查资料
  • HessianServlet、HessianServiceExporter场景下的Payload
    所以可构造出x48x02x00x43 ,此时即可不报错,并且执行 payload
  • HessianServlet、HessianServiceExporter场景下的Payload
    • 倘若协议头为 C,通过 reset() 回滚流的位置,重新读取数据,也没啥特殊处理的
  • HessianServlet、HessianServiceExporter场景下的Payload
    就加个x43 就行
  • HessianServlet、HessianServiceExporter场景下的Payload
    • 倘若协议头为 c,创建 Hessian 1.0 输入流,如果主版本 ≥2,用 Hessian 2.0 输出,否则用 Hessian 1.0 输出
  • HessianServlet、HessianServiceExporter场景下的Payload
         那根据上文就可以构造出x63x02x00,同时这里的 HessianInput 是用 1.0 的 payload 打,实际上这样是不能触发 payload 的,因为 HessianInput 的 readMethod 并不像 HessianInput2 一样里面可以调到 readObject
  • HessianServlet、HessianServiceExporter场景下的Payload
    所以反序列化点是在这里
  • HessianServlet、HessianServiceExporter场景下的Payload
    但是目前 in.readHeader()获取到的是 null,导致无法反序列化,看下逻辑
  • HessianServlet、HessianServiceExporter场景下的Payload
    这段代码的意思就是判断首位是否是 H,如果是再根据第二位和第三位指定要读取的长度,使用 parseChar 解析出指定的长度并返回,就看到三个 read(),第一位需要是 H,然后第二位给个x00 赋 0 值即可,第三位随意,给个几,后面就跟几个字符串(感觉可以天然的加垃圾数据)那也就是在原先的基础上加一个x48x00x03abc 这样就满足了
HessianServlet、HessianServiceExporter场景下的Payload

HessianServlet 代码分析

先到 HessianServlet 的 service

HessianServlet、HessianServiceExporter场景下的Payload

也是读取协议头,跟进看下

HessianServlet、HessianServiceExporter场景下的Payload

不一样的地方是这里直接读了三位,且由之前的 c、C、H 换成了 c、r、H ,C 没了,r 后续也用不上。所以就两种,c、H,c 和之前的一样也是根据第二位判断回复版本,H 就是 Hessian2.0,第三位为x00 即可

HessianServlet、HessianServiceExporter场景下的Payload

通用的,逻辑还是一样,Hessian2.0 依然会多读一位 x43

HessianServlet、HessianServiceExporter场景下的Payload

然后到了 invoke 就一模一样了,不再赘述。

HessianServlet、HessianServiceExporter场景下的Payload

总结

对于 chains 生成的 payload,针对的是入参点直接在 HessianInput()的情况,也就是自封装的比如 xxl-job 最后的调用点,如下代码,就没协议头的判断,就是正常的 Hessian 序列化流就行

// 直接反序列化用户输入的字节流publicclassVulnerableService {public Object deserialize(byte[] data)throws IOException {ByteArrayInputStreambis=newByteArrayInputStream(data);HessianInputinput=newHessianInput(bis);return input.readObject(); // 关键漏洞点:直接反序列化为Object    }}

而对于 Spring-web 包内,则是提供了 org.springframework.remoting.caucho.HessianServiceExporter 用来暴露远程调用的接口和实现类。

对于 Servlet,则是提供了 com.caucho.hessian.server.HessianServlet用来暴露远程调用的接口和实现类。

使用该类则需要添加协议头处理,对应的协议头总结如下。

协议头
ascii
hex
版本
输入流
输出流
备注
'H'
72
x48x02x00x43
Hessian 2.0
Hessian2Input Hessian2Output
Hessian2.0 稳定利用
'C'
67
x43
Hessian 2.0
Hessian2Input Hessian2Output
仅适用于 HessianServiceExporter
'c'
99
x63x02x00x48x00x03abc
Hessian 1.0
HessianInput Hessian(2)Output
根据版本选择输出流。

原文始发于微信公众号(L0una):HessianServlet、HessianServiceExporter场景下的Payload

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月23日21:46:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   HessianServlet、HessianServiceExporter场景下的Payloadhttps://cn-sec.com/archives/3992161.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息