“谁做了什么”究竟留在哪里
一句话总结
检测不是从规则开始的,而是从这个事件最初有没有被记录在某个地方开始的。在 Linux 主机上,这样的地方有三处,三处所看到的内容互不重叠——内核审计(auditd)看系统调用,journald 看程序自己说出来的内容,Kubernetes 审计日志看进入 API 服务器的请求。
为什么需要它
假设凌晨三点,一个计划任务向外发出了请求。早上上班后要证明这句话,需要弄清四件事:什么被执行了,是谁预约了它,这项预约是什么时候产生的,以及那个请求去了哪里。
在这里,人们最常撞上的墙是“日志都收集了,却没有答案”。因为收集日志和留下能回答问题的记录是两回事。/var/log/syslog 里会留下 cron 执行了什么,但那个 cron 文件是谁在什么时候创建的却没有。反过来,内核审计会准确记录文件被创建的那一刻,但没有设置监视规则的路径,它根本不看。
所以检测工程的第一件事不是编写规则,而是源头调查。先写下问题,再逐个确认能回答这些问题的记录现在是否在留存。如果没有留存,让它留下记录就是第一项工作,在这之后才有资格编写规则。
工作原理
内核审计子系统接受两种规则。一种是文件监视,一种是系统调用过滤器。
-w /etc/cron.d -p wa -k cron_change
└ 경로 └ 권한 └ 검색용 키
이 디렉터리에 쓰기(w)나 속성 변경(a)이 일어나면 남긴다
-a always,exit -F arch=b64 -S execve -F exe=/usr/bin/curl -k outbound_exec
└ 언제 └ 아키텍처 └ 시스콜 └ 추가 필터 └ 키
64비트 execve 중 실행 파일이 curl 인 것만 남긴다
该代码块中的韩文注释依次说明:第一条规则由路径、权限和检索用键组成,对该目录发生写入(w)或属性变更(a)时就会留下记录;第二条规则由时机、架构、系统调用、附加过滤条件和键组成,只记录 64 位 execve 中可执行文件为 curl 的事件。
-w 其实是系统调用规则的简写形式,用 -k 附加的键,之后会成为 ausearch -k 的检索键。规则语法和过滤字段见 auditctl(8),写在规则文件中的格式见 audit.rules(7)。手动敲 auditctl 加入的规则在重启后会消失,所以要让它保留下来,就写到 /etc/audit/rules.d/ 中,让 augenrules(8) 把它们合并后加载。读取时,ausearch(1) 会按键、用户、时间进行筛选,aureport(8) 给出汇总。
journald 的性质完全不同。它记录的不是内核观察到的内容,而是程序自己说出来的内容。所以 cron 留下的 (root) CMD (…) 这一行,是因为 cron 够友好才有的,并不是内核所保证的。不过 journald 同时带有结构化字段——_PID、_UID、_SYSTEMD_UNIT、SYSLOG_IDENTIFIER 之类,列表见 systemd.journal-fields(7)。用 journalctl -o json 导出,这些字段会原样出现,机器可以读取(journalctl(1))。
Kubernetes 是另一个层面。发生在 API 服务器前面的事情不会留在主机日志中。审计日志默认是关闭的,要通过 --audit-policy-file 提供策略才会开启。策略为每条规则选择级别:None 不记录,Metadata 只记录请求者、时间、资源、动词,Request 连请求体也记录,RequestResponse 连响应体也记录。阶段也有四个——RequestReceived、ResponseStarted、ResponseComplete、Panic。这个选择直接就是成本,也是证据的分辨率。
在现场相遇的样子
第一种情形是“有规则,但位置不对”。常见的主机是监视 /etc/cron.d,却不监视 /usr/local/bin。攻击者不需要新建计划任务——只要把已经被预约的脚本内容改一行就行。那一刻审计日志是安静的。安静不代表安全,而是说明没有在看那个位置。
第二种是“有记录,但读不懂”。内核审计留下的是人很难读的数字。uid 是数字,系统调用也是数字。ausearch -i 会把它们换成名称,我见过好几个团队不知道这个选项,只盯着原始记录看,最后得出“我们的审计日志没用”的结论。
第三种是 Kubernetes 一侧。节点被攻破时,该节点的 kubelet 证书也会一并被带走。主机日志只能看到进程读取了文件,而用那张证书在集群里做了什么,只有 API 服务器审计日志里才有。如果无法把这两份记录接起来,调查就会止步于“有一个文件被读取了”。
下一项实验要做什么
实验中要直接进入一台 VM,设置审计规则,故意制造事件,并用 ausearch 取出相应的记录。接着在 journald 一侧重新找到同一个事件,比较两份记录各自有什么。最后选一个没有监视的位置,亲自确认没有留下记录,然后用新的键添加覆盖该位置的规则。Pod 里没有任何内核权限,所以本实验在 VM 中运行。