服务器被入侵?从CPU飙高到SSH爆破的全面排查与加固指南
2026/9/7 3:22:09 网站建设 项目流程

凌晨两点收到一条 CPU 使用率告警,登录服务器后top显示某个进程把整台机器吃满,名字却陌生得像随机生成的字符串;翻/var/log/secure又看到一整页来自同一网段的Failed password记录。这种时候,“追杀这个服务器的恶人”就成了一句特别具体的运维口令。

这里的“恶人”,并不是某个具名的人,而是服务器上不该存在的异常主体:可能是 CPU 飙高的恶意进程,可能是爆破 SSH 的攻击源,可能是被恶意写入的定时任务,也可能是一段长时间占用数据库连接、让整个业务卡死的慢查询会话。本文会按照一次真实的“服务器狩猎”顺序,从进程、登录、网络、数据库四个层面,说明如何定位异常主体、保留证据、完成处置,并在清理之后做好加固,避免下一次再被同一类问题找上门。

适用读者包括需要独立维护 Linux 服务器的运维工程师、后端开发,以及正在学习服务器排障的新人。文章给出的命令以 CentOS/RHEL 系为例,Ubuntu 等 Debian 系只需把/var/log/secure换成/var/log/auth.log,思路完全一致。

1. 先给“恶人”画个像:异常主体可能藏在哪里

很多新人在收到告警后第一反应是“哪个进程 CPU 高就 kill 哪个”。这个思路不算错,但容易漏掉真正的问题。为了不在一开始就被某个假象带偏,建议先理解异常主体可能存在的四类位置。

1.1 恶人不只是 CPU 高的进程

第一类是操作系统层。最直观的表现是 CPU 或内存异常上升,top里能看到一个高占用进程,或者在ps中出现随机名称、乱码名称、明显不属于你部署服务的命令行。但这里有个容易被忽略的点:并不是所有恶意行为都表现为高 CPU。有些脚本会伪装成系统进程名,比如叫[kworker]systemd-log,如果不看真实路径,很难识别。

第二类是登录和账户层。攻击者可能通过密码爆破成功登录,或者通过某个被泄露的密钥直接用公钥登录。这类异常不一定占用 CPU,但会在登录日志里留下成功记录,在authorized_keys/etc/passwd里留下痕迹。

第三类是网络层。服务器可能存在异常的外连请求,比如有进程持续向某个陌生 IP 发送数据;也可能正在被外部扫描或 DDoS 打流量。网络层问题未必能从top看到,必须同时看连接状态。

第四类是应用和数据库层。对于后端服务,一个“恶人”完全可能是一个恶意脚本在疯狂调用接口,导致服务线程耗尽;也可能是一条没有 WHERE 条件的 SQL,锁住整张表,让其他会话全部卡在Waiting for table metadata lock。这类问题表面上是“服务器很慢”,实际根源在应用线程池和数据库会话里。

1.2 从告警到定位的总体排查链路

无论异常最终出现在哪一层,都建议按下面这个顺序推进:先记录现场,再隔离影响,再溯源,再加固。不要在还没有弄清楚“恶人”是谁之前就乱杀进程或乱删文件,因为很多线索只有一次查看机会。

下面这张表可以作为整个排查过程的骨架:

现象优先检查位置核心命令可能处置方式
CPU 飙高进程列表、/proc 信息topps auxlsof -p PID终止异常进程,清理持久化
登录失败暴增登录日志、lastbgrep Failed /var/log/securelastb封禁攻击源 IP,启用 fail2ban
出现陌生成功登录wtmp、sshd 日志lastgrep Accepted /var/log/secure检查账户和 authorized_keys,撤销风险密钥
存在异常外连网络连接、进程端口ss -tnplsof -i追踪对应进程,终止并排查持久化
业务变慢、连接耗尽数据库会话、应用线程SHOW FULL PROCESSLISTsys.session清理长事务/慢查询,限制连接数
重启后异常复现定时任务、服务、开机脚本crontab -lsystemctl list-units删除恶意定时任务和 systemd 服务

