一、问题背景
某天上午,业务团队反馈核心订单处理服务响应明显变慢,接口 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/0、kworker变体(实际是伪装名)各占一个核心,整体 load average 长期在 16.0 以上,与核心数持平。 - 业务被拖慢:订单 API 的 GC 时间变长,Redis 的
used_cpu_sys飙升,但应用本身进程 CPU 占用反而被挤到很低。 - 进程名可疑:
top里排在最前的进程名频繁变动(一会儿kthreadd,一会儿systemd-network),通过ps -ef看到的完整命令行却指向/var/tmp/.cache/...这种隐藏目录。 - 网络有外联:
iftop显示机器持续向几个境外 IP 的3333、8080端口建立大量 TCP 连接,出向流量不大但连接数极多。 - 文件描述符与连接数异常:
ss -antp看到数百个到矿池域名的 ESTABLISHED 连接,且有规律的重连行为。
基本可以断定:这不是性能问题,是机器"被拿去挖矿了"。
三、排查过程
3.1 定位恶意进程
先看占用最高的进程 PID 及其真实路径:
|
|
发现主进程落在 /var/tmp/.cache/ken/.ken 之类带隐藏目录的路径下。继续看它拉起的子进程与守护脚本:
|
|
3.2 找到守护与自启机制
挖矿木马一般都会做"自杀重启"和"开机自启",否则一眼就能 kill 掉。重点排查三处:
|
|
果然在 /etc/cron.d/ 下发现一个伪装成 0systemd 的定时任务,每分钟执行一次下载并拉起挖矿程序;同时 /etc/systemd/system/ 里有一个 myapp.service 做持久化。
3.3 定位挖矿主体与配置
进入隐藏目录,查看主程序与配置文件:
|
|
config.json 暴露了关键信息:矿池为某 stratum+tcp 地址,钱包是一串 Monero(XMR)地址,threads 被设为机器的全部核心数。确认这就是 XMRig 的变体。
3.4 顺藤摸瓜找入侵入口
既然机器没直接暴露公网,要弄清楚入口。查近期登录与历史命令:
|
|
发现跳板机弱口令账号在凌晨被爆破成功,随后执行了下载脚本:
|
|
进一步比对 Docker 2375 端口的暴露时间窗,确认攻击者先打穿测试机的 Docker 无鉴权端口拿到宿主机,再用内网横向移动,最终落地到 app-ord-07。这也解释了为什么进程名会伪装成 kthreadd/kworker。
3.5 确认横向与持久化范围
为防"只清了一台、别的机器还在",快速横向检查同网段:
|
|
发现另有两台主机症状相同,说明是一次批量入侵,必须统一处置。
四、解决方案
清剿遵循"先止血、再清理、后加固"的顺序,且先在受影响的业务侧做限流降级,避免清理过程抖动放大故障。
- 隔离与止血:在交换机/防火墙侧临时把
app-ord-07与另外两台失陷主机的出向对矿池 IP 段做 DROP(保留跳板机管理通道),阻断挖矿外联。
|
|
- 杀进程:先 kill 掉守护与子进程,再 kill 主进程,避免被守护脚本立刻拉起。
|
|
- 删持久化:移除 crontab、systemd unit、rc.local 条目、隐藏目录。
|
|
-
清登录与凭证:删除被爆破的弱口令账号、重置相关用户密码、回收泄露的 SSH key,并审查
authorized_keys。 -
业务恢复:确认 CPU 回落、矿池连接断开后,逐步放开限流,观察订单 API 延迟与 Redis 指标回归正常。
-
横向处置:对另外两台同症状主机重复上述步骤,并在跳板机强制开启 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 临时分区这三道防线补上,下次同类攻击大概率会在落地前就被拦下。