Go 主机安全面试:Linux 无文件执行与 memfd 检测
Linux 主机被入侵后,攻击者不一定把 payload 写成普通磁盘文件,也可能通过 memfd_create、管道、解释器内存加载或删除已执行文件来降低落地痕迹。面试官通常会追问:Go Agent 怎么发现无文件执行?哪些证据可信?如何避免把正常运行时、升级流程或临时文件误报成攻击?
岗位场景
进程执行事件
-> 采集 exe、cmdline、fd、maps、文件 hash、父子进程和用户上下文
-> 识别 memfd、deleted executable、匿名映射和解释器内存加载线索
-> 关联网络外联、临时目录落地、提权命令和历史基线
-> 输出可复盘的无文件执行告警这类题考的是 Linux 进程取证、procfs、系统调用、攻击链关联、误报治理和 Go 侧低开销采集能力。
高频面试题
1. 什么是无文件执行,为什么 HIDS/EDR 要关注?
简洁答案:无文件执行是指 payload 不以稳定磁盘文件形式存在,或执行后立即删除文件,目的是绕过基于文件 hash、路径和落地扫描的检测。
关键知识点:
memfd_create可以创建匿名内存文件,再通过/proc/<pid>/fd/<n>执行。- 执行中的文件被删除后,
/proc/<pid>/exe常能看到(deleted)线索。 - 脚本解释器、动态链接器、
bash -c、python -c也可能承载无文件或弱落地行为。
Go 落地思路:
- 不只依赖文件扫描,要把进程执行事件作为主线。
- 事件字段至少包含
pid、ppid、uid、exe、cmdline、cwd、fd_targets、maps_summary。 - 告警要保留证据快照,因为进程退出后
/proc证据会消失。
2. Linux 上哪些证据能提示 memfd 或 deleted executable?
简洁答案:重点看 /proc/<pid>/exe、/proc/<pid>/fd/*、/proc/<pid>/maps 和执行事件里的路径形态。
常见线索:
/proc/<pid>/exe -> /memfd:<name> (deleted)
/proc/<pid>/exe -> /tmp/.x (deleted)
/proc/<pid>/fd/5 -> /memfd:<name> (deleted)
/proc/<pid>/maps contains /memfd:<name>
cmdline contains /proc/self/fd/<n>关键知识点:
(deleted)不等于一定恶意,升级、临时文件和容器运行时也可能出现。memfd名称可伪装,不能只依赖 name。maps可以补充可执行映射,但采集成本比读exe更高。
Go 落地思路:
- 先读
/proc/<pid>/exe和少量 fd 链接,命中风险线索后再采集maps。 - 对短生命周期进程,优先在 exec 事件到达时立即快照。
- 解析失败要保留错误类型,例如权限不足、进程已退出、路径不存在。
3. 为什么只看 (deleted) 容易误报?
简洁答案:很多正常软件升级、包管理替换、临时执行和容器镜像层切换都会产生 deleted executable,必须结合上下文评分。
关键知识点:
- 系统升级时旧二进制仍在运行,路径会显示
(deleted)。 - 临时目录执行可能是正常安装脚本,也可能是恶意 payload。
- 容器内路径和宿主机路径需要区分,否则会把容器行为误判成宿主机入侵。
Go 落地思路:
- 评分维度包括路径、签名或 hash、父进程、用户、命令参数、网络外联和历史基线。
- 对包管理器、已知升级进程、可信发布路径设置低风险标签,不直接静默放行。
- 告警等级用分层结果,例如
suspicious、high、critical,方便运营降噪。
4. Go Agent 如何低开销采集这些进程证据?
简洁答案:用事件驱动优先,定时巡检兜底;先采集低成本字段,命中可疑条件后再补充高成本证据。
关键知识点:
- eBPF、auditd 或 netlink 可以提供进程执行触发点。
- procfs 适合补充快照,但全量高频扫描会带来 CPU 和 IO 压力。
- fd 和 maps 数量可能很大,需要限制采集范围。
Go 落地思路:
func suspiciousExePath(path string) bool {
return strings.Contains(path, "/memfd:") || strings.Contains(path, " (deleted)")
}- exec 事件进来后先读取
exe、cmdline、cwd、少量 fd。 - 只有
exe、cmdline或父进程命中规则时,再采集maps摘要。 - 对每个进程设置采集预算,例如 fd 最多读取前 64 个,maps 只提取可执行匿名映射。
5. 如何把无文件执行放进攻击链还原?
简洁答案:不要把 memfd 当成孤立告警,要关联前置入口、payload 加载、权限变化、网络连接和后续持久化动作。
关键知识点:
- 常见链路可能是 Web RCE -> 下载或内存加载 -> memfd 执行 -> 反弹 shell。
- 父子进程能解释来源,例如
nginx -> sh -> memfd比systemd -> trusted daemon更可疑。 - 网络连接和 DNS 查询能补齐外联证据。
Go 落地思路:
- 用
agent_id + boot_id + pid + start_time做进程实体键,避免 pid 复用。 - 事件标准化后按时间窗口聚合,保留父子关系和关键证据。
- 输出告警时给出最短可读链路,而不是堆所有原始事件。
6. 面对客户说“这是误报”,你怎么排查?
简洁答案:先确认进程来源和证据完整性,再用基线、包管理记录、父子进程、网络行为和文件摘要判断是否正常。
关键知识点:
- 先确认采集时间、主机、pid start time,避免查错进程。
- 看父进程是否来自包管理、部署系统、容器运行时或可信安全软件。
- 看 deleted 文件原路径是否属于正常升级路径,是否伴随异常外联。
Go 落地思路:
- 为每条告警保存
raw_evidence,包含 exe readlink、cmdline、fd 摘要和 maps 摘要。 - 支持离线 replay,把同一批事件重新跑规则,验证是否规则阈值过低。
- allowlist 应限定条件,例如 hash + signer + parent + path,避免只按进程名放行。
7. 如何设计面向无文件执行的检测规则?
简洁答案:用多条件组合规则,基础线索负责召回,行为上下文负责提准。
规则示例:
if exe contains "/memfd:" and parent in [nginx, apache2, php-fpm, java]
and outbound_connection within 60s
then high severity关键知识点:
- 单一条件适合打标签,不适合直接高危告警。
- 规则应区分 Linux 发行版、容器、业务部署方式和安全软件行为。
- 阈值要能通过历史样本回放验证。
Go 落地思路:
- 把检测拆成 tag、score、decision 三层,便于解释和调参。
- 规则命中结果保留每个加分项,方便面试时说明可解释性。
- 性能上优先用字符串前缀、包含判断和预编译正则,避免热路径复杂解析。
通俗答案
无文件执行不是“完全没有文件”,而是攻击者尽量不留下稳定、可扫描、可复查的磁盘 payload。HIDS/EDR 的 Go Agent 要做的是在进程还活着时抓住短暂证据:exe 指向哪里、fd 里有没有 memfd、maps 是否有匿名可执行映射、父进程是谁、后面有没有外联。检测时不能看一个关键词就报警,要把路径、用户、父子进程、网络和历史基线合起来判断。
Go 落地思路
采集层:exec 事件 + procfs 快照 + 网络事件
标准化层:统一 pid、start_time、exe、cmdline、fd、maps、container_id
检测层:memfd/deleted 标签 + 行为评分 + 误报压制
证据层:保存 readlink、参数、映射摘要和关联链路
验证层:样本回放 + 客户现场证据复盘实现时保持简单:先把 exe、cmdline、父子进程和网络关联做好,再按命中条件补充 fd/maps。不要一上来全量扫描所有进程的所有 fd 和 maps。
学习要点
- 理解
memfd_create、/proc/<pid>/exe、/proc/<pid>/fd、/proc/<pid>/maps的取证价值。 - 明白
(deleted)是风险线索,不是恶意结论。 - 掌握 pid 复用、短生命周期进程、权限不足和容器路径带来的工程问题。
- 能用 Go 写低开销采集逻辑,并为规则命中保留可解释证据。
- 能从 Web RCE、反弹 shell、提权和持久化角度还原攻击链。
小练习
- 设计一个
ProcessEvidence结构体,覆盖 memfd 检测所需的最小字段。 - 给出 3 条可能误报的 deleted executable 场景,并说明如何降噪。
- 写一个函数判断 exe path 是否包含 memfd 或 deleted 线索,再补充一条父进程评分规则。
- 解释为什么
pid + start_time比单独使用 pid 更适合做进程关联。 - 如果客户只提供一条 memfd 告警,你会要求补哪些证据来判断是否真实攻击?
