From 30ff22776a0bd7644090ad5d79fe061e6ee9fed6 Mon Sep 17 00:00:00 2001 From: Simran Soin Date: Thu, 23 Jul 2020 09:41:33 -0400 Subject: [PATCH] Fix NOT bug --- tools/sigma/backends/base.py | 31 +++++++++++++++------------ tools/sigma/backends/qradar.py | 6 +++++- tools/sigma/backends/stix.py | 39 +++++++++++++++++++++++----------- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/tools/sigma/backends/base.py b/tools/sigma/backends/base.py index 1ef7e175..b9671fd2 100644 --- a/tools/sigma/backends/base.py +++ b/tools/sigma/backends/base.py @@ -154,10 +154,12 @@ class BaseBackend: pass return query - def generateNode(self, node): + def generateNode(self, node, currently_within_NOT_node=False): if type(node) == sigma.parser.condition.ConditionAND: return self.applyOverrides(self.generateANDNode(node)) elif type(node) == sigma.parser.condition.ConditionOR: + if currently_within_NOT_node: + return self.applyOverrides(self.generateANDNode(node)) return self.applyOverrides(self.generateORNode(node)) elif type(node) == sigma.parser.condition.ConditionNOT: return self.applyOverrides(self.generateNOTNode(node)) @@ -246,8 +248,8 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): sort_condition_lists = False # Sort condition items for AND and OR conditions - def generateANDNode(self, node): - generated = [ self.generateNode(val) for val in node ] + def generateANDNode(self, node, currently_within_NOT_node=False): + generated = [ self.generateNode(val, currently_within_NOT_node) for val in node ] filtered = [ g for g in generated if g is not None ] if filtered: if self.sort_condition_lists: @@ -256,8 +258,8 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): else: return None - def generateORNode(self, node): - generated = [ self.generateNode(val) for val in node ] + def generateORNode(self, node, currently_within_NOT_node): + generated = [ self.generateNode(val, currently_within_NOT_node) for val in node ] filtered = [ g for g in generated if g is not None ] if filtered: if self.sort_condition_lists: @@ -266,33 +268,34 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): else: return None - def generateNOTNode(self, node): - generated = self.generateNode(node.item) + def generateNOTNode(self, node, currently_within_NOT_node): + currently_within_NOT_node = True + generated = self.generateNode(node.item, currently_within_NOT_node) if generated is not None: - return self.notToken + generated + return generated else: return None - def generateSubexpressionNode(self, node): - generated = self.generateNode(node.items) + def generateSubexpressionNode(self, node, currently_within_NOT_node): + generated = self.generateNode(node.items, currently_within_NOT_node) if generated: return self.subExpression % generated else: return None - def generateListNode(self, node): + def generateListNode(self, node, currently_within_NOT_node): if not set([type(value) for value in node]).issubset({str, int}): raise TypeError("List values must be strings or numbers") return self.listExpression % (self.listSeparator.join([self.generateNode(value) for value in node])) - def generateMapItemNode(self, node): + def generateMapItemNode(self, node, currently_within_NOT_node): fieldname, value = node transformed_fieldname = self.fieldNameMapping(fieldname, value) if self.mapListsSpecialHandling == False and type(value) in (str, int, list) or self.mapListsSpecialHandling == True and type(value) in (str, int): return self.mapExpression % (transformed_fieldname, self.generateNode(value)) elif type(value) == list: - return self.generateMapItemListNode(transformed_fieldname, value) + return self.generateMapItemListNode(transformed_fieldname, value, currently_within_NOT_node) elif isinstance(value, SigmaTypeModifier): return self.generateMapItemTypedNode(transformed_fieldname, value) elif value is None: @@ -300,7 +303,7 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): else: raise TypeError("Backend does not support map values of type " + str(type(value))) - def generateMapItemListNode(self, fieldname, value): + def generateMapItemListNode(self, fieldname, value, currently_within_NOT_node): return self.mapListValueExpression % (fieldname, self.generateNode(value)) def generateMapItemTypedNode(self, fieldname, value): diff --git a/tools/sigma/backends/qradar.py b/tools/sigma/backends/qradar.py index f29024af..82533221 100644 --- a/tools/sigma/backends/qradar.py +++ b/tools/sigma/backends/qradar.py @@ -58,10 +58,14 @@ class QRadarBackend(SingleTextQueryBackend): """Remove quotes in text""" return value.replace("\'","\\\'") - def generateNode(self, node): + def generateNode(self, node, currently_within_NOT_node=False): if type(node) == sigma.parser.condition.ConditionAND: + if currently_within_NOT_node: + return self.generateORNode(node) return self.generateANDNode(node) elif type(node) == sigma.parser.condition.ConditionOR: + if currently_within_NOT_node: + return self.generateANDNode(node) return self.generateORNode(node) elif type(node) == sigma.parser.condition.ConditionNOT: return self.generateNOTNode(node) diff --git a/tools/sigma/backends/stix.py b/tools/sigma/backends/stix.py index 82e7b3fd..539da043 100644 --- a/tools/sigma/backends/stix.py +++ b/tools/sigma/backends/stix.py @@ -26,15 +26,24 @@ class STIXBackend(SingleTextQueryBackend): def cleanValue(self, value): return value - def generateMapItemListNode(self, key, value): + def generateMapItemListNode(self, key, value, currently_within_NOT_node): items_list = list() for item in value: if type(item) == str and "*" in item: item = item.replace("*", "%") - items_list.append('%s LIKE %s' % (self.cleanKey(key), self.generateValueNode(item))) + if currently_within_NOT_node: + items_list.append('%s NOT LIKE %s' % (self.cleanKey(key), self.generateValueNode(item))) + else: + items_list.append('%s LIKE %s' % (self.cleanKey(key), self.generateValueNode(item))) else: - items_list.append('%s = %s' % (self.cleanKey(key), self.generateValueNode(item))) - return '('+" OR ".join(items_list)+')' + if currently_within_NOT_node: + items_list.append('%s != %s' % (self.cleanKey(key), self.generateValueNode(item))) + else: + items_list.append('%s = %s' % (self.cleanKey(key), self.generateValueNode(item))) + if currently_within_NOT_node: + return '(' + " AND ".join(items_list) + ')' + else: + return '('+" OR ".join(items_list)+')' def generateMapItemTypedNode(self, key, value): if type(value) == SigmaRegularExpressionModifier: @@ -48,18 +57,20 @@ class STIXBackend(SingleTextQueryBackend): else: raise NotImplementedError("Type modifier '{}' is not supported by backend".format(value.identifier)) - def generateMapItemNode(self, node): + def generateMapItemNode(self, node, currently_within_NOT_node): key, value = node if ":" not in key: key = "%s:%s" % (self.sigmaSTIXObjectName, str(key).lower()) if self.mapListsSpecialHandling == False and type(value) in (str, int, list) or self.mapListsSpecialHandling == True and type(value) in (str, int): if type(value) == str and "*" in value: value = value.replace("*", "%") + if currently_within_NOT_node: + return "%s NOT LIKE %s" % (self.cleanKey(key), self.generateValueNode(value)) return "%s LIKE %s" % (self.cleanKey(key), self.generateValueNode(value)) elif type(value) in (str, int): return self.mapExpression % (self.cleanKey(key), self.generateValueNode(value)) elif type(value) == list: - return self.generateMapItemListNode(key, value) + return self.generateMapItemListNode(key, value, currently_within_NOT_node) elif isinstance(value, SigmaTypeModifier): return self.generateMapItemTypedNode(key, value) else: @@ -68,17 +79,21 @@ class STIXBackend(SingleTextQueryBackend): def generateValueNode(self, node): return self.valueExpression % (self.cleanValue(str(node))) - def generateNode(self, node): + def generateNode(self, node, currently_within_NOT_node=False): if type(node) == sigma.parser.condition.ConditionAND: - return self.generateANDNode(node) + if currently_within_NOT_node: + return self.generateORNode(node, currently_within_NOT_node) + return self.generateANDNode(node, currently_within_NOT_node) elif type(node) == sigma.parser.condition.ConditionOR: - return self.generateORNode(node) + if currently_within_NOT_node: + return self.generateANDNode(node, currently_within_NOT_node) + return self.generateORNode(node, currently_within_NOT_node) elif type(node) == sigma.parser.condition.ConditionNOT: - return self.generateNOTNode(node) + return self.generateNOTNode(node, currently_within_NOT_node) elif type(node) == sigma.parser.condition.NodeSubexpression: - return self.generateSubexpressionNode(node) + return self.generateSubexpressionNode(node, currently_within_NOT_node) elif type(node) == tuple: - return self.generateMapItemNode(node) + return self.generateMapItemNode(node, currently_within_NOT_node) else: raise TypeError("Node type %s was not expected in Sigma parse tree" % (str(type(node))))