Linux 服务器入侵应急响应:失陷后的黄金 60 分钟

凌晨 02:17,EDR 报告一台 Linux 生产服务器上的 root 进程从 /dev/shm/.cache/ 启动,连接陌生公网地址;同一时间,云审计日志出现异常角色调用,另一台内网主机也使用相同 SSH 公钥登录。

攻击者已经拥有 root 权限,可能控制多台主机,能够读取本机凭据、篡改日志和安全工具,并可能正在横向移动或外传数据。

此时最危险的错误,不是漏查一个目录,而是仍把它当成单机病毒:值班人员登录主机逐个杀进程、删除文件、临时改密码;攻击者则通过另一条持久化继续观察处置、窃取新密码并转移到其他系统。

处置必须从单机运维切换为组织级响应。受害主机默认不可信,本机保存的凭据默认可能泄露,与它存在管理或信任关系的资产全部进入排查范围。

本文不提供一份机械的 Linux 命令大全,而是建立一套完整调查方法:

1
建立指挥面 → 隔离与保全 → 采集易失证据 → 还原攻击链 → 确认影响面

全文只针对 Linux 服务器,适用于物理机、虚拟机、云主机和 Linux 容器宿主机。命令兼顾 Debian/Ubuntu 与 RHEL/Rocky Linux;不同发行版的日志路径和服务名称可能略有差异。

文中命令以使用 systemd 的主流 Linux 发行版为基础,需要经过授权并由具备 root 权限的响应人员执行。lsof、pstree、crictl、debsums 等工具不一定默认安装;不要在失陷主机上临时联网安装工具,应使用事先验证的响应工具包或从外部控制面完成等效采集。涉及服务停止、unit mask、容器缩容的命令会改变现场,必须先保存证据并记录操作。

核心原则:攻击者可能仍在线,证据可能被篡改,影响范围暂时未知。先阻止损失扩大,再通过独立证据还原攻击链,为后续清场、凭据轮换和可信重建提供依据。

一、先建立指挥面:决定谁做什么,而不是所有人一起登录

1.1 建立事件假设和响应边界

响应开始时采用以下假设,直到证据能够逐项排除:

默认假设 立即产生的行动
root 已被控制 不信任本机命令、日志、包数据库和安全工具输出
攻击者仍有访问能力 从外部控制面限制会话,不在受害主机上轮换密码或零散清理
本机可访问凭据已泄露 盘点 SSH 密钥、Token、证书、云角色和服务账号,准备从干净终端撤销
攻击者已尝试横向移动 检查同账号、同公钥、同网段、同镜像和所有管理目标
数据可能已经外传 保存 DNS、代理、流量、对象存储和云审计记录
日志可能被清理或伪造 以集中日志、网络设备、云控制面、内存和离线磁盘交叉验证
同源部署可能批量中招 暂停相关流水线、镜像发布和自动化任务,核验供应链

这种响应基线可以避免遗漏仍存活的访问路径。多排查几台关联主机的成本,远低于让攻击者保留一条未发现的回连或信任通道。

1.2 建立三个角色和一份决策记录

即使只有三个人,也应明确角色:

  • 事件负责人:确定优先级、批准隔离、协调业务和对外沟通;
  • 技术调查人:采集证据、建立假设、还原攻击链;
  • 记录人:记录时间、操作者、命令、结果、哈希和每次决策。

避免多人同时登录同一主机。新建 shell、读取文件、运行工具、安装软件和操作服务都可能写入认证或审计日志、更新文件访问时间、改变缓存或产生新文件。没人记录的操作,之后很难与攻击者活动区分。

调查记录应把三种内容分开:

类型 示例
事实 02:17:43,PID 18422 从 /dev/shm/.cache/kd 运行并连接 203.0.113.10:443
假设 该进程可能是下载器或 C2 客户端
决定 立即从业务网络隔离,仅保留受控取证通道,并并行创建快照和采集内存

这种写法可以防止团队把猜测写成结论,也能解释为什么某个时间点隔离主机、撤销凭据或扩大排查范围。

1.3 隔离、快照和取证必须并行启动

处置不能等待完整调查后再隔离。事件负责人应同时启动三个工作流:

  1. 控制损失:从负载均衡摘除节点,在云安全组、交换机或防火墙侧阻断互联网出口和横向访问,仅保留来源固定、全程审计的取证通道;
  2. 保全证据:创建云盘或虚拟机快照,采集内存、会话、连接、进程和打开文件,保存集中日志与原始告警;
  3. 控制身份与供应链:冻结高风险自动化任务,阻断被盗账号的新会话,盘点待轮换凭据,但不把新凭据下发到旧主机。

