crontab 与定时任务排查
Linux 的 cron 由 crond 常驻进程驱动,每分钟扫描一次任务表并按时间字段触发命令。任务表分布在三类位置:用户自己的任务存在 /var/spool/cron/<用户>(RHEL 系)或 /var/spool/cron/crontabs/<用户>(Debian 系),系统级任务在 /etc/crontab 与 /etc/cron.d/,另外 /etc/cron.hourly 这类目录由 run-parts 拉起。攻击者偏爱这里,因为一条每分钟执行的任务就足以让被杀的木马在六十秒内复活。
基本信息
| 单用户查询 | crontab -l(当前用户);crontab -l -u 用户名 需 root |
|---|---|
| 系统级文件 | /etc/crontab、/etc/cron.d/*、/etc/cron.hourly|daily|weekly|monthly |
| 用户任务目录 | /var/spool/cron/<用户>(RHEL);/var/spool/cron/crontabs/<用户>(Debian) |
| 时间字段 | 分 时 日 月 星期,之后是执行用户(仅系统级文件有该列)与命令 |
| 特殊简写 | @reboot、@hourly、@daily、@weekly、@monthly、@yearly |
| 执行记录 | RHEL 系 /var/log/cron,Debian 系在 /var/log/syslog,部分环境依赖 rsyslog 配置 |
典型使用场景
- 木马反复复活时定位是谁在拉起它,确认是否被写成短周期任务
- 挖矿处置后按清单复查,防止任务表里留着重新下载的脚本
- 排查 CPU、带宽夜里周期性飙高的告警,判断是否有隐藏任务
- 做主机基线时留存全量任务表,日后与新版本对比发现变更
- 交接与上线评审时核对计划任务的归属与执行账号
遍历全部用户的任务
遍历全部用户的任务
for u in $(cut -d: -f1 /etc/passwd); do # 按 passwd 用户逐个查
out=$(crontab -l -u "$u" 2>/dev/null); [ -n "$out" ] && { echo "== $u"; echo "$out"; }; done
ls -la /var/spool/cron /var/spool/cron/crontabs 2>/dev/null # 直接用目录文件交叉核对
cat /etc/crontab; ls -la /etc/cron.d/ # 系统级任务表
ls -la /etc/cron.hourly /etc/cron.daily 2>/dev/null # run-parts 目录
systemctl list-timers --all | head -n 20 # systemd timer 是另一条时间线,不能忽略时间字段读法
| 字段 | 取值范围 | 读法与注意 |
|---|---|---|
| 分 | 0-59 | */5 表示每 5 分钟;逗号枚举;连字符表示区间 |
| 时 | 0-23 | 使用服务器本地时区,跨时区团队要换算后再判断 |
| 日 | 1-31 | 与星期同时限定时各实现行为有差异,攻击者常借此混淆 |
| 月 | 1-12 | 部分实现支持英文缩写 |
| 星期 | 0-7 | 0 与 7 都代表周日 |
| @ 简写 | @reboot 等 | @reboot 每次开机执行,是后门最常用的形态 |
命令列可疑特征
| 特征 | 典型写法 | 说明 |
|---|---|---|
| 下载即执行 | curl -fsSL 地址 | sh | 最常见的一条命令式投毒 |
| 编码混淆 | echo 长串 | base64 -d | sh | 隐藏真实命令内容,规避关键字检查 |
| 掩藏输出 | 命令 > /dev/null 2>&1 | 防止产生邮件与异常日志,正常业务也这么做需结合命令本身判断 |
| 落在临时目录 | /tmp、/dev/shm 下的脚本或二进制 | 重启即失,符合攻击者用完即弃的习惯 |
| 短周期 | */1 * * * * | 每分钟执行是保活任务的典型节奏 |
不要用 crontab -r 清空:它会直接删掉任务表且不留备份,任务内容与文件时间证据一起消失。正确做法是先 cat 出来存档、记录 mtime 与 sha256,再按行删除或整表替换。
留证与清理
留证与清理
mkdir -p /evidence/cron_$(date +%F)
sudo cp -a /var/spool/cron /evidence/cron_$(date +%F)/ 2>/dev/null
sudo sha256sum /var/spool/cron/crontabs/* 2>/dev/null | tee /evidence/cron_hash.txt
sudo grep -RInE 'curl|wget|base64|/dev/shm|/tmp/' /etc/cron.d /etc/crontab /var/spool/cron 2>/dev/null
sudo grep -i cron /var/log/cron 2>/dev/null | tail -n 30 # 查任务真实执行记录目录法比遍历用户更保险:/var/spool/cron 里可能存在已从 /etc/passwd 删除的账号残留任务文件,按 passwd 遍历会直接漏掉这类条目。
容易漏掉的位置
- /etc/cron.d 下的独立片段文件:不属于任何用户,删除账号也不影响它生效
- /etc/crontab 的第六列:系统级任务多一个执行用户列,按用户任务格式读会读错字段
- /etc/cron.hourly 等目录:脚本文件本身就是任务,不需要时间字段
- at 队列与 systemd timer:at 是一次性任务,timer 与 cron 互不影响
- anacron:在笔记本或长期关机的主机上补跑每日任务,容易被当成无任务
- 容器与宿主机两套 cron:容器内的任务在容器重启后可能消失,宿主机的任务不受影响
扩展核对命令
扩展核对命令
sudo ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ 2>/dev/null # 目录型任务
sudo cat /etc/crontab # 系统级任务表
sudo atq # 一次性任务队列
systemctl status crond 2>/dev/null || systemctl status cron 2>/dev/null # 确认 cron 服务本身是否被停掉
sudo lsattr /var/spool/cron/* 2>/dev/null # 任务文件是否被加不可变属性排查时顺手确认 cron 服务是运行状态:把 crond 停掉或替换成假服务,也是攻击者让已有任务失效、转而依赖自身守护进程的一种手法,看到任务却从不执行就要往这个方向想。