Go 主机安全面试:Linux 日志清理与审计痕迹破坏检测
攻击者拿到主机权限后,常见动作不是马上横向移动,而是先清理痕迹:删除 shell 历史、截断日志、篡改登录记录、关闭 auditd、清理 journald。面试官通常会追问:Go Agent 怎么发现“日志被动过”?怎么区分正常 logrotate、运维清理和入侵后的痕迹破坏?如何把日志异常和进程链路关联成可解释告警?
岗位场景
Linux 主机
-> 采集 /var/log、journald、auditd、utmp/wtmp/btmp、shell 历史文件状态
-> 标准化文件事件、进程执行、服务状态、登录会话和时间线
-> 识别 truncate、unlink、权限漂移、日志服务停止、异常 vacuum
-> 关联用户、TTY、父进程、sudo、Web RCE、反弹 Shell 等上下文
-> 输出“日志清理/审计绕过/痕迹破坏”告警这类题考的是 Linux 日志体系、文件系统事件、审计链路、攻击后清理手法、误报治理和 Go Agent 的低开销采集能力。
高频面试题
1. 为什么 HIDS/EDR 要检测日志清理行为?
简洁答案:日志清理通常发生在入侵后期,用来破坏溯源证据;它本身不一定等于攻击成功,但和异常登录、提权、Web RCE、反弹 Shell 连在一起时,风险很高。
关键知识点:
- 攻击者常清理
auth.log、secure、messages、wtmp、btmp、.bash_history、journald。 - 痕迹破坏会降低后续取证质量,因此需要尽早告警。
- 正常系统也会轮转、压缩、过期删除日志,不能把所有删除都判为攻击。
- 日志清理检测的价值在于“补证据链”,不是孤立地报一个文件删除。
Go 落地思路:
- 定义统一的
LogTamperEvent,同时表达文件变化、进程、用户、主机角色和相邻安全事件。 - 文件事件只保留必要字段:路径、操作、旧大小、新大小、inode、mtime、进程、uid、容器。
- 规则侧按时间窗口关联,例如 10 分钟内“异常登录 -> sudo -> 日志截断”。
type LogTamperEvent struct {
Path string
Op string
OldSize int64
NewSize int64
PID int
UID uint32
Process string
Timestamp int64
}2. 日志清理常见有哪些技术动作?
简洁答案:主要包括删除日志文件、截断文件内容、覆盖关键记录、修改权限、停止日志服务、清理二进制登录记录和调整时间戳。
关键知识点:
| 动作 | 常见命令或行为 | 检测重点 |
|---|---|---|
| 删除 | rm /var/log/auth.log | unlink、文件消失、非 logrotate 进程 |
| 截断 | > /var/log/secure、truncate -s 0 | 文件大小突然变小 |
| 覆盖 | echo "" > file、脚本重写 | 写入进程和内容长度 |
| 清理会话 | 修改 wtmp、btmp、utmp | 二进制日志大小和 mtime 异常 |
| 关闭审计 | systemctl stop auditd | 服务状态变化和执行者 |
| 清理 journal | journalctl --vacuum-time=1s | 命令参数和删除规模 |
Go 落地思路:
- 不要只匹配命令字符串,优先从文件事件和进程事件两边取证。
- 对关键路径建立 watch 或周期基线,路径集合要小,避免全盘监控。
- 对
logrotate、systemd-journald、包管理脚本建立可信上下文,不简单白名单进程名。
3. 怎么区分正常 logrotate 和攻击者清理日志?
简洁答案:看执行主体、时间窗口、配置来源、文件变化形态和相邻行为。正常 logrotate 通常按固定配置、固定时间、固定进程链路执行;攻击清理更常出现在异常登录或提权之后,并由 shell、解释器、Web 子进程触发。
关键知识点:
logrotate会按配置轮转、压缩、创建新文件,通常伴随.1、.gz文件。- 攻击清理常表现为原文件直接归零、删除后不重建、修改权限或清空部分记录。
- 运维临时清理也可能手工执行,需要结合工单窗口、用户、TTY 和主机角色降噪。
- 不能只用“是否进程名叫 logrotate”判断可信,攻击者可以伪装进程名。
Go 落地思路:
- 采集父子进程链:
cron -> logrotate和sshd -> bash -> truncate风险完全不同。 - 对轮转结果做形态校验:旧文件是否被 rename、压缩文件是否出现、新文件权限是否合理。
- 对同一主机建立日志文件大小和轮转周期基线,异常时间点提高风险分。
正常轮转:
cron/systemd timer -> logrotate -> rename auth.log -> create auth.log
可疑清理:
sshd -> bash -> truncate -s 0 /var/log/auth.log
nginx -> sh -> rm -f /var/log/nginx/access.log4. 主机侧应该采集哪些日志与审计对象?
简洁答案:优先覆盖认证日志、系统日志、审计日志、journald、登录会话记录和 shell 历史文件,再按发行版和客户场景扩展。
关键知识点:
- Debian/Ubuntu 常见
auth.log,RHEL/CentOS 常见secure。 wtmp、btmp、utmp记录登录会话和失败登录,是攻击者常清理对象。auditd记录系统调用和关键文件访问,关闭或规则被删本身就是风险。.bash_history、.zsh_history不是强证据,但能辅助判断交互式痕迹。- journald 存储在
/var/log/journal或/run/log/journal,需要关注 vacuum 和服务状态。
Go 落地思路:
- 根据发行版探测路径,不把所有路径写死。
- 事件模型里保留
source字段,例如file_watch、auditd、journald_status、process_exec。 - 采集 shell 历史时只记录元数据或风险片段,不默认上传全量内容,避免泄露敏感命令。
5. 文件大小变小就一定是日志被清理吗?
简洁答案:不是。日志轮转、服务重启、压缩归档、容器日志截断、磁盘清理工具都可能让文件变小。判断要结合路径、进程、时间、变更幅度和历史模式。
关键知识点:
copytruncate策略会复制后截断原文件,这是合法轮转。- 容器运行时可能对 JSON log 做大小限制和轮转。
- 业务日志可能由应用自行重建,不能和系统安全日志同等处理。
- 一次大小变化风险有限,连续变化或攻击链邻近事件更有价值。
Go 落地思路:
- 只对安全关键日志路径给高权重,普通业务日志默认低权重。
- 文件大小变化规则要有最小阈值和冷却时间,避免高频抖动。
- 结合进程可信度和时间窗口评分,而不是单条件告警。
func suspiciousShrink(oldSize, newSize int64, critical bool) bool {
if !critical || oldSize < 1024*1024 {
return false
}
return newSize >= 0 && newSize < oldSize/10
}6. 如何发现 auditd 被关闭或审计规则被删除?
简洁答案:同时看服务状态、进程存活、配置文件变化、规则列表变化和执行命令。auditd 停止、规则被清空、关键 watch 被删除都可能表示审计绕过。
关键知识点:
systemctl stop auditd、service auditd stop、auditctl -D都值得关注。/etc/audit/rules.d/规则文件变化可能影响重启后的审计能力。auditctl -l能看到当前生效规则,但 Agent 不宜高频执行外部命令。- 客户可能在排障时临时关闭 auditd,需要结合变更窗口和操作者。
Go 落地思路:
- 进程执行事件中识别
auditctl -D、auditctl -e 0、systemctl stop auditd。 - 文件事件中关注
/etc/audit/audit.rules和/etc/audit/rules.d/。 - 低频采样服务状态,失败时不要阻塞主采集链路。
7. 日志清理检测怎样降低误报?
简洁答案:先分清“系统自动轮转”“业务日志治理”“人工运维清理”“攻击后痕迹破坏”,再用主机角色、进程链路、变更窗口和相邻安全事件做分级。
关键知识点:
- 安全关键日志和普通应用日志使用不同规则阈值。
- 有变更单、固定维护窗口、可信自动化账号时可以降级。
- 来自 Web 进程、临时目录脚本、异常登录会话的清理动作要升级。
- 告警解释必须说明为什么不是普通 logrotate。
Go 落地思路:
- 规则输出
Reason列表,而不是只输出风险分。 - 对同类主机学习正常轮转时间,偏离基线再提高置信度。
- 允许客户配置可信账号、维护窗口和业务日志路径,但不要让配置绕过系统安全日志。
8. 客户反馈日志清理告警误报,你怎么排查?
简洁答案:先拿到告警时间线,再核对执行进程、用户、TTY、父进程、文件变化形态、logrotate 配置、审计服务状态和客户变更窗口。
关键知识点:
- 误报排查不能只看命中规则,要看完整证据链。
- 如果是
copytruncate,应能看到归档文件、logrotate 配置和固定调度。 - 如果是人工命令,要确认登录来源、sudo 记录和操作原因。
- 如果是攻击清理,常能看到异常登录、Web 子进程、临时目录脚本、外联或提权事件。
Go 落地思路:
- Agent 提供本地诊断摘要:关键路径状态、最近文件事件、相关进程链、服务状态。
- 服务端保留规则版本、命中字段和降噪决策,方便复盘。
- 排障包只导出必要元数据,避免上传整份日志正文。
学习要点
| 方向 | 需要掌握的内容 |
|---|---|
| Linux 日志体系 | syslog、journald、auditd、utmp/wtmp/btmp、shell history |
| 文件系统事件 | unlink、rename、truncate、chmod、chown、mtime、inode |
| 攻击链还原 | 异常登录、sudo、Web RCE、反弹 Shell、日志清理的时间线 |
| Go 工程实现 | 小路径集合 watch、周期基线、事件标准化、规则解释、敏感信息控制 |
| 误报治理 | logrotate、维护窗口、主机角色、可信自动化、客户排障证据 |
小练习
- 设计一个
LogFileState结构体,能表达路径、大小、inode、mtime、owner、mode 和 hash 摘要。 - 写一个函数判断日志文件是否发生异常截断,要求排除小文件和正常轮转窗口。
- 设计一条规则:SSH 异常登录后 5 分钟内清空
auth.log或secure,生成高危告警。 - 说明为什么
journalctl --vacuum-size不一定是攻击,但为什么需要记录执行者和参数。 - 给出客户误报排查清单,区分 logrotate、人工运维清理和攻击后痕迹破坏。
复盘题
- 日志清理检测为什么不能只看
rm命令? - 如何用文件大小、进程链路和登录事件还原一次痕迹破坏?
copytruncate对日志清理检测有什么干扰?- Go Agent 如何避免把日志正文和敏感命令全量上传?
- 一条高质量日志清理告警应该包含哪些证据?
