diff --git a/tools/sigma/backends/base.py b/tools/sigma/backends/base.py index ffcb7b432..51e110f83 100644 --- a/tools/sigma/backends/base.py +++ b/tools/sigma/backends/base.py @@ -20,7 +20,7 @@ import sigma import yaml from .mixins import RulenameCommentMixin, QuoteCharMixin - +from sigma.parser.modifiers.base import SigmaTypeModifier class BackendOptions(dict): """ @@ -150,6 +150,8 @@ class BaseBackend: return self.generateValueNode(node) elif type(node) == list: return self.generateListNode(node) + elif isinstance(node, SigmaTypeModifier): + return self.generateTypedValueNode(node) else: raise TypeError("Node type %s was not expected in Sigma parse tree" % (str(type(node)))) @@ -174,6 +176,9 @@ class BaseBackend: def generateValueNode(self, node): raise NotImplementedError("Node type not implemented for this backend") + def generateTypedValueNode(self, node): + raise NotImplementedError("Node type not implemented for this backend") + def generateNULLValueNode(self, node): raise NotImplementedError("Node type not implemented for this backend") @@ -209,6 +214,7 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): listExpression = None # Syntax for lists, %s are list items separated with listSeparator listSeparator = None # Character for separation of list items valueExpression = None # Expression of values, %s represents value + typedValueExpression = dict() # Expression of typed values generated by type modifiers. modifier identifier -> expression dict, %s represents value nullExpression = None # Expression of queries for null values or non-existing fields. %s is field name notNullExpression = None # Expression of queries for not null values. %s is field name mapExpression = None # Syntax for field/value conditions. First %s is fieldname, second is value @@ -258,6 +264,8 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): return self.mapExpression % (transformed_fieldname, self.generateNode(value)) elif type(value) == list: return self.generateMapItemListNode(transformed_fieldname, value) + elif isinstance(value, SigmaTypeModifier): + return self.generateMapItemTypedNode(transformed_fieldname, value) elif value is None: return self.nullExpression % (transformed_fieldname, ) else: @@ -266,9 +274,18 @@ class SingleTextQueryBackend(RulenameCommentMixin, BaseBackend, QuoteCharMixin): def generateMapItemListNode(self, fieldname, value): return self.mapListValueExpression % (fieldname, self.generateNode(value)) + def generateMapItemTypedNode(self, fieldname, value): + return self.mapExpression % (fieldname, self.generateTypedValueNode(value)) + def generateValueNode(self, node): return self.valueExpression % (self.cleanValue(str(node))) + def generateTypedValueNode(self, node): + try: + return self.typedValueExpression[type(node)] % (self.cleanValue(str(node))) + except KeyError: + raise NotImplementedError("Type modifier '{}' is not supported by backend".format(node.identifier)) + def generateNULLValueNode(self, node): return self.nullExpression % (node.item) diff --git a/tools/sigma/backends/elasticsearch.py b/tools/sigma/backends/elasticsearch.py index a723a5767..68c4dd69d 100644 --- a/tools/sigma/backends/elasticsearch.py +++ b/tools/sigma/backends/elasticsearch.py @@ -20,6 +20,7 @@ import sys import sigma import yaml +from sigma.parser.modifiers.type import SigmaRegularExpressionModifier from .base import BaseBackend, SingleTextQueryBackend from .mixins import RulenameCommentMixin, MultiRuleOutputMixin from .exceptions import NotSupportedError @@ -79,6 +80,9 @@ class ElasticsearchQuerystringBackend(ElasticsearchWildcardHandlingMixin, Single listExpression = "(%s)" listSeparator = " " valueExpression = "%s" + typedValueExpression = { + SigmaRegularExpressionModifier: "/%s/" + } nullExpression = "NOT _exists_:%s" notNullExpression = "_exists_:%s" mapExpression = "%s:%s" diff --git a/tools/sigma/parser/modifiers/base.py b/tools/sigma/parser/modifiers/base.py index 0c5b7da07..6d5b5ed44 100644 --- a/tools/sigma/parser/modifiers/base.py +++ b/tools/sigma/parser/modifiers/base.py @@ -76,3 +76,9 @@ class SigmaTypeModifier(SigmaModifier): """Modify the type of the value. This must be handled by the backend.""" identifier = "type_base" modifier_type = SigmaModifierTypes.TYPE + + def apply(self): + return self + + def __str__(self): + return str(self.value) diff --git a/tools/sigma/parser/modifiers/type.py b/tools/sigma/parser/modifiers/type.py new file mode 100644 index 000000000..32a3fa732 --- /dev/null +++ b/tools/sigma/parser/modifiers/type.py @@ -0,0 +1,23 @@ +# Sigma value modifiers +# Copyright 2019 Thomas Patzke, Florian Roth + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with this program. If not, see . + +from .base import SigmaTypeModifier + +class SigmaRegularExpressionModifier(SigmaTypeModifier): + """Treat value as regular expression""" + identifier = "re" + active = True + valid_input_types = (str,)