Skip to content

Instantly share code, notes, and snippets.

@JorgeMarinoDev
Created November 18, 2023 00:24
Show Gist options
  • Select an option

  • Save JorgeMarinoDev/cee4994a80f080fed4c69fb0f767eae5 to your computer and use it in GitHub Desktop.

Select an option

Save JorgeMarinoDev/cee4994a80f080fed4c69fb0f767eae5 to your computer and use it in GitHub Desktop.
Wazuh simple Rule Correlation example

How to write correlation between Rules in Wazuh

November 1st 2023 | by Jorge Marino | Wazuh 4.6

A common question among Wazuh users is how to trigger a rule, after two other rules were triggered.

The current Wazuh Rules' syntax does not include any mechanism to achieve this purpose right out of the box.

But it's possible to achieve this behavior using a workaround.

To illustrate the example approach, we define this setup:

  • We want to get an alert if a successful login is triggered after several failed logins were attempted.

    • RULE LOGIN FAILED
    • RULE LOGIN FAILED
    • ...
    • RULE LOGIN FAILED
    • RULE MULTIPLE FAILED LOGINS
    • RULE LOGIN SUCCESS
    • REQUIRED RULE IS TRIGGERED
  • We have two possible incoming logs into Wazuh.

    • Dec 25 20:45:02 MyHost example[12345]: User 'admin' accept from '192.168.1.100'

      • Received after the user admin successfully logged from this IP,
    • Dec 25 20:45:02 MyHost example[12345]: User 'admin' reject from '192.168.1.100'

      • Received after the user admin got rejected by the login system.

Objective

  • Trigger an alert with these conditions:
    • Receive N reject messages.
    • Receive 1 success login message.

Actions

  1. First of all we need to write our own decoders.

  2. Write correlated rules.

Decoders

<decoder name="example">
    <program_name>example</program_name>
</decoder>

<decoder name="example">
    <parent>example</parent>
    <regex>User '(\w+)' (\w+) from '(\d+.\d+.\d+.\d+)'</regex>
  <order>user, $result, srcip</order>
</decoder>

Here we parse "accept" or "reject" and store it into the $result field.

Rules

Intuitively, we write our own custom rules, to be triggered after this decoder is matched with both results.

Login success Rule

<group name="custom_rules,">
  <rule id="10010" level="3">
    <program_name>example</program_name>
    <description>User login success</description>
    <field name="$result">accept</field>
  </rule>
</group>

Login Failed Rule

<group name="custom_rules,">
  <rule id="10011" level="3">
    <program_name>example</program_name>
    <description>User login failed</description>
    <field name="$result">reject</field>
  </rule>
</group>

How it works

  • Rule 10010 is triggered after a successful login happened
  • Rule 10011 is triggered after a failed login.

Multiple failed logins RULE - Correlation

First we must have a rule that detect many FAILED logins.

<group name="custom_rules,">
  <rule id="10012" level="6" frequency="2" timeframe="30">
      <if_matched_sid>10011</if_matched_sid>
      <same_srcip/>
      <same_user/>
      <description>Multiple login failure</description>
  </rule>
</group>

This rule will be triggered after 2 failed login attempts were made within a 30 seconds time frame.

Correlate the next successful login with this rule

We don't have a default mechanism to define a rule to be triggered after two other rules were triggered.

But as a workaround we can write another rule that is triggered only after multiple login failures (10012) rule was set off.

<group name="custom_rules,">
  <rule id="10013" level="10" timeframe="20">
    <if_matched_sid>10012</if_matched_sid>
    <same_srcip/>
    <same_user/>
    <description>Success Login after multiple failed logins</description>
  </rule>

How do we link this rule with the successful login rule (10010) ?

We can do this by linking not the rule id (10010), but instead using a custom group that's 1:1 related to this rule (10010).

Let's modify rule 10010.

<group name="custom_rules,">
  <rule id="10010" level="3">
    <program_name>example</program_name>
    <description>User ok</description>
    <field name="$result">accept</field>
    <group>login_ok</group>
  </rule>
</group>

We added the group login_ok to be set by this rule.

Now let's modify the workaround rule (10013) and require a group match with login_ok.

<group name="custom_rules,">
  <rule id="10013" level="10" timeframe="20">
    <if_matched_sid>10012</if_matched_sid>
    <if_group>login_ok</if_group>>
    <same_srcip/>
    <same_user/>
    <description>Success Login after multiple failed logins</description>
  </rule>