这个顺序不是固定的。如果告警明确指向数据库,可以直接跳到数据库层;如果日志里已经看到大量爆破记录,就应该先处理登录层。关键是不要只看单一维度。

这里还要强调一个原则:学习环境和生产环境的处置姿态不同。学习环境里可以大胆重启、重装、快速 kill;生产环境第一原则是保留现场证据,再做最小变更,降低对业务的影响。后面在第 5 节会专门对比这一差异。

2. 第一现场:CPU 飙高时,先在进程和内核日志里找证据

假设你现在已经通过监控平台或uptime确认系统负载非常高,接下来需要找到具体是哪个进程在消耗资源。

2.1 用 top 和 ps 找出高占用进程,但别只看第一屏

第一个命令基本都是top。执行后按P按 CPU 排序,按M按内存排序。超出一屏的结果无法在一个画面里完整展示,所以正确做法是使用批处理模式输出到文件,方便后续分析:

top -b -n 1 -o %CPU | head -n 30

如果想看全部进程而不是只看 top 默认的前 20 行,可以用下面的形式:

ps aux --sort=-%cpu | head -n 30 ps aux --sort=-%mem | head -n 30

看到异常进程后,把 PID、USER、%CPU、%MEM、COMMAND 这几列记录下来。判断一个进程“异常”并不只看 CPU 高:还要结合命令路径是否合理、启动用户是否符合预期、进程名是否存在混淆。比如进程名显示为-bash,但 PID 很小、启动时间很早,这可能就是一次可疑的交互登录,而不是正常的 bash 进程。

这里有一个很常见的坑:只用pstop看一眼进程名就做判断。许多恶意进程会把自己的命令行伪装成系统常见名字,或者通过覆盖/proc/PID/comm来改名。所以下一步必须进入/proc目录查看更真实的运行状态。

2.2 从 /proc 看进程的真实身份

/proc目录是 Linux 暴露进程运行信息的虚拟文件系统,查看进程的 exe 软链接、当前工作目录、打开的文件、网络连接,比ps展示的信息更接近真实。

以 PID 为 12345 的进程为例:

ls -l /proc/12345/exe cat /proc/12345/cmdline | tr '\0' ' '; echo readlink /proc/12345/cwd cat /proc/12345/status | grep -E "Name|Pid|PPid|Uid|Gid"
  • /proc/PID/exe指向进程可执行文件的真实路径。如果显示路径为临时目录、/tmp/var/tmp/dev/shm,几乎可以判定为高风险。
  • /proc/PID/cmdline\0分隔参数,转成空格后可以看清完整启动命令。
  • /proc/PID/cwd显示当前工作目录,如果工作目录在可疑路径,也说明进程来源异常。
  • /proc/PID/status可以查看进程的 UID/GID,确认运行身份是否合理。

查看进程打开的文件和网络连接,是判断进程是否在进行可疑读写或外连的关键:

lsof -p 12345 | head -n 20 ss -tnp | grep 12345

lsof -p能列出该进程打开的文件、共享库、日志文件;ss -tnp能列出进程建立的 TCP 连接。如果发现进程持有大量可疑的文件句柄,或正在持续向某个陌生 IP 发起连接,就需要把网络层也纳入排查范围。

如果无法使用lsof,可以安装后再执行;也可以直接遍历/proc/PID/fd目录:

ls -l /proc/12345/fd | head -n 20

2.3 遇到异常进程后的处置顺序

确认进程可疑之后,第一反应是“kill 掉”,但更稳妥的处置顺序是先保留证据,再终止进程。

推荐做法是先把现场信息完整保存下来,例如:

# 保存进程信息和打开文件 ps aux | grep 12345 > /tmp/abnormal_process_info.txt ls -l /proc/12345/exe > /tmp/abnormal_process_exe.txt lsof -p 12345 > /tmp/abnormal_process_fd.txt ss -tnp | grep 12345 > /tmp/abnormal_process_net.txt # 如果怀疑是二进制文件,先不要删除,复制到隔离目录用于分析 cp -a /proc/12345/exe /tmp/sample_$(date +%F).bin 2>/dev/null || true

