strace 默认不捕获 open/openat 等系统调用,需显式指定 -e trace=open,openat,openat2,stat,fstatat 并配合 -f、-s 256、-o 等参数才能完整覆盖敏感文件访问行为。

strace 怎么捕获 open/openat 系统调用
默认 strace 不会过滤系统调用,必须显式指定才能聚焦文件访问行为。直接运行 strace -e trace=open,openat,openat2,stat,fstatat 才能覆盖绝大多数敏感文件探测路径(比如配置、密钥、环境变量文件)。openat2 是较新内核(5.6+)引入的,漏掉它可能错过容器或 sandbox 环境下的绕过行为。
常见错误是只加 -e trace=open,结果看不到通过 openat 相对路径打开的文件(如 /proc/self/fd/3 或 chroot 内路径),也捕获不到 stat 类探针——很多程序先 stat 判断文件是否存在再决定是否读取。
- 加
-f跟踪子进程,否则 fork 出的子进程(如 shell 脚本调用的命令)完全不显示 - 加
-s 256避免文件路径被截断(默认只显示前 32 字符) - 加
-o trace.log保存输出,避免滚动丢失关键行
怎么识别“非法读取”的敏感文件路径
不是所有 open 都可疑,关键看路径是否属于程序权限不该触达的区域。重点关注以下几类:
-
/etc/shadow、/root/.ssh/id_rsa、/proc/*/mem:明显越权,普通用户进程无权读取 -
/proc/self/environ、/proc/self/cmdline:可能用于进程间信息窃取,尤其在容器中暴露父进程环境 -
/sys/kernel/security/下的文件:SELinux/AppArmor 策略相关,非安全模块不应直接读取 - 路径含
.env、secrets.yaml、config.json且位于非应用目录(如从/tmp或/home/otheruser加载)
注意:有些合法行为也会触发类似路径,比如 systemd 服务读 /etc/systemd/system.conf,要结合进程 UID、执行上下文(是否在容器里)、调用栈(用 -k 显示调用函数)交叉判断。
如何减少噪声并定位真实问题
strace 默认输出极多,尤其有日志轮转、临时文件操作时。直接扫屏基本无效,得靠过滤和重放:
- 用
strace ... 2>&1 | grep -E "(open|openat|stat).*ENOENT|EACCES|EPERM"快速筛出失败但意图明显的尝试(比如试图读/etc/shadow被拒) - 对成功打开的敏感路径,用
grep -E "/etc/shadow|/root/\.ssh|/proc/[0-9]+/mem" trace.log提取后,再用awk '{print $NF}'抽出文件名去重统计 - 如果程序启动快、行为短,加
-tt打时间戳,配合timeout 5s strace ...控制捕获窗口
一个容易忽略的点:某些程序(如 Python 的 import)会批量尝试多个路径(./mymodule.so → /usr/lib/mymodule.so → /lib/mymodule.so),这些不算非法,但可能掩盖真正的问题调用。需要结合 read 系统调用是否紧随其后判断是否真读了内容。
strace 捕获到非法读取后下一步做什么
确认非法行为存在后,strace 只是诊断工具,不能修复。接下来动作取决于场景:
- 如果是自己写的程序:检查代码里是否有硬编码路径、未校验的用户输入拼接路径(如
open("/tmp/" + filename))、或误用了getpwuid等函数间接触发/etc/shadow访问 - 如果是第三方二进制:用
readelf -d binary | grep NEEDED查依赖库,再用ltrace看动态库函数调用(比如某加密库内部尝试读/dev/random失败后 fallback 到读文件) - 在容器中发生:检查是否错误挂载了宿主机敏感目录(如
-v /etc:/etc:ro),或 seccomp profile 未禁用openat
最麻烦的情况是调用链很深——比如主程序调 dlopen 加载插件,插件又调另一个库,最终触发非法读取。strace -k 输出的函数栈在 glibc 符号未剥离时有用,但一旦遇到 stripped 二进制,就得结合 perf record -e syscalls:sys_enter_openat 做更底层追踪。











