def generate_group_heat_map(groups, overlay, overlay_type, stage, platform, software_groups, search_visibility, search_detection, health_is_called, include_all_score_objs=False): """ Calls all functions that are necessary for the generation of the heat map and write a json layer to disk. :param groups: threat actor groups :param overlay: group(s), visibility or detections to overlay (group ID, group name/alias, YAML file with group(s), detections or visibility) :param overlay_type: group, visibility or detection :param stage: attack or pre-attack :param platform: one of the values from PLATFORMS constant or 'all' :param software_groups: specify if techniques from related software should be included :param search_visibility: visibility EQL search query :param search_detection: detection EQL search query :param health_is_called: boolean that specifies if detailed errors in the file will be printed :param include_all_score_objs: include all score objects within the score_logbook for the EQL query :return: returns nothing when something's wrong """ overlay_dict = {} groups_software_dict = {} groups_file_type = None if os.path.isfile(groups): groups_file_type = check_file(groups, file_type=FILE_TYPE_GROUP_ADMINISTRATION, health_is_called=health_is_called) if not groups_file_type: return else: # remove whitespaces (leading and trailing), convert to lower case and put in a list groups = groups.split(',') groups = list(map(lambda x: x.strip().lower(), groups)) # set the correct value for platform if groups_file_type == FILE_TYPE_GROUP_ADMINISTRATION: _yaml = init_yaml() with open(groups, 'r') as yaml_file: group_file = _yaml.load(yaml_file) platform_yaml = get_platform_from_yaml(group_file) if platform_yaml: platform = platform_yaml if isinstance(platform, str) and platform.lower() != 'all': platform = [platform] overlay_file_type = None if overlay: if os.path.isfile(overlay): expected_file_type = FILE_TYPE_GROUP_ADMINISTRATION if overlay_type == OVERLAY_TYPE_GROUP \ else FILE_TYPE_TECHNIQUE_ADMINISTRATION \ if overlay_type in [OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION] else None overlay_file_type = check_file(overlay, expected_file_type, health_is_called=health_is_called) if not overlay_file_type: return else: overlay = overlay.split(',') overlay = list(map(lambda x: x.strip().lower(), overlay)) else: overlay = [] # load the techniques (visibility or detection) from the YAML file all_techniques = None if overlay_file_type == FILE_TYPE_TECHNIQUE_ADMINISTRATION: # filter out visibility and/or detection objects using EQL if search_detection or search_visibility: overlay = techniques_search( overlay, search_visibility, search_detection, include_all_score_objs=include_all_score_objs) if not overlay: return None # something went wrong in executing the search or 0 results where returned if overlay_type == OVERLAY_TYPE_VISIBILITY: overlay_dict, all_techniques = _get_visibility_techniques(overlay) elif overlay_type == OVERLAY_TYPE_DETECTION: overlay_dict, all_techniques = _get_detection_techniques(overlay) # we are not overlaying visibility or detection, overlay group will therefore contain information on another group elif len(overlay) > 0: overlay_dict = _get_group_techniques(overlay, stage, platform, overlay_file_type) if overlay_dict == -1: return groups_dict = _get_group_techniques(groups, stage, platform, groups_file_type) if groups_dict == -1: return if len(groups_dict) == 0: print('[!] Empty layer.' ) # the provided groups dit not result in any techniques return # check if we are doing a software group overlay if software_groups and overlay: if overlay_type not in [ OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION ]: # if a group overlay is provided, get the software techniques for the overlay groups_software_dict = _get_software_techniques( overlay, stage, platform) elif software_groups: groups_software_dict = _get_software_techniques( groups, stage, platform) technique_count, max_count = _get_technique_count(groups_dict, overlay_dict, groups_software_dict, overlay_type, all_techniques) technique_layer = _get_technique_layer(technique_count, groups_dict, overlay_dict, groups_software_dict, overlay_file_type, overlay_type, all_techniques) # make a list group names for the involved groups. if groups == ['all']: groups_list = ['all'] else: groups_list = _get_group_list(groups_dict, groups_file_type) overlay_list = _get_group_list(overlay_dict, overlay_file_type) desc = 'stage: ' + stage + ' | platform(s): ' + platform_to_name(platform, separator=', ') + ' | group(s): ' \ + ', '.join(groups_list) + ' | overlay group(s): ' + ', '.join(overlay_list) layer = get_layer_template_groups( stage[0].upper() + stage[1:] + ' - ' + platform_to_name(platform, separator=', '), max_count, desc, stage, platform, overlay_type) layer['techniques'] = technique_layer json_string = simplejson.dumps(layer).replace('}, ', '},\n') if stage == 'pre-attack': filename = '_'.join(groups_list) elif overlay: filename = platform_to_name(platform) + '_' + '_'.join( groups_list) + '-overlay_' + '_'.join(overlay_list) else: filename = platform_to_name(platform) + '_' + '_'.join(groups_list) write_file(stage, filename[:255], json_string)
def generate_group_heat_map(groups, overlay, overlay_type, platform, software_groups, search_visibility, search_detection, health_is_called, output_filename, layer_name, include_all_score_objs=False): """ Calls all functions that are necessary for the generation of the heat map and write a json layer to disk. :param groups: threat actor groups :param overlay: group(s), visibility or detections to overlay (group ID, group name/alias, YAML file with group(s), detections or visibility) :param overlay_type: group, visibility or detection :param platform: one or multiple the values from PLATFORMS constant or None (default = Windows) :param software_groups: specify if techniques from related software should be included :param search_visibility: visibility EQL search query :param search_detection: detection EQL search query :param health_is_called: boolean that specifies if detailed errors in the file will be printed :param output_filename: output filename defined by the user :param layer_name: the name of the Navigator layer :param include_all_score_objs: include all score objects within the score_logbook for the EQL query :return: returns None when something went wrong """ overlay_dict = {} groups_software_dict = {} groups_file_type = None if groups == None: groups = ['all'] elif os.path.isfile(groups[0]): groups = groups[0] groups_file_type = check_file(groups, file_type=FILE_TYPE_GROUP_ADMINISTRATION, health_is_called=health_is_called) if not groups_file_type: return None # the groups_file_type is not of the type FILE_TYPE_GROUP_ADMINISTRATION elif isinstance(groups[0], str) and len(groups) == 1: # reached when the groups are provided via the interactive menu, or when the CLI only contained one argument groups = groups[0] groups = groups.split(',') groups = list(map(lambda x: x.strip().lower(), groups)) else: # reached when the groups are provided via CLI arguments groups = list(map(lambda x: x.strip().lower(), groups)) # set the correct value for platform platform_yaml = None if groups_file_type == FILE_TYPE_GROUP_ADMINISTRATION: _yaml = init_yaml() with open(groups, 'r') as yaml_file: group_file = _yaml.load(yaml_file) platform_yaml = get_platform_from_yaml(group_file) if platform == None and platform_yaml != None: platform = platform_yaml elif platform == None: platform = ['Windows'] elif 'all' in platform: platform = list(PLATFORMS.values()) overlay_file_type = None if overlay: if os.path.isfile(overlay[0]): overlay = overlay[0] expected_file_type = FILE_TYPE_GROUP_ADMINISTRATION if overlay_type == OVERLAY_TYPE_GROUP \ else FILE_TYPE_TECHNIQUE_ADMINISTRATION \ if overlay_type in [OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION] else None overlay_file_type = check_file(overlay, expected_file_type, health_is_called=health_is_called) if not overlay_file_type: return None # the overlay_file_type is not of the expected type elif isinstance(overlay, str): # reached when the overlay is provided via the interactive menu overlay = overlay.split(',') overlay = list(map(lambda x: x.strip().lower(), overlay)) else: # reached when the groups are provided via CLI arguments overlay = list(map(lambda x: x.strip().lower(), overlay)) else: overlay = [] # load the techniques (visibility or detection) from the YAML file all_techniques = None if overlay_file_type == FILE_TYPE_TECHNIQUE_ADMINISTRATION: # filter out visibility and/or detection objects using EQL if search_detection or search_visibility: overlay = techniques_search( overlay, search_visibility, search_detection, include_all_score_objs=include_all_score_objs) if not overlay: return None # something went wrong in executing the search or 0 results where returned if overlay_type == OVERLAY_TYPE_VISIBILITY: overlay_dict, all_techniques = _get_visibility_techniques(overlay) elif overlay_type == OVERLAY_TYPE_DETECTION: overlay_dict, all_techniques = _get_detection_techniques(overlay) # we are not overlaying visibility or detection, overlay group will therefore contain information on another group elif len(overlay) > 0: overlay_dict = _get_group_techniques(overlay, platform, overlay_file_type) if overlay_dict == -1: return None # returns None when the provided Group(s) to be overlaid, contains Groups not part of ATT&CK groups_dict = _get_group_techniques(groups, platform, groups_file_type) if groups_dict == -1: return None # returns None when the provided Group contains Groups not part of ATT&CK if len(groups_dict) == 0: print('[!] Empty layer.' ) # the provided groups dit not result in any techniques return None # check if we are doing a software group overlay if software_groups and overlay: if overlay_type not in [ OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION ]: # if a group overlay is provided, get the software techniques for the overlay groups_software_dict = _get_software_techniques(overlay, platform) elif software_groups: groups_software_dict = _get_software_techniques(groups, platform) technique_count, max_count = _get_technique_count(groups_dict, overlay_dict, groups_software_dict, overlay_type, all_techniques) technique_layer = _get_technique_layer(technique_count, groups_dict, overlay_dict, groups_software_dict, overlay_file_type, overlay_type, all_techniques) # make a list group names for the involved groups. if groups == ['all']: groups_list = ['all'] else: groups_list = _get_group_list(groups_dict, groups_file_type) overlay_list = _get_group_list(overlay_dict, overlay_file_type) desc = 'stage: attack | platform(s): ' + platform_to_name(platform, separator=', ') + ' | group(s): ' \ + ', '.join(groups_list) + ' | overlay group(s): ' + ', '.join(overlay_list) if not layer_name: layer_name = 'Attack - ' + platform_to_name(platform, separator=', ') layer = get_layer_template_groups(layer_name, max_count, desc, platform, overlay_type) layer['techniques'] = technique_layer json_string = simplejson.dumps(layer).replace('}, ', '},\n') if not output_filename: if overlay: filename = platform_to_name(platform) + '_' + '_'.join( groups_list) + '-overlay_' + '_'.join(overlay_list) else: filename = platform_to_name(platform) + '_' + '_'.join(groups_list) filename = create_output_filename('attack', filename) write_file(filename, json_string) else: write_file(output_filename, json_string)