Go 主机安全面试:Cron 与 at 定时任务持久化检测
Linux 主机被入侵后,攻击者不一定注册 systemd 服务,也可能写入 cron、at、anacron 或用户级定时任务,让后门周期执行、下载脚本或低频反连。面试官通常会追问:Go Agent 怎么采集这些配置?怎么判断异常?怎么避免把正常运维任务误报成攻击?
岗位场景
text
cron / at / anacron 配置
-> Agent 扫描系统级和用户级任务入口
-> 解析执行时间、执行用户、命令、脚本路径和环境变量
-> 关联文件落地、进程启动、网络外连和历史基线
-> 识别异常定时任务、低频后门和持久化线索
-> 输出可复盘证据这类题考的是 Linux 定时任务机制、文件系统取证、命令解析、误报治理和 Go 侧低开销扫描能力。
高频面试题
1. 为什么 HIDS/EDR 要关注 cron 和 at?
简洁答案:cron 和 at 都能让命令在未来自动执行,攻击者可以用它们维持权限、周期下载 payload 或延迟触发反弹 shell。
关键知识点:
- cron 适合周期执行,at 适合一次性延迟执行。
- 攻击者常把命令藏在用户 crontab、
/etc/cron.d、/var/spool/cron或脚本目录里。 - 低频任务不一定立刻产生进程事件,配置落地本身就是重要证据。
Go 落地思路:
- 同时采集系统级目录和用户级 spool,不只看
/etc/crontab。 - 任务事件至少包含
task_path、user、schedule、command、mtime、owner。 - 把配置事件和后续进程、文件、网络事件关联,避免孤立判断。
2. Linux 上常见定时任务入口有哪些?
简洁答案:重点看系统 crontab、/etc/cron.*、用户 crontab spool、anacron 配置和 at 队列。
常见入口:
text
/etc/crontab
/etc/cron.d/
/etc/cron.hourly/
/etc/cron.daily/
/etc/cron.weekly/
/etc/cron.monthly/
/var/spool/cron/
/var/spool/cron/crontabs/
/etc/anacrontab
/var/spool/at/关键知识点:
- 不同发行版的用户 crontab 路径可能不同。
/etc/cron.d格式通常包含执行用户,用户 crontab 通常不包含用户字段。cron.hourly这类目录里可能是脚本,检测时要读取脚本元信息和摘要。
Go 落地思路:
- 用固定目录白名单扫描,不递归全盘。
- 目录不存在是正常情况,记录一次环境信息即可。
- 对单文件大小设上限,避免异常大文件拖慢 Agent。
go
func isCronEntry(path string) bool {
return strings.Contains(path, "/cron") || strings.Contains(path, "/anacron") || strings.Contains(path, "/at")
}3. 可疑 cron 任务通常有哪些特征?
简洁答案:重点看可写目录执行、解释器拉脚本、下载执行、base64 解码、反弹 shell 和伪装命名。
典型特征:
- 命令包含
curl、wget、bash -c、python -c、perl -e、base64 -d。 - 执行文件来自
/tmp、/dev/shm、/var/tmp、Web 上传目录或普通用户隐藏目录。 - 任务名伪装成系统维护任务,但路径、属主或 hash 不可信。
- 计划频率异常,例如每分钟执行下载脚本或每小时尝试外连。
- 修改时间接近 Web RCE、提权或异常登录时间。
Go 落地思路:
- 先用命令形态和路径做候选筛选,再补充 hash、属主、权限和文件来源。
- 输出命中原因,例如
download_execute、temp_path、reverse_shell_token。 - 不要只靠字符串黑名单,至少关联用户、父目录权限和历史基线。
4. Go Agent 如何解析 crontab,避免一上来写复杂解析器?
简洁答案:安全检测先稳定抽取注释、环境变量、时间字段、用户字段和命令字段,不必完整模拟 cron 调度器。
关键知识点:
- 空行和
#注释要跳过。 SHELL=、PATH=、MAILTO=是环境变量,不是任务。/etc/crontab和/etc/cron.d/*一般是“时间字段 + 用户 + 命令”。- 用户 crontab 一般是“时间字段 + 命令”。
Go 落地思路:
- 解析结果保留原始行号,便于客户复盘。
- 识别失败的行不要丢弃,可以作为低置信度原始证据。
- 先按空白切分提取前 5 个时间字段,复杂表达式保留原样。
go
func cronCommand(fields []string, hasUser bool) string {
start := 5
if hasUser {
start = 6
}
if len(fields) <= start {
return ""
}
return strings.Join(fields[start:], " ")
}5. 如何降低 cron 持久化检测误报?
简洁答案:把命令、用户、路径、资产角色、发布来源、历史基线和维护窗口放在一起判断,不要看到新增任务就报警。
常见误报:
- 备份、日志轮转、监控探针和资产管理脚本。
- 运维平台批量下发的定时巡检任务。
- 业务为了清理缓存或报表生成配置的周期任务。
- 安全软件自身的巡检或升级任务。
Go 落地思路:
- 首次上线建立基线,只对后续新增、修改和异常频率提高风险。
- 白名单必须带作用域,例如固定路径、固定 hash、固定用户和过期时间。
- 被白名单压制的命中仍要计数,避免白名单过宽遮蔽真实攻击。
6. 如何把定时任务和攻击链关联起来?
简洁答案:定时任务说明“计划执行什么”,进程、文件和网络事件说明“实际发生了什么”,两者按时间窗口和命令指纹关联。
关键知识点:
- cron 执行时父进程通常是
cron、crond或anacron。 - wrapper 脚本可能继续拉起子进程,不能只看第一层命令。
- 低频任务可能跨天触发,配置修改时间和首次执行时间都要保留。
- PID 会复用,关联时要带进程启动时间和事件时间。
Go 落地思路:
- 生成命令指纹,例如规范化后的命令、脚本路径和 hash。
- 关联字段包含
task_path、line_no、command_hash、process_tree。 - 告警展示从配置落地到进程执行再到网络外连的证据链。
7. at 一次性任务怎么检测?
简洁答案:at 任务适合延迟触发,检测重点是队列文件、执行用户、计划时间和任务内容摘要。
关键知识点:
- at 队列通常位于
/var/spool/at或发行版对应目录。 - at 文件格式和权限依赖发行版,不同系统不能硬编码一种解析方式。
- 攻击者可能用 at 延迟清理痕迹或延迟下载后门。
Go 落地思路:
- 优先采集文件元信息、任务 owner、mtime、计划时间和内容摘要。
- 解析失败时保留 hash 与原始大小,不阻塞其他采集。
- 把 at 任务创建和异常登录、Web RCE、提权事件按时间窗口关联。
8. 客户反馈 cron 告警误报,怎么排查?
简洁答案:先确认任务来源和变更时间,再看命中原因、执行用户、脚本 hash、基线差异和相关进程行为。
排查顺序:
- 确认任务路径、原始行号、执行用户和修改时间。
- 查看命令是否来自可信发布系统、包管理器或运维账号。
- 对脚本路径补充 hash、属主、权限和最近变更记录。
- 关联该任务触发后的进程树和网络连接。
- 检查白名单是否过窄或过宽,并记录规则版本。
Go 落地思路:
- 每条告警带
rule_id、rule_version、risk_reasons和原始证据摘要。 - Agent 输出扫描错误计数,例如权限拒绝、文件过大、解析失败。
- 排障包只导出必要字段,避免上传完整脚本内容和敏感环境变量。
学习要点
| 方向 | 要掌握的内容 |
|---|---|
| Linux 原理 | cron、at、anacron、用户 crontab、spool 目录 |
| 检测工程 | 配置落地检测、命令特征、攻击链关联、基线降噪 |
| Go 实现 | filepath.WalkDir、逐行解析、文件元信息、hash 摘要、错误计数 |
| 客户排障 | 任务来源、维护窗口、白名单作用域、规则版本、证据复盘 |
小练习
- 设计一个
ScheduledTaskFinding结构体,字段至少包含任务路径、行号、执行用户、调度表达式、命令、风险原因和证据摘要。 - 写一个函数解析
/etc/cron.d风格任务行,返回时间字段、用户和命令;空行、注释和环境变量要跳过。 - 设计一条规则:Web RCE 写入脚本后新增 cron 每 5 分钟执行,并在执行后外连公网 IP。列出至少 6 个证据字段。
- 复盘一个误报场景:运维平台下发合法巡检 cron,如何降噪但保留审计记录?