保存完证据后,再使用kill终止进程。如果普通 kill 无法终止,可以考虑kill -9。但要注意,这里的“无法终止”可能是进程处于 D 状态(不可中断的 IO),也可能是被内核锁住;强行终止前建议先看/proc/PID/status中的进程状态。

第二个常见坑也随之出现:只 kill 进程而不清理持久化。恶意程序通常不会只以孤立进程存在,它往往会在crontab/etc/cron.*、systemd 服务、~/.bashrc~/.profile/etc/rc.local里留下启动入口。如果不清除这些入口,进程被 kill 后会在一定时间后再次启动,出现“重启之后又回来了”的现象。

建议在杀进程之后立即检查:

crontab -l ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/ systemctl list-units --type=service --state=running | grep -vE "systemd|polkit|dbus|sshd|crond|network|rsyslog"

配合启动脚本检查:

grep -nE "curl|wget|base64|/tmp/|/dev/shm" ~/.bashrc ~/.profile /etc/rc.local 2>/dev/null

如果找到了可疑脚本,在确认内容前不要贸然删除,先把内容复制出来,再移除对应的 crontab 条目或 systemd 服务,最后重启验证是否还会出现。

第三类坑是“只看 CPU,不看网络外连”。恶意程序的真实危害往往不在于抢占 CPU,而在于它会持续外联把数据传出去。因此只要发现了异常进程,就必须同步用sslsof -i检查外连地址。否则即使杀掉了 CPU 高的进程,潜伏在其他名字下的通信进程仍然可能继续工作。

3. 追查“恶人”的入口:登录和认证日志

很多时候,异常进程只是结果,真正的入口在登录环节。攻击者能够投放恶意程序,通常意味着成功登录过服务器,或者获得了某个高权限账户的访问通道。因此要回答“恶人是怎么进来的”,接下来必须查登录记录。

3.1 找出今天谁成功登录过

Linux 下最直接的登录历史是last,它读取/var/log/wtmp,记录所有成功登录和重启历史:

last -n 30 last -u root -n 20

第一行会显示最近登录的用户、来源 IP、登录时间。重点关注几个信息:来源 IP 是否属于你熟悉的办公出口、云安全组或堡垒机;登录时间是否在业务低谷或深夜;是否存在root直接从外网 IP 登录的记录。

同时查看 SSH 服务日志中Accepted记录。多数情况下,成功登录一定有Accepted publickeyAccepted password字样:

# CentOS/RHEL grep "Accepted" /var/log/secure | tail -n 30 # Ubuntu/Debian grep "Accepted" /var/log/auth.log | tail -n 30 # 使用 systemd 日志时 journalctl -u sshd --since "today" | grep Accepted

如果发现成功登录的来源 IP 异常,需要继续确认这个会话当前是否还存活。可以通过whoss -tnp查看当前的 SSH 会话:

who

如果看到异常来源 IP 仍保持连接,可以考虑断开该会话。通过pkill -u username可以踢掉某个用户的所有会话,但这种操作要谨慎,可能影响其他正常登录。更精确的做法是先用tty信息定位,再只结束对应会话进程。

3.2 密码爆破的痕迹怎么看

大量Failed password是攻击者正在爆破 SSH 密码的典型现象。查看失败登录最多的来源 IP 可以这样做:

# CentOS/RHEL grep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -n 20 # Ubuntu/Debian grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -n 20

awk '{print $(NF-3)}'提取的是日志行末尾倒数第四个字段,通常就是来源 IP。之后可以继续查看这些 IP 是否有过成功登录:

grep "Accepted" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -n 20

lastb读取/var/log/btmp,显示失败的登录尝试,适合快速判断爆破账号:

lastb | head -n 30

如果大量失败尝试来自少数几个 IP,临时封禁这些 IP 是必要的。但下一节会提到,封 IP 前要先确认不是自己的 NAT 出口或云平台健康检查。

3.3 检查 authorized_keys 和用户账户

密码爆破属于外部入口,还有一种更隐蔽的入口:攻击者可能已经拿到了某个用户的私钥,或者往authorized_keys里写入了自己的公钥。这类后门登录不使用密码,不容易在Failed password里看到,所以必须主动检查。

