def main(): argparser = set_argparser() cmdargs = argparser.parse_args() scm = SigmaConfigurationManager() logger = logging.getLogger(__name__) if cmdargs.debug: # pragma: no cover logger.setLevel(logging.DEBUG) if cmdargs.lists: print("Backends (Targets):") list_backends(cmdargs.debug) print() print("Configurations (Sources):") list_configurations(backend=cmdargs.target, scm=scm) print() print("Modifiers:") list_modifiers(modifiers=modifiers) sys.exit(0) elif len(cmdargs.inputs) == 0: print("Nothing to do!") argparser.print_usage() sys.exit(0) if cmdargs.target is None: print("No target selected, select one with -t/--target") argparser.print_usage() sys.exit(ERR_NO_TARGET) rulefilter = None if cmdargs.filter: try: rulefilter = SigmaRuleFilter(cmdargs.filter) except SigmaRuleFilterParseException as e: print("Parse error in Sigma rule filter expression: %s" % str(e), file=sys.stderr) sys.exit(ERR_RULE_FILTER_PARSING) sigmaconfigs = SigmaConfigurationChain() backend_class = backends.getBackend(cmdargs.target) if cmdargs.config is None: if backend_class.config_required and not cmdargs.shoot_yourself_in_the_foot: print("The backend you want to use usually requires a configuration to generate valid results. Please provide one with --config/-c.", file=sys.stderr) print("Available choices for this backend (get complete list with --lists/-l):") list_configurations(backend=cmdargs.target, scm=scm) sys.exit(ERR_CONFIG_REQUIRED) if backend_class.default_config is not None: cmdargs.config = backend_class.default_config if cmdargs.config: order = 0 for conf_name in cmdargs.config: try: sigmaconfig = scm.get(conf_name) if sigmaconfig.order is not None: if sigmaconfig.order <= order and not cmdargs.shoot_yourself_in_the_foot: print("The configurations were provided in the wrong order (order key check in config file)", file=sys.stderr) sys.exit(ERR_CONFIG_ORDER) order = sigmaconfig.order try: if cmdargs.target not in sigmaconfig.config["backends"]: print("The configuration '{}' is not valid for backend '{}'. Valid choices are: {}".format(conf_name, cmdargs.target, ", ".join(sigmaconfig.config["backends"])), file=sys.stderr) sys.exit(ERR_CONFIG_ORDER) except KeyError: pass sigmaconfigs.append(sigmaconfig) except OSError as e: print("Failed to open Sigma configuration file %s: %s" % (conf_name, str(e)), file=sys.stderr) exit(ERR_OPEN_CONFIG_FILE) except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e: print("Sigma configuration file %s is no valid YAML: %s" % (conf_name, str(e)), file=sys.stderr) exit(ERR_CONFIG_INVALID_YAML) except SigmaConfigParseError as e: print("Sigma configuration parse error in %s: %s" % (conf_name, str(e)), file=sys.stderr) exit(ERR_CONFIG_PARSING) backend_options = BackendOptions(cmdargs.backend_option, cmdargs.backend_config) backend = backend_class(sigmaconfigs, backend_options) filename = cmdargs.output if filename: try: out = open(filename, "w", encoding='utf-8') except (IOError, OSError) as e: print("Failed to open output file '%s': %s" % (filename, str(e)), file=sys.stderr) exit(ERR_OUTPUT) else: out = sys.stdout error = 0 for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse): logger.debug("* Processing Sigma input %s" % (sigmafile)) try: if cmdargs.inputs == ['-']: f = sigmafile else: f = sigmafile.open(encoding='utf-8') parser = SigmaCollectionParser(f, sigmaconfigs, rulefilter) results = parser.generate(backend) for result in results: print(result, file=out) except OSError as e: print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)), file=sys.stderr) error = ERR_OPEN_SIGMA_RULE except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e: print("Sigma file %s is no valid YAML: %s" % (sigmafile, str(e)), file=sys.stderr) error = ERR_INVALID_YAML if not cmdargs.defer_abort: sys.exit(error) except (SigmaParseError, SigmaCollectionParseError) as e: print("Sigma parse error in %s: %s" % (sigmafile, str(e)), file=sys.stderr) error = ERR_SIGMA_PARSING if not cmdargs.defer_abort: sys.exit(error) except NotSupportedError as e: print("The Sigma rule requires a feature that is not supported by the target system: " + str(e), file=sys.stderr) if not cmdargs.ignore_backend_errors: error = ERR_NOT_SUPPORTED if not cmdargs.defer_abort: sys.exit(error) except BackendError as e: print("Backend error in %s: %s" % (sigmafile, str(e)), file=sys.stderr) if not cmdargs.ignore_backend_errors: error = ERR_BACKEND if not cmdargs.defer_abort: sys.exit(error) except (NotImplementedError, TypeError) as e: print("An unsupported feature is required for this Sigma rule (%s): " % (sigmafile) + 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) if not cmdargs.ignore_backend_errors: error = ERR_NOT_IMPLEMENTED if not cmdargs.defer_abort: sys.exit(error) except PartialMatchError as e: print("Partial field match error: %s" % str(e), file=sys.stderr) if not cmdargs.ignore_backend_errors: error = ERR_PARTIAL_FIELD_MATCH if not cmdargs.defer_abort: sys.exit(error) except FullMatchError as e: print("Full field match error", file=sys.stderr) if not cmdargs.ignore_backend_errors: error = ERR_FULL_FIELD_MATCH if not cmdargs.defer_abort: sys.exit(error) finally: try: f.close() except: pass result = backend.finalize() if result: print(result, file=out) out.close() sys.exit(error)
def main(): argparser = set_argparser() cmdargs = argparser.parse_args() scm = SigmaConfigurationManager() logger = logging.getLogger(__name__) if cmdargs.debug: # pragma: no cover logging.basicConfig(filename='sigmac.log', filemode='w', level=logging.DEBUG) logger.setLevel(logging.DEBUG) if cmdargs.lists: print("Backends (Targets):") list_backends(cmdargs.debug) print() print("Configurations (Sources):") list_configurations(backend=cmdargs.target, scm=scm) print() print("Modifiers:") list_modifiers(modifiers=modifiers) sys.exit(0) elif len(cmdargs.inputs) == 0: print("Nothing to do!") argparser.print_usage() sys.exit(0) if cmdargs.target is None: print("No target selected, select one with -t/--target") argparser.print_usage() sys.exit(ERR_NO_TARGET) logger.debug("* Target selected %s" % (cmdargs.target)) rulefilter = None if cmdargs.filter: try: rulefilter = SigmaRuleFilter(cmdargs.filter) except SigmaRuleFilterParseException as e: print("Parse error in Sigma rule filter expression: %s" % str(e), file=sys.stderr) sys.exit(ERR_RULE_FILTER_PARSING) sigmaconfigs = SigmaConfigurationChain() backend_class = backends.getBackend(cmdargs.target) if cmdargs.config is None: if backend_class.config_required and not cmdargs.shoot_yourself_in_the_foot: print( "The backend you want to use usually requires a configuration to generate valid results. Please provide one with --config/-c.", file=sys.stderr) print( "Available choices for this backend (get complete list with --lists/-l):" ) list_configurations(backend=cmdargs.target, scm=scm) sys.exit(ERR_CONFIG_REQUIRED) if backend_class.default_config is not None: cmdargs.config = backend_class.default_config if cmdargs.config: order = 0 for conf_name in cmdargs.config: try: sigmaconfig = scm.get(conf_name) if sigmaconfig.order is not None: if sigmaconfig.order <= order and not cmdargs.shoot_yourself_in_the_foot: print( "The configurations were provided in the wrong order (order key check in config file)", file=sys.stderr) sys.exit(ERR_CONFIG_ORDER) order = sigmaconfig.order try: if cmdargs.target not in sigmaconfig.config["backends"]: print( "The configuration '{}' is not valid for backend '{}'. Valid choices are: {}" .format(conf_name, cmdargs.target, ", ".join(sigmaconfig.config["backends"])), file=sys.stderr) sys.exit(ERR_CONFIG_ORDER) except KeyError: pass sigmaconfigs.append(sigmaconfig) except OSError as e: print("Failed to open Sigma configuration file %s: %s" % (conf_name, str(e)), file=sys.stderr) exit(ERR_OPEN_CONFIG_FILE) except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e: print("Sigma configuration file %s is no valid YAML: %s" % (conf_name, str(e)), file=sys.stderr) exit(ERR_CONFIG_INVALID_YAML) except SigmaConfigParseError as e: print("Sigma configuration parse error in %s: %s" % (conf_name, str(e)), file=sys.stderr) exit(ERR_CONFIG_PARSING) if cmdargs.output_fields: if cmdargs.output_format: output_fields_rejected = [ field for field in cmdargs.output_fields.split(",") if field not in allowed_fields ] # Not allowed fields if output_fields_rejected: print( "These fields are not allowed (check help for allow field list) : %s" % (", ".join(output_fields_rejected)), file=sys.stderr) exit(ERR_OUTPUT_FORMAT) else: output_fields_filtered = [ field for field in cmdargs.output_fields.split(",") if field in allowed_fields ] # Keep only allowed fields else: print( "The '--output-fields' or '-of' arguments must be used with '--output-format' or '-oF' equal to 'json' or 'yaml'", file=sys.stderr) exit(ERR_OUTPUT_FORMAT) backend_options = BackendOptions(cmdargs.backend_option, cmdargs.backend_config) backend = backend_class(sigmaconfigs, backend_options) filename_ext = cmdargs.output_extention filename = cmdargs.output fileprefix = None if filename: if filename_ext: if filename_ext[0] == '.': pass else: filename_ext = '.' + filename_ext else: filename_ext = '.rule' if filename[-1:] in ['_', '/', '\\']: fileprefix = filename else: try: out = open(filename, "w", encoding='utf-8') except (IOError, OSError) as e: print("Failed to open output file '%s': %s" % (filename, str(e)), file=sys.stderr) exit(ERR_OUTPUT) else: out = sys.stdout error = 0 output_array = [] for sigmafile in get_inputs(cmdargs.inputs, cmdargs.recurse): logger.debug("* Processing Sigma input %s" % (sigmafile)) success = True try: if cmdargs.inputs == ['-']: f = sigmafile else: f = sigmafile.open(encoding='utf-8') parser = SigmaCollectionParser(f, sigmaconfigs, rulefilter, sigmafile) results = parser.generate(backend) nb_result = len(list(copy.deepcopy(results))) inc_filenane = None if nb_result < 2 else 0 newline_separator = '\0' if cmdargs.print0 else '\n' results = list( results ) # Since results is an iterator and used twice we convert it a list for result in results: if not fileprefix == None and not inc_filenane == None: #yml action try: filename = fileprefix + str(sigmafile.name) filename = filename.replace( '.yml', '_' + str(inc_filenane) + filename_ext) inc_filenane += 1 out = open(filename, "w", encoding='utf-8') except (IOError, OSError) as e: print("Failed to open output file '%s': %s" % (filename, str(e)), file=sys.stderr) exit(ERR_OUTPUT) elif not fileprefix == None and inc_filenane == None: # a simple yml try: filename = fileprefix + str(sigmafile.name) filename = filename.replace('.yml', filename_ext) out = open(filename, "w", encoding='utf-8') except (IOError, OSError) as e: print("Failed to open output file '%s': %s" % (filename, str(e)), file=sys.stderr) exit(ERR_OUTPUT) if not cmdargs.output_fields: print(result, file=out, end=newline_separator) if cmdargs.output_fields: # Handle output fields output = {} f.seek(0) docs = yaml.load_all(f, Loader=yaml.FullLoader) for doc in docs: for k, v in doc.items(): if k in output_fields_filtered: output[k] = v output['rule'] = [result for result in results] if "filename" in output_fields_filtered: output['filename'] = str(sigmafile.name) output_array.append(output) if nb_result == 0: # backend get only 1 output if not fileprefix == None: # want a prefix anyway try: filename = "%s%s_mono_output%s" % ( fileprefix, cmdargs.target, filename_ext) out = open(filename, "w", encoding='utf-8') fileprefix = None # no need to open the same file many time except (IOError, OSError) as e: print("Failed to open output file '%s': %s" % (filename, str(e)), file=sys.stderr) exit(ERR_OUTPUT) except OSError as e: print("Failed to open Sigma file %s: %s" % (sigmafile, str(e)), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False error = ERR_OPEN_SIGMA_RULE except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e: print("Error: Sigma file %s is no valid YAML: %s" % (sigmafile, str(e)), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False error = ERR_INVALID_YAML if not cmdargs.defer_abort: sys.exit(error) except (SigmaParseError, SigmaCollectionParseError) as e: print("Error: Sigma parse error in %s: %s" % (sigmafile, str(e)), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False error = ERR_SIGMA_PARSING if not cmdargs.defer_abort: sys.exit(error) except NotSupportedError as e: print( "Error: The Sigma rule requires a feature that is not supported by the target system: " + str(e), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False if not cmdargs.ignore_backend_errors: error = ERR_NOT_SUPPORTED if not cmdargs.defer_abort: sys.exit(error) except BackendError as e: print("Error: Backend error in %s: %s" % (sigmafile, str(e)), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False if not cmdargs.ignore_backend_errors: error = ERR_BACKEND if not cmdargs.defer_abort: sys.exit(error) except (NotImplementedError, TypeError) as e: print( "An unsupported feature is required for this Sigma rule (%s): " % (sigmafile) + str(e), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False if not cmdargs.ignore_backend_errors: error = ERR_NOT_IMPLEMENTED if not cmdargs.defer_abort: sys.exit(error) except PartialMatchError as e: print("Error: Partial field match error: %s" % str(e), file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False if not cmdargs.ignore_backend_errors: error = ERR_PARTIAL_FIELD_MATCH if not cmdargs.defer_abort: sys.exit(error) except FullMatchError as e: print("Error: Full field match error", file=sys.stderr) logger.debug("* Convertion Sigma input %s FAILURE" % (sigmafile)) success = False if not cmdargs.ignore_backend_errors: error = ERR_FULL_FIELD_MATCH if not cmdargs.defer_abort: sys.exit(error) finally: try: f.close() except: pass if success: logger.debug("* Convertion Sigma input %s SUCCESS" % (sigmafile)) result = backend.finalize() if result: print(result, file=out) if cmdargs.output_fields: if cmdargs.output_format == 'json': print(json.dumps(output_array, indent=4, ensure_ascii=False), file=out) elif cmdargs.output_format == 'yaml': print(ruamel.yaml.round_trip_dump(output_array), file=out) out.close() sys.exit(error)