引言
在我们之前的博客《藏在眼皮底下:Linux中/proc文件系统操纵技术与防御》中,我们探讨了如何将恶意进程从取证审查工具中隐藏的技术。取证分析师通常依赖Linux虚拟文件系统/proc来枚举进程、重建时间线并将活动归因于特定的可执行文件。诸如ps、top和各种审查脚本之类的工具从/proc/<pid>/下的文件中提取进程元数据,包括cmdline和stat。因此,这些文件的完整性对许多事件响应工作流程至关重要。
我们的研究表明,拥有特权访问权限的威胁行为者或某些类型的恶意代码如何操纵/proc视图,以制造误导性的取证伪影。通过改变这个内核接口,威胁行为者可以通过替换显示的命令行参数来混淆进程身份,并破坏诸如记录的进程启动时间等时间数据,从而破坏时间线分析。
关键发现
/proc/<pid>/cmdline可以被替换或编辑以显示任意命令字符串,使进程识别复杂化。/proc/<pid>/stat中的starttime字段可以被修改以产生时间线差异,包括破坏事件关联的未来时间戳。- 由于操纵需要提升的权限或内核修改,成功的篡改是主机严重被入侵的强烈指标。
- 单独依赖
/proc会增加审查假阴性和归因错误的风险;必须与内核和远程遥测数据进行交叉验证。
技术演练
环境与目标
实验在Linux主机上进行,使用了一个从交互式shell运行的最小测试脚本(“evasion”脚本)。目标有两个:
- 演示替换
/proc/<pid>/cmdline如何改变ps/top显示的命令字符串。 - 强调编辑
/proc/<pid>/stat中的starttime字段如何能在转换为人类可读形式时产生时间不一致(包括“未来”)的进程启动时间。
测试过程:小型交互式脚本
目标进程是一个名为evasion的最小脚本:
|
|
启动脚本后,该进程出现在进程列表中(ps aux的示例摘录):
|
|
克隆目标进程的/proc数据
我们创建了一个工作区,并将实时的/proc/<pid>字段复制到该工作区:
|
|
复制后,工作区的内容为空,直到工件被放置在那里。我们的意图是稍后将该文件夹用作绑定挂载的目标。
通过绑定挂载将克隆的数据覆盖到/proc/
通过将/tmp/evil绑定挂载到/proc/18316上,用户空间工具的内核导出视图可以被重定向到被替换的文件:
|
|
绑定后,ps aux不再报告原始的./evasion字符串,因为进程视图已被挂载的工件替换。例如,初始的ps aux显示了该进程,但在挂载工作区后,ps输出不再显示evasion,只出现grep进程。
将编辑后的工件放入挂载的文件夹
接下来,将先前复制的文件写入挂载的文件夹并进行编辑。以下是我们调查期间执行的命令示例。
|
|
演示:替换命令字符串
/tmp/evil中的cmdline文件被编辑为包含一个单独的标记safeprocess:
|
|
经过此替换后,ps aux显示PID 18316的新命令字符串:
|
|
因此,进程名称的实时显示从/usr/bin/bash ./evasion更改为safeprocess,这纯粹是通过替换/proc/18316/cmdline处暴露的cmdline内容实现的。这演示了当攻击者控制导出文件时,基于/proc的识别如何能被轻易欺骗。
Stat工件:原始与解析
stat文件包含许多数字字段。在本实验中,捕获的进程stat行如下:
|
|
根据proc_pid_stat(5),我们可以将这些数字字段转换为更易理解的顺序:
| 字段编号 | 字段名 | 格式 | 字段编号 | 字段名 | 格式 |
|---|---|---|---|---|---|
| (1) | pid | %d | (8) | tpgid | %d |
| (2) | comm | %s | (9) | flags | %u |
| (3) | state | %c | (10) | minflt | %lu |
| (4) | ppid | %d | (11) | cminflt | %lu |
| (5) | pgrp | %d | (12) | majflt | %lu |
| (6) | session | %d | (13) | cmajflt | %lu |
| (7) | tty_nr | %d | (14) | utime | %lu |
| (15) | stime | %lu | (22) | starttime | %llu |
| (16) | cutime | %ld | (23) | vsize | %lu |
| (17) | cstime | %ld | (24) | rss | %ld |
| (18) | priority | %ld | (25) | rsslim | %lu |
| (19) | nice | %ld | (26) | startcode | %lu [PT] |
| (20) | num_threads | %ld | (27) | endcode | %lu [PT] |
| (21) | itrealvalue | %ld | (28) | startstack | %lu [PT] |
解释这些字段需要参考proc(5);特别是,字段(22)是starttime,即进程在系统启动后开始的时间,以时钟滴答数表示。
为了简化解释,实验使用了一个小型辅助程序procstat将数字字段转换为人类可读的形式。以下是一个示例输出:
|
|
将starttime转换为绝对时间
starttime值必须相对于系统启动时间来解释。在本实验中,系统的正常运行时间捕获如下:
|
|
使用原始stat文件中存在的starttime数值(48156368),报告显示该进程在观察点之前大约85秒启动(实验室通过_SC_CLK_TCK将时钟滴答转换为秒,并与正常运行时间进行比较的计算)。procstat工具随后报告start_time: 10.12 22:06 (698.43s),说明了原始stat数据如何转换为人类可解释的形式。
篡改starttime以制造时间假情报
为了说明时间线是如何被操纵的,/tmp/evil/stat中的starttime字段从48156368被编辑为98156368:
|
|
修改starttime并保持cmdline替换后,ps aux现在报告该PID的日历日期相对于实际系统时间而言是未来的。在我们的测试期间,系统日期/时间为:Sun Oct 12 10:24:07 PM +07 2025。更改starttime后,ps输出为PID 18316显示的进程开始日期为Oct18(即未来的日期)。
这演示了编辑stat记录中的starttime数字字段如何导致工具显示误导性的日历时间戳——有效地将进程“移动”到未来并破坏时间线关联。
结论
/proc伪文件系统是进程自省的一个便捷通道,但在对抗性环境中并非不可变的证据存储。特权参与者可以操纵/proc条目以隐藏身份并破坏时间线重建所依赖的时间数据。有效的检测需要将/proc视为众多证据来源之一,并根据源自内核的遥测数据和远程不可变日志验证进程事件。
检测与防御建议
- 监控挂载和卸载系统调用:使用
auditd或eBPF等工具检测可疑的挂载活动。 - 对异常的/proc挂载发出警报:如果
/proc/[pid]被挂载到tmpfs或任何外部文件系统,则生成警报。 - 交叉验证/proc数据:将
/proc条目与内核和远程遥测源(如auditd、EDR内核事件流、systemd日志时间戳和远程日志)进行比较,以维护可信的时间线。 - 限制特权访问:应用最小权限原则,强制执行RBAC,并且只允许加载经过签名或授权的内核模块。
- 实施不可变审计转发:将关键审计日志转发到远程的、一次性写入的存储,以在主机被篡改时保留证据。
- 启用内核完整性控制:在可能的情况下启用安全启动、内核模块签名和主机证明以确保完整性。
- 增强审查工具:更新审查脚本,以标记
/proc与独立遥测源之间的不一致,并默认将/proc条目视为“不可信”。