如果正在加密、删库、擦盘或攻击身份系统,应立即完全断开网络,不为采集等待。若破坏行为暂时稳定,可以保留一个严格限制的取证通道,但不能继续允许业务流量、任意公网出口或对内管理访问。

不要在失陷主机上临时安装大量工具或手工堆叠防火墙规则。外部控制面更可信,也更容易回滚。没有带外通道时,应接受主机暂时失联,而不是为了方便调查继续暴露整个网络。

二、黄金 60 分钟:按照证据消失速度行动

NIST SP 800-86 强调易失数据会随时间变化,采集顺序直接影响调查结果。实际 Linux 响应中,可以把第一个小时拆成三个阶段。

2.1 0—5 分钟:隔离受害面并冻结高风险变更

立即执行:

  • 从负载均衡摘除确认失陷的 Linux 节点;
  • 在网络或云控制面阻断其公网出口和对内横向访问;
  • 对同一异常账号、公钥、镜像或行为链命中的关联主机立即分级;高置信度命中直接隔离,其余限制横向访问并优先调查;
  • 暂停向相关主机发布的 CI/CD、配置管理和自动扩缩容任务;
  • 保护备份系统,阻止受害账号删除快照或修改保留策略;
  • 创建磁盘/虚拟机快照,并保留实例、卷、网卡和安全组元数据;
  • 建立独立通信与记录渠道,避免在可能受控的平台讨论驱逐计划。

与此同时,从主机外部保存不易被攻击者控制的数据:

  • EDR/NDR 原始告警;
  • 防火墙、DNS、WAF、负载均衡和 VPC Flow Logs;
  • 云控制面、堡垒机、VPN、统一身份和 CI/CD 审计日志;
  • 资产信息、实例 ID、镜像版本、部署记录和最近变更;
  • 同一 IP、域名、账号、哈希在其他主机上的命中情况。

然后记录主机时间与身份,避免后续时间线错位:

1
2
3
4
5
6
7
date --iso-8601=ns
timedatectl
hostnamectl
uname -a
cat /etc/os-release
ip -br address
uptime

注意比较主机时间、日志平台时间和告警平台时间。若时间偏移 8 分钟,应在记录中保留这个偏差,不要直接修改系统时钟;改时间本身会污染时间线。

2.2 5—20 分钟:采集最容易消失的现场

建议从可信的只读介质或已验证工具包执行命令,并把输出写到外部证据盘或远端接收端。不要为了排查在受影响主机上临时安装一整套软件,它会新增包、文件、网络连接和日志。以下示例假设 /mnt/ir 是已挂载的可信证据盘:

1
2
EVIDENCE_DIR="/mnt/ir/$(hostname)-$(date +%Y%m%dT%H%M%S)"
sudo install -d -m 700 "$EVIDENCE_DIR"

第一轮采集关注会话、网络、进程、打开文件和 namespace:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
who -a
w
loginctl list-sessions

ss -Hantup
ip route
ip neigh

ps -eo pid,ppid,user,lstart,etime,%cpu,%mem,stat,comm,args --sort=-%cpu
pstree -alp
lsof -nP # 输出可能很大;负载紧张时优先针对可疑 PID 采集

lsns
systemd-cgls --all
mount
findmnt
lsmod
dmesg -T | tail -n 300

这里加入 lsns、systemd-cgls 和 findmnt 很重要。Linux 恶意进程可能位于独立 PID 或 mount namespace 中,宿主机上看到的路径、进程树和容器内并不一致;只检查宿主机默认视图,可能遗漏容器或隐藏挂载中的活动。

若组织具备内存取证能力,应在这一阶段采集内存。内存中可能保留已经删除的可执行文件、解密后的配置、网络会话、命令参数和纯内存载荷。采集工具必须与内核版本兼容,并提前在同类环境验证;不要在事故现场第一次尝试未经验证的内核模块。

2.3 20—60 分钟:建立攻击者证据包并启动全网狩猎

不要立刻全盘扫描。先围绕告警给出的 PID、IP、账号、文件或时间点形成“小而完整”的证据包。

已知可疑 PID 时:

1
2
3
4
5
6
7
8
9
10
11
12
13
PID=<可疑PID>

readlink /proc/$PID/exe
tr '\0' ' ' < /proc/$PID/cmdline; echo
tr '\0' '\n' < /proc/$PID/environ
cat /proc/$PID/status
cat /proc/$PID/loginuid
cat /proc/$PID/maps
cat /proc/$PID/cgroup
ls -al /proc/$PID/fd
ls -al /proc/$PID/cwd /proc/$PID/root
ls -al /proc/$PID/ns
lsof -nP -p "$PID"

