What the stock 4.14.9 ruleset does with these lines
| log line (program) | decoder | rule, level | alert |
|---|---|---|---|
usermod: add 'monitor' to group 'sudo' | none | none | no |
usermod: add 'monitor' to shadow group 'sudo' | none | none | no |
mysqld: [MY-010926] Access denied for user 'root'@'10.0.2.55' | mariadb-syslog | 88100, 0 | no |
mariadbd: [Warning] Access denied for user 'root'@'10.0.2.55' | none | 1002, 2 | no |
gpasswd: user monitor added by root to group sudo (control) | gpasswd | 2961, 5 | yes |
The same change made with gpasswd alerts at level 5, which is why the gap is easy to miss: a test with one tool passes, the other tool is silent.
Fix: local decoder and rules
Add to /var/ossec/etc/decoders/local_decoder.xml:
<decoder name="usermod-group">
<program_name>^usermod$</program_name>
</decoder>
<decoder name="usermod-group-add">
<parent>usermod-group</parent>
<regex type="pcre2">^add '([^']+)' to (shadow )?group '([^']+)'</regex>
<order>dstuser, extra_data, group</order>
</decoder>
<decoder name="mariadbd-syslog">
<program_name>^mariadbd$</program_name>
</decoder>
<decoder name="mariadbd-access-denied">
<parent>mariadbd-syslog</parent>
<regex type="pcre2">Access denied for user '([^']*)'@'([^']+)'</regex>
<order>dstuser, srcip</order>
</decoder>
Add as /var/ossec/etc/rules/0900-local-40589.xml (rule 100220 is a child of 88100, which lives in 0535-mariadb_rules.xml, so the file name must sort after that one; 0900- and local_rules.xml both do):
<group name="local,adduser,">
<rule id="100210" level="5">
<decoded_as>usermod-group</decoded_as>
<regex type="pcre2">^add '[^']+' to group '</regex>
<description>usermod: user $(dstuser) added to group $(group).</description>
<mitre><id>T1098</id></mitre>
<group>pci_dss_10.2.5,gdpr_IV_32.2,</group>
</rule>
<rule id="100211" level="0">
<decoded_as>usermod-group</decoded_as>
<regex type="pcre2">^add '[^']+' to shadow group '</regex>
<description>usermod: shadow group entry (same change, already alerted by 100210).</description>
</rule>
</group>
<group name="local,mysql_log,authentication_failed,">
<rule id="100220" level="5">
<if_sid>88100</if_sid>
<match>Access denied for user</match>
<description>MySQL: access denied (user and host are in full_log).</description>
<mitre><id>T1110</id></mitre>
</rule>
<rule id="100221" level="5">
<decoded_as>mariadbd-syslog</decoded_as>
<match>Access denied for user</match>
<description>MariaDB: access denied for $(dstuser) from $(srcip).</description>
<mitre><id>T1110</id></mitre>
</rule>
</group>
With the fix, same five lines
| log line | rule, level | fields |
|---|---|---|
usermod add to group | 100210, 5 | dstuser=monitor, group=sudo |
usermod shadow group | 100211, 0 (no duplicate alert) | dstuser=monitor, group=sudo |
mysqld access denied | 100220, 5 | none extracted (see below) |
mariadbd access denied | 100221, 5 | dstuser=root, srcip=10.0.2.55 |
gpasswd (control) | 2961, 5 | unchanged |
No load warnings. Level 5 and MITRE T1098 / T1110 mirror the stock gpasswd and authentication rules; raise them if group changes to sudo should page someone.
What we could not do. The mysqld line alerts, but user and host stay in full_log. The stock file defines four parent decoders all named mariadb-syslog, and a local child decoder we attached by that name did not take effect in our test. Extracting those fields needs a change to the stock decoder, which we did not want to recommend in a local fix.
Scope. Measured with the manager's logtest engine on 4.14.9. Brute-force correlation (frequency rules over many failures) is not covered by this test.
Check it on your own lines
Paste your log lines into /var/ossec/bin/wazuh-logtest on your manager after adding the files. Or let your AI agent run them on a real 4.14.9 manager through our MCP server: add https://atkvn.com/agent/mcp and ask it to test these rules against your lines (test_wazuh_rule, free for one line). How to connect.