2017-02-13 22:29:56 +00:00
|
|
|
# Sigma parser
|
2018-07-26 20:28:33 +00:00
|
|
|
# Copyright 2016-2018 Thomas Patzke, Florian Roth
|
2017-12-07 20:55:43 +00:00
|
|
|
|
|
|
|
# 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 <http://www.gnu.org/licenses/>.
|
2017-02-13 22:29:56 +00:00
|
|
|
|
|
|
|
import yaml
|
2018-07-26 22:02:07 +00:00
|
|
|
from .rule import SigmaParser
|
2017-02-15 23:40:08 +00:00
|
|
|
|
2017-10-31 22:06:18 +00:00
|
|
|
class SigmaCollectionParser:
|
2017-10-31 23:17:55 +00:00
|
|
|
"""
|
|
|
|
Parses a Sigma file that may contain multiple Sigma rules as different YAML documents.
|
|
|
|
|
|
|
|
Special processing of YAML document if 'action' attribute is set to:
|
|
|
|
|
|
|
|
* global: merges attributes from document in all following documents. Accumulates attributes from previous set_global documents
|
|
|
|
* reset: resets global attributes from previous set_global statements
|
|
|
|
* repeat: takes attributes from this YAML document, merges into previous rule YAML and regenerates the rule
|
|
|
|
"""
|
2017-11-14 21:17:18 +00:00
|
|
|
def __init__(self, content, config=None, rulefilter=None):
|
|
|
|
if config is None:
|
2018-07-27 21:54:18 +00:00
|
|
|
from sigma.configuration import SigmaConfiguration
|
2017-11-14 21:17:18 +00:00
|
|
|
config = SigmaConfiguration()
|
2017-10-31 22:06:18 +00:00
|
|
|
self.yamls = yaml.safe_load_all(content)
|
2017-10-31 23:17:55 +00:00
|
|
|
globalyaml = dict()
|
|
|
|
self.parsers = list()
|
|
|
|
prevrule = None
|
|
|
|
for yamldoc in self.yamls:
|
|
|
|
action = None
|
|
|
|
try:
|
|
|
|
action = yamldoc['action']
|
|
|
|
del yamldoc['action']
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if action == "global":
|
|
|
|
deep_update_dict(globalyaml, yamldoc)
|
|
|
|
elif action == "reset":
|
|
|
|
globalyaml = dict()
|
|
|
|
elif action == "repeat":
|
|
|
|
if prevrule is None:
|
|
|
|
raise SigmaCollectionParseError("action 'repeat' is only applicable after first valid Sigma rule")
|
2017-11-01 23:02:15 +00:00
|
|
|
newrule = prevrule.copy()
|
|
|
|
deep_update_dict(newrule, yamldoc)
|
|
|
|
if rulefilter is None or rulefilter is not None and not rulefilter.match(newrule):
|
|
|
|
self.parsers.append(SigmaParser(newrule, config))
|
|
|
|
prevrule = newrule
|
2017-10-31 23:17:55 +00:00
|
|
|
else:
|
|
|
|
deep_update_dict(yamldoc, globalyaml)
|
2017-11-01 23:02:15 +00:00
|
|
|
if rulefilter is None or rulefilter is not None and rulefilter.match(yamldoc):
|
|
|
|
self.parsers.append(SigmaParser(yamldoc, config))
|
|
|
|
prevrule = yamldoc
|
2017-10-31 22:06:18 +00:00
|
|
|
self.config = config
|
|
|
|
|
|
|
|
def generate(self, backend):
|
2017-10-31 23:17:55 +00:00
|
|
|
"""Calls backend for all parsed rules"""
|
2018-08-02 20:41:32 +00:00
|
|
|
return filter(
|
|
|
|
lambda x: bool(x), # filter None's and empty strings
|
|
|
|
[ backend.generate(parser) for parser in self.parsers ]
|
|
|
|
)
|
2017-10-31 22:06:18 +00:00
|
|
|
|
2017-11-14 21:17:18 +00:00
|
|
|
def __iter__(self):
|
|
|
|
return iter([parser.parsedyaml for parser in self.parsers])
|
|
|
|
|
2017-10-31 23:17:55 +00:00
|
|
|
def deep_update_dict(dest, src):
|
|
|
|
for key, value in src.items():
|
|
|
|
if isinstance(value, dict) and key in dest and isinstance(dest[key], dict): # source is dict, destination key already exists and is dict: merge
|
|
|
|
deep_update_dict(dest[key], value)
|
|
|
|
else:
|
|
|
|
dest[key] = value
|