Wazuh Auditd 解码器添加 proctitle 字段提取

问题背景

Wazuh 内置的 auditd 解码器(0040-auditd_decoders.xml)不提取 proctitle 字段。直接添加自定义解码器会遇到以下问题:

方案 结果
自定义解码器使用不同名称(如 auditd-proctitle)并在内置之前加载 只能提取 proctitle,其他字段丢失
自定义解码器使用不同名称并在内置之后加载 内置解码器先匹配,proctitle 解码器不执行
复制内置解码器 + 在末尾添加不同名称的 proctitle 解码器,并在内置之前加载 同上,proctitle 无法提取

根本原因

Wazuh 解码器的工作机制:

  1. 父解码器(如 auditd)先匹配日志
  2. 子解码器按顺序尝试匹配,第一个名称匹配成功的子解码器组会被选中
  3. 同名子解码器会依次执行,字段累加
  4. 不同名子解码器不会累加,只执行第一个匹配成功的
日志 → auditd (父) → auditd-syscall (子,有prematch匹配SYSCALL/EXECVE)
→ auditd-generic (子,无prematch,兜底匹配)

当日志是 type=EXECVE ... 时:

  • auditd-syscall 的 prematch ^SYSCALL|^EXECVE 匹配成功
  • 所有名为 auditd-syscall 的解码器依次执行,字段累加
  • auditd-generic 和其他名称的解码器不会执行

解决方案

在对应的解码器组中添加同名的 proctitle 提取解码器。

修改位置

  1. auditd-syscall 组(第 52-57 行)- 处理 SYSCALL/EXECVE 类型日志
  2. auditd-generic 组(第 388-393 行)- 处理其他类型日志(兜底)

完整解码器文件(含中文标注)

<!--
- Copyright (C) 2015, Wazuh Inc.
-->

<!--
Audit decoders (logformat must be "audit")
-->

<decoder name="auditd">
<prematch>^node=\S+ type=|^type=</prematch>
</decoder>

<!--
type=SYSCALL msg=audit(1479982525.380:50): arch=c000003e syscall=2 success=yes exit=3 a0=7ffedc40d83b a1=941 a2=1b6 a3=7ffedc40cce0 items=2 ppid=432 pid=3333 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="touch" exe="/bin/touch" key="audit-wazuh-w" type=CWD msg=audit(1479982525.380:50): cwd="/var/log/audit" type=PATH msg=audit(1479982525.380:50): item=0 name="/var/log/audit/tmp_directory1/" inode=399849 dev=ca:02 mode=040755 ouid=0 ogid=0 rdev=00:00 nametype=PARENT type=PATH msg=audit(1479982525.380:50): item=1 name="/var/log/audit/tmp_directory1/malware.py" inode=399852 dev=ca:02 mode=0100644 ouid=0 ogid=0 rdev=00:00 nametype=CREATE type=PROCTITLE msg=audit(1479982525.380:50): proctitle=746F756368002F7661722F6C6F672F61756469742F746D705F6469726563746F7279312F6D616C776172652E7079
-->
<!-- ID -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<prematch offset="after_parent">^SYSCALL|^EXECVE</prematch>
<regex offset="after_parent">^(\S+) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<!-- SYSCALL -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">^arch=(\S+) syscall=(\d+) success=(\S+) exit=(\S+) a0=\S+ a1=\S+ a2=\S+ a3=\S+ items=\S+ ppid=(\S+) pid=(\S+) auid=(\S+) uid=(\S+) gid=(\S+) euid=(\S+) suid=(\S+) fsuid=(\S+) egid=(\S+) sgid=(\S+) fsgid=(\S+) tty=(\S+) ses=(\S+) comm="(\S+)" exe="(\S+)"</regex>
<order>audit.arch,audit.syscall,audit.success,audit.exit,audit.ppid,audit.pid,audit.auid,audit.uid,audit.gid,audit.euid,audit.suid,audit.fsuid,audit.egid,audit.sgid,audit.fsgid,audit.tty,audit.session,audit.command,audit.exe</order>
</decoder>

<!-- SYSCALL - command -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">comm="(\S+)"</regex>
<order>audit.command</order>
</decoder>

<!-- SYSCALL - exe -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">exe="(\S+)"</regex>
<order>audit.exe</order>
</decoder>

<!-- SYSCALL - key -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">key=\((\S+)\)|key="(\S+)"|key=(\S+) </regex>
<order>audit.key</order>
</decoder>