需要同时记录:

  • PID、PPID、真实用户、登录 UID、启动时间和父进程;
  • 完整命令行、环境变量、工作目录和根目录;
  • 可执行文件、映射库、打开文件和网络连接;
  • 所属 cgroup、容器和 namespace;
  • 文件哈希、包归属、签名或可信基线差异。

若 /proc/<PID>/exe 指向的文件带有 (deleted),说明磁盘目录项已删除,但进程仍持有可执行对象。终止进程前可保留副本:

1
2
3
sudo cp --dereference /proc/$PID/exe "$EVIDENCE_DIR/suspect-$PID.bin"
sudo sha256sum "$EVIDENCE_DIR/suspect-$PID.bin"
sudo file "$EVIDENCE_DIR/suspect-$PID.bin"

environ 可能包含口令、Token 和云凭据,输出必须进入访问受控的证据目录,不要直接粘贴到聊天或工单。样本只在隔离分析环境中处理,不要在生产主机上直接执行。哈希查询、反病毒扫描和沙箱上传还应遵守组织的数据分类要求,内部程序可能包含敏感信息。

2.4 自动化采集:UAC 为主,rkhunter 补充

如果希望“运行一次,把可能与后门有关的现场统一记录下来”,可以使用开源的 UAC(Unix-like Artifacts Collector)。UAC 无需安装,支持 Linux 等 Unix-like 系统,可以按配置文件自动采集运行进程、无磁盘文件的进程、可执行文件哈希、系统与用户配置、日志和文件元数据,并按照易失性顺序执行采集。UAC 官方文档提供了 ir_triage、full 和自定义 artifact 等模式。

UAC 的定位是证据采集器,不是“后门判定器”。它可以把 systemd、cron、SSH、进程、网络、日志和可疑文件等线索收集到同一个归档,但不会证明某个文件一定恶意,也不能证明没有发现结果就代表系统干净。

建议事先在干净环境中下载并验证固定版本,把完整工具目录放入只读响应介质。事故现场不要使用 curl 或 git clone 临时从互联网获取最新版。以可信证据盘 /mnt/ir 为输出目录时,先执行 UAC:

1
2
cd /mnt/ir-tools/uac
sudo ./uac -p ir_triage -H --case-number IR-20260703-001 --evidence-number HOST-01 --examiner responder01 /mnt/ir

其中 ir_triage 适合首轮快速采集,-H 会计算已采集文件的哈希,案件编号、证据编号和采集人会写入采集上下文。完成后还应对最终归档单独计算 SHA-256,并把哈希保存到案件记录:

1
sha256sum /mnt/ir/uac-* > /mnt/ir/uac-archives.sha256

使用时注意:

  • 首轮不要启用 --enable-modifiers,它允许执行会改变系统状态的 artifacts;
  • full 配置采集量更大,可能产生明显 I/O,应在主机隔离且确认磁盘空间后使用;
  • 输出必须写到外部证据盘、网络证据库或其他受控位置,不要默认写入受害主机的 /tmp;
  • UAC 仍通过受害系统执行命令。若存在 Rootkit,输出可能被欺骗,必须与内存、离线磁盘和网络侧日志交叉验证;
  • 在投入事故现场前,先在同类 Linux 版本上测试耗时、输出大小和 artifact 覆盖范围。

rkhunter:补充扫描并保存告警

UAC 采集完成并固定归档哈希后,再按需运行 rkhunter。rkhunter(Rootkit Hunter)用于寻找已知 Rootkit 和恶意软件特征、被修改的系统命令与启动文件,以及可疑监听程序等迹象。它只做补充扫描,不代替进程、持久化、内存和离线磁盘分析。

如果 rkhunter 已在事发前部署,或组织的只读响应介质中保存了经过验证的版本和数据文件,可以把完整日志直接写入证据目录:

1
2
sudo rkhunter --check --skip-keypress --report-warnings-only --nocolors --nomow --logfile "$EVIDENCE_DIR/rkhunter.log"
sha256sum "$EVIDENCE_DIR/rkhunter.log" > "$EVIDENCE_DIR/rkhunter.log.sha256"

扫描结果中的 Warning 只表示需要继续核实,并不等于已经发现 Rootkit。软件更新、自定义内核、容器环境、特殊权限和管理员修改都可能产生告警,应与发行版软件包、可信镜像和变更记录比较。

事故现场不要执行以下操作:

