Hello Marc,
Thanks for posting on the Wazuh mailing list. Hope everything is very well.
The following guide can help you to understand and create your custom decoders and rules:
In this case, this is in json format, so the json decoder will show you the needed information depending on the log (key: value).
To test this, you can use the ossec-logtest script in the /var/ossec/bin/ directory. The result by default:
---
log: '{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}'
**Phase 2: Completed decoding.
decoder: 'json'
level: 'error'
label: 'EXPRESS'
message: '170.145.61.26 - - [19/May/2021:19:44:41 +0000] "GET / HTTP/1.1" 418 14 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"'
environment: 'development'
meta.environment: 'development'
---
I've created the following decoder as a json's child in the /var/ossec/etc/decoders/local_decoder.xml file:
---
{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}
-->
<decoder name="express-custom">
<parent>json</parent>
<regex>"message":"(\d+.\d+.\d+.\d+) \S+ \S+ [(\d+/\w+/\d+:\S+) \S+] \\"(\S+ / \S+)\\" (\S+) (\S+) \\"(\S+)\\" \\"(\.+)\\"</regex>
<order>source_ip,timestamp,request_type,response_code,response_type,referrer,user_agent</order>
</decoder>
---
With the following result that looks something like you need:
Note that I've not used the day, month, year, and time separately since it's not possible to format in the decoder the time format as you need. But you can use the Wazuh template in Filebeat or Logstash to use a known date format by ElasticSearch and get it as date to Elasticsearch. For example using the entire date as timestamp (keyword): [(\d+/\w+/\d+:\S+) \S+]
---
**Phase 1: Completed pre-decoding.
full event: '{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}'
timestamp: '(null)'
hostname: 'manager4-1'
program_name: '(null)'
log: '{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}'
**Phase 2: Completed decoding.
decoder: 'json'
source_ip: '170.145.61.26'
timestamp: '19/May/2021:19:44:41'
request_type: 'GET / HTTP/1.1'
response_code: '418'
response_type: '14'
referrer: '-'
user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
---
Since Phase 3 is a level 2 rule, it won't create an alert, so you'll need something like this in your /var/ossec/etc/rules/local_rules.xml. I've created the following example to create alerts when the json decoder finds the label:EXPRESS as the parent, then the child as level 10 when it finds the level:error.
---
{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}
-->
<group name="express-group">
<rule id="102000" level="0">
<decoded_as>json</decoded_as>
<field name="label">^EXPRESS$</field>
<description>Express rules group</description>
</rule>
<rule id="102001" level="10">
<if_sid>102000</if_sid>
<field name="level">^error$</field>
<description>Error: Environment: $(environment) - Level: $(level) - IP: $(source_ip)</description>
<!--options>no_full_log</options-->
</rule>
</group>
---
And the final output is the following:
---
**Phase 1: Completed pre-decoding.
full event: '{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}'
timestamp: '(null)'
hostname: 'manager4-1'
program_name: '(null)'
log: '{"level":"error","label":"EXPRESS","message":"170.145.61.26 - - [19/May/2021:19:44:41 +0000] \"GET / HTTP/1.1\" 418 14 \"-\" \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\"","environment":"development","meta":{"environment":"development"}}'
**Phase 2: Completed decoding.
decoder: 'json'
level: 'error'
label: 'EXPRESS'
source_ip: '170.145.61.26'
timestamp: '19/May/2021:19:44:41'
request_type: 'GET / HTTP/1.1'
response_code: '418'
response_type: '14'
referrer: '-'
user_agent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
environment: 'development'
**Phase 3: Completed filtering (rules).
Rule id: '102001'
Level: '10'
Description: 'Error: Environment: development - Level: error - IP: 170.145.61.26'
**Alert to be generated.
---
Hope this helps you to understand how it works.
Any questions, please let me know, I'm glad to help.
Kind regards,
Cesar Moreno.