<!-- ==================== 新增部分 1:auditd-syscall 组 proctitle 提取 ==================== -->
<!-- SYSCALL - proctitle (处理 SYSCALL/EXECVE 类型日志) -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">proctitle=(\S+)</regex>
<order>audit.proctitle</order>
</decoder>
<!-- ==================== 新增部分 1 结束 ==================== -->

<!-- EXECVE -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">argc=(\d+)</regex>
<order>audit.execve.argc</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a0="(\S+)"</regex>
<order>audit.execve.a0</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a1="(\S+)"</regex>
<order>audit.execve.a1</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a2="(\S+)"</regex>
<order>audit.execve.a2</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a3="(\S+)"</regex>
<order>audit.execve.a3</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a4="(\S+)"</regex>
<order>audit.execve.a4</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a5="(\S+)"</regex>
<order>audit.execve.a5</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a6="(\S+)"</regex>
<order>audit.execve.a6</order>
</decoder>

<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">a7="(\S+)"</regex>
<order>audit.execve.a7</order>
</decoder>

<!-- CWD -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">type=CWD msg=audit\(\S+\):\s+cwd="(\S+)" </regex>
<order>audit.cwd</order>
</decoder>

<!-- PATH - DIRECTORY: mode=04* -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">type=PATH msg=audit\(\S+\): item=\S+ name="(\S+)" inode=(\S+) dev=\S+ mode=(04\S+) ouid=\S+ ogid=\S+ </regex>
<order>audit.directory.name, audit.directory.inode, audit.directory.mode</order>
</decoder>

<!-- PATH - FILE -->
<decoder name="auditd-syscall">
<parent>auditd</parent>
<regex offset="after_regex">type=PATH msg=audit\(\S+\): item=\S+ name="(\S+)" inode=(\S+) dev=\S+ mode=(\S+) ouid=\S+ ogid=\S+ |type=PATH msg=audit\(\S+\): item=\S+ name=\((null)\) inode=(\S+) dev=\S+ mode=(\S+) ouid=\S+ ogid=\S+ </regex>
<order>audit.file.name, audit.file.inode, audit.file.mode</order>
</decoder>

<!--
type=CONFIG_CHANGE msg=audit(1480085540.632:5846): auid=0 ses=1 op="remove rule" key="audit-wazuh-w" list=4 res=1
-->
<decoder name="auditd-config_change">
<parent>auditd</parent>
<prematch offset="after_parent">^CONFIG_CHANGE </prematch>
<regex offset="after_parent">^(CONFIG_CHANGE) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<decoder name="auditd-config_change">
<parent>auditd</parent>
<regex offset="after_regex">^auid=(\S+) ses=(\S+) op="(\S+)"</regex>
<order>audit.auid,audit.session,audit.op</order>
</decoder>

<decoder name="auditd-config_change">
<parent>auditd</parent>
<regex offset="after_regex">key=\((\S+)\)|key="(\S+)"|key=(\S+) </regex>
<order>audit.key</order>
</decoder>

<decoder name="auditd-config_change">
<parent>auditd</parent>
<regex offset="after_regex">list=(\S+)</regex>
<order>audit.list</order>
</decoder>

<decoder name="auditd-config_change">
<parent>auditd</parent>
<regex offset="after_regex">res=(\S+)</regex>
<order>audit.res</order>
</decoder>

<!--
type=ANOM_PROMISCUOUS msg=audit(1390181243.575:738): dev=vethDvSeyL prom=0 old_prom=256 auid=4294967295 uid=0 gid=0 ses=4294967295
-->
<decoder name="auditd-promiscuous">
<parent>auditd</parent>
<prematch offset="after_parent">^ANOM_PROMISCUOUS </prematch>
<regex offset="after_parent">^(ANOM_PROMISCUOUS) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<decoder name="auditd-promiscuous">
<parent>auditd</parent>
<regex offset="after_regex">^dev=(\S+) prom=(\S+) old_prom=(\S+) auid=(\S+) uid=(\S+) gid=(\S+) ses=(\S+)</regex>
<order>audit.dev,audit.prom,audit.old_prom,audit.auid,audit.uid,audit.gid,audit.session</order>
</decoder>

