2017-02-13 22:14:40 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# A Sigma to SIEM converter
|
|
|
|
|
|
|
|
import sys
|
|
|
|
import argparse
|
|
|
|
import yaml
|
|
|
|
import json
|
2017-03-06 08:36:10 +00:00
|
|
|
import pathlib
|
|
|
|
import itertools
|
2017-03-05 22:44:52 +00:00
|
|
|
from sigma import SigmaParser, SigmaParseError, SigmaConfiguration, SigmaConfigParseError
|
2017-02-13 22:14:40 +00:00
|
|
|
import backends
|
|
|
|
|
|
|
|
def print_verbose(*args, **kwargs):
|
|
|
|
if cmdargs.verbose or cmdargs.debug:
|
|
|
|
print(*args, **kwargs)
|
|
|
|
|
|
|
|
def print_debug(*args, **kwargs):
|
|
|
|
if cmdargs.debug:
|
|
|
|
print(*args, **kwargs)
|
|
|
|
|
2017-03-06 08:36:10 +00:00
|
|
|
def alliter(path):
|
|
|
|
for sub in path.iterdir():
|
|
|
|
if sub.is_dir():
|
|
|
|
yield from alliter(sub)
|
|
|
|
else:
|
|
|
|
yield sub
|
|
|
|
|
|
|
|
def get_inputs(paths, recursive):
|
|
|
|
if recursive:
|
|
|
|
return list(itertools.chain.from_iterable([list(alliter(pathlib.Path(p))) for p in paths]))
|
|
|
|
else:
|
2017-03-07 08:41:46 +00:00
|
|
|
return [pathlib.Path(p) for p in paths]
|
2017-03-06 08:36:10 +00:00
|
|
|
|
2017-02-13 22:14:40 +00:00
|
|
|
argparser = argparse.ArgumentParser(description="Convert Sigma rules into SIEM signatures.")
|
2017-03-06 08:36:10 +00:00
|
|
|
argparser.add_argument("--recurse", "-r", action="store_true", help="Recurse into subdirectories (not yet implemented)")
|
2017-03-01 08:40:51 +00:00
|
|
|
argparser.add_argument("--target", "-t", default="es-qs", choices=backends.getBackendDict().keys(), help="Output target format")
|
2017-02-13 22:14:40 +00:00
|
|
|
argparser.add_argument("--target-list", "-l", action="store_true", help="List available output target formats")
|
2017-03-04 22:36:46 +00:00
|
|
|
argparser.add_argument("--config", "-c", help="Configuration with field name and index mapping for target environment (not yet implemented)")
|
|
|
|
argparser.add_argument("--output", "-o", help="Output file or filename prefix if multiple files are generated (not yet implemented)")
|
2017-02-13 22:14:40 +00:00
|
|
|
argparser.add_argument("--verbose", "-v", action="store_true", help="Be verbose")
|
|
|
|
argparser.add_argument("--debug", "-d", action="store_true", help="Debugging output")
|
|
|
|
argparser.add_argument("inputs", nargs="*", help="Sigma input files")
|
|
|
|
cmdargs = argparser.parse_args()
|
|
|
|
|
|
|
|
if cmdargs.target_list:
|
|
|
|
for backend in backends.getBackendList():
|
|
|
|
print("%10s: %s" % (backend.identifier, backend.__doc__))
|
|
|
|
sys.exit(0)
|
|
|
|
|
2017-03-04 22:36:46 +00:00
|
|
|
if cmdargs.output:
|
|
|
|
print("--output/-o not yet implemented", file=sys.stderr)
|
|
|
|
sys.exit(99)
|
2017-03-06 21:07:04 +00:00
|
|
|
|
|
|
|
sigmaconfig = SigmaConfiguration()
|
2017-03-04 22:36:46 +00:00
|
|
|
if cmdargs.config:
|
2017-03-05 22:44:52 +00:00
|
|
|
try:
|
|
|
|
conffile = cmdargs.config
|
|
|
|
f = open(conffile)
|
2017-03-06 21:07:04 +00:00
|
|
|
sigmaconfig = SigmaConfiguration(f)
|
2017-03-05 22:44:52 +00:00
|
|
|
except OSError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Failed to open Sigma configuration file %s: %s" % (conffile, str(e)), file=sys.stderr)
|
2017-03-05 22:44:52 +00:00
|
|
|
except yaml.parser.ParserError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Sigma configuration file %s is no valid YAML: %s" % (conffile, str(e)), file=sys.stderr)
|
2017-03-05 22:44:52 +00:00
|
|
|
except SigmaParseError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Sigma configuration parse error in %s: %s" % (conffile, str(e)), file=sys.stderr)
|
2017-03-04 22:36:46 +00:00
|
|
|
|
2017-02-22 21:47:12 +00:00
|
|
|
try:
|
2017-03-06 21:07:04 +00:00
|
|
|
backend = backends.getBackend(cmdargs.target)(sigmaconfig)
|
2017-02-22 21:47:12 +00:00
|
|
|
except LookupError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Backend not found!", file=sys.stderr)
|
2017-02-22 21:47:12 +00:00
|
|
|
sys.exit(1)
|
|
|
|
|
2017-03-06 08:36:10 +00:00
|
|
|
for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse):
|
2017-02-22 21:43:35 +00:00
|
|
|
print_verbose("* Processing Sigma input %s" % (sigmafile))
|
2017-02-13 22:14:40 +00:00
|
|
|
try:
|
2017-03-06 20:26:56 +00:00
|
|
|
f = sigmafile.open()
|
2017-03-12 22:12:21 +00:00
|
|
|
parser = SigmaParser(f, sigmaconfig)
|
2017-02-22 21:47:12 +00:00
|
|
|
print_debug("Parsed YAML:\n", json.dumps(parser.parsedyaml, indent=2))
|
2017-02-22 21:43:35 +00:00
|
|
|
parser.parse_sigma()
|
2017-02-16 22:58:44 +00:00
|
|
|
for condtoken in parser.condtoken:
|
2017-02-22 21:47:12 +00:00
|
|
|
print_debug("Condition Tokens:", condtoken)
|
|
|
|
for condparsed in parser.condparsed:
|
|
|
|
print_debug("Condition Parse Tree:", condparsed)
|
2017-02-22 21:47:12 +00:00
|
|
|
print(backend.generate(condparsed))
|
2017-02-13 22:14:40 +00:00
|
|
|
except OSError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)), file=sys.stderr)
|
2017-02-13 22:14:40 +00:00
|
|
|
except yaml.parser.ParserError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Sigma file %s is no valid YAML: %s" % (sigmafile, str(e)), file=sys.stderr)
|
2017-02-22 21:43:35 +00:00
|
|
|
except SigmaParseError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("Sigma parse error in %s: %s" % (sigmafile, str(e)), file=sys.stderr)
|
2017-02-22 21:43:35 +00:00
|
|
|
except NotImplementedError as e:
|
2017-03-06 22:01:33 +00:00
|
|
|
print("An unsupported feature is required for this Sigma rule: " + str(e), file=sys.stderr)
|
|
|
|
print("Feel free to contribute for fun and fame, this is open source :) -> https://github.com/Neo23x0/sigma", file=sys.stderr)
|
2017-02-13 22:14:40 +00:00
|
|
|
finally:
|
|
|
|
f.close()
|
2017-02-22 21:43:35 +00:00
|
|
|
print_debug()
|