1
2
rkhunter --propupd
rkhunter --update

--propupd 会把当前文件属性写入 rkhunter 数据库;如果系统已经失陷,相当于把攻击者修改后的文件登记成新的“正常基线”。--update 会联网并修改本地数据文件,也会污染现场。基线更新只能在已确认干净的系统上进行。rkhunter 手册也明确指出,该工具只能报告文件发生变化,不能判断变化原因,文件是否可信仍由响应人员确认。不要把 rkhunter 的“未发现”结果当作安全证明。

三、不要按目录排查,要沿攻击链追问

一个健全的调查流程不应止于“有没有可疑进程”。应该沿攻击者必须完成的步骤逐段验证:如何进入、如何执行、如何提权、如何驻留、如何取证书和密钥、如何横向移动、如何外传,以及如何掩盖活动。

3.1 初始入口:攻击者第一条可执行路径在哪里

Linux 服务器常见入口可以归为四类:

  1. SSH、VPN、堡垒机或云控制台凭据被盗;
  2. 对外服务漏洞、错误配置或 Web 应用命令执行;
  3. CI/CD、软件包、镜像或自动化账号供应链失陷;
  4. 已失陷的内网主机使用信任关系横向进入。

SSH 与身份入口

1
2
3
4
5
6
7
who -a
last -Fai
sudo lastb -Fai
lastlog

sudo journalctl -u sshd --since '<开始时间>' --until '<结束时间>' -o short-iso-precise
sudo journalctl -u ssh --since '<开始时间>' --until '<结束时间>' -o short-iso-precise

Debian/Ubuntu 常见认证日志为 /var/log/auth.log,RHEL 系常见为 /var/log/secure。需要同时检查轮转文件、集中日志、堡垒机和身份提供商记录。

关注登录成功时间、来源地址、账号、认证方式、会话持续时间,以及登录后是否立即发生 sudo、下载、解码或外连。last 依赖的 wtmp 可以被删除或篡改,不能单独作为“无人登录”的证明。

对外服务与 Web 入口

如果可疑 shell 的父进程是 nginx、apache2、httpd、php-fpm、java、python、node 或数据库进程,应反向检查同一秒附近的访问日志、错误日志、反向代理日志和应用审计日志:

1
2
3
4
可疑进程启动时间
← 前后 30 秒的 HTTP 请求
← 请求来源 IP、Host、URI、User-Agent、响应码和响应大小
← 应用账号、容器、发布版本和文件改动

仅仅找到 Webshell 不是根因。还要确认它通过文件上传、模板注入、反序列化、插件漏洞、弱口令还是供应链发布进入,否则清除后仍会复发。

软件包、镜像与自动化入口

检查事故时间窗内的软件安装与部署记录:

1
2
3
4
5
6
# RHEL/Rocky Linux
sudo dnf history
sudo rpm -qa --last | head -n 50

# Debian/Ubuntu
sudo zgrep -hE 'Start-Date:|Commandline:|Install:|Upgrade:' /var/log/apt/history.log*

主机已失陷时,本机包数据库和历史文件也可能被篡改,因此还要核对镜像摘要、部署流水线、制品仓库下载记录、配置管理平台和自动化密钥使用记录。若同一镜像创建的多台 Linux 主机同时出现相同文件,问题可能发生在构建阶段,而不是单台主机。

3.2 执行:名字可以伪装,运行上下文更难伪装

判断进程时,至少同时看六个属性:

1
进程名 + 可执行路径 + 父进程 + 启动时间 + 用户 + 网络/文件行为

高风险组合包括:

  • 系统风格名称,却从 /tmp、/dev/shm、用户家目录或隐藏目录运行;
  • Web、数据库或监控进程启动交互 shell;
  • 可执行文件已删除,进程仍长期运行;
  • 父进程为 cron、未知 systemd unit 或异常容器入口;
  • 命令行包含下载后管道执行、编码解码或非常规解释器参数;
  • 进程位于陌生 namespace/cgroup,并映射宿主机敏感目录。

对容器宿主机,必须把 PID 映射回容器:

1
2
3
4
5
6
PID=<可疑PID>

cat /proc/$PID/cgroup
readlink /proc/$PID/ns/pid
readlink /proc/$PID/ns/mnt
systemd-cgls --all

如果使用 Docker、containerd 或 CRI-O,再从对应运行时查询容器 ID、镜像摘要、挂载、特权模式和启动命令。不要只删除容器;如果恶意内容来自镜像或编排配置,新容器会再次生成相同问题。