全盘查找所有authorized_keys文件和known_hosts文件:

find / -name "authorized_keys" -type f 2>/dev/null find / -name "authorized_keys2" -type f 2>/dev/null

逐个查看找到的文件,确认每一行公钥是否真的是你能识别的密钥。如果存在不明公钥,在备份后移除对应行。同时检查系统里是否存在异常用户:

# 查看 UID=0 的高权限用户,以及可登录 shell 的非 root 用户 getent passwd | awk -F: '$3==0 || ($1!="root" && $7 ~ /(bash|sh)$/) {print}'

正常情况下,UID 为 0 的只有 root;若出现其他 UID=0 用户,风险极高。非 root 用户即使有 bash shell 也不一定异常,需要结合创建时间和最近登录记录判断。

检查完账户后,不要忽略 shell 初始化文件。攻击者为了在用户登录后自动执行恶意代码,常把内容写入~/.bashrc~/.profile/etc/profile.d/

cat ~/.bashrc | grep -nE "curl|wget|base64|nohup|/tmp|/dev/shm" grep -rnE "curl|wget|base64|nohup|/tmp|/dev/shm" /etc/profile.d/ 2>/dev/null

这一层排查的目标不是抓到某一个 IP,而是确认后续清理之后,攻击者没有保留重新进入服务器的入口。

4. “恶人”还可能躲在数据库里:阻塞和异常会话

除了操作系统层面的进程和登录,数据库里的“恶人”也经常会让服务器看起来像是“被追杀”。表现是应用接口大面积超时,数据库 CPU 或连接数飙升,应用日志里不断出现连接获取超时。很多人的第一反应是重启应用,但如果根因是数据库里的某个会话锁住了关键行,重启应用往往没有效果。

4.1 数据库“恶人”指什么

数据库层面的异常主体主要有三类:长时间不释放连接的 Sleep 会话、把某张表或某些行锁住的 DML 会话,以及完全没有索引或 WHERE 条件缺失的大查询。

以 MySQL 为例,最直观的查询是SHOW PROCESSLIST

SHOW FULL PROCESSLIST;
  • Id是会话 ID,对应后续KILL操作的标识;
  • Command显示会话正在干什么,常见值包括QuerySleepLocked
  • Time表示会话已经在该状态持续的时间,秒为单位;
  • State给出更细的执行状态,例如Sending dataWaiting for table metadata lockWaiting for lock
  • Info显示正在执行的 SQL,有时能看到完整 SQL,有时只有前 100 个字符。

判断一个会话是否是“恶人”,不能只看Command = Sleep。短时间的 Sleep 是连接池的正常状态,但几百秒甚至上千秒的 Sleep 就可能是在连接建立后长时间不操作,占用了连接数上限。

4.2 用 SHOW PROCESSLIST 和 sys.session 定位

SHOW FULL PROCESSLIST是最基础的方式,但输出比较长。MySQL 5.7 之后的版本提供了sys.session视图,查询更灵活:

-- 查看正在执行非 Sleep 命令的会话,按耗时倒序 SELECT * FROM sys.session WHERE command <> 'Sleep' ORDER BY time DESC; -- 查看长时间 Sleep 的会话 SELECT * FROM sys.session WHERE command = 'Sleep' AND time > 100 ORDER BY time DESC;

sys.session里的conn_id对外对应SHOW PROCESSLISTIdcurrent_statement对应正在执行的 SQL。如果能看到command = 'Query'time达到几十秒甚至上百秒,而它执行的是一条没有 WHERE 条件的DELETEUPDATE,基本可以判定就是它阻塞了其他会话。

进一步确认锁等待关系,可以查询:

SELECT * FROM sys.innodb_lock_waits;

sys.innodb_lock_waits会输出阻塞者与被阻塞者之间的关系字段,例如blocking_pidwaiting_pid。在确认阻塞者之后,需要谨慎处理:先通过SHOW PROCESSLISTInfosys.sessioncurrent_statement确认这条 SQL 是什么,是否有业务在跑,再决定是否终止。

