VYPR
High severityNVD Advisory· Published Aug 15, 2024· Updated Aug 19, 2024

CVE-2024-42681

CVE-2024-42681

Description

XXL-JOB v2.4.1 lacks permission checks on sub-task IDs, allowing ordinary users to execute arbitrary tasks on unassigned executors.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

XXL-JOB v2.4.1 lacks permission checks on sub-task IDs, allowing ordinary users to execute arbitrary tasks on unassigned executors.

Vulnerability

Overview

CVE-2024-42681 is an insecure permissions vulnerability in XXL-JOB v2.4.1, a distributed task scheduling framework. The root cause is that the sub-task ID feature does not validate whether the referenced task belongs to an executor the current user is authorized to access. The fix, introduced in commit a2dc9011310628f3e18c3a5095e7e6a946d017bd, moves permission filtering for job groups into the PermissionInterceptor class, ensuring that the sub-task ID is resolved within the user's authorized scope [1][2].

Exploitation

An unprivileged (ordinary) user can exploit this by creating a task on an executor they are assigned to (e.g., executor A) and then specifying a sub-task ID that belongs to a task on an executor they are not assigned to (e.g., executor B). When the ordinary user's task runs, the sub-task on executor B is also executed, despite the user lacking explicit permission to access that executor. The attack requires network access to the XXL-JOB admin interface and a valid account, but no special privileges beyond that [4].

Impact

A remote attacker with a low-privileged account can execute arbitrary pre-defined tasks on any executor managed by the XXL-JOB cluster, bypassing the intended authorization boundaries. This can lead to unauthorized data processing, resource consumption, or execution of sensitive business logic, depending on the tasks configured on the target executor [3][4].

Mitigation

The vulnerability is patched in the commit referenced above. Users should upgrade to a version that includes the fix (v2.4.2 or later). As of publication, no workarounds have been documented. The CVE is not listed on CISA's Known Exploited Vulnerabilities (KEV) catalog [1][2][3].

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
com.xuxueli:xxl-job-coreMaven
< 2.4.22.4.2

Affected products

2

Patches

1
a2dc90113106

【修复】"CVE-2024-42681" 子任务越权漏洞修复;