3.3 权限提升:确认攻击者到达了哪一层信任边界

首先检查账号、sudo、SUID/SGID 和 Linux Capabilities:

1
2
3
4
5
6
7
8
9
getent passwd
awk -F: '$3 == 0 {print $1,$3,$6,$7}' /etc/passwd
getent group sudo
getent group wheel
sudo visudo -c
sudo find /etc/sudoers.d -maxdepth 1 -type f -ls

sudo find / -xdev -type f -perm /6000 -ls 2>/dev/null
sudo getcap -r / 2>/dev/null

-xdev 只检查根文件系统,不会跨越独立挂载的数据盘、容器存储或网络文件系统。应先用 findmnt 列出挂载点,再根据业务范围对其他本地文件系统分别检查;大范围 find 可能产生明显 I/O,优先在磁盘快照或离线副本上执行。

继续检查:

  • sudo/su 日志是否与异常会话吻合;
  • 新增 UID 0 账号、组成员或 sudoers 规则;
  • 非预期 SUID 文件或 cap_setuid、cap_sys_admin 等高风险能力;
  • Docker/containerd socket 是否被普通账号访问;
  • 主机目录、设备或 /proc 是否被特权容器挂载;
  • 内核日志中是否出现崩溃、模块加载、保护机制关闭或提权利用迹象。

“进程当前以 root 运行”并不能单独说明它如何获得 root。需要向前追踪父进程、认证日志、auditd、服务配置、容器权限和漏洞利用时间点。

3.4 持久化:攻击者如何在重启、退出和清理后回来

应按机制而不是只按目录检查。

systemd 与服务管理

1
2
3
4
5
6
7
8
9
systemctl list-unit-files --state=enabled
systemctl list-units --type=service --state=running
systemctl list-timers --all
systemctl --failed

sudo find /etc/systemd/system /usr/lib/systemd/system /lib/systemd/system -type f -printf '%TY-%Tm-%Td %TH:%TM:%TS %u %m %p\n' 2>/dev/null | sort

systemctl cat <可疑unit>
systemctl show <可疑unit> -p FragmentPath -p DropInPaths -p ExecStart -p Restart -p RestartUSec -p User

不能只检查 .service 文件。攻击者还可能使用 .timer、用户级 unit、drop-in、generator 或修改现有服务的 ExecStart。

Cron、at 与用户级任务

1
2
3
4
sudo cat /etc/crontab
sudo find /etc/cron.d /etc/cron.hourly /etc/cron.daily /etc/cron.weekly /etc/cron.monthly -maxdepth 2 -type f -ls 2>/dev/null
sudo ls -al /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
sudo atq

crontab -l 只会显示指定用户的任务。还要依据 getent passwd 枚举可登录账号,检查其用户 crontab;不要只看 root。

SSH 公钥与账号后门

真正决定谁能使用公钥登录本机的通常是 authorized_keys,而不是 known_hosts:

1
2
3
sudo find /root /home -type f -path '*/.ssh/authorized_keys' -printf '%TY-%Tm-%Td %TH:%TM:%TS %u %m %p\n' 2>/dev/null

sudo sshd -T | grep -Ei 'authorizedkeysfile|authorizedkeyscommand|permitrootlogin|passwordauthentication|pubkeyauthentication|allowusers|allowgroups'

MITRE ATT&CK T1098.004 将修改 authorized_keys 作为 Linux 持久化的重要方式。还应检查 sshd_config 的 Include 文件、AuthorizedKeysCommand、账号家目录权限,以及云平台通过实例元数据下发 SSH 公钥的记录。

PAM、启动链和动态链接器

1
2
3
sudo stat /etc/rc.local /etc/ld.so.preload 2>/dev/null
sudo find /etc/pam.d /etc/profile.d /etc/init.d -type f -ls 2>/dev/null
sudo find /root /home -maxdepth 3 -type f '(' -name '.bashrc' -o -name '.bash_profile' -o -name '.profile' -o -name '.zshrc' ')' -ls 2>/dev/null

高权限事件还应检查内核模块、initramfs、udev 规则、包管理 hooks、动态链接器配置和安全模块状态。若这些信任根被改动,不应依赖手工清理恢复可信。

3.5 木马杀掉又启动:不要和子进程玩“打地鼠”

一个恶意进程被终止后几秒或几分钟再次出现,说明背后存在负责拉起它的机制。反复执行 kill -9 只会不断销毁当前进程的内存、连接和父子关系,真正的启动源仍然存在。

常见拉起源包括:

