RCTF 2025 maybe_easy题解:Hessian反序列化结合Spring动态代理的JNDI利用链
字数 3566
更新时间 2026-02-27 12:47:35

这篇教学文档基于先知社区文章《RCTF 2025 maybe_easy题解:Hessian反序列化结合Spring动态代理的JNDI利用链》撰写,旨在详细解析该CTF赛题的漏洞原理、利用链构造过程及最终的利用方法。

RCTF 2025 maybe_easy 赛题深度解析与教学文档

一、 前言与题目概述

本题出自RCTF 2025,是一个Java安全挑战,主要考察Hessian反序列化漏洞与Spring动态代理机制的结合利用。题目最终目标是通过构造一个反序列化利用链,触发JNDI注入,实现远程命令执行。

核心环境与依赖:

  • 框架: Spring Boot
  • 反序列化组件: Hessian (版本 4.0.66)
  • 白名单限制: 允许实例化的类包路径限定如下:
    • com.rctf.server.tool.
    • java.util.
    • org.apache.commons.logging.
    • org.springframework.beans.
    • org.springframework.jndi.

二、 题目核心代码分析

  1. 入口点分析
    题目提供了一个关键类 Maybe,它是一个实现了 Comparable 接口的动态代理类。其 compareTo 方法会成为利用链的触发点。在Hessian反序列化过程中,当反序列化对象被放入 TreeMap 时,TreeMap.put() 方法会调用 compare 方法,进而触发动态代理的 compareTo,最终调用其关联的 InvocationHandler.invoke() 方法。

  2. 核心挑战
    利用链的构造被严格限制在白名单包名下。因此,攻击者需要找到一个位于白名单包(尤其是 org.springframework.beans.org.springframework.jndi.)中,且实现了 InvocationHandler 接口的类,其 invoke 方法能够被导向一个危险的“sink”方法。

三、 动态代理利用链的手动挖掘

在白名单范围内,通过分析Spring相关源码,发现了三个潜在的 InvocationHandler 实现类。经过逐一审计,确定了可利用的链。

  1. EarlySingletonInvocationHandler: 其 invoke 方法对 equals, hashCode, toString 方法进行了特殊处理,而我们的触发点是 compareTo,因此无法直接利用。
  2. ServiceLocatorInvocationHandler: 虽然包含 getBean 调用,但其所需的 beanName 字段在上下文中不可控,难以利用。
  3. ObjectFactoryDelegatingInvocationHandler (关键类):
    • 该类位于 org.springframework.beans.factory.support.AutowireUtils
    • invoke 方法的核心逻辑是调用 this.objectFactory.getObject(),并将结果返回。
    • 这里的 objectFactoryorg.springframework.beans.factory.ObjectFactory 类型,这为链的延伸提供了可能。

四、 利用链构造详解

ObjectFactoryDelegatingInvocationHandlerobjectFactory.getObject() 出发,寻找能够连接到危险sink的路径。

  1. 寻找可用的 ObjectFactory 实现:
    在白名单中寻找 ObjectFactory 接口的实现类。其中 org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean.TargetBeanObjectFactory 成为关键跳板。

    • getObject() 方法内部调用了 this.beanFactory.getBean(this.targetBeanName, this.targetBeanClass)
    • 这里的 beanFactory 类型为 org.springframework.beans.factory.BeanFactory
  2. 连接到最终的 Sink:
    目标是将 beanFactory 指向一个能够触发JNDI注入的实现。org.springframework.jndi.support.SimpleJndiBeanFactory 符合要求。

    • getBean(String name, Class requiredType) 方法能够根据 name 参数进行JNDI查找。
    • 如果 name 是一个恶意的JNDI URL(如 ldap://attacker.com/Exploit),且环境允许,则会触发远程类加载/命令执行。
  3. 完整的利用链:

    Hessian反序列化 -> TreeMap.put() -> Maybe.compareTo()
    -> InvocationHandler.invoke() (ObjectFactoryDelegatingInvocationHandler)
    -> ObjectFactory.getObject() (TargetBeanObjectFactory)
    -> BeanFactory.getBean() (SimpleJndiBeanFactory)
    -> JNDI注入
    

五、 JNDI攻击载荷构造

题目环境为JDK 8,且使用了Spring相关依赖,并未限制高版本的JNDI攻击缓解措施。因此可以采用经典的 BadAttributeValueExpException 结合 POJOTemplatesImpl 的链来构造最终的恶意序列化对象,以触发JNDI注入并执行任意代码。

六、 利用脚本(EXP)核心步骤解析

以下为基于原文提供的EXP代码提炼的核心构造步骤:

  1. 创建并配置 TargetBeanObjectFactory:

    • 通过Unsafe等反射工具实例化此类。
    • 将其 targetBeanName 字段设置为恶意的JNDI URL(例如 ldap://127.0.0.1:50389/exploit)。
  2. 创建并配置 SimpleJndiBeanFactory:

    • 实例化此类。
    • 配置其 shareableResources 集合,以绕过某些资源检查。
    • 为其设置一个 JndiTemplate
  3. 连接两个组件:

    • 将配置好的 SimpleJndiBeanFactory 实例赋值给 TargetBeanObjectFactorybeanFactory 字段。
  4. 创建动态代理 Handler:

    • 实例化 ObjectFactoryDelegatingInvocationHandler
    • 将其 objectFactory 字段指向我们已配置好的 TargetBeanObjectFactory 实例。
  5. 触发链的封装:

    • 使用上一步的 InvocationHandler 创建 Maybe 动态代理对象。
    • 将该 Maybe 对象作为 key 放入一个 TreeMap 中。
    • 调用 HessianFactorydeserialize 方法序列化这个精心构造的 TreeMap 对象,生成最终的Payload。

七、 自动化辅助挖掘方法

除了手动审计,文中还介绍了三种自动化辅助挖掘此类链的方法:

  1. Tabby: 可以通过图数据库查询,寻找从特定 invoke 方法(Source)到 SimpleJndiBeanFactory.getBean (Sink) 的调用路径,并应用白名单过滤。
  2. CodeQL: 可以编写查询来定位白名单内实现 InvocationHandler 的类,并分析其调用关系。
  3. AI辅助(Claude Code + GLM4): 通过自然语言描述分析需求(包括白名单和源-汇条件),AI可以在指定源码目录中自动化分析并输出可能的利用链。

八、 总结与要点

  • 入口关键: Hessian反序列化触发 TreeMap 比较逻辑,调用动态代理的 compareTo
  • 限制突破: 利用白名单内Spring框架自身的 ObjectFactoryDelegatingInvocationHandlerTargetBeanObjectFactory 类,将调用流转至JNDI注入点。
  • 利用条件: 需要目标环境能够出网进行JNDI请求,且未启用高版本JDK的JNDI防护(如 trustURLCodebase 限制,本题JDK8环境可利用)。
  • 核心技巧: 熟练运用反射、Unsafe等机制在序列化构造阶段动态设置对象的私有字段,是构造复杂Java反序列化利用链的必备技能。
 全屏