终止一个数据库会话用KILL

KILL 12345;

如果 KILL 后会话仍不释放,可能是元数据锁等待,常见原因是有其他长事务持有表结构引用。此时要先把持有引用的事务也找出来,否则 KILL 掉当前会话后,新的 DDL 或 DML 仍可能继续等待。

这里必须提醒:不要在未确认会话对应的业务影响之前,直接对大查询执行 KILL。对于线上数据库,一个被 KILL 的长事务可能导致部分业务数据未提交、应用侧抛出异常。如果影响较大,应先在维护窗口或业务低峰处理,并且确保应用有连接失败重试机制。

4.3 阻止疯狂会话的配置建议

清理完当前“恶人”,还需要从配置上减少后续风险。MySQL 常见相关参数如下:

参数默认值(常见版本)作用配置过小的影响配置过大的影响
max_connections151限制最大并发连接数正常业务无法建连高并发下 DB 负载失控
max_user_connections0(不限)限制单用户并发连接数部分客户端被拒绝占用太多了,仍可能拖垮实例
wait_timeout28800非交互连接空闲超时连接频繁被断开Sleep 会话占用连接不释放
interactive_timeout28800交互连接空闲超时mysql 客户端操作易断交互连接长期挂起
lock_wait_timeout31536000(某些版本)等待锁超时时间业务快速失败但可能报错锁等待时间过长,线程堆积
long_query_time10记录超过该秒数的查询到慢查询日志日志量变大慢查询被漏掉
max_execution_time0(不限)SELECT 最大执行时间长查询被中断大查询占资源,拖垮实例

生产环境调整这些参数要平滑,不能通过重启数据库这种粗暴方式验证。wait_timeoutinteractive_timeout可以通过SET GLOBAL动态修改,但新值只对新连接生效;max_connections可以通过SET GLOBAL max_connections = 200;临时调整,同时要注意监控连接数趋势。真正持久化还需要修改配置文件,并在维护窗口逐步重启。

对于大查询,还可以在应用侧增加超时和限流。例如 MySQL 8.0 可通过SET_VAR或会话变量设置max_execution_time,但根本上还是需要代码审查,确保SELECTDELETEUPDATE都带有合理索引和必要的 WHERE 条件。

5. 从排查到处置:封禁、清理与加固

当你已经定位了恶人的来源、异常进程和入口,下一步就是处置。处置不是一个动作,而是一组动作,包括封禁攻击源、清理持久化、加固 SSH、修复被篡改的账户和配置文件。

5.1 临时封禁攻击源 IP,还要考虑误封

对于从日志里确认的攻击源 IP,可以先做临时封禁。这里要非常小心一个误封问题:很多公司出口是 NAT 的,多个办公区可能共享少量公网 IP;云平台健康检查、监控探针、堡垒机也可能来自固定 IP。封禁前至少要做三件事:

  • 确认该 IP 没有出现在最近的成功登录记录里;
  • 确认该 IP 不在安全组或白名单配置中;
  • 确认该 IP 不是你自己当前办公出口。

CentOS/RHEL 7 及以上使用 firewalld 时,可以通过富规则封禁:

firewall-cmd --permanent --add-rich-rule='rule family=ipv4 source address=1.2.3.4 reject' firewall-cmd --reload firewall-cmd --list-all

如果系统使用 iptables 服务:

iptables -I INPUT -s 1.2.3.4 -j DROP service iptables save iptables -L INPUT -n

临时封禁可以快速降低服务器被继续爆破的压力,但它不能持久阻止有代理池的攻击者。更系统的方法是部署 fail2ban。它的工作方式是监控日志文件,发现多次失败后自动调用防火墙封禁来源 IP,并在一段时间后自动解封。

# /etc/fail2ban/jail.local [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/secure maxretry = 5 bantime = 3600

配置完成后执行:

systemctl enable --now fail2ban fail2ban-client status sshd

fail2ban-client status sshd会显示当前被封禁的 IP 列表和封禁次数。这种方式的优点是自动、可解封,误封影响比手动永久封禁小;缺点是对分布式低频爆破不敏感,所以不能只依赖它。

