这篇教学文档基于先知社区文章《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.
二、 题目核心代码分析
-
入口点分析
题目提供了一个关键类Maybe,它是一个实现了Comparable接口的动态代理类。其compareTo方法会成为利用链的触发点。在Hessian反序列化过程中,当反序列化对象被放入TreeMap时,TreeMap.put()方法会调用compare方法,进而触发动态代理的compareTo,最终调用其关联的InvocationHandler.invoke()方法。 -
核心挑战
利用链的构造被严格限制在白名单包名下。因此,攻击者需要找到一个位于白名单包(尤其是org.springframework.beans.或org.springframework.jndi.)中,且实现了InvocationHandler接口的类,其invoke方法能够被导向一个危险的“sink”方法。
三、 动态代理利用链的手动挖掘
在白名单范围内,通过分析Spring相关源码,发现了三个潜在的 InvocationHandler 实现类。经过逐一审计,确定了可利用的链。
EarlySingletonInvocationHandler: 其invoke方法对equals,hashCode,toString方法进行了特殊处理,而我们的触发点是compareTo,因此无法直接利用。ServiceLocatorInvocationHandler: 虽然包含getBean调用,但其所需的beanName字段在上下文中不可控,难以利用。ObjectFactoryDelegatingInvocationHandler(关键类):- 该类位于
org.springframework.beans.factory.support.AutowireUtils。 - 其
invoke方法的核心逻辑是调用this.objectFactory.getObject(),并将结果返回。 - 这里的
objectFactory是org.springframework.beans.factory.ObjectFactory类型,这为链的延伸提供了可能。
- 该类位于
四、 利用链构造详解
从 ObjectFactoryDelegatingInvocationHandler 的 objectFactory.getObject() 出发,寻找能够连接到危险sink的路径。
-
寻找可用的 ObjectFactory 实现:
在白名单中寻找ObjectFactory接口的实现类。其中org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean.TargetBeanObjectFactory成为关键跳板。- 其
getObject()方法内部调用了this.beanFactory.getBean(this.targetBeanName, this.targetBeanClass)。 - 这里的
beanFactory类型为org.springframework.beans.factory.BeanFactory。
- 其
-
连接到最终的 Sink:
目标是将beanFactory指向一个能够触发JNDI注入的实现。org.springframework.jndi.support.SimpleJndiBeanFactory符合要求。- 其
getBean(String name, Class requiredType)方法能够根据name参数进行JNDI查找。 - 如果
name是一个恶意的JNDI URL(如ldap://attacker.com/Exploit),且环境允许,则会触发远程类加载/命令执行。
- 其
-
完整的利用链:
Hessian反序列化 -> TreeMap.put() -> Maybe.compareTo() -> InvocationHandler.invoke() (ObjectFactoryDelegatingInvocationHandler) -> ObjectFactory.getObject() (TargetBeanObjectFactory) -> BeanFactory.getBean() (SimpleJndiBeanFactory) -> JNDI注入
五、 JNDI攻击载荷构造
题目环境为JDK 8,且使用了Spring相关依赖,并未限制高版本的JNDI攻击缓解措施。因此可以采用经典的 BadAttributeValueExpException 结合 POJO 和 TemplatesImpl 的链来构造最终的恶意序列化对象,以触发JNDI注入并执行任意代码。
六、 利用脚本(EXP)核心步骤解析
以下为基于原文提供的EXP代码提炼的核心构造步骤:
-
创建并配置
TargetBeanObjectFactory:- 通过Unsafe等反射工具实例化此类。
- 将其
targetBeanName字段设置为恶意的JNDI URL(例如ldap://127.0.0.1:50389/exploit)。
-
创建并配置
SimpleJndiBeanFactory:- 实例化此类。
- 配置其
shareableResources集合,以绕过某些资源检查。 - 为其设置一个
JndiTemplate。
-
连接两个组件:
- 将配置好的
SimpleJndiBeanFactory实例赋值给TargetBeanObjectFactory的beanFactory字段。
- 将配置好的
-
创建动态代理 Handler:
- 实例化
ObjectFactoryDelegatingInvocationHandler。 - 将其
objectFactory字段指向我们已配置好的TargetBeanObjectFactory实例。
- 实例化
-
触发链的封装:
- 使用上一步的
InvocationHandler创建Maybe动态代理对象。 - 将该
Maybe对象作为key放入一个TreeMap中。 - 调用
HessianFactory的deserialize方法序列化这个精心构造的TreeMap对象,生成最终的Payload。
- 使用上一步的
七、 自动化辅助挖掘方法
除了手动审计,文中还介绍了三种自动化辅助挖掘此类链的方法:
- Tabby: 可以通过图数据库查询,寻找从特定
invoke方法(Source)到SimpleJndiBeanFactory.getBean(Sink) 的调用路径,并应用白名单过滤。 - CodeQL: 可以编写查询来定位白名单内实现
InvocationHandler的类,并分析其调用关系。 - AI辅助(Claude Code + GLM4): 通过自然语言描述分析需求(包括白名单和源-汇条件),AI可以在指定源码目录中自动化分析并输出可能的利用链。
八、 总结与要点
- 入口关键: Hessian反序列化触发
TreeMap比较逻辑,调用动态代理的compareTo。 - 限制突破: 利用白名单内Spring框架自身的
ObjectFactoryDelegatingInvocationHandler和TargetBeanObjectFactory类,将调用流转至JNDI注入点。 - 利用条件: 需要目标环境能够出网进行JNDI请求,且未启用高版本JDK的JNDI防护(如
trustURLCodebase限制,本题JDK8环境可利用)。 - 核心技巧: 熟练运用反射、Unsafe等机制在序列化构造阶段动态设置对象的私有字段,是构造复杂Java反序列化利用链的必备技能。