diff --git a/dettact.py b/dettact.py index e93e145..10c47df 100644 --- a/dettact.py +++ b/dettact.py @@ -67,7 +67,10 @@ def init_menu(): parser_detection.add_argument('-fd', '--file-ds', help='path to the data source administration YAML file (used in ' 'the overlay with visibility to add metadata on the ' 'involved data sources)') - parser_detection.add_argument('-l', '--layer', help='generate detection layer for the ATT&CK navigator', + parser_detection.add_argument('-a', '--applicable', help='filter techniques in the layer file based on the' + 'applicable_to field in the technique administration YAML' + 'file', default='all') + parser_detection.add_argument('-l', '--layer', help='generate detection layer for the ATT&CK navigator', action='store_true') parser_detection.add_argument('-e', '--excel', help='generate an Excel sheet with all administrated techniques', action='store_true') @@ -174,9 +177,9 @@ def menu(menu_parser): if check_file_type(args.file_tech, FILE_TYPE_TECHNIQUE_ADMINISTRATION): if args.layer: - generate_detection_layer(args.file_tech, args.file_ds, False) + generate_detection_layer(args.file_tech, args.file_ds, False, args.applicable) if args.overlay and check_file_type(args.file_ds, FILE_TYPE_DATA_SOURCE_ADMINISTRATION): - generate_detection_layer(args.file_tech, args.file_ds, True) + generate_detection_layer(args.file_tech, args.file_ds, True, args.applicable) if args.graph: plot_detection_graph(args.file_tech) if args.excel: diff --git a/interactive_menu.py b/interactive_menu.py index 5b169e1..856251e 100644 --- a/interactive_menu.py +++ b/interactive_menu.py @@ -11,6 +11,7 @@ platform = 'Windows' stage = 'attack' groups_overlay = '' overlay_type = '' +filter_applicable_to = 'all' MENU_NAME_DATA_SOURCE_MAPPING = 'Data source mapping' MENU_NAME_VISIBILITY_MAPPING = 'Visibility coverage mapping' @@ -250,32 +251,39 @@ def menu_detection(filename_t): :param filename_t: :return: """ + global filter_applicable_to clear() print('Menu: %s' % MENU_NAME_DETECTION_COVERAGE_MAPPING) print('') print('Selected techniques YAML file: %s' % filename_t) print('') + print('Options:') + print('1. Filter techniques in the layer file based on the applicable_to field in the technique administration YAML file: %s' % filter_applicable_to) + print('') print('Select what you want to do:') - print('1. Generate a layer for detection coverage for the ATT&CK Navigator.') - print('2. Generate a layer for detection coverage overlayed with visibility for the ATT&CK Navigator.') - print('3. Generate a graph with detections added through time.') - print('4. Generate an Excel sheet with all administrated techniques.') + print('2. Generate a layer for detection coverage for the ATT&CK Navigator.') + print('3. Generate a layer for detection coverage overlayed with visibility for the ATT&CK Navigator.') + print('4. Generate a graph with detections added through time.') + print('5. Generate an Excel sheet with all administrated techniques.') print('9. Back to main menu.') choice = ask_input() if choice == '1': - print('Writing detection coverage layer...') - generate_detection_layer(filename_t, None, False) - wait() + print('Specify your filter for the applicable_to field:') + filter_applicable_to = ask_input().lower() elif choice == '2': - filename_ds = select_file(MENU_NAME_DETECTION_COVERAGE_MAPPING, 'data sources (used to add metadata on the involved data sources to the heat map)', FILE_TYPE_DATA_SOURCE_ADMINISTRATION, False) - print('Writing detection coverage layer with visibility as overlay...') - generate_detection_layer(filename_t, filename_ds, True) + print('Writing detection coverage layer...') + generate_detection_layer(filename_t, None, False, filter_applicable_to) wait() elif choice == '3': + filename_ds = select_file(MENU_NAME_DETECTION_COVERAGE_MAPPING, 'data sources (used to add metadata on the involved data sources to the heat map)', FILE_TYPE_DATA_SOURCE_ADMINISTRATION, False) + print('Writing detection coverage layer with visibility as overlay...') + generate_detection_layer(filename_t, filename_ds, True, filter_applicable_to) + wait() + elif choice == '4': print('Drawing the graph...') plot_detection_graph(filename_t) wait() - elif choice == '4': + elif choice == '5': print('Generating Excel file...') export_techniques_list_to_excel(filename_t) wait() diff --git a/technique_mapping.py b/technique_mapping.py index ac12a87..b5a31e8 100644 --- a/technique_mapping.py +++ b/technique_mapping.py @@ -4,7 +4,7 @@ import xlsxwriter # Imports for pandas and plotly are because of performance reasons in the function that uses these libraries. -def generate_detection_layer(filename_techniques, filename_data_sources, overlay): +def generate_detection_layer(filename_techniques, filename_data_sources, overlay, filter_applicable_to): """ Generates layer for detection coverage and optionally an overlayed version with visibility coverage. :param filename_techniques: the filename of the yaml file containing the techniques administration @@ -12,17 +12,17 @@ def generate_detection_layer(filename_techniques, filename_data_sources, overlay :param overlay: boolean value to specify if an overlay between detection and visibility should be generated :return: """ - my_techniques, name, platform = _load_detections(filename_techniques) + my_techniques, name, platform = _load_detections(filename_techniques, filter_applicable_to) if not overlay: mapped_techniques_detection = _map_and_colorize_techniques_for_detections(my_techniques) - layer_detection = get_layer_template_detections('Detections ' + name, 'description', 'attack', platform) - _write_layer(layer_detection, mapped_techniques_detection, 'detection', name) + layer_detection = get_layer_template_detections('Detections ' + name + ' ' + filter_applicable_to, 'description', 'attack', platform) + _write_layer(layer_detection, mapped_techniques_detection, 'detection', filter_applicable_to, name) else: my_data_sources = _load_data_sources(filename_data_sources) mapped_techniques_both = _map_and_colorize_techniques_for_overlayed(my_techniques, my_data_sources) - layer_both = get_layer_template_layered('Visibility and Detection ' + name, 'description', 'attack', platform) - _write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', name) + layer_both = get_layer_template_layered('Visibility and Detection ' + name + ' ' + filter_applicable_to, 'description', 'attack', platform) + _write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', filter_applicable_to, name) def generate_visibility_layer(filename_techniques, filename_data_sources, overlay): @@ -39,11 +39,11 @@ def generate_visibility_layer(filename_techniques, filename_data_sources, overla if not overlay: mapped_techniques_visibility = _map_and_colorize_techniques_for_visibility(my_techniques, my_data_sources) layer_visibility = get_layer_template_visibility('Visibility ' + name, 'description', 'attack', platform) - _write_layer(layer_visibility, mapped_techniques_visibility, 'visibility', name) + _write_layer(layer_visibility, mapped_techniques_visibility, 'visibility', '', name) else: mapped_techniques_both = _map_and_colorize_techniques_for_overlayed(my_techniques, my_data_sources) layer_both = get_layer_template_layered('Visibility and Detection ' + name, 'description', 'attack', platform) - _write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', name) + _write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', '', name) def plot_detection_graph(filename): @@ -75,17 +75,23 @@ def plot_detection_graph(filename): print("File written: " + output_filename) -def _load_detections(filename): +def _load_detections(filename, filter_applicable_to=''): """ Loads the techniques (including detection and visibility properties) from the given yaml file. :param filename: the filename of the yaml file containing the techniques administration :return: dictionary with techniques (incl. properties), name and platform """ + my_techniques = {} with open(filename, 'r') as yaml_file: yaml_content = yaml.load(yaml_file, Loader=yaml.FullLoader) for d in yaml_content['techniques']: - my_techniques[d['technique_id']] = d + applicable_to = True + if 'applicable_to' in d['detection'].keys(): + if filter_applicable_to != 'all' and filter_applicable_to not in d['detection']['applicable_to'] and 'all' not in d['detection']['applicable_to']: + applicable_to = False + if applicable_to: + my_techniques[d['technique_id']] = d name = yaml_content['name'] platform = yaml_content['platform'] return my_techniques, name, platform @@ -107,7 +113,7 @@ def _load_data_sources(filename): return my_data_sources -def _write_layer(layer, mapped_techniques, filename_prefix, name): +def _write_layer(layer, mapped_techniques, filename_prefix, filename_suffix, name): """ Writes the json layer file to disk. :param layer: the prepped layer dictionary @@ -119,7 +125,8 @@ def _write_layer(layer, mapped_techniques, filename_prefix, name): layer['techniques'] = mapped_techniques json_string = simplejson.dumps(layer).replace('}, ', '},\n') - output_filename = 'output/%s_%s.json' % (filename_prefix, normalize_name_to_filename(name)) + filename_suffix = '_' + filename_suffix if filename_suffix != '' else '' + output_filename = 'output/%s_%s%s.json' % (filename_prefix, normalize_name_to_filename(name), filename_suffix) with open(output_filename, 'w') as f: f.write(json_string) print("File written: " + output_filename) @@ -150,7 +157,10 @@ def _map_and_colorize_techniques_for_detections(my_techniques): for tactic in technique['tactic']: location = ', '.join(c['detection']['location']) if 'detection' in c.keys() else '-' location = location if location != '' else '-' - applicable_to = ', '.join(c['detection']['applicable_to']) if 'detection' in c.keys() else '-' + if 'applicable_to' in c['detection'].keys(): + applicable_to = ', '.join(c['detection']['applicable_to']) if 'detection' in c.keys() else '-' + else: + applicable_to = '-' x = {} x['techniqueID'] = d x['color'] = color @@ -162,8 +172,7 @@ def _map_and_colorize_techniques_for_detections(my_techniques): {'name': '-Comment', 'value': comment}, {'name': '-Applicable to', 'value': applicable_to}] x['score'] = s - - mapped_techniques.append(x) + mapped_techniques.append(x) except Exception: print('[!] Possible error in YAML file at: ' + d) quit()