需要特别说明:封 IP 是止血,不是根治。如果攻击者已经从这份服务器里拿到了账号或密钥,封掉当前 IP 只是暂时让他换一台机器接着打。真正需要的是在第 5.3 节中把 SSH 入口收紧。

5.2 清理定时任务、系统服务和可疑文件

之前第 2 节提到只 kill 进程不清理持久化,问题会复发。实际清理时要同时检查以下几处:

  • 当前用户的 crontab:crontab -l
  • 系统定时任务:/etc/crontab/etc/cron.d//etc/cron.hourly//etc/cron.daily//etc/cron.weekly//etc/cron.monthly/
  • systemd 定时器:systemctl list-timers
  • systemd 服务:systemctl list-units --type=service --state=running
  • 启动脚本:/etc/rc.local/etc/rc.d/rc.local
  • 用户级初始化文件:~/.bashrc~/.profile~/.bash_profile

发现可疑项后,先复制一份内容留档,再删除或注释对应条目。示例:

# 查看某个具体 cron 文件内容 cat /etc/cron.d/evil_task # 删除可疑 cron 任务,操作前确认内容 crontab -r rm -f /etc/cron.d/evil_task

删除 systemd 服务时,需要先停止再移除:

systemctl stop evil.service systemctl disable evil.service rm -f /etc/systemd/system/evil.service systemctl daemon-reload

清理可疑文件时,不要随手删除。相对稳妥的做法是先移动到隔离目录:

mkdir -p /tmp/quarantine mv /tmp/evil_script.sh /tmp/quarantine/

这样如果后续需要分析样本或确认文件来源,仍然有地方可查。如果确认无用,再彻底删除。

同时要留意日志里被人为清理的空白期。如果某段时间的登录日志里完全没有记录,但last显示在这段时间存在登录会话,那可能是攻击者清理过日志。这类情况通常意味着入侵痕迹已经被故意抹除,处理优先级要高于普通爆破。

5.3 SSH 加固清单

清理完入口和持久化之后,必须把 SSH 配置收紧,否则服务器依然暴露在公网上,只是暂时没有被打而已。下面是一组常见但有效的加固方式:

# /etc/ssh/sshd_config PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes MaxAuthTries 3 LoginGraceTime 30 AllowUsers ops deploy
  • PermitRootLogin no:禁止 root 直接远程登录,日常操作先登录普通用户再su切换。
  • PasswordAuthentication no:在纯密钥环境下可以关闭密码登录,能极大降低爆破成功概率。但前提是已经确认至少有一台机器能通过密钥正常登录,否则一旦配置错误,会导致自己被关在门外。
  • MaxAuthTries 3:限制单次连接尝试次数,爆破脚本的低效会明显下降。
  • LoginGraceTime 30:限制登录超时,避免建立连接后长时间不认证。
  • AllowUsers:明确指定允许登录的用户名单,效果比“禁止某些用户”更稳。
  • PermitRootLoginPasswordAuthentication这两项改动较大,一定先开着新会话验证密钥登录可用,再重启 sshd。

修改完成后,先执行语法检查:

sshd -t

再平滑重启 SSH 服务:

systemctl reload sshd

注意不要轻易使用systemctl restart sshdreload对已连接会话影响更小。

“修改默认端口”也是一个常见操作,但它的本质是降低自动化扫描的命中率,不能从安全模型上解决问题。修改端口前需要评估业务防火墙、云安全组、监控脚本是否有依赖 22 端口的地方,否则会导致部分服务无法连接。

5.4 学习环境与生产环境的处置差异

整个处置流程里,最容易造成事故的往往不是“处置不了”,而是在生产环境里用了学习环境那种大刀阔斧的手法。两者的差异用一张表可以看得很清楚:

环节学习/测试环境生产环境
发现可疑进程可直接 kill,通常无业务影响先确认进程归属,保留现场,按变更流程处置
修改 sshd_config可大胆测试,失败后重装即可必须先验证密钥可用,再reload,并保留一个可回滚窗口
封禁 IP误封影响小先排除办公出口、堡垒机、健康检查 IP
清理文件直接删除即可先移到隔离目录,记录 md5、路径、源信息
重启服务直接systemctl restart优先reload,高并发业务需评估连接中断影响
数据库 KILL 会话基本可以直接杀先确认事务影响,业务低峰处理,观察应用日志
样本分析随意分析复制后离线分析,不要在生产机直接执行可疑文件

