跑生产业务的 Linux 服务器,安全这件事上大多数翻车现场并不高深:root 弱口令被爆破、数据库端口裸奔公网、一年不打补丁撞上公开利用代码。
排查和处置则完全是另一回事------ps aux 看一眼就下结论,在真正的入侵面前等于没查。root 权限的入侵者会让你的排查工具说谎:进程名可以伪造,系统命令可以被整体替换,动态库可以被劫持。本文按"动机定性 → 命令可信度与 /proc 直查 → 网络流向 → 持久化清单 → 日志与时间线 → 内存与横向 → 加固基线"的顺序,把可直接落地的排查思路和命令完整过一遍。每个方向都不只给命令,也给判读------命令的输出只是线索,怎么解读才是排查。
以下命令在 CentOS/RHEL 8+/9 与 Ubuntu 20.04/22.04 上验证过写法,属于通用基线示例,非生产代码,落地前请按实际环境核对。
零、先定性:入侵动机决定排查方向
动手之前先回答"他图什么",否则排查没有重点。同样是这台机器被拿下,六种常见动机的排查重点和恢复策略完全不同:
| 动机 | 排查重点 | 恢复策略 |
|---|---|---|
| 挖矿 | CPU 占用曲线、矿池外联(3333/5555/14444/45700)、environ 里的钱包地址 | 连窝端 + 封外联,数据一般没被碰,恢复可快 |
| 勒索 | 文件 mtime 批量变化、加密中的进程、对共享盘的访问 | 隔离 + 留样本 + 备份恢复,不建议付赎金 |
| 窃密 | 外传通道(大流量外发、DNS 查询、陌生端点)、登录时间线 | 时间线还原 + 全量改口令 + 影响范围评估 |
| 跳板 | 对外扫描记录、异常出站连接、邮件队列暴涨 | 清理 + 留证自证 + 评估连带风险 |
| 破坏 | 删除操作痕迹、日志批量清空、磁盘写满 | 备份恢复优先,排查次之 |
| 无差别扫中 | 陌生 IP 零散登录,之后无大动作 | 按流程走一遍即可,堵上入口就完事 |
三个快速判据:CPU/IO 被谁占着(挖矿、勒索动静大,窃密往往很安静);流量往哪去、有没有规律(持续小流量连固定端口 vs 集中大流量外发);文件系统被怎么动过(批量改后缀 = 勒索,只读悄悄打包 = 窃密,成批删除 = 破坏)。三条一对,动机基本能定性------定性了再排查,才知道该重点保连接记录还是重点圈数据范围。
补一个实战经验:动机可能叠加。挖矿的木马顺手把 /etc/shadow、业务数据打包外发的情况并不罕见,所以即使定性为挖矿,也要按窃密的标准扫一遍外传痕迹,不要提前收队。
一、命令可信度:为什么不能只信 ps
root 权限的入侵者,第一件事是让排查工具说谎:
- 进程名可伪造 :
ps显示的只是 argv0,exec -a kworker ./miner之类的手法能让恶意进程伪装成系统名。 - 命令可被替换 :
ps、netstat、lsof、ss被换成过滤恶意进程/连接的版本,你看到的"干净"是设计好的。 - 动态库劫持 :
/etc/ld.so.preload注入的库在系统命令运行时过滤输出------命令是原版的,输出被动了手脚。
对策是交叉验证,直读内核暴露的原始信息:
bash
# 1) 静态 busybox 跑一遍,和系统命令输出比对(busybox 不依赖系统库,劫持不了)
# 两边对不上的差异,就是要查的方向;建议平时就把静态 busybox 备在 U 盘/跳板机上
busybox ps aux | sort -k3 -rn | head -20
ps aux --sort=-%cpu | head -20
# 2) /proc 直查进程五件套
ls -l /proc/<pid>/exe # 真实可执行文件;指向 (deleted) = 干完活清现场,赶紧 cp 留证
cat /proc/<pid>/cmdline | tr '\0' ' '
ls -l /proc/<pid>/cwd # 在翻哪个目录
cat /proc/<pid>/environ | tr '\0' '\n' | grep -iE 'pool|wallet|proxy|c2' # 矿池/钱包地址 = 动机物证
ls -l /proc/<pid>/fd/ # 开着哪些文件、连着哪些 socket
exe 显示 (deleted) 而进程还在跑,这是最典型的"干完活清现场"痕迹------二进制的 inode 还挂着,赶紧 cp /proc/<pid>/exe /mnt/evidence/ 留证,删掉的样本本身就是物证,事后做特征比对、报案都用得上。判读顺序:exe 定位真实文件 → 拷贝留证 → cmdline + environ 看它连什么 → fd 看它摸过哪些业务文件,四步下来这个进程是干什么的基本清楚。
二、网络:/proc/net 直读与流向判读
bash
# 1) 绕过可能被替换的 ss,直读内核记账(/proc/net/tcp 的 st 字段:01=ESTABLISHED)
ss -antp | grep -v -E '127.0.0.1|::1'
awk 'NR>1 && $4=="01"' /proc/net/tcp | while read line; do
# 十进制端口转换(第 2 列 local_address 第 5 段是十六进制端口)
echo "$line"
done
解析 /proc/net/tcp 时注意:里面的 IP 和端口都是十六进制,端口要转十进制才好比对(比如 :D3B0 是 54192)。逐条读很累,可以只提取对端地址列排序去重,看有没有眼生、且反复出现的目标。
流向判读比"有没有连接"更重要:
bash
# 2) 看连接去向比"有没有连接"更重要
# - 连的是 IP 还是域名(域名会换,IP 相对稳定,先盯 IP)
# - 端口是否命中矿池常用端口 3333/5555/14444/45700
# - 是否在往陌生对象存储端点持续上传(后半夜的大流量外发 = 窃密高危信号)
# 3) DNS 隧道别漏:数据编码进域名问出去,按 IP 封拦不住,看解析日志
grep -Ei 'TXT|NXDOMAIN' /var/log/... # 按实际 DNS 服务(named/systemd-resolved)调整
流量画像比单点更能说明问题:挖矿是持续小流量(7×24 挂着),窃密常是集中大流量(打包上传那几十分钟),跳板是对外扫描的碎片化连接。把非工作时间的外发流量单独拉出来看,规律往往一眼可见。
三、持久化清单:一个都别漏
杀进程半小时又长出来,因为根还在。按这个清单逐一过:
bash
# 1) 定时任务全家桶(crontab -l 只看得到当前用户)
cat /etc/crontab; ls /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/
for u in $(cut -f1 -d: /etc/passwd); do crontab -l -u $u 2>/dev/null; done
atq; ls /var/spool/cron/
# 2) systemd service + timer(timer 比 cron 更少有人想得起来查)
systemctl list-unit-files --state=enabled | grep -vE 'known-good|sshd|network|cron'
systemctl list-timers --all
# 3) 全局库劫持
cat /etc/ld.so.preload 2>/dev/null # 正常机器上这行内容不该有东西
# 4) SUID 后门(多出来的 SUID = 永久 root 入口)
find / -perm -4000 -type f 2>/dev/null | sort > /tmp/suid_now.txt # 与基线比对
# 5) SSH 后门:陌生公钥 + 被改过的配置(攻击者进来第一步常把门留着)
find / -name authorized_keys 2>/dev/null -exec ls -l {} \;
grep -E '^(PermitRootLogin|AllowUsers|Port|PubkeyAuth)' /etc/ssh/sshd_config
# 6) 账号与提权
awk -F: '$3==0' /etc/passwd # 除 root 外的 UID 0 账号 = 后门
cat /etc/sudoers; ls /etc/sudoers.d/; grep -rn '' /etc/sudoers.d/
# 7) shell 启动文件与内核模块
ls -l /etc/profile.d/; tail -5 ~/.bashrc ~/.bash_profile
lsmod # 注意:root 之下 lsmod 输出亦非完全可信
三个实操提醒:
- SUID 清单要有基线 。
find / -perm -4000的输出只有在"平时存过一份"的前提下才有意义------刚装好系统时跑一遍存到跳板机,出事时 diff 一下,多出来的就是候选。没基线就现场逐个人工判断,/usr/bin之外的每一个都值得怀疑。 - authorized_keys 要全用户查 ,不只 root。
find / -name authorized_keys一把梭,运维账号、服务账号里被塞的公钥同样危险。 - sshd_config 被改过也是后门。常见手法是放开 root 登录、加一个高位端口、把 AllowUsers 改宽------公钥是钥匙,改配置是"把门留着",两头都要看。
四、按时间定位:文件系统里找入侵痕迹
日志之外,文件系统自己的时间戳也是一条时间线。以"发现异常的那天"为锚点,往前翻:
bash
# 1) 最近 7 天被改过的可执行文件与 Web 目录文件(植入物的高发地)
find /tmp /var/tmp /dev/shm -type f -mtime -7 2>/dev/null
find /var/www -type f -mtime -7 2>/dev/null
# 2) 更精细的锚点:以某个"确定干净"的文件为基准,找比它新的
find / -xdev -newer /usr/bin/sshd -type f -perm -o+x 2>/dev/null
# 3) 时间戳三态都要看(mtime 内容修改 / ctime inode 变动 / atime 读取------atime 常被 noatime 挂载选项关掉)
stat /tmp/suspicious_binary
注意时间戳可能被 touch -r 抹平------攻击者把植入文件的时间戳改成和系统文件一样。改得掉 mtime,改不掉"全目录里就它一个新"的突兀感,结合第三节的持久化清单看,多数藏不住。
五、日志与时间线重建
bash
# 登录痕迹:多个来源交叉,别只盯 lastb
lastb | head -30 # 失败登录(爆破的前兆)
last | head -30 # 成功登录
grep -E 'Accepted' /var/log/secure | tail -30 # 或 Ubuntu 的 auth.log
lastlog | head -30 # 各账号最近登录
journalctl -u sshd --since "-30d" | grep -iE 'fail|accept'
# Web 日志:webshell 的典型信号是 POST 到可疑脚本 + 异常 UA
awk '$6 ~ /POST/ && $7 !~ /(login|api|upload)$/' /var/log/nginx/access.log | head
排查的目标不是"找到一个恶意进程",而是把事件按时间串起来 :首次异常登录(谁、从哪、哪个账号)→ 怎么提的权 → 何时植入 → 何时布持久化 → 何时开始外传。一条还原出来的时间线长这样:凌晨 2:17 陌生 IP 首次 SSH 登录成功(弱口令)→ 2:19 sudo -i 成功 → 2:25 二进制落进 /tmp → 2:31 crontab 多出一行 → 3:00 起 CPU 持续 90%+ → 隔天起每天凌晨 3 点固定外联。线一拉出来,入口、手法、动机全清楚。注意日志时间戳和文件 mtime 都可能被动手脚,多来源交叉验证;"该有的日志没有了"(history 被清空、secure 被 truncate)本身就是线索,说明来者花了心思收尾------本地日志被清,远端日志服务器、云平台操作审计还在,这就是日志必须外发的原因。
六、webshell 与外传通道排查
Web 类业务(后台、官网、商城)被入侵,十有八九先中 webshell------一个能通过浏览器执行命令的脚本,是窃密和跳板最常用的通道:
bash
# 1) Web 目录里最近出现的可写脚本(php/jsp/aspx/ashx 按实际技术栈调整)
find /var/www -type f \( -name '*.php' -o -name '*.jsp' \) -mtime -14 2>/dev/null
# 2) 内容特征:命令执行函数是 webshell 的指纹
grep -rlE '(eval|assert|system|exec|shell_exec|passthru)\s*\(' /var/www --include='*.php' | head -20
# 3) 对照干净版本 diff:有代码仓库或备份的,直接比对文件清单
diff <(cd /var/www && find . -type f | sort) <(cd /backup/www && find . -type f | sort)
发现 webshell 之后的正确动作是四件套:删文件 + 找上传入口(应用漏洞是根因,不修掉还会再被传) + 查访问日志确认它被用来干了什么(外传过哪些请求) + 补应用层加固。只删文件不修漏洞,等于没清。
七、内存取证与横向移动
bash
# 重启前抓内存镜像------重启即丢失:在跑的进程、没落盘的连接、内存里的密钥配置
# LiME 内核模块(需提前准备或现场编译);无 LiME 时至少 dd /proc/kcore
dd if=/proc/kcore of=/mnt/evidence/mem.raw bs=1M status=progress
# 横向:共用口令、同网段的机器全查;日志里出现对内网扫描就是实锤
grep -Ei 'refused|invalid user' /var/log/secure | awk '{print $NF}' | sort | uniq -c | sort -rn
内存镜像的另一层价值:/proc/PID/fd 里看到的文件、连接在镜像里都能还原,磁盘上删掉的东西内存里可能还活着。抓镜像的目标盘要足够大(内存多大镜像多大),且先算好存储再动手,别把磁盘写满造成二次事故。
八、基线加固:四个最高优先级项
1. SSH:把远程入口锁死
/etc/ssh/sshd_config 关键项:
bash
# 禁 root 直登 + 只放行指定用户组
PermitRootLogin no
AllowGroups sshusers
# 密钥登录,关口认证
PubkeyAuthentication yes
PasswordAuthentication no
# 闲置断连,防挂机
ClientAliveInterval 300
ClientAliveCountMax 2
改完 sshd -t 验语法,systemctl restart sshd 生效。先把公钥配上再关口认证,顺序反了会把自己关在门外------这是加固现场最常见的自伤事故。
再叠两层:
bash
# firewalld 只放行公司固定 IP 段连 22
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="203.0.113.0/24" port port="22" protocol="tcp" accept'
firewall-cmd --reload
# Fail2ban:同一 IP 5 次失败封 1 小时
yum install -y fail2ban # Ubuntu: apt install -y fail2ban
systemctl enable --now fail2ban
2. 防火墙:默认拒绝,按需放行(含出站)
bash
# 入站:确认默认区域,只显式放行业务端口
firewall-cmd --get-default-zone
firewall-cmd --permanent --add-service=http --add-service=https
firewall-cmd --reload
# 数据库/缓存端口(3306/5432/6379)绝不 add-service
# 出站:只放行业务必需的外部端点------挖矿外联、数据外传走的都是出站连接
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" destination address="198.51.100.7/32" port port="443" protocol="tcp" accept'
firewall-cmd --permanent --zone=public --set-target=DROP # 其余出站默认拒(按业务清单细化)
云服务器同理:安全组只放 80/443 和运维来源 IP,其余全关。出站管控是这两年性价比最高的一条------机器被拿下后,外联不通,矿池连不上、数据传不出,损害直接减半。
3. 补丁:自动安全更新
bash
# RHEL/CentOS 9
dnf install -y dnf-automatic
systemctl enable --now dnf-automatic-install.timer
# Ubuntu/Debian
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades
基础组件漏洞是近年重灾区:Heartbleed(OpenSSL)、Shellshock(bash)、Log4Shell(Log4j2)、XZ Utils 后门(2024 年 3 月曝光,CVE-2024-3094,供应链级后门,影响 liblzma 及其下游 sshd)。跟着发行版和组件社区的安全公告走,比任何事后补救都便宜。 策略上:安全补丁自动装,普通更新攒月度窗口,高危漏洞单独应急。
4. 备份与日志
备份独立存储(异地/离线/另一台机)+ 定期恢复演练;日志外发集中存 (rsyslog 推远端,本地日志会被删,异地留一份才查得了);开 auditd、盯 /var/log/secure 与 lastb,资源超阈值告警。没演练过的备份等于没有备份,这句话值得贴在机房门上。
九、文件完整性校验与历史审计
排查命令"是不是被换掉",除了 busybox 交叉比对,还有更系统的手段------用发行版自带的完整性校验:
bash
# 1) RPM 系:核对已安装包的文件与 rpmdb 里的校验和(被改过的会列出,5=校验和 S=大小 T=时间)
rpm -Va | grep -vE '^\.{8}' | head -30
rpm -qf /usr/bin/ps # 这个文件属于哪个包(不在任何包里 = 可疑)
# 2) Debian/Ubuntu 系
debsums -c 2>/dev/null | head -30 # -c 只列出校验和对不上的
dpkg -S /usr/bin/ps
rpm -Va 的输出解读:重点看 /usr/bin、/bin、/usr/sbin 下的系统命令 出现的属性变化------那里多出来的校验和变化,就是命令被换掉的实锤;/etc 下的配置文件变化是正常的(改过配置),要区别对待。正常机器上,系统命令那一列应该是空的。
历史记录也是一层:~/.bash_history 被清空本身就是线索;反过来,history 里如果留下不认识的命令(wget/curl 下载、chmod +x、crontab -e),时间戳能和上面的时间线对上。日常就该做的两个动作:
bash
# 登录超时:闲置 shell 自动断,降低"挂着的 root 会话被捡漏"的风险
echo 'TMOUT=600' >> /etc/profile.d/timeout.sh
# 让 history 带上时间戳,事后时间线多一个来源
echo 'export HISTTIMEFORMAT="%F %T "' >> /etc/profile.d/histformat.sh
十、几个明确的边界
- rootkit 检测的诚实边界 :用户态 rootkit,
chkrootkit、rkhunter能发现一部分;内核态 rootkit 加载后系统本身已不可全信,检测极其困难。务实答案是:说不清干不干净就重装 + 全量改口令 + 从干净备份恢复------风险收敛到可接受范围,不用无限期停摆,也不能幻想杀杀毒就干净。 - 本文只覆盖基线加固和应急排查的通用套路 ,不涉及:等保合规的完整技术整改项、WAF/IDS 的部署调优、主机安全 agent 的选型、加密流量层的检测、完整的司法取证规范。这些要么超出单篇能讲清的深度,要么强依赖具体环境------需要时按项目逐个来。
写在最后
生产服务器的安全就两件事:平时按基线把入口堵死,出事按"断网→留证→动机定性→排查→清持久化→恢复"走。排查没有"看一眼 ps 就下班"的捷径,但有"先判动机、按清单走"的章法。攻击者盯的是入口和松懈,不是机器重要性;加固清单不复杂,难的是当回事、逐项做、定期复查。
我们摩杰斯十四年做制造企业的 Linux 系统,自己的生产管理软件也常年跑在 Linux 上------加固、应急、迁移这些活,在几百家客户的现场一台台做过,踩过的坑基本都写在上面的命令和顺序里了。
生产系统为什么值得建在 Linux 上、半夜宕机怎么分钟级拉起,可以看我们之前的实录文章。