Now the setup is complete.

Here is a wazuh-logtest evidence.

Starting wazuh-logtest v4.5.3
Type one log per line

Dec 25 20:45:02 MyHost example[12345]: User 'admin' reject from '192.168.1.100'

**Phase 1: Completed pre-decoding.
	full event: 'Dec 25 20:45:02 MyHost example[12345]: User 'admin' reject from '192.168.1.100''
	timestamp: 'Dec 25 20:45:02'
	hostname: 'MyHost'
	program_name: 'example'

**Phase 2: Completed decoding.
	name: 'example'
	$result: 'reject'
	dstuser: 'admin'
	srcip: '192.168.1.100'

**Phase 3: Completed filtering (rules).
	id: '10011'
	level: '3'
	description: 'User failed'
	groups: '['custom_rules']'
	firedtimes: '1'
	mail: 'False'
**Alert to be generated.

Dec 25 20:45:02 MyHost example[12345]: User 'admin' reject from '192.168.1.100'

**Phase 1: Completed pre-decoding.
	full event: 'Dec 25 20:45:02 MyHost example[12345]: User 'admin' reject from '192.168.1.100''
	timestamp: 'Dec 25 20:45:02'
	hostname: 'MyHost'
	program_name: 'example'

**Phase 2: Completed decoding.
	name: 'example'
	$result: 'reject'
	dstuser: 'admin'
	srcip: '192.168.1.100'

**Phase 3: Completed filtering (rules).
	id: '10012'
	level: '6'
	description: 'Multiple login failure'
	groups: '['custom_rules']'
	firedtimes: '1'
	frequency: '2'
	mail: 'False'
**Alert to be generated.

Dec 25 20:45:02 MyHost example[12345]: User 'admin' accept from '192.168.1.100'

**Phase 1: Completed pre-decoding.
	full event: 'Dec 25 20:45:02 MyHost example[12345]: User 'admin' accept from '192.168.1.100''
	timestamp: 'Dec 25 20:45:02'
	hostname: 'MyHost'
	program_name: 'example'

**Phase 2: Completed decoding.
	name: 'example'
	$result: 'accept'
	dstuser: 'admin'
	srcip: '192.168.1.100'

**Phase 3: Completed filtering (rules).
	id: '10013'
	level: '10'
	description: 'Success Login after multiple failed logins'
	groups: '['custom_rules']'
	firedtimes: '1'
	frequency: '2'
	mail: 'False'
**Alert to be generated.

Constraints

  • Multiple failed logins will be detected after 2 times in 30 seconds.
  • Successful login after Multiple Fails will be detected in 20 seconds.

Please check timeframe settings in the rules to customize behavior.

Conclusion

  • We need static fields as srcip and user to glue together this pipeline.

  • We need a custom group name to bind these rules.

  • Correlation is possible using if_group instead of an intuitive rule id match. Logical operators not yet supported.

  • There is a 1:1 relationship between custom binding group names and leading rules.

    • Rule 10010 (login ok) is 1:1 with login_ok group name.
    • There is no AND operator between rules. Instead use if_group to bind them.

Final decoders and rules

Decoders

<decoder name="example">
    <program_name>example</program_name>
</decoder>

<decoder name="example">
    <parent>example</parent>
    <regex>User '(\w+)' (\w+) from '(\d+.\d+.\d+.\d+)'</regex>
  <order>user, $result, srcip</order>
</decoder>

Rules

<group name="custom_rules,">
  <rule id="10010" level="3">
    <program_name>example</program_name>
    <description>User ok</description>
    <field name="$result">accept</field>
    <group>login_ok</group>
  </rule>
</group>

<group name="custom_rules,">
  <rule id="10011" level="3">
    <program_name>example</program_name>
    <description>User failed</description>
    <field name="$result">reject</field>
  </rule>
</group>

<group name="custom_rules,">
  <rule id="10012" level="6" frequency="2" timeframe="30">
      <if_matched_sid>10011</if_matched_sid>
      <same_srcip/>
      <same_user/>
      <description>Multiple login failure</description>
  </rule>
</group>

<group name="custom_rules,">
  <rule id="10013" level="10" timeframe="20">
    <if_matched_sid>10012</if_matched_sid>
    <if_group>login_ok</if_group>>
    <same_srcip/>
    <same_user/>
    <description>Success Login after multiple failed logins</description>
  </rule>
</group>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment