服务器被挖矿程序入侵后的完整排查与应急响应记录
2026/9/15 21:53:00 网站建设 项目流程

那天下午我正盯着监控面板,一台闲置测试服务器的CPU曲线突然拉成了一条直线,负载直接从0.3冲到了8以上。我第一反应是“是不是有人跑了个什么压测脚本”,可翻了半天也没找到提交记录,登录服务器一看,top里一个名叫“kdevtmpfsi”的进程正安静地吃着300%多的CPU。说实话,当时心里咯噔一下,我知道,这台机器被矿工盯上了。

这类事情放在今天的网络环境里,一点都不算稀罕。弱口令爆破、开放端口扫描、未授权访问,每天都有大量自动化脚本在全网不停试探,只要你有一台公网IP的服务器,密码稍微弱一点,或者某个服务忘了加访问控制,用不了多久就会被“借走”算力。我这次踩的就是典型的SSH弱口令爆破,整个过程从入侵到发现,前后不超过48小时。这篇记录我想把完整的排查、定位、清除和事后加固过程写出来,包括我自己踩过的坑和后来总结出的经验,希望对正在做服务器运维的朋友有帮助。

1. 异常初现:从负载飙升到确定被入侵

1.1 第一眼看到的现象,别急着上去kill

发现异常的那天,我先是看了一眼Zabbix的CPU监控曲线,发现从凌晨三点左右开始,CPU使用率就莫名其妙地往上爬,到了早上八点多已经持续跑满。这台服务器配置是4核8G,平时只跑了一个内部用的Nginx和几个定时备份脚本,正常CPU占用很少超过20%。这种曲线变化几乎可以肯定是异常任务在跑。

我SSH登上去之后,没有急着结束进程,而是先做了一轮信息收集。top按CPU排序,一眼看到了那个占用最高的进程,进程名看起来像系统内核模块,但我心里清楚这多半是挖矿程序伪装的名字。这时候最忌讳的就是直接kill掉进程,因为绝大多数挖矿程序都有守护进程或者定时任务,你杀掉一个,过几分钟它又会拉起来一个新的,名字还可能变了。必须先把这个程序的启动方式找出来,才能断根。

我先用systemctl list-units --type=service --state=running看了一下有没有异常服务,再用ps -ef看了一下进程的父子关系。一般情况下,挖矿程序会由一个shell脚本拉起,这个脚本通常藏在/tmp、/var/tmp、/dev/shm这类目录里。后面果然在/tmp下找到了一个叫x的脚本文件,里面的内容大致就是下载挖矿程序、设置执行权限、写入定时任务这几件事。

1.2 初步判断:是业务高峰还是被人“借”走了CPU

在把所有现象串起来之前,我先冷静判断了一下有没有可能是业务自身的问题。这台服务器上跑的应用很简单,Nginx的访问日志我也翻了一遍,没有突然增长的流量,没有异常的爬虫或并发请求。数据库、PHP-FPM这些服务都没有明显的慢查询和错误日志。既然业务侧没有任何变化,CPU却持续跑满,那么只剩下一个可能:这台机器被外部控制了,正在执行挖矿任务。

这时候我注意到了另外一个细节,用ss -antp查看网络连接时,发现这个进程和一堆外部IP建立了连接,目标端口大多是3333、4444、5555这类不常见的端口,也有一些走443的加密流量。挖矿程序一般需要连接矿池,而矿池地址很多时候会走这些非常规端口来躲避流量监控。如果你看到服务器上有进程在向这些端口持续发包,基本可以实锤是矿机了。

另外我检查了/proc/<PID>/exe这个符号链接,发现这个进程实际执行的二进制文件指向/home目录下的一个隐藏文件夹,而不是系统目录。这里提一句,很多挖矿程序会把二进制文件伪装成系统路径的名字,比如libudev.so、kworkerds,但exe链接会暴露真实位置。这也是排查时一个非常有效的判断依据。

1.3 排查前的准备工作:快照与记录

确认是被入侵之后,我做的第一件事不是清理,而是取证。很多有经验的老运维都会告诉你一个原则:先取证、后处置。因为一旦你动了进程或者删了文件,很多攻击痕迹就会被破坏,后面想溯源就难了。

我先在云控制台给这台服务器打了一个快照,这个动作很重要,万一清理过程中搞坏了系统,还能回滚;同时快照也相当于把现场完整保留下来。然后我用history命令查看了root用户的历史命令,不过这里要提醒一下,攻击者一般都会清理或篡改history记录,所以这个只能作为辅助参考。更重要的是去看/var/log/secure/var/log/auth.log,这是SSH登录日志,里面会记录所有成功和失败的登录尝试。

