一、概述
Linux 取证最反直觉的一点是它没有集中入口。Windows 上 Security.evtx 一个文件就能回答「谁在什么时候登录了什么」,Linux 没有等价物:同样的信息被拆散在文本日志、文件时间戳、utmp 二进制库、shell 历史、包管理器账本,以及只有运行中才存在的 /proc 里。
这个结构直接导致两种失败模式,而且都很隐蔽:
- 只看日志会漏掉一半证据。 文件创建时间、SUID 位、用户 crontab、
authorized_keys 里的公钥——这些不写在任何日志里。清理日志能挡住一部分攻击痕迹,但完全挡不住配置文件类的证据。
- 只看文件时间戳会误判。 Linux 有四个时间戳,
atime 还受挂载选项影响,「文件在某时被访问过」推不出「某人某天读过它」。
还有一个更隐蔽的错误:把 Debian 系的路径套到 RHEL 系上。/var/log/auth.log 在 CentOS 上根本不存在,去那里找认证日志会得到「这台机器没有日志」的错误结论——而真相是它叫 secure。
按「谁记录了它」可以把工件分成六类,下面这张速查表就是按这个维度组织的:
| 类别 |
典型路径 |
记录什么 |
| 系统服务记录 |
/var/log/、/var/log/journal/<machine-id>/、/run/utmp |
谁登录过、哪个服务发生了什么 |
| 账户体系 |
/etc/passwd、/etc/shadow、/etc/group |
账户清单与口令哈希(只复制不破解) |
| 应用配置 |
/etc/ssh/sshd_config、/etc/cron.d/、/etc/systemd/system/ |
服务如何被配置、如何被自动拉起 |
| 用户私有数据 |
/home/<user>/.bash_history、.zsh_history、.ssh/ |
交互行为、登录凭据材料 |
| 包管理器账本 |
/var/lib/dpkg/status、/var/lib/rpm/、/var/log/apt/history.log |
装过什么、什么时候装、哪些是手动装的 |
| 运行时状态 |
/proc/、/tmp/、/var/tmp/ |
活系统才有的进程与连接快照 |
配套的判定信息有两处容易被忽略:/etc/os-release 与 /etc/machine-id 决定家族判定与 journal 子目录定位;/etc/fstab 决定 atime 是否可信。
关于 /etc/shadow:取证对它只做复制与哈希记录,不做破解。离线镜像里它就是一个普通文件,权限位记在 inode 里。
动手之前要先有东西可看。上游是镜像制作与接入——你得先有一份哈希可验证的镜像(见镜像制作),并以只读方式挂载(见只读挂载与安全接入),否则后面每一次「读一下」都会改掉你要证明的东西。这一步要产出三样:发行版家族判定、关键目录的存在性清点、接入未污染证据的验证。下游是按类展开的日志、持久化、SSH、包清单、Shell 痕迹与运行时状态,有了地图才知道哪个方向是空的、哪个方向有料。
「某目录不存在」本身就是结论,而且往往是重要结论。它可能意味着这台机器上从未配置过该服务、被清理过、或路径被人为改写。
答得出来的是这台机器属于哪个发行版家族、哪些关键工件实际存在、哪些路径的缺失本身值得追问、账户体系与自动启动机制的整体轮廓、以及一次接入操作有没有污染原始卷。
答不出来的同样明确:
- 任何具体行为。 地图只告诉你「去
/var/log/apt/history.log 看装了什么」,不解释那行记录的含义。
- 时间线的完整形态。 时间戳的语义、精度与可信度分层需要专门方法,见时间线重建。
- 运行时状态。
/proc 在离线镜像里是空的,这不是「没有进程」,而是「这个证据形态不适用于离线分析」,见proc 虚拟文件系统取证。
- 已删除内容。
/tmp 与 /var/tmp 里已经 unlink 的文件不再出现在目录中,地图层面看不见。
- 跨主机的东西。 单台主机的地图无法回答「它连着谁」,那需要网络侧或服务器侧材料。
走这套流程需要已经完成镜像制作与完整性校验,知道为什么要先验证哈希再分析;能区分 Debian 系与 RHEL 系的基础差异,至少知道认证日志路径在这两族里不同;了解 ext4 的基本概念(inode、目录项、挂载选项),否则无法判断 atime 是否可信,见ext4 结构与 inode;
会使用只读挂载与 loop 设备,理解为什么「先复制再分析」和「直接挂载只读」是两种不同的操作。这份地图是清点清单性质的,具备基本的命令操作能力即可,不需要深度解析能力。
二、核心原理
2.1 Linux 证据按"记录者"分为六类
理解 Linux 取证的关键不是背路径,而是记住每类痕迹由谁写入、什么时候写入。这决定了它的可信度和清除难度。
| 类别 |
记录者 |
典型工件 |
清除难度 |
| 系统服务日志 |
rsyslog / journald |
/var/log/auth.log、/var/log/messages、journal |
★★★ 可 truncate -s 0 |
| 文件系统元数据 |
内核 VFS 层 |
atime/mtime/ctime/crtime、SUID 位、目录 mtime |
★★★★ 需改系统时间或卸载卷 |
| 用户行为痕迹 |
Shell 自身 |
~/.bash_history、.viminfo、.mysql_history |
★★ 用户一键 history -c |
| 子系统账本 |
包管理器 / 登录记账 |
dpkg 状态、/var/lib/rpm、utmp/wtmp/btmp |
★★~★★★ 需 root 且会留不一致 |
| 运行时状态 |
内核 /proc |
进程命令行、环境变量、打开的 fd、网络连接 |
★★ 进程退出即消失 |
| 配置与自启 |
管理员 / 攻击者 |
crontab、systemd unit、ld.so.preload |
★ 攻击者不会清理自己 |
实战推论:反取证通常是从上往下清的——先 history -c,再清日志文件,最后删自启项。而篡改 ld.so.preload、在 crontab 里留后门的成本几乎为零,且不写任何日志。所以「配置与自启」这一类的价值密度最高,见第 04 篇。
2.2 关键目录速查表
这是本篇最实用的一张表。 拿到镜像后按图索骥:
| 路径 |
内容 |
取证价值 |
参见 |
/var/log/ |
系统与服务日志 |
★★★★★ |
02 |
/var/log/journal/ |
systemd journal(二进制) |
★★★★★ |
02 |
/var/log/wtmp / /var/log/btmp |
成功 / 失败登录记录 |
★★★★★ |
05 |
/run/utmp |
当前在线会话(重启即清空) |
★★★★ |
05 |
/var/log/apt/history.log |
谁在何时装/卸/升级了什么 |
★★★★★ |
07 |
/etc/passwd、/etc/shadow |
账户与口令哈希 |
★★★★★ |
本篇 3.2 |
/etc/ssh/sshd_config |
SSH 服务端配置 |
★★★★★ |
05 |
/etc/cron.d/ |
系统级 crontab 片段(无用户字段) |
★★★★★ |
04 |
/var/spool/cron/crontabs/<user> |
各用户 crontab |
★★★★★ |
04 |
/var/spool/cron/atjobs/ |
at / batch 一次性任务队列 |
★★★★ |
04 |
/etc/systemd/system/ |
管理员自定义 unit(含 .wants/) |
★★★★★ |
04 |
/etc/ld.so.preload |
动态链接器劫持(存在即高度可疑) |
★★★★★ |
04 |
/home/<user>/.bash_history |
命令历史(默认无时间戳) |
★★★★★ |
03 |
/home/<user>/.zsh_history |
zsh 历史(带时间戳) |
★★★★★ |
03 |
/home/<user>/.ssh/ |
authorized_keys、known_hosts、config |
★★★★★ |
05 |
/root/.ssh/authorized_keys |
root 的授权公钥(含密钥植入) |
★★★★★ |
05 |
/tmp/、/var/tmp/ |
临时文件,/var/tmp 常驻盘 |
★★★★★ |
本篇 2.5 |
/proc/ |
运行时快照,离线读不到 |
★★★★★ |
06 |
/var/lib/dpkg/status |
Debian 已安装软件清单 |
★★★★★ |
07 |
/var/lib/rpm/ |
RHEL 已安装软件清单 |
★★★★★ |
07 |
⚠️ /etc/shadow 只能以 root 读取。 离线分析时它只是一个普通文件,权限位记录在 inode 里;取证不需要破解它,只需要复制并记录哈希。在线取证用 sudo cp 而不要 cat,避免终端滚屏泄漏。
2.3 发行版差异:最常见的错误来源
拿到镜像第一件事是判断发行版家族。 判错会导致整条证据链走错目录。判定方法按可靠度排序:
- 读标识文件(最可靠):
/etc/os-release 字段标准化,/etc/debian_version 与 /etc/redhat-release 各自明确。
- 看包管理器数据库:
/var/lib/dpkg/ 存在即 Debian 系,/var/lib/rpm/ 存在即 RHEL 系。
- 看认证日志文件名:
auth.log vs secure(辅助判据——管理员可自定义 rsyslog 位置,见第 02 篇)。
| 对比项 |
Debian / Ubuntu |
RHEL / CentOS / Rocky / Alma |
| 认证日志 |
/var/log/auth.log |
/var/log/secure |
| 系统总日志 |
/var/log/syslog |
/var/log/messages |
| 包管理数据库 |
/var/lib/dpkg/status |
/var/lib/rpm/Packages |
| 安装历史 |
/var/log/apt/history.log |
/var/log/dnf.log、/var/log/yum.log |
| init 脚本(遗留) |
/etc/init.d/ |
/etc/rc.d/init.d/(软链到 init.d) |
| 安装器 |
apt / apt-get / dpkg |
dnf(8+)/ yum(7)/ rpm |
登录成功/失败账本(wtmp/btmp/utmp)与自定义 unit 目录(/etc/systemd/system/)两系相同。其他家族:Arch/Alpine(/var/log/messages、apk)、SUSE(/var/log/messages、zypper)。以 /etc/os-release 为准,不要靠猜。
2.4 时间戳在 Linux 上的特殊性
Linux 有四个时间戳,Windows 只有三个:
| 时间戳 |
含义 |
什么时候变 |
atime |
访问/读取时间 |
文件被读时(受 relatime/noatime 影响) |
mtime |
内容修改时间 |
文件数据被写时 |
ctime |
inode 状态变更 |
权限、属主、链接数、mtime 任一变化时 |
crtime |
创建时间(ext4 特有) |
文件创建时写入,此后不变 |
取证含义:
ctime 变不等于内容被改。chmod、chown 都会更新它——改权限不改内容会留下 ctime 但不动 mtime,这是识别该手法的判据。
atime 的可信度受挂载选项支配:relatime 下若 atime 与 mtime/ctime 相差超过 24 小时则不更新;noatime 下完全不更新。读一次 stat 就可能改掉证据。详见第 08 篇。
2.5 /tmp 与 /var/tmp:两个都要查
/tmp 可能是 tmpfs(内存),systemd-tmpfiles 会按 age 清理;若是 tmpfs,重启即全部丢失。/var/tmp 通常是磁盘,同样受 tmpfiles age 约束,但重启不丢,是更常见的持久暂存地。
判定 /tmp 是否为 tmpfs(离线镜像上从 /etc/fstab 判断,不能靠「通常是内存」的经验):
grep -E ' /tmp | /var/tmp ' /mnt/df/etc/fstab
tmpfs /tmp tmpfs defaults,noatime,mode=1777 0 0 ← 内存,重启丢失
/var/tmp 无独立行 → 属根分区,持久
/tmp 的 mode=1777(world-writable + sticky bit)意味着任何用户可写。这使它成为恶意载荷的首选暂存地,也是「文件属主为 root 但 mtime 很新」这类异常的高发区。
2.6 取证环境准备:dd 后的只读挂载
镜像制作完成后,第一步是建立只读接入环境。 挂载本身会写盘(更新 atime、可能回放 journal),必须控制住。
步骤 1:确认 ext4 分区起始扇区(示例:Slot 03, Start 1181696)
mmls /work/DigiForensics.dd
步骤 2:只读挂载——ro / noload / noatime 三项缺一不可
sudo mkdir -p /mnt/df
sudo mount -o loop,ro,offset=1181696,noload,noatime /work/DigiForensics.dd /mnt/df
│ │ │ │ └─ 禁止更新 atime(否则 stat 本身就在改证据)
│ │ │ └─ 禁止回放 ext journal,保持崩溃现场原始状态
│ │ └─ 只读
└──────┴─ 建立 loop 设备
步骤 3:验证 + 记录镜像哈希(证据链要求)
findmnt -no OPTIONS /mnt/df
sha256sum /work/DigiForensics.dd | tee /evidence/evidence.sha256
为什么 noload 是关键:ext4 有 journal,操作系统挂载时会回放未提交事务以修复文件系统——这会改变 inode 时间戳和目录结构。取证要的是原始的崩溃现场,不是修复后的整洁状态。
| 需求 |
方式 |
说明 |
| 完整浏览(首选) |
mount -o ro,noload,noatime |
看不到已删 inode,但目录树完整 |
| 查已删除 inode |
debugfs 离线,不挂载 |
直接读镜像,见《ext4 结构与 inode》 |
| 需要 inode 号对照 |
fls / istat 直接读镜像 |
不挂载,inode 号天然对应 |
⚠️ 绝不使用 mount -o rw 或 debugfs -w。也不要用 GUI 文件管理器浏览挂载点——它会生成缩略图、访问文件(改 atime)。
三、操作步骤
3.1 第 1 步:确认发行版家族(决定后续所有路径)
已挂载到 /mnt/df
grep -E '^(NAME|VERSION_ID|ID)=' /mnt/df/etc/os-release
ls /mnt/df/etc/*release /mnt/df/etc/debian_version 2>/dev/null
包管理器侧交叉验证(两条命令只有一条会成功)
ls -d /mnt/df/var/lib/dpkg 2>/dev/null && echo "→ Debian 系"
ls -d /mnt/df/var/lib/rpm 2>/dev/null && echo "→ RHEL 系"
认证日志路径由上一步决定
ls -l /mnt/df/var/log/auth.log 2>/dev/null # Debian 系
ls -l /mnt/df/var/log/secure 2>/dev/null # RHEL 系
3.2 第 2 步:账户与口令材料清点
账户清单(含 UID、GECOS、登录 shell)—— 非敏感,可直接读
cut -d: -f1,3,4,6,7 /mnt/df/etc/passwd
口令哈希文件(只记录元数据与哈希,不尝试破解)
stat -c '%n size=%s mtime=%y mode=%a' /mnt/df/etc/shadow
sha256sum /mnt/df/etc/shadow /mnt/df/etc/passwd | tee /evidence/acct-hash.txt
UID 0 的账户 —— 除 root 外出现即异常
awk -F: '$3==0 {print