Skip to content

Commit

Permalink
Various correlation query improvements
Browse files Browse the repository at this point in the history
* Added possibility to pass arbitrary parameters to convert_correlation_search that are passed to the string format context.
* Correlation multi-rule search query and typing query postprocessing.
* Fix: missing aliasing in backend correlation support only raises errors if aliases appear in rule.
  • Loading branch information
thomaspatzke committed Mar 26, 2024
1 parent 8fdbffc commit de73540
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 14 deletions.
49 changes: 35 additions & 14 deletions sigma/conversion/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1613,6 +1613,7 @@ def convert_correlation_temporal_ordered_rule(
def convert_correlation_search(
self,
rule: SigmaCorrelationRule,
**kwargs,
) -> str:
if ( # if the correlation rule refers only a single rule and this rule results only in a single query
len(rule.rules) == 1
Expand All @@ -1625,6 +1626,7 @@ def convert_correlation_search(
normalization=self.convert_correlation_search_field_normalization_expression(
rule.aliases, rule_reference
),
**kwargs,
)
else:
return self.correlation_search_multi_rule_expression.format(
Expand All @@ -1633,7 +1635,9 @@ def convert_correlation_search(
self.correlation_search_multi_rule_query_expression.format(
rule=rule_reference.rule,
ruleid=rule_reference.rule.name or rule_reference.rule.id,
query=query,
query=self.convert_correlation_search_multi_rule_query_postprocess(
query
),
normalization=self.convert_correlation_search_field_normalization_expression(
rule.aliases,
rule_reference,
Expand All @@ -1642,33 +1646,43 @@ def convert_correlation_search(
for rule_reference in rule.rules
for query in rule_reference.rule.get_conversion_result()
)
)
),
**kwargs,
)

def convert_correlation_search_multi_rule_query_postprocess(
self,
query: str,
) -> str:
"""This function is called for each query in the multi-rule correlation search phase. It can be used to postprocess the query before it is joined with the other queries."""
return query

def convert_correlation_search_field_normalization_expression(
self,
aliases: SigmaCorrelationFieldAliases,
rule_reference: SigmaRule,
) -> str:
if (
if len(aliases) == 0:
return ""
elif (
self.correlation_search_field_normalization_expression is None
or self.correlation_search_field_normalization_expression_joiner is None
):
raise NotImplementedError(
"Correlation field normalization is not supported by backend."
)

return self.correlation_search_field_normalization_expression_joiner.join(
(
self.correlation_search_field_normalization_expression.format(
alias=alias.alias,
field=field,
else:
return self.correlation_search_field_normalization_expression_joiner.join(
(
self.correlation_search_field_normalization_expression.format(
alias=alias.alias,
field=field,
)
for alias in aliases
for alias_rule_reference, field in alias.mapping.items()
if alias_rule_reference == rule_reference
)
for alias in aliases
for alias_rule_reference, field in alias.mapping.items()
if alias_rule_reference == rule_reference
)
)

# Implementation of the typing phase of the correlation query.
def convert_correlation_typing(self, rule: SigmaCorrelationRule) -> str:
Expand All @@ -1681,14 +1695,21 @@ def convert_correlation_typing(self, rule: SigmaCorrelationRule) -> str:
self.typing_rule_query_expression.format(
rule=rule_reference.rule,
ruleid=rule_reference.rule.name or rule_reference.rule.id,
query=query,
query=self.convert_correlation_typing_query_postprocess(query),
)
for rule_reference in rule.rules
for query in rule_reference.rule.get_conversion_result()
)
)
)

def convert_correlation_typing_query_postprocess(
self,
query: str,
) -> str:
"""This function is called for each query in the typing phase of the correlation query. It can be used to postprocess the query before it is joined with the other queries."""
return query

# Implementation of the aggregation phase of the correlation query.
def convert_correlation_aggregation_from_template(
self, rule: SigmaCorrelationRule, correlation_type: SigmaCorrelationTypeLiteral, method: str
Expand Down
3 changes: 3 additions & 0 deletions sigma/correlations.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ class SigmaCorrelationFieldAliases:
def __iter__(self):
return iter(self.aliases.values())

def __len__(self):
return len(self.aliases)

@classmethod
def from_dict(cls, d: dict):
aliases = {}
Expand Down
11 changes: 11 additions & 0 deletions tests/test_conversion_correlations.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ def test_event_count_correlation_single_rule_with_grouping(
]


def test_correlation_without_normalization_support(
monkeypatch, test_backend, event_count_correlation_rule
):
monkeypatch.setattr(test_backend, "correlation_search_field_normalization_expression", None)
assert test_backend.convert(event_count_correlation_rule) == [
"""EventID=4625
| aggregate window=5min count() as event_count by TargetUserName, TargetDomainName
| where event_count >= 10"""
]


def test_generate_query_without_referenced_rules_expression(
monkeypatch, test_backend, event_count_correlation_rule
):
Expand Down

0 comments on commit de73540

Please sign in to comment.