同时我把top、ps、ss、crontab -l这些命令的输出都重定向保存到了本地文件,并且记录下来当时的时间点。这些都是后面溯源和分析的重要依据。不要觉得自己处理完就完事了,如果这是一台生产服务器,或者存着重要数据,后续可能还需要向相关人员交代事件经过,这些记录就是最好的说明材料。

2. 顺藤摸瓜:挖矿进程与入侵路径定位

2.1 用 top、ps、lsof 几个命令锁定可疑进程

定位可疑进程是应急响应的第一步,也是最关键的一步。我常用的命令组合是这几条:

top -c -b -n 1 | head -30 ps -ef --sort=-%cpu | head -20 lsof -p <PID> | head -50 ls -l /proc/<PID>/exe cat /proc/<PID>/cmdline | tr '\0' ' '

第一条看CPU占用最高的进程,第二条按CPU排序看完整命令行,第三条查看这个进程打开了哪些文件,第四条和第五条看执行路径和启动参数。这套组合下来,基本能把挖矿程序的老底摸清。

我在这次事件里发现,可疑进程的名字叫kdevtmpfsi,它的父进程是PID 1,也就是被init直接托管的,说明它已经成功脱离终端,以daemon方式在跑。另外在/proc/<PID>/cmdline里看到它带有-c参数,后面跟着一串矿池配置信息。我还发现机器上同时存在一个名字叫kinsing的进程,这是经常和挖矿程序一起出现的后门程序,它主要负责持久化、横向传播,有时候还会下载其他恶意模块。如果发现机器上有这个进程,说明情况可能比单纯挖矿更严重,要重点排查。

顺便说一句,网上很多人喜欢直接推荐用ps aux | grep -i miner这种方式找挖矿进程,但在实战中,攻击者会给进程起各种各样的名字,有些叫systemd,有些叫java,有些叫kworker,只看名字很容易漏掉。我自己的经验是,优先看CPU使用率和/proc/<PID>/exe指向的路径,这两个指标比进程名可靠得多。

2.2 定时任务、启动脚本与持久化驻留

找到可疑进程之后,紧接着就要回答一个问题:它是怎么做到开机自启或者持续存在的?在Linux系统上,最常见的持久化手段无非是这么几类:crontab定时任务、systemd服务、init.d脚本、rc.local、还有Shell启动文件里加载的恶意代码。

我检查了root和当前用户的crontab,果然在/var/spool/cron/root里发现了一个之前没见过的任务,内容是每隔几分钟执行一次/tmp/x脚本。这个x脚本的作用很简单,就是检查挖矿程序是否存在,不存在就从远程服务器下载并重新拉起,同时把自身重新写进crontab,防止被清除后不再出现。

这里分享一个小技巧:除了检查当前用户的crontab,还要重点看/etc/crontab/etc/cron.d//etc/cron.hourly//etc/cron.daily/这些目录,因为很多攻击者为了隐蔽,会把定时任务写到系统的cron目录下,而不是单独的某个用户任务里。另外,/var/spool/cron/下面如果出现了一些奇怪的用户名文件,也要留意。

systemd方面,我在/etc/systemd/system/下发现了一个名叫dbus.service的恶意服务(正常的dbus服务应该在/lib/systemd/system/目录下),里面配置了ExecStart指向那个挖矿二进制。这种伪装手法很常见,所以排查systemd服务时,一定要看这个服务文件的路径是否在标准目录下,以及内容是否合理。

2.3 日志审计:从auth.log挖出入侵入口

进程和定时任务都确认了,接下来就要找出攻击者是从哪里进来的。这一步决定了你后续的加固方向。如果入口不堵住,你就算清得再干净,人家随时还能再进来。

我重点看了/var/log/secure(CentOS/RHEL)或/var/log/auth.log(Debian/Ubuntu)。因为攻击时间大概率在凌晨,我先用grep把当天的登录记录过滤出来,统计了一下失败的密码尝试次数和来源IP。结果显示,从几天前开始,就有一个IP段在不断尝试SSH登录,一开始是字典式的爆破,尝试了上千次,直到某一次成功了——那一次登录用的账号是root,密码居然是我图省事设置的简单密码“Admin@123”。看到这个我真的脸红,这台机器虽然是测试环境,但暴露在公网上还设置弱口令,被爆破进来只是时间问题。

