什么是audit
Linux下的audit框架提供可靠的搜集系统信息的服务,任何与安全相关(或无关)的事件都可被此框架搜集和审计。通过audit,人们可以更加详细地了解系统上发生的事情,然而,该框架并不提供额外的安全措施,在安全事件中,它仅仅是帮助人们更加清晰地认识整个事件的始末,此后亡羊补牢,为时未晚。
简单地说,audit审计框架的工作就是监听来自内核的事件报告,并将它们写入日志文件中。
而较为系统地说,audit框架的组件结构如图所示:
安装
内核层的audit服务在Linux(4.18及以后)、Linux-lts(4.19及以后)、Linux-zen(4.18及以后)和Linux-hardened等版本中默认可用。如用户使用自行编译的内核,需要在编译中设置参数 CONFIG_AUDIT
。
我们通过查看内核编译参数,可以看到系统对audit内核审计的支持情况,以CentOS 7为例:
用户层的audit服务可以通过普通的软件安装方式获取,可以使用 systemctl
或 service
命令来控制服务的启停。
audit内核审计的消息可以由应用或系统活动来触发,而 audit daemon(audit守护进程) 是audit框架中的重要组成部分,它负责处理内核生成的消息,例如将这些消息写入日志中或选择性地忽略。
我们可以通过若干命令和文件来控制 audit daemon :
- auditctl :直接控制守护进程的行为,如添加规则等;
/etc/audit/audit.rules
:内含audit守护进程的规则和各种变量;- aureport :生成系统活动的审计报告;
- ausearch :在审计日志中对各类事件进行搜索;
- auditspd :用于将事件消息通知给其他应用的守护进程,有了此进程,事件就不需要通过磁盘上的audit日志来进行传递了;
- autrace :用于追踪某一进程的命令,类似
strace
; etc/audit/auditd.conf
:关于日志记录的配置文件。
添加规则
基础
注意,audit框架对事件的审计是全面而细致的,添加规则之前,我们需要对规则有充分的认知,否则,可能仅仅由于一条规则,我们的日志文件就被瞬间写满了。
audit规则的加载可以通过命令行工具 auditctl
或使用命令 auditctl -R /etc/audit/audit.rules
从规则文件中批量读取。
通过 auditctl
命令添加的规则会在系统重启之后丢失。如希望使规则持续生效,就将它们写入 /etc/audit/audit.rules
文件中,通过命令 rcauditd resatrt
重启audit服务使规则文件生效。
对文件和目录访问的审计
监控某一特定文件的访问行为属于audit框架的基础用法。使用 -w
选项(表示”watch“)来指定你要监控的文件或目录。最常见的规则是监控对 passwd
文件的访问:
# auditctl -w /etc/passwd -p rwxa
也可以监控对某一路径的访问:
# auditctl -w /etc/security/
上面给出的第一条规则监控了对 etc/passwd
文件的所有读操作 r
、写操作 w
、执行 x
和属性变更操作 a
,此类规则指定的文件需要事先存在;
第二条规则监控了对 etc/security
目录的所有访问,默认情况下,目录相关的规则只记录这一目录下的文件创建/删除,希望对其下的文件进行进一步的监控,就使用第一条规则来指定具体文件。
可以通过以下命令来查看所有处于活动状态的规则:
# auditctl -l
可以通过以下命令来删除 所有 规则:
# auditctl -D
当确认规则无误之后,可以将它们添加到 /etc/audit/audit.rules
文件末尾:
-w /etc/passwd -p rwxa
-w /etc/security/
对系统调用的审计
通过 -a
选项可以对系统调用进行审计。
例如,对 chmod
调用进行审计,以检测文件属主的变更:
# auditctl -a entry,always -S chmod
可以通过 syscalls 页面找到所有系统调用。
对系统调用进行审计时,可以使用 -F
来添加对某个键值的筛选,例如:
# 审计所有第二个参数为 4 的 access 调用
-a entry,always -S access -F a1=4
# 审计所有不成功的 open 调用
-a exit,always -S open -F success!=0
下面这条规则也使用了 -F
选项,但是它的审计内容较为特殊:
-a task,always -F auid=0
当一个进程被创建或克隆时, task 规则就起作用了。在上面这条示例中,我们将选中所有具备 auid=0
的任务,并在它们发生时进行记录。每个进程都会在用户登录时被赋予一个 audit ID ,这一ID不随着用户身份的改变而改变,且子进程会继承父进程的 auid
,通过这个ID我们可以唯一确定原始用户。
对无用消息的过滤
在日志记录过程中,有必要避免对不重要的消息进行记录,从而既方便我们对日志进行分析,也减小日志文件的体积。
可以添加如下形式的规则来对部分消息进行过滤:
# 以下规则添加在 /etc/audit/rules.d/quiet.rules 文件中
---
-A exclude,always -F msgtype=SERVICE_START
-A exclude,always -F msgtype=SERVICE_STOP
-A exclude,always -F msgtype=BPF
audit 日志
日志的基本格式
默认情况下,audit日志存放在 /var/log/audit
目录下。下面先看一条典型的audit日志示例:
# 命令执行审计
type=SYSCALL msg=audit(1234874638.599:5207): arch=c000003e syscall=2 success=yes exit=4 a0=62fb60 a1=0 a2=31 a3=0 items=1 ppid=25400 pid
=25616 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=1164 comm="less" exe="/usr/bin/less" key="doc_log"
type=CWD msg=audit(1234874638.599:5207): cwd="/root"
type=PATH msg=audit(1234874638.599:5207): item=0 name="/var/log/audit/audit.log" inode=1219041 dev=08:06 mode=0100644 ouid=0 ogid=0 rdev=00:00
这是执行了命令 less /var/log/audit/audit.log
之后得到的日志,audit通过一事件产生了 三条 消息,这三条消息紧密联系,单独拎出任何一条来看都是没有意义的。
我们逐一解释这些日志中的字段。首先是第一条日志,它呈现了如下的信息:
-
type :事件的类型。第一条消息中的
type=SYSCALL
表明这一消息是由系统调用触发;CWD
表明这一消息记录了命令执行时的工作目录(current working directory);PATH
消息记录了传递给系统调用的路径。注意,PATH
消息仅仅是忠实地记录了命令执行时传递的路径参数,对于一个相对路径,我们需要结合CWD
来分析出绝对路径。 -
msg :包裹在小括号之间的内容,表示消息ID。它由两部分组成,冒号
:
前的部分是UNIX时间戳,冒号后的部分是真实的事件ID。同一个系统调用触发的所有消息都具备相同的ID,便于我们进行关联分析。 -
arch :执行系统调用的CPU架构。在使用
ausearch
命令搜寻日志时(下文会提到),使用-i
选项来解码这一字段的值。 -
syscall :记录了系统调用的编号,一般从
/usr/include/asm/unistd.h
文件中得出,也因此可能随着架构的不同而改变。本例中,syscall=2
表示less
程序所使用的open
系统调用。 -
success :事件是否成功。
-
exit :系统调用的返回值,随着不同系统调用的实现而改变。
-
a0~a3 :系统调用的前四个参数,以数字形式呈现。这些值也会随着不同系统调用的实现而不同。在本例中,我们记录到如下的值:
a0=62fb60 # 文件路径字符串的地址 a1=0 # 标志位 a2=31 # 工作模式 a3=0 # 未使用
-
items :传递给本应用的字符串个数。
-
ppid :父进程ID。
-
pid :进程ID。
-
auid :在 对系统调用的审计 一节中提过的 audit ID ,可以作为用户的标识。
-
uid :启动本进程的用户ID,
0
表示root用户。 -
gid :启动本进程的用户组ID。
-
euid,suid,fsuid,egid,sgid,fsgid :略。
-
tty :应用启动时使用的终端,本例中使用了一个 伪终端(pseudoterminal) 。
-
ses :登录会话ID。用户登录时将为这一属性赋值,可以用于将进程活动与某一次登录事件联系起来。
-
comm :任务列表中呈现的应用名称。
-
exe :解析得到的二进制文件路径。
-
subj :记录本进程是否属于某个安全上下文,例如在AppArmor中,如果定义了本进程的安全上下文,则其属性也将得到记录。
-
key :如果需要对大量的文件或目录进行监控,可以在规则中添加这一字段,方便后续使用 ausearch 等工具查找。
第二条日志则呈现了 less
命令执行时的工作目录信息,别无其他意义。
第三条日志呈现的信息解释如下:
- item :在本例中, item 字段表示 a0 参数,即
SYSCALL
消息中记录的系统调用的第一个参数。如果某个系统调用接收了多个路径参数,就会有更多的PATH
类型的消息产生。 - name :呈现传递给系统调用的具体路径名称。
- inode :表示与 name 相关的 inode 编号。
- dev :文件存储的具体设备。
- mode :文件访问控制权限的数字表示。在本例中,root用户具有读写的权限、root所在的用户组具有读取文件的权限,而其余的所有用户(组)则完全不具备本文件的访问权限。
- ouid、ogid :指向相关 inode 的UID和GID。
- rdev :本例中没有意义。该字段仅适用于块设备或字符设备,与文件无关。
日志的搜索与分析
audit框架提供了一些日志查找工具,以便查看系统上发生的一系列事件。
通过pid
通过进程id来查看特定事件:
# ausearch -p 1
该命令将呈现所有与ID为1的进程相关的事件。
通过key
通过 key
来管理事件是audit框架中一个很棒的特性,推荐日常使用。
可以在规则中使用 -k
选项来为相关事件打上标签,方便后续查找:
# auditctl -w /etc/passwd -p rwxa -k KEY_pwd
紧接着,查找所有带有键值 KEY_pwd
的事件,ausearch将呈现所有与文件 /etc/passwd
相关的事件:
# ausearch -k KEY_pwd
查看异常事件
方才介绍了 ausearch 的基本用法,而此前提及的 aureport 工具更擅长快速地报告系统上的异常事件,例如,网卡工作在混杂模式、进程或线程由于ENOMEM等错误崩溃推出等等。
最简单的查看异常的命令是:
# aureport -n
也可以使用此工具生成自定义报告,详见 aureport 文档 。
值得记录的文件或系统调用
始终注意,所有列入审计的文件和系统调用都将产生日志,因此要对日志的数量保持谨慎。一般来说,与安全相关的事件和文件都需要被记录,如IDS、IPS、anti-rootkits等等;与此相对地,对于 write
类型的系统调用就没必要进行记录,否则即便是最微不足道的写入操作都会被记录到日志中。
人们可以设置一系列复杂的规则来实现对审计事件的精细管控,详见 auditctl文档 。
本文中给出两份可用度较高的规则文件 [附件1](#附件1 推荐规则(1)) 和 [附件2](#附件2 推荐规则(2)) ,可以直接将它们复制到 /etc/audit/audit.rules
中。
远程日志传输
audit框架提供了用于实现日志转发的插件,可以将本地日志转发到远程auditd主机上。
发送日志
实现日志的发送,需要具备 audisp-remote
插件,该插件由audit安装包默认自带。通过修改如下文件来激活插件:
# 文件 /etc/audisp/plugins.d/au-remote.conf
---
active = yes
direction = out
path = /usr/bin/audisp-remote
type = always
format = string
并在如下文件中设置目标主机(即日志发送的对象):
# 文件 /etc/audisp/audisp-remote.conf
---
remote_server = [domain_name | ip]
port = 60
## localport = optional
transport = tcp
接收日志
接受日志的配置较为简单:
# 文件 /etc/audit/auditd.conf
---
tcp_listen_port = 60
tcp_listen_queue = 5
tcp_max_per_addr = 1
## tcp_client_ports = 1024-65535 #optional
tcp_client_max_idle = 0
参考资料
[1] Audit framework - ArchWiki (archlinux.org)
[2] Understanding Linux Audit | Security Guide | SUSE Linux Enterprise Server 11 SP4
附件1 推荐规则(1)
# This file contains the auditctl rules that are loaded
# whenever the audit daemon is started via the initscripts.
# The rules are simply the parameters that would be passed
# to auditctl.
# First rule - delete all
-D
# Increase the buffers to survive stress events.
# Make this bigger for busy systems
-b 320
# Feel free to add below this line. See auditctl man page
-w /etc/passwd -p wa -k passwd_changes
-w /etc/selinux/ -p wa -k selinux_changes
-w /sbin/insmod -p x -k module_insertion
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sysconfig -p wa -k sysconfig_changes
-w /etc/audit/audit.rules -p wa -k audit_rules_changes
-w /etc/audit/auditd.conf -p wa -k audit_conf_changes
-w /usr/sbin/vpnc -k vpnc_exec -p x
-w /usr/bin/stunnel -k stunnel_exec -p x
-w /usr/bin/nc -p x -k ncat_exec
-w /usr/bin/nmap -p x -k nmap_exec
-w /usr/bin/sudo -p x -k sudo_exec
-w /usr/bin/su -p x -k su_exec
-w /etc/group -p wa -k group_changes
-w /var/log/wtmp -p w -k wtmp_changes
-w /var/log/btmp -p w -k btmp_changes
-w /usr/bin/getent -k getent_exec -p x
-w /etc/rc.d/init.d -p wa -k initd_changes
-w /etc/security/opasswd -p wa -k opasswd_changes
-w /etc/gshadow -p wa -k gshadow_changes
-w /etc/securetty -p wa -k securetty_changes
-w /etc/login.defs -p wa -k login_defs_changes
-w /etc/ld.so.conf -p wa -k ld_so_conf_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/profile -p wa -k profile_changes
-w /etc/hosts -p wa -k hosts_changes
-w /etc/inittab -p wa -k inittab_changes
-w /etc/sysconfig/iptables -p wa -k iptables_changes
-w /etc/hosts.allow -p wa -k hosts_allow_changes
-w /etc/hosts.deny -p wa -k hosts_deny_changes
-w /root/.ssh/authorized_keys -p wa -k authorized_keys_changes
-w /etc/crontab -p wa -k crontab_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
-a always,exit -F arch=b32 -S ptrace -k process_inject
-a always,exit -F arch=b64 -S ptrace -k process_inject
-w /usr/bin/rdesktop -p x -k rdesktop_exec
-w /etc/exports -p wa -k exports_changes
-w /lib/modules/ -p wa -k lib_modules_changes
-w /sbin/iptables-multi -p x -k iptables_exec
-w /var/spool/cron/root -p wa -k spool_cron_root_changes
-w /etc/resolv.conf -p wa -k resolv_conf_changes
-a always,exit -S execve -k execve_event
-a exit,always -S all -F euid=0 -F perm=awx -k root-commands