重启特征 可能的启动源
退出后立即出现,PID 改变 systemd 的 Restart=、Supervisor、Monit、runit、s6 或 shell watchdog
每分钟、每小时或固定时间出现 cron、systemd timer、anacron、at
容器停止后自动创建或重新运行 Docker restart policy、Kubernetes Deployment/DaemonSet、容器编排控制器
用户退出后仍会启动 user systemd unit、linger、用户 cron、shell 初始化文件
重启主机后早期出现 initramfs、system service、udev、启动脚本、内核模块
各工具看到的 PID、端口或文件不一致 namespace 隔离、用户态 Hook、Rootkit 或系统工具被替换

第一步:保留一次完整生命周期

先隔离网络,防止木马继续接收命令或外传数据。不要马上终止新进程,先记录它从哪里来:

1
2
3
4
5
6
7
8
9
10
11
PID=<可疑PID>

date --iso-8601=ns
ps -o pid,ppid,user,lstart,etime,stat,comm,args -p "$PID"
pstree -aps "$PID"
cat /proc/$PID/cgroup
ls -al /proc/$PID/ns
readlink /proc/$PID/exe
tr '\0' ' ' < /proc/$PID/cmdline; echo
lsof -nP -p "$PID"
systemctl status "$PID" --no-pager

重点记录旧 PID 的退出时间、新 PID 的出现时间、PPID、可执行路径和 cgroup。优先利用现有日志、EDR 和 systemd 记录还原“退出—重新生成”过程;不要为了观察而主动杀死进程。若完成证据采集后因止损必须终止,再监控它是否重新生成。

如果木马仍在执行破坏操作,可以在网络已经隔离的前提下使用 SIGSTOP 暂停进程,保留其内存和打开文件:

1
sudo kill -STOP "$PID"

暂停不是清除,也不能阻止其他副本运行。完成必要采集后,仍要处理启动源和整条进程树。

第二步:定位真正的拉起者

如果进程属于 systemd unit:

1
2
3
4
systemctl status "$PID" --no-pager
systemctl cat <可疑unit>
systemctl show <可疑unit> -p FragmentPath -p DropInPaths -p ExecStart -p Restart -p RestartUSec -p User
journalctl -u <可疑unit> --since '-30 min' -o short-iso-precise --no-pager

如果重启有固定周期,检查 timer 与 cron:

1
2
3
4
systemctl list-timers --all
sudo crontab -l
sudo ls -al /var/spool/cron /var/spool/cron/crontabs 2>/dev/null
sudo grep -RIn --exclude='*.log' '<文件名或路径片段>' /etc/cron* /var/spool/cron 2>/dev/null

如果父进程是守护或进程管理工具:

1
2
ps -ef | grep -E '[s]upervisord|[m]onit|[s]6-svscan|[r]unsv|[p]m2|[f]orever'
sudo grep -RIn '<文件名或路径片段>' /etc/supervisor* /etc/monit* /etc/systemd 2>/dev/null

如果进程位于容器:

1
2
3
cat /proc/$PID/cgroup
docker ps --no-trunc 2>/dev/null
crictl ps -a 2>/dev/null

继续从可信管理端检查容器的 restart policy、Kubernetes Deployment、DaemonSet、StatefulSet 和静态 Pod。只在节点上 kill 容器进程没有意义,控制器会按期望状态重新创建它;必须隔离节点,并在编排层停止控制器、封禁恶意镜像和修复工作负载配置。

用户级 systemd 也容易被漏掉:

1
2
3
4
5
loginctl list-users
loginctl show-user <用户> -p Linger
# 在该用户的有效登录会话中执行
systemctl --user list-unit-files
sudo find /root /home -path '*/.config/systemd/user/*' -type f -ls 2>/dev/null

如果 pstree 显示由 bash、sh 或脚本循环拉起,应沿 PPID 一直向上追踪,保存整个祖先进程的命令行、工作目录、打开文件和脚本内容。不要只杀最末端的木马进程。

第三步:保留证据后阻断重启链

先保存 unit、cron、脚本、容器配置、日志和样本,并计算哈希。随后根据拉起源执行阻断:

  • systemd:停止并在当前启动周期临时 mask 对应 unit,防止再次启动;
  • cron/timer:禁用具体恶意条目,不要无差别停止全部业务定时任务;
  • Supervisor/Monit/runit/s6:停止对应任务并隔离其配置与执行文件;
  • Docker/Kubernetes:从编排层缩容或停止控制器,取消 restart policy,隔离节点和镜像;
  • shell watchdog:在采集后终止负责循环的祖先进程及其子树;
  • 内核模块、initramfs、PAM 或 Rootkit:保持网络隔离,完成内存和磁盘采集后关闭旧主机,不再尝试原地修复。