<!--
SELinux: MAC_STATUS
type=MAC_STATUS msg=audit(1480086668.032:1327): enforcing=0 old_enforcing=1 auid=0 ses=8 type=SYSCALL msg=audit(1480086668.032:1327): arch=c000003e syscall=1 success=yes exit=1 a0=3 a1=7fff9eff0d40 a2=1 a3=7fff9eff0ac0 items=0 ppid=4765 pid=4788 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=8 comm="setenforce" exe="/usr/sbin/setenforce" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
-->
<decoder name="auditd-selinux_macstatus">
<parent>auditd</parent>
<prematch offset="after_parent">^MAC_STATUS </prematch>
<regex offset="after_parent">^(MAC_STATUS) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<decoder name="auditd-selinux_macstatus">
<parent>auditd</parent>
<regex offset="after_regex">^enforcing=(\S+) old_enforcing=(\S+) auid=(\S+) ses=(\S+)</regex>
<order>audit.enforcing,audit.old_enforcing,audit.auid,audit.session</order>
</decoder>

<decoder name="auditd-selinux_macstatus">
<parent>auditd</parent>
<regex offset="after_regex">ppid=(\S+) pid=(\S+) auid=(\S+) uid=(\S+) gid=(\S+) euid=(\S+) suid=(\S+) fsuid=(\S+) egid=(\S+) sgid=(\S+) fsgid=(\S+) tty=(\S+) ses=(\S+) comm="(\S+)" exe="(\S+)"</regex>
<order>audit.ppid,audit.pid,audit.auid,audit.uid,audit.gid,audit.euid,audit.suid,audit.fsuid,audit.egid,audit.sgid,audit.fsgid,audit.tty,audit.session,audit.command,audit.exe</order>
</decoder>

<decoder name="auditd-selinux_macstatus">
<parent>auditd</parent>
<regex offset="after_regex">subj=(\S+)</regex>
<order>audit.subj</order>
</decoder>

<!--
type=USER_ACCT msg=audit(1480087217.108:6042): pid=6013 uid=0 auid=4294967295 ses=4294967295 msg='op=PAM:accounting acct="root" exe="/usr/sbin/sshd" hostname=10.10.10.100 addr=10.10.10.100 terminal=ssh res=success'
... (其他 USER_* 和 CRED_* 示例日志)
-->
<decoder name="auditd-user_and_cred">
<parent>auditd</parent>
<prematch offset="after_parent">^USER_ACCT |^CRED_ACQ |^USER_START |^CRED_REFR|^CRYPTO_KEY_USER|^CRYPTO_SESSION |^USER_AUTH |^USER_ROLE_CHANGE </prematch>
<regex offset="after_parent">^(\S+) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<decoder name="auditd-user_and_cred">
<parent>auditd</parent>
<regex offset="after_regex">^pid=(\S+) uid=(\S+) auid=(\S+) ses=(\S+)</regex>
<order>audit.pid,audit.uid,audit.auid,audit.session</order>
</decoder>

<decoder name="auditd-user_and_cred">
<parent>auditd</parent>
<regex offset="after_regex">subj=(\S+)</regex>
<order>audit.subj</order>
</decoder>

<decoder name="auditd-user_and_cred">
<parent>auditd</parent>
<regex offset="after_regex">acct="(\S+)"</regex>
<order>audit.acct</order>
</decoder>

<decoder name="auditd-user_and_cred">
<parent>auditd</parent>
<regex offset="after_regex">exe="(\S+)"</regex>
<order>audit.exe</order>
</decoder>

<decoder name="auditd-user_and_cred">
<parent>auditd</parent>
<regex offset="after_regex">addr=(\S+)</regex>
<order>srcip</order>
</decoder>

<!--
type=LOGIN msg=audit(1480087217.108:6044): pid=6013 uid=0 old-auid=4294967295 auid=0 old-ses=4294967295 ses=107 res=1
type=LOGIN msg=audit(1480087533.043:1428): pid=4885 uid=0 subj=system_u:system_r:sshd_t:s0-s0:c0.c1023 old-auid=4294967295 auid=0 old-ses=4294967295 ses=14 res=1
-->
<decoder name="auditd-login">
<parent>auditd</parent>
<prematch offset="after_parent">^LOGIN </prematch>
<regex offset="after_parent">^(\S+) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<decoder name="auditd-login">
<parent>auditd</parent>
<regex offset="after_regex">^pid=(\S+) uid=(\S+)</regex>
<order>audit.pid,audit.uid</order>
</decoder>

<decoder name="auditd-login">
<parent>auditd</parent>
<regex offset="after_regex">subj=(\S+)</regex>
<order>audit.subj</order>
</decoder>

