Linux后门排查实战:从进程到Rootkit的完整链路
接手一台被喊去“帮看一下”的Linux服务器时最怕的不是找不到问题而是被一句“应该没事吧”把思路带偏。Linux后门排查这活儿听着像安全专家才干的事但真等你登录上去看到CPU跑满、端口列表里躺着一个眼生的连接或者root用户多了一个你从没见过的主机信任文件你就明白这件事到底有多现实。这篇文章我不打算给你背手册。我会把自己实际排查Linux后门时走过的路、敲过的命令、踩过的坑整理成一条相对完整的排查链路。从进程、网络、登录痕迹到文件时间戳、定时任务、SSH信任关系再到内核模块和Rootkit检测最后附上一套可以直接复制保存的应急命令清单和日常防御基线。适合谁看刚接手服务器运维的新人、公司里被临时拉去应急的兼职安全人员以及那些“想知道自己机器到底有没有被搞过”的开发同学。1. 为什么后门排查必须有自己的排查顺序很多人一上来就敲netstat看到几个可疑端口就紧张得不行然后随便杀两个进程就算完事。这种排查方式的问题在于你以为你堵住了一个洞实际上攻击者至少有四条完全不同的路径可以把后门重新扶起来。后门这个东西本质上就是攻击者留在系统里的“再次进入能力”。它可能是一段被替换的系统命令可能是一个可疑的定时任务可能是LD_PRELOAD环境变量里加载的一个共享库也可能是一个内核模块。单点排查一定会漏。所以我的习惯是先定顺序先看活的东西再看死的东西最后看被藏起来的东西。所谓“活的”就是当前正在运行的进程、建立的连接、活跃的登录会话。这些东西变化最快也最容易暴露。所谓“死的”就是文件系统里被改动过的文件、定时任务、开机启动项、日志记录。这些东西是后门的“长期生存机制”攻击者花了很大力气让后门在重启之后依然能回来所以排查时它们往往是突破口。所谓“被藏起来的”就是Rootkit那类东西专门用来欺骗进程列表、文件列表和网络状态让前面两个环节看到的结果本身就不完整。在开始之前还有一件重要的事保存现场。所有先执行的命令尽量把输出重定向到U盘或者远程收集端。因为你后续每执行一个命令都可能改变系统状态比如访问一个文件会更新它的atime。排查完之后需要复盘时这些原始输出就是你最可靠的依据。我会在最开始就执行一条history时间戳记录和关键命令输出的收集后面细说。2. 第一轮排查进程、网络、登录痕迹先把“活的”揪出来2.1 看进程时别只看top用这三板斧交叉验证top和ps aux是大家最熟悉的但也是最好被欺骗的。一个有root权限的攻击者完全可以替换ps命令让你看不到他的挖矿进程。所以我从来不信单独某一个命令的返回结果。我先执行ps auxf看进程树。用f参数的目的是让父子关系清晰可见很多木马进程会刻意把自己挂在某个正常服务名下比如nginx的worker进程里混进一个同名进程不展开树根本看不出来。接着用top -b -n 1取一次CPU和内存的瞬时快照重点看CPU占用异常的进程PID。再配合ls -l /proc/[PID]/exe直接查看这个进程对应的可执行文件真实路径。这个方法很实用很多攻击者会从内存中直接执行恶意代码或者在进程启动后删掉原始文件这时/proc/[PID]/exe会显示(deleted)这就是非常明确的危险信号。同样值得检查的是/proc/[PID]/cwd也就是进程的工作目录。如果一个系统服务的当前工作目录竟然在/tmp或/var/tmp下面那基本可以断定有问题。正常服务不会从tmp目录启动这是我一直坚持的判断准则。2.2 网络连接把“谁在跟谁说话”变成一张清单网络层面的排查老命令是netstat -antlp新环境里我推荐ss -antlp。两者都行但ss更快而且netstat在新发行版上经常没装。排查时我会把所有ESTABLISHED状态的连接单独列出来重点看两类一类是发往外网的连接一类是监听在高端口上的服务。从经验看常见挖矿木马倾向于连3333、4444、6666这类端口但这不能作为唯一标准更可靠的方法是看连接的进程PID是否和已知业务进程匹配。执行ss -antlp时顺手记下所有处于LISTEN状态的端口然后和公司内部的端口登记表对照。没有登记表的话就用一个笨办法把所有监听进程全部lsof -i一次写进临时文件再问自己一句这些进程里有没有哪个是我完全没印象的。网络排查还有个很容易被忽略的点/proc/net/tcp和/proc/net/udp。某些Rootkit会劫持netstat和ss命令本身但对/proc文件系统的过滤做得不够细致直接读这两个文件反而能看到真实连接。我看到过因为读取/proc/net/tcp才发现异常连接的案例虽然不常见但关键时刻能救命。2.3 登录记录里藏着后门的第一脚印如果我是被叫去排查的我第一个想看的就是登录记录。last和lastlog可以看用户的登录历史和最后一次登录时间who和w可以看当前登录会话。我说的“想看”不是说扫一眼有没有陌生IP而是要对照业务环境去推理。举个例子一台只允许办公网IP访问的数据库服务器如果last里出现了一个来自境外IP的root登录那不管现在有没有异常进程这台机器都已经被突破了。攻击者的习惯是拿到权限后先留一个后门保证下次还能进来然后再进行下一步操作。登录记录就是这个链条上最容易残留证据的一环。检查时我还会看/var/log/secure或/var/log/auth.log这里的Accepted publickey for root和Accepted password for root是最重要的线索。单独一次Accepted password还可以解释成有人输错密码碰巧成功但如果同一个IP在短时间内连续出现多次Failed password之后有一次Accepted password那基本可以确定登录爆破成功。账户排查是另一块。cat /etc/passwd看哪些用户有shell比如是否出现了一个/bin/bash但你没见过的新用户;awk -F: ($3 0) /etc/passwd看UID为0的用户——UID 0意味着超级用户权限正常的系统里只应该有root一个多出来的任何一个都必须立刻深挖。还有/etc/sudoers重点看是否有某个普通用户被加进了NOPASSWD免密sudo列表。3. 第二轮排查文件系统里的长期生存机制3.1 按时间戳找“不该在这个时间出现的文件”查完活的东西就要开始从文件系统里找后门的落脚点。最实用的两个时间维度是mtime内容修改时间和ctime状态变更时间。atime访问时间在大多数系统上默认关闭或延迟更新参考价值有限。我的做法是先做一个全盘扫描把最近7天内被修改过的文件全部列出来然后再人工过滤。命令大概是这样的find / -xdev -mtime -7 -type f -printf %TY-%Tm-%Td %TH:%TM %p\n 2/dev/null | sort -r | head -200-xdev的意思是不要跨越挂载点避免把/proc、/sys、/dev这些虚拟文件系统也扫进来否则输出会被刷屏。先看前200条把系统升级、自己人改的配置、日志文件排除掉剩下的就是重点嫌疑对象。有几个目录位置需要额外警惕/tmp、/var/tmp、/dev/shm这三个地方权限通常是1777也就是任何人都能写。这三个目录是后门脚本和恶意二进制最常见的藏身地尤其是文件名字看起来特别像正常的比如/tmp/.x、/tmp/...这种带点号、带空格、带特殊字符的文件名专门用来“藏文件”。查的时候记得用ls -la看包括隐藏文件在内的所有文件不要因为文件名只有两个字符就忽略它。3.2 定时任务和启动项重启也杀不死的后门进程杀了、连接断了攻击者不慌因为定时任务会在几分钟后把后门重新拉起来。清理后门时如果漏掉定时任务白干一场。检查定时任务要覆盖的地方很多crontab -l是当前用户的任务cat /var/spool/cron/root是root用户的任务/etc/crontab是系统任务/etc/cron.d/、/etc/cron.hourly/、/etc/cron.daily/等目录里也可能被塞进脚本。我以前还遇到过把恶意脚本写成/etc/cron.hourly/update这种看起来特别像系统维护任务的名字就知道攻击者也在琢磨怎么让你一眼看不出异常。除了cron启动项也得顺着系统初始化流程走一遍。使用systemd的系统检查/etc/systemd/system/下面有没有近期新增的service文件重点看ExecStart字段指向的路径是不是合法程序以及WantedBymulti-user.target这种比较隐蔽的服务。另外/etc/rc.local、/etc/init.d/、/etc/profile.d/都是老牌的自启动位置。还有一类比较隐蔽的启动方式是把命令写进/etc/ld.so.preload。这个文件的内容如果非空就会被动态链接器在启动任何程序时加载达到注入代码的目的。踩过几次坑之后我现在每次排查都会先看一眼这个文件是不是空的。3.3 SSH信任关系一条authorized_keys就足够打开门SSH后门是Linux系统上最流行的后门方式之一没有复杂的二进制没有恶意进程就是往某用户家目录的.ssh/authorized_keys里加一行公钥。攻击者只要持有对应的私钥就能免密登录该用户而且日志里只会有正常的Accepted publickey记录很难被察觉。排查方式并不复杂。对整个/home目录做一次全量检查重点看每个用户目录下有没有不该存在的.ssh目录以及authorized_keys文件里的内容是否都能对应到公司自己人生成的公钥。同时检查/root/.ssh/authorized_keys这是最值钱的目标也是攻击者最想改的文件之一。检查时还要注意文件权限。正常情况~/.ssh目录权限应该是700authorized_keys文件应该是600或644。如果发现权限被改成了755说明有人修改过即使目前没发现问题也要把权限恢复并记录下来作为疑点。/etc/ssh/sshd_config里有两项值得检查PermitRootLogin是否是yes或prohibit-passwordPasswordAuthentication是否是yes。如果密码登录开着那爆破风险会高很多。我见过把permitRootLogin悄悄改回yes的操作手法排查时只看一眼网络层可能发现不了但配置层会有明显痕迹。3.4 命令历史与别名最容易忽视的“坑”/root/.bash_history里的内容代表root用户执行过什么命令。如果攻击者是通过root权限操作系统的那这里大概率留有线索。我见过有人在历史记录里看到了攻击者下载工具、添加用户的完整命令链这就是最直接的证据。但实践中有个问题攻击者很可能已经执行了history -c清空历史或者根本就不是通过交互shell操作的。所以命令历史没被清空的情况下可以作为证据被清空了反而本身就是异常信号要结合shell日志和auditd来补充分析。别名alias是另一个小坑。检查/etc/profile、/etc/bashrc、/root/.bashrc里有没有alias lsls -a这种正常的自定义更要紧的是有没有alias把一个普通命令指向了恶意程序。曾经见到过把alias top/usr/bin/top.orig替换为指向恶意程序的手法用户每次执行top实际跑的都是别的东西这种藏在用户级配置里的后门非常难被发现。4. 第三轮排查深入内核与Rootkit跟“看不见的敌人”过招4.1 Rootkit为什么你看到的都不是真的攻击者拿到root权限后可以选择装一套Rootkit来隐藏自己的行踪。Rootkit最常见的机制是劫持系统调用比如read、kill、getdents这些让用户态程序“看不见”特定的进程、文件和端口。出现以下迹象时要立刻怀疑Rootkitps输出和/proc目录对不上——比如top里看到PID是666的进程但ls /proc/666根本不存在;netstat显示一个端口但lsof -i:端口查不到任何进程;ls看到的文件数和目录stat信息不一致。这些都是典型的“用户态被欺骗”现象。一个比较经典的排查命令是ls -l /proc/[PID]/fd/[描述符]可以跟踪进程实际打开的文件。另一个是对比系统调用追踪用strace去跟踪某个可疑进程的系统行为看它是否访问了不该访问的文件路径。4.2 内核模块从未加载的模块和不该加载的模块内核模块是Rootkit的另一个高发区。lsmod查看当前加载的内核模块/lib/modules/$(uname -r)/目录下存放所有可加载的模块文件。平时极少加载额外模块的生产服务器如果lsmod里多出一个名字生僻的模块就要重点关注。单纯看lsmod还不够因为高级Rootkit会隐藏自身模块。可以用cat /proc/modules看一遍再对比lsmod的输出。两个结果如果对不上说明有模块在尝试隐藏自己。更狠的做法是直接查看/sys/module目录下的目录名某些Rootkit对/proc做了过滤但没处理/sys。另外可以检查内核符号表是否被篡改。cat /proc/kallsyms | grep sys_call_table看看系统调用表的位置如果得到的是正常的函数地址而不是__x64_sys_xxx这种标准符号需要进一步排查。不过这个操作对新手来说门槛较高我建议普通场景先用专业工具。4.3 专业工具来做交叉验证手工排查永远不应该单独作为唯一依据。我强烈建议配合以下几个开源检测工具交叉验证chkrootkit老牌工具检测常见rootkit特征胜在轻量和快。rkhunter功能更全面会检查命令哈希、启动项、网络端口、内核模块但误报也比较多需要结合基线判断。ClamAV虽然定位是杀毒软件但对Linux端常见的恶意脚本和挖矿程序识别效果还不错适合在怀疑文件层面问题时使用。使用时需要注意检测工具的规则库和特征文件时效性很关键旧版本检测不出新Rootkit相当于白跑所以第一次用之前务必先更新规则库。工具输出有告警时不要急着下结论。比如rkhunter经常报/usr/bin/unhide可疑但实际上这个文件本身也是排查工具属于正常误报。把工具的告警当作线索再回到手工排查去验证这才是我推荐的做法。4.4 LD_PRELOAD和动态库劫持动态库劫持在Linux后门里是个经典话题。原理很简单程序启动时动态链接器会加载LD_PRELOAD环境变量里指定的共享库这个库里的同名函数会覆盖程序原本调用的函数从而实现劫持效果。排查方式分几步。第一看/etc/ld.so.preload文件是否有内容正常都应该为空。第二查看环境变量echo $LD_PRELOAD如果非空就要确认这个路径是不是合法业务需要的。第三重点检查/etc/ld.so.conf.d/目录下的配置文件有没有被改过以及ldconfig -p显示的缓存是否和实际文件系统对得上。我遇到过一个很典型的案例某台机器上ls、ss、ps这类的命令输出总是怪怪的比如ps看不到某个进程但top能看到后来一查就是/etc/ld.so.preload被写入了一个恶意so文件。清理完恶意so重建缓存重启服务现象全部消失。5. 排查实例复盘从“觉得不对”到“确认被入侵”的完整过程5.1 现象描述某天临时接到一个消息说一台内部测试服务器变卡了。我登录上去第一眼看见的是load average高达12但CPU空闲率又很高这种“负载高但CPU没跑满”的错位感本身就是一个值得追查的线索。再往下看top里排前面的进程不多有一个叫kworker的进程始终保持在CPU列表前两名。kworker是内核工作队列的正常进程但正常情况它不会持续高占用。我当时就怀疑是不是有内核层面的东西在作怪于是先从/proc下手验证。5.2 我执行的关键命令序列第一步保存现场。将当前进程列表、连接列表、登录记录和时间戳信息一并输出到外部目录。mkdir -p /tmp/evidence_$(date %Y%m%d) ps auxf /tmp/evidence_$(date %Y%m%d)/ps_auxf.txt ss -antlp /tmp/evidence_$(date %Y%m%d)/ss_antlp.txt last -a /tmp/evidence_$(date %Y%m%d)/last_a.txt find / -xdev -mtime -3 -type f -printf %TY-%Tm-%Td %TH:%TM %p\n 2/dev/null /tmp/evidence_$(date %Y%m%d)/recent_files.txt第二步验证进程真实性。查了一下那个占CPU的kworker对应的PID在/proc/[PID]/exe里显示为[kworker/u...]这种带中括号的通常是内核线程。但问题是真正的kworker不会突然持续高占用所以我决定查内核模块。执行lsmod | sort和cat /proc/modules | sort分别导出然后对比差异。这一比就发现问题了/proc/modules里多了一行模块而lsmod里完全没有显示。这就是典型的模块隐藏迹象。第三步用rkhunter和chkrootkit做交叉扫描。chkrootkit跑完报了一个可疑标记指向/lib/modules/$(uname -r)/extra/目录下的某个.ko文件。手工查看那个文件编译时间和文件描述都跟现有驱动完全不同基本可以确认是一个挖矿内核模块。5.3 处置过程确认之后我没有直接rm那个模块文件因为正在运行的模块如果直接删除文件可能造成系统不稳定。正确顺序是先卸载模块modprobe -r 模块名再把模块文件移动到隔离目录而不是直接删除方便后续分析最后重启相关服务确认恢复正常。随后我还做了几件收尾的事清掉了该模块在/etc/modules-load.d/和/lib/modules里的残留配置;修改了所有root密码和管理员密码;检查authorized_keys和定时任务确认没有其他后门路径;最后把证据目录打包带走。这个案例最值得注意的点是负载高但CPU空闲说明问题很可能不在用户态进程而在内核态或磁盘IO排查方向偏到内核模块就对了。如果只盯着ps查挖矿进程这单大概率会扑空。6. 常见误判与翻车现场哪些坑我替你踩过了6.1 高CPU进程不一定是挖矿挖矿程序、病毒、异常脚本确实会占满CPU但有些正常任务也会让CPU飙高比如未优化的数据库查询、大文件压缩、并发量突增的业务请求。我在排查中学会的第一件事就是不要只看CPU要结合进程的可执行文件路径、启动参数、网络连接一起判断。一个规律是如果进程高CPU但没有任何对外网络连接那它更可能是正常计算或死循环。反之高CPU同时对外建立加密连接才更符合挖矿或木马通信的特征。当然现在很多挖矿木马也会断网运行把算力写好再定时上传所以网络连接不是必要条件只是参考维度。6.2 端口扫描结果要交叉验证别被“正常业务”骗了排查网络时最容易犯的错是把所有眼生的端口都当后门。某个端口确实在监听但不代表一定是恶意的可能是某个业务依赖的中间件忘了登记。我建议交叉验证的手段有三个lsof -i:端口看进程名和启动路径systemctl status 进程名看服务描述curl 127.0.0.1:端口看返回内容是不是常见协议特征。反过来也别因为端口眼熟就忽略。有些攻击者会复用8080、3306这些常用端口来开后门达到伪装效果。所以判断标准不是“端口是否眼熟”而是“监听进程和启动路径是否和业务一致”。6.3 清理后必须做的三件事第一改密码。所有账户的密码和SSH密钥对都换掉旧的私钥立即吊销不然你清理完等于白清攻击者手里的后门随时能回来。第二检查所有定时任务、启动项、SSH信任关系。这是后门最常驻的“复活方式”清理不彻底等于给攻击者留了后门。第三补安全补丁关掉不需要的服务。排查完不加固等于重新把窗户打开。哪怕今天清理干净了明天同一漏洞还可能被再利用一次。很多人清理完就把这事抛在脑后这是我们应急响应中最常见的失误。记住排查只是防御的一部分修复、加固、复盘才是让这次排查产生长期价值的关键。7. 日常防御基线怎么让下次排查轻松十倍7.1 平时存好“正常态”基线排查时最难的问题不是找不到异常而是分不清什么是异常。如果平时就把系统正常状态记录好排查时就能靠对比来快速定位。我给自己的标准是新装或重大变更后做一次基线采集包括三个部分系统命令的哈希值md5sum或sha256sum记录所有系统常用命令的哈希;启动项和定时任务列表cron、systemd、rc.local、ld.so.preload全部文本化存档;监听端口和关键文件时间戳ss -antlp输出、/etc/passwd、/etc/ssh/sshd_config的哈希。平时把基线归档到安全位置排查时直接把当前状态和基线做diff就能快速定位新增文件、新增用户、新增服务和被修改的命令。这个习惯帮我省了无数次逐个比对文件的时间。7.2 最小化攻击面能不开的就别开安全永远比性能更重要尤其在对外暴露的服务器上。我建议至少做以下几件事关闭root远程密码登录改用普通用户SSH密钥;修改SSH默认端口不是必须的但可以减少大量爆破干扰配合防火墙策略限制来源IP更有效;禁止/tmp、/var/tmp、/dev/shm挂载成可执行通过在/etc/fstab里加noexec选项;及时打安全补丁尤其针对内核和高危服务漏洞;开启auditd审计记录关键文件的读写行为和用户的命令执行记录。这些是成本最低的防护手段不需要复杂的安全架构就能显著提高攻击门槛。7.3 一张可直接抄的快速排查命令清单以下清单是我应急排查时每次都会执行的命令直接复制进终端即可。实际使用时根据系统差异调整# 进程与网络 ps auxf top -b -n 1 ss -antlp ls -l /proc/*/exe | grep -E deleted|/tmp # 登录痕迹 last -a lastlog grep Accepted /var/log/secure 2/dev/null || grep Accepted /var/log/auth.log # 文件与持久化 find / -xdev -mtime -3 -type f -printf %TY-%Tm-%Td %TH:%TM %p\n 2/dev/null crontab -l cat /etc/crontab cat /var/spool/cron/root 2/dev/null ls -la /etc/systemd/system/ | grep ^. cat /etc/ld.so.preload 2/dev/null # 用户、信任关系与Rootkit awk -F: ($3 0) {print $1} /etc/passwd find /home -name authorized_keys -exec cat {} \; cat /root/.ssh/authorized_keys 2/dev/null cat /proc/modules lsmod chkrootkit 2/dev/null || rkhunter --check把这份清单整理成脚本每次排查先跑一遍再针对疑点深入调查效率会提升一个量级。8. 写在最后排查容易难的是相信自己的判断Linux后门排查没有银弹不存在“跑一遍某工具就搞定了”的捷径。我见过太多案例工具扫不出来就收工结果过两周系统又被入侵。真正的排查是把手上的信息交叉验证ps看不到但要/proc看得到ss看不到但要/proc/net/tcp看得到登录记录、时间戳、定时任务、SSH信任关系这些分散的线索拼在一起才能还原真相。我个人排查后门时的最大体会是永远不要单凭一个命令的结果做结论。一个现象至少要有两个独立维度来印证进程对网络、文件对时间戳、登录对日志只有证据链闭合结论才站得住。清理完也别急着宣布胜利持续观察一周确认没有复发再回到正常状态。这行当里谨慎永远不过分。