|
| 1 | +"""Index analyzer plugin for sigma.""" |
| 2 | +from __future__ import unicode_literals |
| 3 | + |
| 4 | +import logging |
| 5 | +import os |
| 6 | + |
| 7 | +from sigma.backends import elasticsearch as sigma_elasticsearch |
| 8 | +import sigma.configuration as sigma_configuration |
| 9 | +from sigma.parser import collection as sigma_collection |
| 10 | + |
| 11 | + |
| 12 | +from timesketch.lib.analyzers import interface |
| 13 | +from timesketch.lib.analyzers import manager |
| 14 | + |
| 15 | + |
| 16 | +class SigmaPlugin(interface.BaseSketchAnalyzer): |
| 17 | + """Index analyzer for Sigma.""" |
| 18 | + |
| 19 | + NAME = 'sigma' |
| 20 | + |
| 21 | + _CONFIG_FILE = 'sigma_config.yaml' |
| 22 | + |
| 23 | + # Path to the directory containing the Sigma Rules to run, relative to |
| 24 | + # this file. |
| 25 | + _RULES_PATH = '' |
| 26 | + |
| 27 | + |
| 28 | + def __init__(self, index_name, sketch_id): |
| 29 | + """Initialize the Index Analyzer. |
| 30 | +
|
| 31 | + Args: |
| 32 | + index_name: Elasticsearch index name. |
| 33 | + sketch_id: Sketch ID. |
| 34 | + """ |
| 35 | + super(SigmaPlugin, self).__init__(index_name, sketch_id) |
| 36 | + sigma_config_path = interface.get_config_path(self._CONFIG_FILE) |
| 37 | + logging.debug('[sigma] Loading config from {0!s}'.format( |
| 38 | + sigma_config_path)) |
| 39 | + with open(sigma_config_path, 'r') as sigma_config_file: |
| 40 | + sigma_config = sigma_config_file.read() |
| 41 | + self.sigma_config = sigma_configuration.SigmaConfiguration(sigma_config) |
| 42 | + |
| 43 | + def run_sigma_rule(self, query, tag_name): |
| 44 | + """Runs a sigma rule and applies the appropriate tags. |
| 45 | +
|
| 46 | + Args: |
| 47 | + query: elastic search query for events to tag. |
| 48 | + tag_name: tag to apply to matching events. |
| 49 | +
|
| 50 | + Returns: |
| 51 | + int: number of events tagged. |
| 52 | + """ |
| 53 | + return_fields = [] |
| 54 | + tagged_events = 0 |
| 55 | + events = self.event_stream( |
| 56 | + query_string=query, return_fields=return_fields) |
| 57 | + for event in events: |
| 58 | + event.add_tags([tag_name]) |
| 59 | + event.commit() |
| 60 | + tagged_events += 1 |
| 61 | + return tagged_events |
| 62 | + |
| 63 | + def run(self): |
| 64 | + """Entry point for the analyzer. |
| 65 | +
|
| 66 | + Returns: |
| 67 | + String with summary of the analyzer result. |
| 68 | + """ |
| 69 | + sigma_backend = sigma_elasticsearch.ElasticsearchQuerystringBackend( |
| 70 | + self.sigma_config, {}) |
| 71 | + tags_applied = {} |
| 72 | + |
| 73 | + rules_path = os.path.join(os.path.dirname(__file__), self._RULES_PATH) |
| 74 | + for rule_filename in os.listdir(rules_path): |
| 75 | + tag_name, _ = rule_filename.rsplit('.') |
| 76 | + tags_applied[tag_name] = 0 |
| 77 | + rule_file_path = os.path.join(rules_path, rule_filename) |
| 78 | + rule_file_path = os.path.abspath(rule_file_path) |
| 79 | + logging.info('[sigma] Reading rules from {0!s}'.format( |
| 80 | + rule_file_path)) |
| 81 | + with open(rule_file_path, 'r') as rule_file: |
| 82 | + rule_file_content = rule_file.read() |
| 83 | + parser = sigma_collection.SigmaCollectionParser( |
| 84 | + rule_file_content, self.sigma_config, None) |
| 85 | + try: |
| 86 | + results = parser.generate(sigma_backend) |
| 87 | + except NotImplementedError as exception: |
| 88 | + logging.error( |
| 89 | + 'Error generating rule in file {0:s}: {1!s}'.format( |
| 90 | + rule_file_path, exception)) |
| 91 | + continue |
| 92 | + |
| 93 | + for result in results: |
| 94 | + logging.info( |
| 95 | + '[sigma] Generated query {0:s}'.format(result)) |
| 96 | + number_of_tagged_events = self.run_sigma_rule( |
| 97 | + result, tag_name) |
| 98 | + tags_applied[tag_name] += number_of_tagged_events |
| 99 | + |
| 100 | + total_tagged_events = sum(tags_applied.values()) |
| 101 | + output_string = 'Applied {0:d} tags\n'.format(total_tagged_events) |
| 102 | + for tag_name, number_of_tagged_events in tags_applied.items(): |
| 103 | + output_string += '* {0:s}: {1:d}'.format( |
| 104 | + tag_name, number_of_tagged_events) |
| 105 | + return output_string |
| 106 | + |
| 107 | + |
| 108 | +class LinuxRulesSigmaPlugin(SigmaPlugin): |
| 109 | + """Sigma plugin to run Linux rules.""" |
| 110 | + |
| 111 | + _RULES_PATH = '../../../data/linux' |
| 112 | + |
| 113 | + NAME = 'sigma_linux' |
| 114 | + |
| 115 | + |
| 116 | +manager.AnalysisManager.register_analyzer(LinuxRulesSigmaPlugin) |
0 commit comments