除了auth日志,我还看了last命令的输出,它记录的是最近登录过的用户、时间、来源IP。这能帮我确认攻击者登录之后做了哪些操作。另外,/var/log/wtmp/var/log/btmp分别是成功登录、失败登录的二进制日志,可以用lastlastb来读。

这里要特别强调一下:在排查登录日志的时候,千万别忽略“时间同步”这件事。我在分析日志时发现攻击者登录时间和实际时间有几个小时的偏差,后来才想起这台服务器的NTP时间同步没配好,日志里的时间戳和真实时间对不上,排查起来非常痛苦。服务器一定要配置好时间同步,这不仅影响日志分析,很多安全审计机制也依赖准确的时间戳。

3. 清除与取证:处理挖矿程序时的几个关键动作

3.1 杀进程前先取证,别急着rm

很多人发现挖矿进程后的第一反应是kill -9rm -rf,但实际操作中这个做法风险很大,而且很容易清理不干净。我的建议是,在动手之前,至少先完成下面这几件事:

  • cp /proc/<PID>/exe /tmp/malware.bin把漏洞程序样本备份下来,后面可以拿去给杀毒引擎分析,也能留作证据。
  • strings /tmp/malware.bin | grep -E 'http|stratum|pool'看一下这个程序连接了哪个矿池,这能帮你了解攻击者的资源和算力去向。
  • 把crontab、启动脚本、恶意服务的配置内容都复制一份,方便后面分析和写处置报告。
  • 查看并记录/root/.ssh/authorized_keys,重点看有没有陌生的SSH公钥被写入。攻击者经常会在成功登录后植入自己的公钥,这样即使你改了密码,他还能通过密钥免密登录。

我这次就在/root/.ssh/authorized_keys里发现了一个陌生公钥,后面清理时一并删掉了。这一步很容易被忽略,但却是后门驻留最常见的做法之一。

3.2 清理定时任务、SSH公钥和后门账户

完成取证之后,才进入真正的清除阶段。我按下面的顺序操作:

  1. 备份并清空可疑的crontab任务:crontab -u root -e,把异常的任务删除,同时检查/etc/cron.d/批量清理。
  2. 停止并禁用恶意服务:systemctl stop dbus.service && systemctl disable dbus.service,然后删除对应的service文件。
  3. 杀掉所有恶意进程:先用pkill -f kdevtmpfsipkill -f kinsing,再用ps -ef确认没有残留。
  4. 删除恶意文件和脚本:包括/tmp/x/home下的隐藏目录、以及下载的挖矿二进制。
  5. 清理SSH公钥:编辑/root/.ssh/authorized_keys,移除来源不明的公钥。
  6. 检查系统账户:cat /etc/passwdawk -F: '$3==0{print $1}' /etc/passwd,看有没有新增的UID为0的超级用户。攻击者有时候会直接创建一个root权限的账号,比如“sysadmin”或者“support”。

做完这几步之后,我又在系统上全盘搜了一下有没有残留文件:

find / -name "*kdevtmpfsi*" -o -name "*kinsing*" -o -name "*xmrig*" 2>/dev/null

这个命令可以把常见挖矿木马相关的文件名都找出来。不过要说明一下,这只能作为辅助,攻击者文件名经常伪装,最好再结合/proc目录里有没有不正常的进程来判断。

3.3 系统级加固:防火墙、密钥登录与fail2ban

清完恶意程序之后,最关键的还是要堵住入口,否则一切都是白费。这次入侵的根源就是SSH弱口令,所以我第一步就是把root的密码改成高强度随机密码,同时立刻启用SSH密钥登录,禁止密码登录。

我修改了/etc/ssh/sshd_config,关键配置如下:

PermitRootLogin prohibit-password PubkeyAuthentication yes PasswordAuthentication no RSAAuthentication no Port 22

然后把本机的公钥重新写进/root/.ssh/authorized_keys。这里要提醒一个坑:如果你直接关了密码登录,万一密钥没配置对,你可能会把自己锁在外面。所以建议先配置好密钥,确认能正常登录之后,再关闭密码认证,并且在操作时开一个新的SSH会话测试,不要断开当前会话。

防火墙方面,我用firewalld(如果是Ubuntu则是ufw)把端口策略收紧了一些。原来这台服务器除了22端口,还开着很多用不上的端口,比如3306、6379、8080,这些服务其实根本没在跑,但端口暴露在外面等于给扫描器提供了入口。我用下面的命令把不必要的端口全部关掉:

firewall-cmd --permanent --remove-port=3306/tcp firewall-cmd --permanent --remove-port=6379/tcp firewall-cmd --permanent --remove-port=8080/tcp firewall-cmd --reload