systemd 的临时阻断示例:

1
2
sudo systemctl mask --runtime --now <可疑unit>
systemctl status <可疑unit> --no-pager

该操作会改变现场,必须在证据保存后执行并记录时间、操作者和命令。旧主机最终仍按可信重建流程处理。

第四步:证明它没有从另一条通道回来

攻击者经常同时设置两种以上持久化。阻断一个 unit 后,至少继续检查:

  • 是否在已知重启周期内产生新 PID;对 timer/cron 还要覆盖其最长已知周期;
  • 同一哈希、文件路径、命令行和网络目标是否再次出现;
  • 是否换成另一个账号、容器、unit 或临时目录运行;
  • systemd、cron、用户 unit、SSH 公钥、PAM 和内核模块是否还有第二条入口;
  • 网络侧是否仍有来自该主机或关联主机的相同 Beacon。

“进程不见了”只能证明当前副本停止,不能证明持久化已经清除。旧主机应继续隔离,其结果只用于调查,不能作为系统已经恢复可信的证明。

3.6 凭据访问与横向移动:一台主机可能只是跳板

Linux 主机常保存比自身权限更有价值的凭据:SSH 私钥、Agent 转发会话、云实例角色、Kubernetes kubeconfig、数据库口令、CI/CD Token、备份密钥和服务账号配置。

调查目标不是把所有秘密打印出来,而是确认哪些秘密可能被读取或使用:

  • 可疑进程打开过哪些密钥、配置和 socket;
  • auditd 是否记录对敏感文件的访问;
  • 事故期间是否存在异常 ssh、scp、sftp、rsync 或远程管理进程;
  • known_hosts、SSH 配置和运维脚本暴露了哪些潜在横向目标;
  • 同一账号或密钥是否在其他 Linux 主机出现新会话;
  • 云审计日志是否出现异常角色调用、快照、密钥或安全组操作。

known_hosts 只能证明该账号曾见过某个远端主机身份,不能证明登录成功,也不能代替网络和认证日志。

由于主机已经确认失陷,应假设攻击者能够读取该主机可访问的凭据。后续撤销与轮换必须在干净设备上进行,并按依赖关系排序,避免新凭据再次同步到受控主机。

3.7 C2、数据外传与防御规避:攻击者做了什么,带走了什么

从主机侧查看实时连接只是第一步:

1
2
3
4
ss -Hantup
lsof -nP -i
ip route
ip neigh

还要从 DNS、防火墙、代理、流量日志和云网络日志检查:

  • 周期固定、流量很小的 Beacon;
  • 与新注册域名、动态 DNS、代理或匿名网络的通信;
  • 非业务时间的大规模上传;
  • DNS 查询量、子域长度或失败率异常;
  • 主机突然扫描大量内网地址或管理端口;
  • curl、wget、nc、socat、ssh、rclone 等工具与异常时间线重合。

数据外传之前常有归档和暂存。应围绕时间窗检查新增的大文件、压缩包、数据库导出和临时目录,但不能仅凭扩展名下结论:

1
sudo find /tmp /var/tmp /dev/shm /root /home -xdev -type f -newermt '<开始时间>' ! -newermt '<结束时间>' -printf '%TY-%Tm-%Td %TH:%TM:%TS %u %s %p\n' 2>/dev/null

防御规避常见信号包括:

  • 日志在关键时间段突然中断或轮转规律异常;
  • 文件时间早于软件部署时间,或多个文件时间完全相同;
  • /proc/<PID>/exe 显示 (deleted);
  • 进程、端口或文件在不同工具中结果不一致;
  • auditd、SELinux/AppArmor、EDR 或防火墙被停止;
  • /etc/ld.so.preload、PAM、内核模块或 namespace 出现异常。

必须把 Rootkit 和系统工具篡改纳入威胁模型。本机 ps、ss、ls、包数据库乃至内核接口都可能说谎,因此活系统结果只能作为线索,关键结论必须用内存镜像、离线磁盘、网络侧记录和可信基线交叉验证。

四、从“确认失陷”到“掌握全貌”:建立可验证的时间线

4.1 先确定锚点,再向两侧扩展

不要一开始就读取几个月的全部日志。先选一个可靠锚点,例如:

  • EDR 记录的进程启动时间;
  • 防火墙记录的首次外连;
  • SSH 登录成功时间;
  • Web 请求返回异常响应的时间;
  • 文件首次出现在制品或备份中的时间。