生产环境还有一个不可省略的步骤:出了问题之后,如果判断是安全事件,应保留日志和样本至少一段时间,并复盘攻击路径。很多团队会忽略这个环节,结果下次被同样方式入侵时,又从头开始找日志。

6. 常见问题排查速查表和加固检查清单

最后给出一份可以直接复用的排查速查表和加固检查清单。平时可以把它们贴在运维文档或告警响应文档里。

6.1 现象对应排查命令速查

现象检查命令常见原因处置建议
CPU 持续 100%,进程名随机top -b -n 1 -o %CPUls -l /proc/PID/exe挖矿或恶意脚本保留证据后终止进程,清理 cron/systemd 持久化
大量 Failed passwordgrep "Failed password" /var/log/secure | awk '{print $(NF-3)}' | sort | uniq -c | sort -nrSSH 爆破封禁 IP,启用 fail2ban,考虑关闭密码登录
出现陌生 IP 成功登录lastgrep Accepted /var/log/secure密钥泄露或弱密码登录成功撤销对应密钥,改密码,检查 authorized_keys
端口被占用/异常外连ss -tnplsof -i恶意进程外联定位进程,终止并清理持久化
定时任务异常crontab -lls /etc/cron.d/systemctl list-timers被写入持久化任务先备份再删除,检查 systemd 服务
数据库连接耗尽,业务超时SHOW FULL PROCESSLISTSELECT * FROM sys.session长事务、慢查询、无索引确认后 KILL 异常会话,优化 SQL,限制连接数
应用重启后异常复现重点检查~/.bashrc/etc/rc.local、systemd 服务持久化后门未清理清理脚本文件与开机启动项
某用户存在不明公钥find / -name authorized_keys -type f攻击者写入了自己的公钥备份后移除不明公钥,复查账户和 shell

6.2 验证加固是否生效的最小检查清单

做完一轮处置之后,不要直接认为“好了”。建议按下面的清单再走一遍,确认清理和加固确实生效。

  • [ ]crontab -l已经是空的,或者只保留了业务需要的任务。
  • [ ]systemctl list-units --type=service --state=running中没有陌生服务。
  • [ ]last最近的成功登录记录里没有未知 IP。
  • [ ] 所有authorized_keys文件都能追溯到已知团队成员。
  • [ ]/etc/ssh/sshd_config关键参数已生效,sshd -t没有报错。
  • [ ] 用一台干净的机器测试新的密钥登录,确认还能正常进入服务器。
  • [ ] 如果开启了 fail2ban,fail2ban-client status sshd能看到已监控的 jail。
  • [ ] 数据库SHOW FULL PROCESSLIST里没有长时间 Sleep 或 Locked 会话。
  • [ ] 监控平台上 CPU、内存、连接数回到基线值,并且持续观察至少一个完整业务周期。
  • [ ] 对外防火墙和安全组只放行必要的端口,其余端口默认拒绝。

6.3 下一步:把“狩猎”变成日常监控

“追杀这个服务器的恶人”不能永远靠人工处理。真正稳定的服务器,应该把“找恶人”变成自动化的一部分:进程和端口监控、登录日志集中采集、失败次数阈值告警、数据库慢查询和连接数监控,这些都应该在日常就位。否则等到告警爆发时,就从“一个普通的排查任务”升级成了一次安全事件。

对于新人,建议先在测试服务器上模拟一遍整个流程:开启 SSH 密码登录,用错误密码尝试连接,观察secure日志和lastb;再手动写一个耗 CPU 的脚本放在后台,练习用/proc/PID定位并清理。练习过程中不要只记命令,关键是理解每个命令对应的是哪一种“恶人”藏匿方式。这样真正遇到问题时,才能从一条告警一路追到完整证据链。

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

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

立即咨询