<decoder name="auditd-login">
<parent>auditd</parent>
<regex offset="after_regex">old-auid=(\S+) auid=(\S+) old-ses=(\S+) ses=(\S+) res=(\S+)</regex>
<order>audit.old-auid,audit.auid,audit.old-ses,audit.session,audit.res</order>
</decoder>

<!--
Generic
type=TEST_GENERIC msg=audit(1234567890.123:1234): addr=10.10.10.10 ses=20 exe="ls" comm="ls" ppid=432 pid=3333 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
-->
<decoder name="auditd-generic">
<parent>auditd</parent>
<regex offset="after_parent">^(\S+) msg=audit\(\d+.\d+:(\d+)\): </regex>
<order>audit.type,audit.id</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>pid=(\S+)</regex>
<order>audit.pid</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>auid=(\S+)</regex>
<order>audit.auid</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex> uid=(\S+)</regex>
<order>audit.uid</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>gid=(\S+)</regex>
<order>audit.gid</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>euid=(\S+)</regex>
<order>audit.euid</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>ses=(\S+)</regex>
<order>audit.session</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>comm=(\S+)</regex>
<order>audit.command</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>exe=(\S+)</regex>
<order>audit.exe</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>addr=(\S+)</regex>
<order>srcip</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>res=(\w+)</regex>
<order>audit.res</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>name=(\S+)</regex>
<order>audit.directory.name</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>inode=(\S+)</regex>
<order>audit.inode</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>mode=(\S+)</regex>
<order>audit.mode</order>
</decoder>

<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>cwd="(\S+)"</regex>
<order>audit.cwd</order>
</decoder>

<!-- ==================== 新增部分 2:auditd-generic 组 proctitle 提取 ==================== -->
<!-- proctitle for generic audit messages (处理其他未被上述解码器匹配的审计日志) -->
<decoder name="auditd-generic">
<parent>auditd</parent>
<regex>proctitle=(\S+)</regex>
<order>audit.proctitle</order>
</decoder>
<!-- ==================== 新增部分 2 结束 ==================== -->

部署步骤

1. 上传自定义解码器文件

scp custom_auditd_decoders.xml root@wazuh-server:/var/ossec/etc/decoders/

2. 修改 ossec.conf

vim /var/ossec/etc/ossec.conf

确保配置如下:

<ruleset>
<decoder_dir>ruleset/decoders</decoder_dir>
<!-- 排除内置 auditd 解码器 -->
<decoder_exclude>ruleset/decoders/0040-auditd_decoders.xml</decoder_exclude>
<!-- 加载自定义解码器目录 -->
<decoder_dir>etc/decoders</decoder_dir>
<rule_dir>ruleset/rules</rule_dir>
...
</ruleset>

可以输入下面命令快速查看

grep -A10 "<ruleset>" /var/ossec/etc/ossec.conf

3. 重启服务

systemctl restart wazuh-manager

4. 测试验证

/var/ossec/bin/wazuh-logtest

输入测试日志:

type=EXECVE msg=audit(1678886400.123:100): arch=c000003e syscall=59 success=yes exit=0 a0=... ppid=1234 pid=5678 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=1 comm="sudo" exe="/usr/bin/sudo" key=(null) proctitle=2F7573722F62696E2F7375646F002D6C0064617461

预期输出应包含:

**Phase 2: Completed decoding.
name: 'auditd'
parent: 'auditd'
audit.type: 'EXECVE'
audit.id: '100'
audit.command: 'sudo'
audit.exe: '/usr/bin/sudo'
audit.key: 'null'
audit.proctitle: '2F7573722F62696E2F7375646F002D6C0064617461'
...

文件结构

/var/ossec/
├── etc/
│ ├── ossec.conf # 主配置文件
│ └── decoders/
│ └── custom_auditd_decoders.xml # 自定义解码器(含 proctitle)
└── ruleset/
└── decoders/
└── 0040-auditd_decoders.xml # 内置解码器(已排除)

关键点总结

要点 说明
解码器命名 必须使用相同名称(如 auditd-syscall)才能累加字段
添加位置 添加到/var/ossec/etc/decoders,位置不限
排除内置 使用 <decoder_exclude> 排除内置文件,避免冲突
完整复制 需要完整复制内置解码器内容,再添加 proctitle 提取

proctitle 字段说明

proctitle 是进程标题的十六进制编码,包含命令行参数。解码示例:

echo "2F7573722F62696E2F7375646F002D6C0064617461" | xxd -r -p | tr '\0' ' '
# 输出: /usr/bin/sudo -l data

可在 Wazuh 规则中使用 audit.proctitle 字段进行检测和告警。