以锚点前后 15—30 分钟为窗口,关联 Journal、认证、auditd、应用、DNS 和网络记录:

1
2
3
4
5
6
journalctl --list-boots
journalctl --since '<开始时间>' --until '<结束时间>' -o short-iso-precise --no-pager

journalctl -k --since '<开始时间>' --until '<结束时间>' -o short-iso-precise --no-pager

sudo ausearch -ts '<开始时间>' -te '<结束时间>' -i

systemd 服务名因发行版而异,例如 ssh/sshd、cron/crond。传统日志还可能位于 /var/log/auth.log、/var/log/secure、/var/log/syslog、/var/log/messages、/var/log/cron 和 /var/log/audit/audit.log。

systemd 的 journalctl 文档 支持按时间、启动批次、unit、PID 和可执行文件过滤。若 Journal 未配置持久化,重启前的日志可能只存在于 /run/log/journal/,重启后无法恢复。

ausearch 接受的绝对日期格式受系统区域设置影响,执行前应以本机 man ausearch 或 ausearch --help 为准。不要为了方便关联而修改受害主机时间,应在时间线中记录时钟偏差并统一换算。

4.2 用假设驱动调查,而不是见到什么查什么

每一轮调查都应写出可证伪的假设:

1
2
3
4
5
6
7
8
9
10
11
假设:攻击者通过被盗 SSH 密钥登录 deploy 账号,随后 sudo 到 root。

支持证据:
- 02:11:08 deploy 使用公钥从异常 IP 登录;
- 02:12:03 sudo 日志记录 deploy 执行 /bin/bash;
- 02:12:10 root 启动下载进程。

反证检查:
- 同一时间是否有合法变更单?
- 来源 IP 是否属于堡垒机或管理员出口?
- 公钥指纹是否属于已登记人员?

如果证据推翻假设,就更新方向。不要因为最初告警写着“挖矿木马”,后续所有异常都被强行解释为挖矿。

4.3 给证据标置信度

不同来源的可信度不同:

证据来源 一般置信度 主要限制
独立网络设备、云控制面、集中日志 较高 保留期、采样、时钟偏差
EDR 原始遥测 较高 Agent 可能被停止或绕过
内存镜像、离线磁盘镜像 较高 采集时间和工具正确性
受影响主机上的 Journal/auditd 中等 root 可清理或篡改
history、last、文件时间戳 较低到中等 易缺失、延迟落盘或伪造
单一杀毒标签或文件名 较低 误报、伪装和命名不一致

结论应尽量由两个相互独立的来源支持。例如,本机 ss 显示外连,同时防火墙日志也记录同一五元组;authorized_keys 出现新公钥,同时 auditd 记录写入行为。

4.4 影响面确认不能只查最初那台主机

从已确认 IOC 和行为模式向外扩展:

  • 同一 IP、域名、URL、证书、哈希和文件路径;
  • 同一账号、SSH 公钥指纹、Token 或云角色;
  • 同一镜像、软件版本、部署流水线和配置模板;
  • 同一父子进程关系、命令行和 systemd unit;
  • 同一入口漏洞和对外暴露方式。

IOC 适合快速筛查,但攻击者可以更换文件名和地址。行为搜索通常更稳健,例如“Web 服务启动 shell 后访问公网”“root 向非预期账号写入 authorized_keys”“新 systemd timer 执行用户目录文件”。

4.5 用调查结果形成处置输入

隔离不需要等待所有问题都有答案,但后续清场不能只凭一份恶意文件列表开始。调查阶段至少要交付以下结果:

交付物 必须包含的内容
攻击时间线 首次已知活动、登录、执行、提权、持久化、横向移动、外联和关键处置时间
资产范围表 已确认失陷、疑似受影响和暂未发现异常的主机,以及各自判定依据
身份暴露表 可能泄露的账号、SSH 密钥、Token、证书、云角色和它们可访问的系统
持久化清单 unit、timer、cron、用户服务、容器控制器、脚本、PAM 或内核级机制
数据影响说明 被访问、暂存或可能外传的数据,证据来源及结论置信度
证据索引 文件名、来源主机、采集时间、采集人、SHA-256 和保存位置

进入下一阶段前,团队应能回答:攻击者从哪里进入、何时取得 root、通过什么机制持续运行、哪些凭据可能泄露、触达了哪些主机、是否访问或外传数据,以及还有哪些结论无法证实。

无法回答的问题不能被省略,应作为明确的调查缺口记录。这样后续处置针对的是已知入口、持久化和信任关系,而不是只把当前看得见的木马进程删除。

上一篇
下一篇