如果用的是云服务器,还要同时检查云控制台里的安全组规则。很多情况下,系统内部的防火墙即使配好了,安全组如果放开了所有端口,外部流量依然能直接进来。这次事件之后我才发现,原来安全组里有一条约等于“允许所有来源访问所有端口”的规则,这应该是之前调试某些服务时临时加的,后来忘了删,等于把大门敞开了。

最后安装fail2ban,它能实时监控SSH登录日志,发现多次登录失败的IP就自动封禁一段时间。配置很简单,安装后稍作调整即可:

yum install fail2ban -y systemctl enable fail2ban

默认配置下,fail2ban会监控SSH并封禁尝试5次以上的IP,时间为10分钟。我后来把最大重试次数调到了3次,封禁时间调成了24小时,效果更明显。

4. 后续防护与监控体系建设

4.1 端口最小化与访问控制

这次被入侵之后,我把这台服务器的网络暴露面重新梳理了一遍。所谓暴露面,就是对外开放的IP和端口,暴露面越小,被攻击的概率越低。我整理了一份开放端口清单,和业务负责人逐个确认,凡是说不清楚用途的端口全部关闭。

对于确实需要对外开放的服务,我做了几层访问控制:

  • 如果是管理类端口(比如SSH、数据库管理面板),只允许公司出口IP访问。云安全组里可以写明细的源IP白名单。
  • 如果是业务端口(比如80、443),只打开必要的协议,禁止把数据库、缓存、消息队列这类内部组件直接暴露到公网。
  • 对于Redis、MongoDB、Elasticsearch这类容易有未授权访问的服务,在配置文件里绑定内网IP,并开启密码认证或使用TLS。

这样做下来,这台服务器的开放端口从原来的十来个降到了三个,攻击面小了很多。我在排查日志时看到,攻击者的扫描行为其实相当频繁,只要你开放了公网端口,几乎每天都会被扫描。端口关掉之后,那些无效扫描自然就消失了。

4.2 补丁、基线检查与漏洞修复

清理完这次的木马之后,我还顺手给系统做了一次全面的补丁升级。因为攻击者既然已经拿下了root权限,他在系统里动了什么手脚,我很难百分之百确认,所以把系统内核、openssh、glibc这些都更新到最新版本,能覆盖已知的提权漏洞。

这里有一个经验:很多挖矿木马会利用内核漏洞提权,比如脏牛(Dirty COW)、脏管道(Dirty Pipe)这类经典漏洞,如果你的内核版本很老,即使你修好了密码和端口,攻击者依然可以通过漏洞二次提权。所以定期打补丁不是可选项,而是必须做的日常操作。

除了打补丁,我还在服务器上安装了RKHunter和chkrootkit做了一次根kit扫描。这两个工具能检测系统里是否被植入了后门程序、可疑的rootkit和异常的系统调用。扫描结果虽然是干净的,但考虑到木马已经运行过一段时间,我把这台机器上运行的关键应用也重新部署了一遍,并重置了相关服务的账号密码。

如果各位有条件,建议做一次基线检查,也就是对照安全基线标准(系统账户、权限、端口、内核参数、日志配置等)逐项核查。现在很多开源工具都能辅助做这个事,比如lynis,一条命令就能输出一份很详细的安全报告,排查效率高很多。

4.3 监控告警与备份恢复:不要等你发现时已经晚了

经历过这次事件,我最大的感受是:发现问题快,损失就小。如果我没有部署Zabbix和Grafana监控,可能等到月底结算流量或者业务报障时才发现机器被控制了。所以事后我加强了两块:监控和备份。

监控方面,我在服务器上部署了agent,采集CPU、内存、磁盘、网络流量等基础指标,并设置了告警规则:CPU使用率超过90%持续5分钟、出网流量突然暴增、登录失败次数激增,都会触发告警。不要觉得“这只是测试服务器不用监控”,攻击者可不分环境和生产,任何一台机器被控制,都有可能变成跳板,去攻击内网里的其他机器。

备份方面,我给这台服务器配置了每日自动快照,并把关键数据定期同步到异地的存储桶里。这里用的就是常说的3-2-1备份原则:至少三份副本,两种不同介质,一份异地存储。服务器被入侵后,最坏的情况下你可能需要重装系统,如果没有备份,数据说没就没。我以前总觉得备份麻烦,但真正出问题时才知道备份的价值,尤其是当你被迫要对系统做重装或大规模清理的时候,有备份在手心里才不慌。

5. 常见问题与排查技巧实录

5.1 挖矿程序隐藏进程的套路

