1. 事件开端:凌晨的两条安全告警
上周值班,晚上十一点多,安全平台的告警突然响了两条。第一条是某台服务器的CPU使用率持续15分钟超过90%,第二条更直接——检测到该服务器与一个已知矿池地址建立外联通信。
半夜看到这种组合告警,基本可以判定是挖矿木马了。单看CPU飙高可能有别的解释,比如业务高峰期或者定时任务跑批,但一旦同时出现外联矿池的行为,那问题就比较明确。
我没有直接登服务器就动手,而是先做了一件应急响应里最容易被忽略的事——确认这台机器上跑的是什么。
这里想强调一个处置原则:接到告警的第一反应不该是赶紧杀进程,而是先搞清楚这台服务器承载什么业务、有没有其他节点可以顶替、能不能直接隔离。因为一旦误操作,比如把还在跑批的业务进程当成木马干掉,或者贸然重启服务器,后果可能比木马本身更严重。
确认了一下,这台机器是某内部系统的单机节点,不是核心数据服务,影响面可控。于是按流程走处置:先查封端口和恶意IP,再做现场定位和清理,整个过程从登录到基本控制住,大约用了两个小时。
2. 排查定位:从CPU异常到挖矿木马实锤
2.1 进程排查:异常进程不会藏太久
登录服务器之后,我习惯性的第一屏操作还是top,先看整体负载。单看下图的意思大家应该都懂:系统满负载,几个核心都跑满了,User态占用非常高。
接着按CPU降序,直接锁定一个可疑进程,进程名看起来是一串随机字符串。随手用ps -ef拉一下这个进程的详细信息,能看到启动时间、运行用户、父进程PID。
这里有个细节值得多说一句:不要只看进程名。挖矿木马很多会伪装成系统进程名,或者用看起来很正常的名字,比如 "php-fpm"、"kworker"、"java" 之类,所以我判断异常进程的要点是——启动时间是不是系统开机或者正常业务部署的时间点、运行用户是不是异常用户、CPU占用率是不是持续居高不下,而不是只凭进程名。
这一步我拿到了关键信息:
- 进程启动时间正好是三天前凌晨,这个时间点没有发布、没有变更记录
- 运行用户是
www,但这台机器不是Web服务器 - 父进程是PID 1(systemd),典型的daemon型驻留方式
实锤基本跑不掉了,继续往下挖。
top -c ps -ef | grep <PID>2.2 从进程反查文件与网络连接
挖矿木马三个要素缺一不可:恶意样本文件、外联矿池的网络连接、持久化机制。
进程已经锁定,接下来要找到它对应的文件。这个不一定要去翻目录,直接用Linux的/proc文件系统就行:
ls -l /proc/<PID>/exe cat /proc/<PID>/cmdline lsof -p <PID>这里可以看到三个东西:
- 可执行文件的实际路径(指向一个奇怪的路径,比如
/tmp/.x或/var/tmp/...) - 命令行参数(矿工程序通常有一堆参数,比如指定矿池地址、钱包地址、线程数)
- 进程打开的文件列表(能看到日志文件、配置文件等)
网络排查也是同步进行的。用netstat或ss查端口连接,直接过滤这个PID:
ss -anop | grep <PID>结果就是刚才告警里提到的那个IP,走的是SSH端口高位区间的一个端口。这里插一句,很多挖矿木马用的外联端口不是常规80/443,而是自定义高位端口,千过滤的时候别只盯着常见端口看。
2.3 持久化机制排查:木马是怎么活下来的
挖矿木马的驻留方式就那么几类,但很多应急响应新手最容易漏掉的恰恰是这一环。进程杀了、文件删了,结果几分钟后木马又自己起来了,就是因为持久化没清干净。
我排查持久化是这么做的,按优先级逐个过:
- 定时任务:
crontab -l、/etc/crontab、/etc/cron.d/、/etc/cron.hourly/、/var/spool/cron/ - 系统服务:
systemctl list-unit-files | grep enabled,重点看近期修改过的service文件 - 启动脚本:
/etc/rc.local、/etc/init.d/、~/.bashrc、~/.profile - SSH后门:
~/.ssh/authorized_keys里有没有陌生的公钥
结果不出所料,在/etc/cron.d/里发现一个隐藏文件,内容是一行定时任务,每三分钟执行一次某个脚本。脚本内容我没急着看,先复制存档,因为这种东西是后面做溯源的关键证据。
又检查了一下/root/.ssh/authorized_keys,果然多了一条陌生的公钥——这属于典型的后门行为,攻击者给自己留了个随时能回来的通道。这一步如果不检查,光杀木马是没用的。
3. 攻击入口还原:这木马是怎么进来的
3.1 矿机木马最常见的入侵路径
处理过几次矿机事件之后,你会发现攻击入口翻来覆去就那么几种,对号入座就能省去大量排查时间:
| 入口类型 | 具体途径 | 典型特征 |
|---|---|---|
| 弱口令爆破 | SSH、数据库、Redis等对外服务口令过于简单 | 登录日志大量失败记录,最终有成功记录 |
| 未授权访问 | Redis空口令、Docker API暴露、Elasticsearch无认证 | 直接可写入crontab或执行命令 |
| Web漏洞 | 反序列化、上传漏洞、命令注入 | 进程运行在Web应用用户下 |
| 系统漏洞 | 补丁长期未更新,被已知漏洞利用 | 通过漏洞利用工具下载样本落地 |
这次的事件基本可以锁定为第一类——弱口令爆破。
3.2 从日志里找证据
我翻了/var/log/secure(CentOS/RHEL系统)和/var/log/auth.log(Debian/Ubuntu系统)的登录记录,两头时间线一对,情况就清楚了。
在木马进程启动的那天凌晨,这台机器从某个IP段发起了连续几百次SSH登录尝试,用户名就是一个常见的弱口令。前面全是Failed password,中间穿插着一次Accepted password for root,时间跟恶意文件的创建时间几乎同步。
这里建议大家排查的时候,重点看这个时间交叉点:
- 成功登录的时间点
- 恶意文件落地的时间点(
stat文件的 mtime/ctime) - 挖矿进程启动的时间点
三个时间点吻合,就能把“入口——落地——执行”这条链还原出来。
至于那个IP的地理归属和具体来源,我不建议花太多精力去深挖,因为挖矿木马基本都是批量扫描批量种,攻击者的肉鸡也可能只是另一台被控机器,溯源到这一步就够了。应急响应的重点永远是先止损、后修复,而不是破案。
4. 清除与恢复:现场处置操作实录
4.1 处置前准备:先留证,再动手
我之前处理过几起应急事件,也踩过“什么都还没留就把样本删了”的坑,所以这次特意等了几分钟,先把该存的东西全部打包:
cp /etc/cron.d/<恶意文件> /root/recovery/sample_cron.txt cp /tmp/.x/挖矿程序 /root/recovery/miner_sample crontab -l > /root/recovery/crontab_backup.txt ss -anop > /root/recovery/network_connections.txt留证据这件事特别重要,不只是为了写报告交差,更实际的意义在于,后续想分析这个木马的传播方式、回连地址、内部特征,手里得有样本和原始数据。如果杀太快,到时候想复盘就只能靠记忆了。
4.2 网络隔离与外联封禁
在确认这台机器不是核心业务之后,我做的第一件事不是杀进程,而是先切断外联。如果最后要保留样本做行为分析,网络隔离是必须的前提;即便是纯处置,限制木马与矿池通信也能避免它继续“干活”。
实操上分两步:
- 在防火墙上将外联目标IP加入黑名单
- 在本地用
iptables临时封禁该IP
iptables -A OUTPUT -d <矿池IP> -j DROP iptables -A INPUT -s <攻击者IP> -j DROP注意,这种操作是临时手段,但能立刻止损。我见过有人不做隔离直接杀进程的案例,结果木马在清理完后又换了端口重新连出去,等于白忙。
4.3 终止进程并清理文件
网络隔离做完,接下来才轮到杀进程。第一步先确认进程状态,然后用kill结束它:
kill -9 <PID>杀掉之后稍等几秒,再用top和ps确认没有自动拉起。这一步是有讲究的,很多木马带了守护进程,或者通过定时任务几分钟后重新拉起,所以杀完进程之后,至少要观察两个周期(也就是定时任务执行周期的两倍),确认没有复活才算初步控制住。
如果出现反复拉起的情况,建议直接用chattr +i给恶意文件加锁,或者先删除定时任务再杀进程,顺序不能反。
进程清理完之后,就是文件清理。我把发现的恶意文件全部记录路径,逐一删除:
rm -rf /tmp/.x rm -f /etc/cron.d/<恶意文件> rm -f /usr/lib/.../... sed -i '/恶意内容/d' /root/.bashrc删除完之后,把定时任务目录又过了一遍ls -la /etc/cron.d/,把带着奇怪文件名和隐藏属性的文件全部找出来。这块真得细心,很多应急响应失败就失败在只清了一半。
4.4 账号与后门清理
文件清理完成,还有三类东西要处理:
第一是SSH公钥后门。直接编辑/root/.ssh/authorized_keys,把陌生公钥那一行删掉。这里顺手检查了所有有home目录的用户,确保不只是root被留了后门。
第二是可疑系统账号。/etc/passwd、/etc/shadow都翻了一遍,重点看有没有UID为0的非root账号,或者最近新增的具备shell登录权限的账号。这次倒是干净,没有这种问题。
第三是密码整改。不管木马是不是通过弱口令进来的,密码全部强制改一遍是必须的。而且这台机器用了多长时间没用过复杂密码,可能连管理员自己都不清楚了。
4.5 观察期:别急着宣布恢复
处置完成不代表可以撒手不管。我把定时任务清除、恶意文件删除、账号清理完之后,没有立即交还业务,而是先观察了12个小时。
观察期间重点看三件事:
- CPU使用率是否始终平稳
- 是否还有新进程或新文件落地
- 安全平台是否还在报外联告警
我见过最尴尬的局面就是上午刚宣布处置完成,下午同样的告警又来一遍。原因就是没观察够时间,或者木马的备用路径没找干净。建议大家宁可把观察期拖长,也不要为了赶进度提前收工。
5. 溯源取证:把攻击者的路径走一遍
5.1 时间线重建
清理完之后,我把手头的信息整理成一条完整的时间线:
| 时间点 | 事件 |
|---|---|
| 三天前 02:15 | 攻击者IP对SSH进行暴力破解 |
| 三天前 02:43 | 弱口令登录成功 |
| 三天前 02:44 | 恶意脚本落地到 /tmp 目录 |
| 三天前 02:45 | 下载矿工程序并设置 /etc/cron.d/ 定时任务 |
| 三天前 02:46 | 挖矿进程启动并连接矿池 |
| 今天 23:10 | 安全平台告警触发 |
这个时间线看似简单,但真正能把时间线完整还原出来的人并不多。还原的关键是不要只看单一日志,而是把登录日志、文件时间戳、进程启动时间、网络连接日志几个维度对在一起,交叉验证。
5.2 恶意脚本的行为画像
我把那个恶意脚本的完整逻辑拆了一遍。这类脚本通常干的事很固定:
- 写入一个自身标记,避免被同类木马重复感染
- 清理其他挖矿木马留下的文件和进程(同行之间会互相攻击,这也是挖矿木马的典型特征之一)
- 下载矿工程序并赋予可执行权限
- 写入定时任务,确保周期性拉起
- 修改
/root/.ssh/authorized_keys,植入后门
我手动复现了一遍它下载文件的URL路径,尝试拉取了同一样本做对比。这一部分不在线描述具体域名了,但方法可以分享:用wget或者curl去访问同一个路径,看返回值是否一致,如果一致,说明攻击源还在活动,需要继续封堵。
5.3 影响面评估
单机处置结束后,还有一个问题需要回答:这木马是不是只感染了这台机器?
挖矿木马一般不太会主动做内网横向扩散(相比勒索病毒,它们的横向移动能力弱得多),但并不意味着没有可能。我在同网段里抽查了几台服务器,重点看了三方面:
- 是否有来自被感染机器的成功登录记录
- 是否存在同样的恶意定时任务
- 是否有外联同一个矿池IP的连接记录
抽查结果正常,只有这台机器本身中招。这是不幸中的万幸,如果内网横传开了,整个排查量会翻好几倍。
6. 防复发加固:从单台服务器到体系防御
6.1 系统基础加固清单
处置完成只是开始,真正的收尾工作在这里。所有中招服务器,我按下面这个清单做了一遍加固:
- SSH服务整改:禁止root直接登录(
PermitRootLogin no),有条件就换密钥登录,弱口令必须改 - 密码策略:强制长度和复杂度,定期更新
- 关闭非必要对外端口:这台机器本身没有对外提供服务的需求,我直接把不必要的端口全关了
- 安装并配置主机安全Agent:文件监控、进程监控、登录行为监控三件套
- 漏洞补丁更新:系统当前已知的高危漏洞补丁全部打上
- 最小化原则:卸载不需要的组件和依赖包,减少攻击面
这里想单独说一下SSH的处置逻辑——攻击者能进来,核心原因就是弱口令。只改一个密码其实不够,因为密码这种东西总有可能再次被猜中。更好的做法是直接用密钥登录,彻底关掉密码认证。如果团队还没有统一的密钥管理系统,至少也要把root密码禁用的规则落地。
6.2 告警与监控策略调优
告警为什么会到第二天才触发?关键原因是挖矿进程的行为特征没有在第一时间被识别。
这次告警其实是靠CPU阈值触发的,说明平台之前没有部署针对外联矿池特征地址的IOC检测规则,也没有针对可疑进程行为的检测模型。
事后我把两条规则补上了:
- 主机外联已知恶意IP或矿池地址的实时告警(参考了公开威胁情报库)
- 针对
/tmp、/var/tmp目录下新增可执行文件的行为监控
这里有一个经验,告警规则宁可比全也不要比精。挖矿这类事件,核心特征是和高频、稳定的数据源(威胁情报、文件监控)做关联,而不是完全依赖单一设备的告警。
6.3 从单点处置到批量巡检
处置完一台机器后,我还做了一件很重要的事,把同网段、同配置的一批服务器全部拉出来做了批量巡检。这一批巡检清单包括:
find /tmp /var/tmp /dev/shm -name "*.sh" -type f -mtime -7 crontab -l cat /etc/cron.d/* ls -la /root/.ssh/authorized_keys cat /etc/passwd | grep -v /bin/false批量巡检这件事很多时候会被省略,但我在实际工作中吃过亏——如果不做全网巡检,第二台中招的机器可能在一周后才被发现,到时候整个安全团队又要熬夜。所以这里强烈建议,任何安全事件处置完毕后,至少在相关网段做一轮“同因隐患排查”。
7. 复盘总结:命令速查与避坑心得
7.1 应急处置速查表
最后把这次用到的排查命令整理成一个速查表,供大家直接复制使用:
| 排查目标 | 推荐命令 |
|---|---|
| 查看CPU高占用进程 | top -c/ps aux --sort=-%cpu` |
| 查看进程详情 | ps -efcat /proc/<PID>/cmdline |
| 查看进程落地文件 | ls -l /proc/<PID>/exelsof -p <PID> |
| 查看网络外联 | ss -anopnetstat -anop |
| 查看定时任务 | crontab -lcat /etc/crontabls /etc/cron.d/ |
| 查看SSH后门 | cat /root/.ssh/authorized_keys |
| 查看异常账号 | cat /etc/passwdcat /etc/shadow |
| 查看系统服务 | `systemctl list-unit-files |
| 查看登录日志 | grep "Accepted" /var/log/secure* |
这张表的逻辑是排查面全覆盖:从进程到网络、从文件到持久化、从账号到日志。建议大家可以打印出来贴工位上,或者存进自己常用的工具笔记里。
7.2 这次踩过的坑
第一个坑:刚开始我只处理了定时任务和矿工程序,直接忽略了SSH公钥后门。如果不是顺手多查了一步,攻击者过几天就能重新连回来,之前的工作全都白做。
第二个坑:清理完文件之后没有立刻做文件系统快照对比。后来想复盘木马具体改动了哪些系统文件时,已经找不到原始状态了。如果条件允许,建议处置前先做一次全盘hash清单(比如find命令配合md5sum),方便事后比对。
第三个坑:处置期间忘了通知业务方。虽然这台机器不是核心业务,但数据平台上的临时任务还是被中断了,业务同事第二天看到告警邮件才来找我。应急响应虽然是安全部门主导的事,但上报和沟通不能漏。
7.3 个人体会
挖矿木马这种事件,说难不难,说简单也不简单。它不像零日漏洞利用那样需要高超的技巧,更像是一次“照妖镜”,把系统弱口令、补丁滞后、监控缺失、权限过大这些日常疏忽照得一清二楚。
这次事件处置完,我最大的感受是——应急响应拼的不是单一的技术环节,而是完整的处置链条:及时发现、准确判断、全面排查、干净处置、深入追溯、彻底加固。这个链条任何一个环节薄弱,都可能让前面的努力白费。
最后再多说一句:如果你发现自己碰到的服务器也出现了CPU异常升高的情况,不要慌,按上面的流程一步步走,先取证、再隔离、再清理、最后加固,每一步都别省,基本就能稳稳收住。安全这个活儿,有时候考验的真的不是知识量,而是能不能耐着性子把最基础的步骤做实做透。