https://github.com/xuxueli/xxl-jobxuxueliNov 10, 2024via ghsa
11 files changed · +104 53
  • doc/XXL-JOB官方文档.md+2 0 modified
    @@ -2368,6 +2368,8 @@ public void execute() {
     
     ### 7.35 版本 v2.4.2 Release Notes[规划中]
     - 1、【升级】多个项目依赖升级至较新稳定版本,涉及netty、groovy、gson、springboot、mybatis等;
    +- 2、【修复】"CVE-2024-42681" 子任务越权漏洞修复;
    +
     - 2、[规划中]登陆态Token声称逻辑优化,混淆登陆时间属性,降低token泄漏风险。
     
     ### TODO LIST
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/controller/IndexController.java+1 1 modified
    @@ -1,7 +1,7 @@
     package com.xxl.job.admin.controller;
    
     
    
     import com.xxl.job.admin.controller.annotation.PermissionLimit;
    
    -import com.xxl.job.admin.service.LoginService;
    
    +import com.xxl.job.admin.service.impl.LoginService;
    
     import com.xxl.job.admin.service.XxlJobService;
    
     import com.xxl.job.core.biz.model.ReturnT;
    
     import org.springframework.beans.propertyeditors.CustomDateEditor;
    
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/controller/interceptor/PermissionInterceptor.java+62 2 modified
    @@ -1,16 +1,20 @@
     package com.xxl.job.admin.controller.interceptor;
    
     
    
     import com.xxl.job.admin.controller.annotation.PermissionLimit;
    
    +import com.xxl.job.admin.core.model.XxlJobGroup;
    
     import com.xxl.job.admin.core.model.XxlJobUser;
    
     import com.xxl.job.admin.core.util.I18nUtil;
    
    -import com.xxl.job.admin.service.LoginService;
    
    +import com.xxl.job.admin.service.impl.LoginService;
    
     import org.springframework.stereotype.Component;
    
     import org.springframework.web.method.HandlerMethod;
    
     import org.springframework.web.servlet.AsyncHandlerInterceptor;
    
     
    
     import javax.annotation.Resource;
    
     import javax.servlet.http.HttpServletRequest;
    
     import javax.servlet.http.HttpServletResponse;
    
    +import java.util.ArrayList;
    
    +import java.util.Arrays;
    
    +import java.util.List;
    
     
    
     /**
    
      * 权限拦截
    
    @@ -50,10 +54,66 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
     			if (needAdminuser && loginUser.getRole()!=1) {
    
     				throw new RuntimeException(I18nUtil.getString("system_permission_limit"));
    
     			}
    
    -			request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);
    
    +			request.setAttribute(LoginService.LOGIN_IDENTITY_KEY, loginUser);	// set loginUser, with request
    
     		}
    
     
    
     		return true;	// proceed with the next interceptor
    
     	}
    
    +
    
    +
    
    +	// -------------------- permission tool --------------------
    
    +
    
    +	/**
    
    +	 * get loginUser
    
    +	 *
    
    +	 * @param request
    
    +	 * @return
    
    +	 */
    
    +	public static XxlJobUser getLoginUser(HttpServletRequest request){
    
    +		XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);	// get loginUser, with request
    
    +		return loginUser;
    
    +	}
    
    +
    
    +	/**
    
    +	 * valid permission by JobGroup
    
    +	 *
    
    +	 * @param request
    
    +	 * @param jobGroup
    
    +	 */
    
    +	public static void validJobGroupPermission(HttpServletRequest request, int jobGroup) {
    
    +		XxlJobUser loginUser = getLoginUser(request);
    
    +		if (!loginUser.validPermission(jobGroup)) {
    
    +			throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]");
    
    +		}
    
    +	}
    
    +
    
    +	/**
    
    +	 * filter XxlJobGroup by role
    
    +	 *
    
    +	 * @param request
    
    +	 * @param jobGroupList_all
    
    +	 * @return
    
    +	 */
    
    +	public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all){
    
    +		List<XxlJobGroup> jobGroupList = new ArrayList<>();
    
    +		if (jobGroupList_all!=null && jobGroupList_all.size()>0) {
    
    +			XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
    
    +			if (loginUser.getRole() == 1) {
    
    +				jobGroupList = jobGroupList_all;
    
    +			} else {
    
    +				List<String> groupIdStrs = new ArrayList<>();
    
    +				if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
    
    +					groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
    
    +				}
    
    +				for (XxlJobGroup groupItem:jobGroupList_all) {
    
    +					if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {
    
    +						jobGroupList.add(groupItem);
    
    +					}
    
    +				}
    
    +			}
    
    +		}
    
    +		return jobGroupList;
    
    +	}
    
    +
    
     	
    
     }
    
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobCodeController.java+2 1 modified
    @@ -1,5 +1,6 @@
     package com.xxl.job.admin.controller;
    
     
    
    +import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
    
     import com.xxl.job.admin.core.model.XxlJobInfo;
    
     import com.xxl.job.admin.core.model.XxlJobLogGlue;
    
     import com.xxl.job.admin.core.util.I18nUtil;
    
    @@ -43,7 +44,7 @@ public String index(HttpServletRequest request, Model model, int jobId) {
     		}
    
     
    
     		// valid permission
    
    -		JobInfoController.validPermission(request, jobInfo.getJobGroup());
    
    +		PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
    
     
    
     		// Glue类型-字典
    
     		model.addAttribute("GlueTypeEnum", GlueTypeEnum.values());
    
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobInfoController.java+9 34 modified
    @@ -1,5 +1,6 @@
     package com.xxl.job.admin.controller;
     
    +import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
     import com.xxl.job.admin.core.exception.XxlJobException;
     import com.xxl.job.admin.core.model.XxlJobGroup;
     import com.xxl.job.admin.core.model.XxlJobInfo;
    @@ -10,7 +11,6 @@
     import com.xxl.job.admin.core.thread.JobScheduleHelper;
     import com.xxl.job.admin.core.util.I18nUtil;
     import com.xxl.job.admin.dao.XxlJobGroupDao;
    -import com.xxl.job.admin.service.LoginService;
     import com.xxl.job.admin.service.XxlJobService;
     import com.xxl.job.core.biz.model.ReturnT;
     import com.xxl.job.core.enums.ExecutorBlockStrategyEnum;
    @@ -56,7 +56,7 @@ public String index(HttpServletRequest request, Model model, @RequestParam(requi
     		List<XxlJobGroup> jobGroupList_all =  xxlJobGroupDao.findAll();
     
     		// filter group
    -		List<XxlJobGroup> jobGroupList = filterJobGroupByRole(request, jobGroupList_all);
    +		List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
     		if (jobGroupList==null || jobGroupList.size()==0) {
     			throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
     		}
    @@ -67,33 +67,6 @@ public String index(HttpServletRequest request, Model model, @RequestParam(requi
     		return "jobinfo/jobinfo.index";
     	}
     
    -	public static List<XxlJobGroup> filterJobGroupByRole(HttpServletRequest request, List<XxlJobGroup> jobGroupList_all){
    -		List<XxlJobGroup> jobGroupList = new ArrayList<>();
    -		if (jobGroupList_all!=null && jobGroupList_all.size()>0) {
    -			XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
    -			if (loginUser.getRole() == 1) {
    -				jobGroupList = jobGroupList_all;
    -			} else {
    -				List<String> groupIdStrs = new ArrayList<>();
    -				if (loginUser.getPermission()!=null && loginUser.getPermission().trim().length()>0) {
    -					groupIdStrs = Arrays.asList(loginUser.getPermission().trim().split(","));
    -				}
    -				for (XxlJobGroup groupItem:jobGroupList_all) {
    -					if (groupIdStrs.contains(String.valueOf(groupItem.getId()))) {
    -						jobGroupList.add(groupItem);
    -					}
    -				}
    -			}
    -		}
    -		return jobGroupList;
    -	}
    -	public static void validPermission(HttpServletRequest request, int jobGroup) {
    -		XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
    -		if (!loginUser.validPermission(jobGroup)) {
    -			throw new RuntimeException(I18nUtil.getString("system_permission_limit") + "[username="+ loginUser.getUsername() +"]");
    -		}
    -	}
    -	
     	@RequestMapping("/pageList")
     	@ResponseBody
     	public Map<String, Object> pageList(@RequestParam(required = false, defaultValue = "0") int start,  
    @@ -105,14 +78,16 @@ public Map<String, Object> pageList(@RequestParam(required = false, defaultValue
     	
     	@RequestMapping("/add")
     	@ResponseBody
    -	public ReturnT<String> add(XxlJobInfo jobInfo) {
    -		return xxlJobService.add(jobInfo);
    +	public ReturnT<String> add(HttpServletRequest request, XxlJobInfo jobInfo) {
    +		XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
    +		return xxlJobService.add(jobInfo, loginUser);
     	}
     	
     	@RequestMapping("/update")
     	@ResponseBody
    -	public ReturnT<String> update(XxlJobInfo jobInfo) {
    -		return xxlJobService.update(jobInfo);
    +	public ReturnT<String> update(HttpServletRequest request, XxlJobInfo jobInfo) {
    +		XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
    +		return xxlJobService.update(jobInfo, loginUser);
     	}
     	
     	@RequestMapping("/remove")
    @@ -137,7 +112,7 @@ public ReturnT<String> start(int id) {
     	@ResponseBody
     	public ReturnT<String> triggerJob(HttpServletRequest request, int id, String executorParam, String addressList) {
     		// login user
    -		XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
    +		XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
     		// trigger
     		return xxlJobService.trigger(loginUser, id, executorParam, addressList);
     	}
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/controller/JobLogController.java+4 3 modified
    @@ -1,5 +1,6 @@
     package com.xxl.job.admin.controller;
    
     
    
    +import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
    
     import com.xxl.job.admin.core.complete.XxlJobCompleter;
    
     import com.xxl.job.admin.core.exception.XxlJobException;
    
     import com.xxl.job.admin.core.model.XxlJobGroup;
    
    @@ -56,7 +57,7 @@ public String index(HttpServletRequest request, Model model, @RequestParam(requi
     		List<XxlJobGroup> jobGroupList_all =  xxlJobGroupDao.findAll();
    
     
    
     		// filter group
    
    -		List<XxlJobGroup> jobGroupList = JobInfoController.filterJobGroupByRole(request, jobGroupList_all);
    
    +		List<XxlJobGroup> jobGroupList = PermissionInterceptor.filterJobGroupByRole(request, jobGroupList_all);
    
     		if (jobGroupList==null || jobGroupList.size()==0) {
    
     			throw new XxlJobException(I18nUtil.getString("jobgroup_empty"));
    
     		}
    
    @@ -73,7 +74,7 @@ public String index(HttpServletRequest request, Model model, @RequestParam(requi
     			model.addAttribute("jobInfo", jobInfo);
    
     
    
     			// valid permission
    
    -			JobInfoController.validPermission(request, jobInfo.getJobGroup());
    
    +			PermissionInterceptor.validJobGroupPermission(request, jobInfo.getJobGroup());
    
     		}
    
     
    
     		return "joblog/joblog.index";
    
    @@ -94,7 +95,7 @@ public Map<String, Object> pageList(HttpServletRequest request,
     										int jobGroup, int jobId, int logStatus, String filterTime) {
    
     
    
     		// valid permission
    
    -		JobInfoController.validPermission(request, jobGroup);	// 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup
    
    +		PermissionInterceptor.validJobGroupPermission(request, jobGroup);	// 仅管理员支持查询全部;普通用户仅支持查询有权限的 jobGroup
    
     		
    
     		// parse param
    
     		Date triggerTimeStart = null;
    
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/controller/UserController.java+4 4 modified
    @@ -1,12 +1,12 @@
     package com.xxl.job.admin.controller;
     
     import com.xxl.job.admin.controller.annotation.PermissionLimit;
    +import com.xxl.job.admin.controller.interceptor.PermissionInterceptor;
     import com.xxl.job.admin.core.model.XxlJobGroup;
     import com.xxl.job.admin.core.model.XxlJobUser;
     import com.xxl.job.admin.core.util.I18nUtil;
     import com.xxl.job.admin.dao.XxlJobGroupDao;
     import com.xxl.job.admin.dao.XxlJobUserDao;
    -import com.xxl.job.admin.service.LoginService;
     import com.xxl.job.core.biz.model.ReturnT;
     import org.springframework.stereotype.Controller;
     import org.springframework.ui.Model;
    @@ -112,7 +112,7 @@ public ReturnT<String> add(XxlJobUser xxlJobUser) {
         public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser) {
     
             // avoid opt login seft
    -        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
    +        XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
             if (loginUser.getUsername().equals(xxlJobUser.getUsername())) {
                 return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
             }
    @@ -140,7 +140,7 @@ public ReturnT<String> update(HttpServletRequest request, XxlJobUser xxlJobUser)
         public ReturnT<String> remove(HttpServletRequest request, int id) {
     
             // avoid opt login seft
    -        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
    +        XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
             if (loginUser.getId() == id) {
                 return new ReturnT<String>(ReturnT.FAIL.getCode(), I18nUtil.getString("user_update_loginuser_limit"));
             }
    @@ -166,7 +166,7 @@ public ReturnT<String> updatePwd(HttpServletRequest request, String password){
             String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
     
             // update pwd
    -        XxlJobUser loginUser = (XxlJobUser) request.getAttribute(LoginService.LOGIN_IDENTITY_KEY);
    +        XxlJobUser loginUser = PermissionInterceptor.getLoginUser(request);
     
             // do write
             XxlJobUser existUser = xxlJobUserDao.loadByUserName(loginUser.getUsername());
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/LoginService.java+7 3 renamed
    @@ -1,12 +1,12 @@
    -package com.xxl.job.admin.service;
    +package com.xxl.job.admin.service.impl;
     
     import com.xxl.job.admin.core.model.XxlJobUser;
     import com.xxl.job.admin.core.util.CookieUtil;
     import com.xxl.job.admin.core.util.I18nUtil;
     import com.xxl.job.admin.core.util.JacksonUtil;
     import com.xxl.job.admin.dao.XxlJobUserDao;
     import com.xxl.job.core.biz.model.ReturnT;
    -import org.springframework.context.annotation.Configuration;
    +import org.springframework.stereotype.Service;
     import org.springframework.util.DigestUtils;
     
     import javax.annotation.Resource;
    @@ -17,7 +17,7 @@
     /**
      * @author xuxueli 2019-05-04 22:13:264
      */
    -@Configuration
    +@Service
     public class LoginService {
     
         public static final String LOGIN_IDENTITY_KEY = "XXL_JOB_LOGIN_IDENTITY";
    @@ -26,6 +26,8 @@ public class LoginService {
         private XxlJobUserDao xxlJobUserDao;
     
     
    +    // ---------------------- token tool ----------------------
    +
         private String makeToken(XxlJobUser xxlJobUser){
             String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser);
             String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16);
    @@ -41,6 +43,8 @@ private XxlJobUser parseToken(String tokenHex){
         }
     
     
    +    // ---------------------- login tool, with cookie and db ----------------------
    +
         public ReturnT<String> login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember){
     
             // param
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/service/impl/XxlJobServiceImpl.java+10 2 modified
    @@ -61,7 +61,7 @@ public Map<String, Object> pageList(int start, int length, int jobGroup, int tri
     	}
     
     	@Override
    -	public ReturnT<String> add(XxlJobInfo jobInfo) {
    +	public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser) {
     
     		// valid base
     		XxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());
    @@ -131,6 +131,10 @@ public ReturnT<String> add(XxlJobInfo jobInfo) {
     						return new ReturnT<String>(ReturnT.FAIL_CODE,
     								MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
     					}
    +					if (!loginUser.validPermission(childJobInfo.getJobGroup())) {
    +						return new ReturnT<String>(ReturnT.FAIL_CODE,
    +								MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
    +					}
     				} else {
     					return new ReturnT<String>(ReturnT.FAIL_CODE,
     							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
    @@ -169,7 +173,7 @@ private boolean isNumeric(String str){
     	}
     
     	@Override
    -	public ReturnT<String> update(XxlJobInfo jobInfo) {
    +	public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser) {
     
     		// valid base
     		if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {
    @@ -223,6 +227,10 @@ public ReturnT<String> update(XxlJobInfo jobInfo) {
     						return new ReturnT<String>(ReturnT.FAIL_CODE,
     								MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));
     					}
    +					if (!loginUser.validPermission(childJobInfo.getJobGroup())) {
    +						return new ReturnT<String>(ReturnT.FAIL_CODE,
    +								MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_permission_limit")), childJobIdItem));
    +					}
     				} else {
     					return new ReturnT<String>(ReturnT.FAIL_CODE,
     							MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));
    
  • xxl-job-admin/src/main/java/com/xxl/job/admin/service/XxlJobService.java+2 2 modified
    @@ -34,15 +34,15 @@ public interface XxlJobService {
     	 * @param jobInfo
     	 * @return
     	 */
    -	public ReturnT<String> add(XxlJobInfo jobInfo);
    +	public ReturnT<String> add(XxlJobInfo jobInfo, XxlJobUser loginUser);
     
     	/**
     	 * update job
     	 *
     	 * @param jobInfo
     	 * @return
     	 */
    -	public ReturnT<String> update(XxlJobInfo jobInfo);
    +	public ReturnT<String> update(XxlJobInfo jobInfo, XxlJobUser loginUser);
     
     	/**
     	 * remove job
    
  • xxl-job-admin/src/test/java/com/xxl/job/admin/controller/JobInfoControllerTest.java+1 1 modified
    @@ -1,6 +1,6 @@
     package com.xxl.job.admin.controller;
     
    -import com.xxl.job.admin.service.LoginService;
    +import com.xxl.job.admin.service.impl.LoginService;
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.api.Test;
     import org.slf4j.Logger;
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.