我在这次排查中见到的挖矿程序,隐藏手段主要有这么几类,大家以后排查时可以对照检查:

  • 进程名伪装成系统名:比如叫kworkersystemdsshdjava,单看名字很难发现问题。
  • 把二进制放在/tmp、/dev/shm、/var/tmp这些高权限目录中,利用这些目录容易写文件、又容易被忽略的特点。
  • 通过定时任务、systemd服务实现持久化,甚至多个手段同时存在,防止只清理一处后重新复活。
  • LD_PRELOAD环境变量劫持系统调用,隐藏进程、网络连接等信息。这种情况下,即使top、ps里也看不到异常进程。

应对手段前面也说了:不要只看进程名,要看/proc/<PID>/exe的真实路径;不要只看常规的crontab,要全面排查系统级定时任务和服务;如果怀疑有LD_PRELOAD劫持,可以用env -i启动一个干净环境再执行ps检查,或者用lsof +L1查找已删除但仍被进程占用的可疑文件。

说句题外话,如果是在Docker或K8s环境里,还要多检查容器有没有被入侵。容器和宿主机共享内核,如果权限配置不当,攻击者有可能通过容器逃逸攻击宿主机。所以容器环境里要尽量避免以root运行容器进程,不要把宿主机的目录随意挂载进容器,以免被利用。

5.2 日志被清除怎么办

这次事件中,攻击者倒是没有清日志,但我之前在其他机器上遇到过日志被清的情况。如果攻击者把/var/log目录清空了,你会发现登录日志、cron日志全都查不到,这时可以尝试从这几个地方找线索:

  • Shell history:如果攻击者没清理干净,~/.bash_history里可能还留着命令记录。
  • 内存信息:一些正在运行的恶意进程的cmdline、环境变量,可以从 /proc 里挖出来。
  • 系统进程树:ps -ef快照、pstree -a,在某些情况下可以还原攻击者的操作痕迹。
  • 云平台审计日志:云控制台里的操作记录、安全告警,有时候比系统日志更全面。
  • 外部日志聚合:如果企业里搭建了ELK或Splunk,日志会集中收集,即使本地被清,远端依然有副本。这也是为什么建议大家有条件就把系统日志集中采集,不然日志被清后真的会陷入被动。

日志被清本身也是一个重要的告警信号,攻击者如果刻意清除日志,说明他希望在系统中长期潜伏,这种情况下你不仅要处理当前的木马,还要对系统做更深入的排查。

5.3 如果入侵发生在云主机上,额外要做的事

现在很多人的服务器是云主机,除了主机内部的常规排查,还要注意几件额外的事情:

  • 检查云平台的访问密钥(AccessKey),尤其是历史遗留的密钥,攻击者可能从服务器上读取了你的云平台凭据,然后利用API创建新机器挖矿。
  • 检查安全组,把不需要的公网访问规则全部删除,严格控制来源IP白名单。
  • 创建新的API密钥或者轮换现有密钥,防止旧密钥被滥用。
  • 查看云平台的费用明细,如果发现未知的按量付费机器或高性能实例,很可能就是攻击者用你的凭据建的矿机,要立即停用并删除。
  • 如果服务器上存过数据库密码、第三方API密钥之类的敏感信息,全部轮换一遍,不要觉得麻烦,这比事后补救省心多了。

我之前听说过一个案例,攻击者从一台被入侵的服务器上拿到了云厂商的API密钥,用被害者的账号创建了上百台GPU云服务器挖矿,两天跑出几万美元的账单。所以云主机被入侵后,账号安全级别的检查绝对不能跳过。

6. 最后的建议:安全运维不是一次性的

写这篇记录的时候,距离事件发生已经过去一段时间了。现在回头看,这次被入侵的根源其实很简单:弱口令加上防火墙开得太多,再叠加安全组配得太随意。整个排查和清理过程大概花了大半天,但事后做加固和监控,反而花了将近两天。我希望各位不要等到出事才来重视这些基础工作。

最后再分享一个小技巧:一定要定期做“恢复演练”。不要只是配置了备份、配置了快照、设置了监控告警,就以为万事大吉。真正的考验是你能不能在系统被攻击、数据被加密、服务中断的时候,用最短的时间把业务拉起来。我第一次做恢复演练的时候,发现备份虽然每天都在跑,但恢复出来的数据根本起不来,后来排查是数据库备份脚本里少写了一个参数,这种问题不改,等真出事的时候就是灾难。安全运维说白了就是靠这些一点一滴的基础工作堆出来的,平时多花点心思,关键时刻能少走很多弯路。

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

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

立即咨询