Added applicable_to parameter in command line arguments and interactive menu, to filter on this field while generating a layer file.

master
Ruben 2019-04-18 15:32:35 +02:00
parent 0dd76c68a1
commit 58497e41ce
3 changed files with 49 additions and 29 deletions

View File

@ -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 ' 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 ' 'the overlay with visibility to add metadata on the '
'involved data sources)') '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') action='store_true')
parser_detection.add_argument('-e', '--excel', help='generate an Excel sheet with all administrated techniques', parser_detection.add_argument('-e', '--excel', help='generate an Excel sheet with all administrated techniques',
action='store_true') action='store_true')
@ -174,9 +177,9 @@ def menu(menu_parser):
if check_file_type(args.file_tech, FILE_TYPE_TECHNIQUE_ADMINISTRATION): if check_file_type(args.file_tech, FILE_TYPE_TECHNIQUE_ADMINISTRATION):
if args.layer: 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): 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: if args.graph:
plot_detection_graph(args.file_tech) plot_detection_graph(args.file_tech)
if args.excel: if args.excel:

View File

@ -11,6 +11,7 @@ platform = 'Windows'
stage = 'attack' stage = 'attack'
groups_overlay = '' groups_overlay = ''
overlay_type = '' overlay_type = ''
filter_applicable_to = 'all'
MENU_NAME_DATA_SOURCE_MAPPING = 'Data source mapping' MENU_NAME_DATA_SOURCE_MAPPING = 'Data source mapping'
MENU_NAME_VISIBILITY_MAPPING = 'Visibility coverage mapping' MENU_NAME_VISIBILITY_MAPPING = 'Visibility coverage mapping'
@ -250,32 +251,39 @@ def menu_detection(filename_t):
:param filename_t: :param filename_t:
:return: :return:
""" """
global filter_applicable_to
clear() clear()
print('Menu: %s' % MENU_NAME_DETECTION_COVERAGE_MAPPING) print('Menu: %s' % MENU_NAME_DETECTION_COVERAGE_MAPPING)
print('') print('')
print('Selected techniques YAML file: %s' % filename_t) print('Selected techniques YAML file: %s' % filename_t)
print('') 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('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 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 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 a graph with detections added through time.')
print('4. Generate an Excel sheet with all administrated techniques.') print('5. Generate an Excel sheet with all administrated techniques.')
print('9. Back to main menu.') print('9. Back to main menu.')
choice = ask_input() choice = ask_input()
if choice == '1': if choice == '1':
print('Writing detection coverage layer...') print('Specify your filter for the applicable_to field:')
generate_detection_layer(filename_t, None, False) filter_applicable_to = ask_input().lower()
wait()
elif choice == '2': 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...')
print('Writing detection coverage layer with visibility as overlay...') generate_detection_layer(filename_t, None, False, filter_applicable_to)
generate_detection_layer(filename_t, filename_ds, True)
wait() wait()
elif choice == '3': 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...') print('Drawing the graph...')
plot_detection_graph(filename_t) plot_detection_graph(filename_t)
wait() wait()
elif choice == '4': elif choice == '5':
print('Generating Excel file...') print('Generating Excel file...')
export_techniques_list_to_excel(filename_t) export_techniques_list_to_excel(filename_t)
wait() wait()

View File

@ -4,7 +4,7 @@ import xlsxwriter
# Imports for pandas and plotly are because of performance reasons in the function that uses these libraries. # 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. 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 :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 :param overlay: boolean value to specify if an overlay between detection and visibility should be generated
:return: :return:
""" """
my_techniques, name, platform = _load_detections(filename_techniques) my_techniques, name, platform = _load_detections(filename_techniques, filter_applicable_to)
if not overlay: if not overlay:
mapped_techniques_detection = _map_and_colorize_techniques_for_detections(my_techniques) mapped_techniques_detection = _map_and_colorize_techniques_for_detections(my_techniques)
layer_detection = get_layer_template_detections('Detections ' + name, 'description', 'attack', platform) layer_detection = get_layer_template_detections('Detections ' + name + ' ' + filter_applicable_to, 'description', 'attack', platform)
_write_layer(layer_detection, mapped_techniques_detection, 'detection', name) _write_layer(layer_detection, mapped_techniques_detection, 'detection', filter_applicable_to, name)
else: else:
my_data_sources = _load_data_sources(filename_data_sources) my_data_sources = _load_data_sources(filename_data_sources)
mapped_techniques_both = _map_and_colorize_techniques_for_overlayed(my_techniques, my_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) 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', name) _write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', filter_applicable_to, name)
def generate_visibility_layer(filename_techniques, filename_data_sources, overlay): 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: if not overlay:
mapped_techniques_visibility = _map_and_colorize_techniques_for_visibility(my_techniques, my_data_sources) 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) 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: else:
mapped_techniques_both = _map_and_colorize_techniques_for_overlayed(my_techniques, my_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) 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): def plot_detection_graph(filename):
@ -75,17 +75,23 @@ def plot_detection_graph(filename):
print("File written: " + output_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. 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 :param filename: the filename of the yaml file containing the techniques administration
:return: dictionary with techniques (incl. properties), name and platform :return: dictionary with techniques (incl. properties), name and platform
""" """
my_techniques = {} my_techniques = {}
with open(filename, 'r') as yaml_file: with open(filename, 'r') as yaml_file:
yaml_content = yaml.load(yaml_file, Loader=yaml.FullLoader) yaml_content = yaml.load(yaml_file, Loader=yaml.FullLoader)
for d in yaml_content['techniques']: 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'] name = yaml_content['name']
platform = yaml_content['platform'] platform = yaml_content['platform']
return my_techniques, name, platform return my_techniques, name, platform
@ -107,7 +113,7 @@ def _load_data_sources(filename):
return my_data_sources 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. Writes the json layer file to disk.
:param layer: the prepped layer dictionary :param layer: the prepped layer dictionary
@ -119,7 +125,8 @@ def _write_layer(layer, mapped_techniques, filename_prefix, name):
layer['techniques'] = mapped_techniques layer['techniques'] = mapped_techniques
json_string = simplejson.dumps(layer).replace('}, ', '},\n') 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: with open(output_filename, 'w') as f:
f.write(json_string) f.write(json_string)
print("File written: " + output_filename) print("File written: " + output_filename)
@ -150,7 +157,10 @@ def _map_and_colorize_techniques_for_detections(my_techniques):
for tactic in technique['tactic']: for tactic in technique['tactic']:
location = ', '.join(c['detection']['location']) if 'detection' in c.keys() else '-' location = ', '.join(c['detection']['location']) if 'detection' in c.keys() else '-'
location = location if location != '' 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 = {}
x['techniqueID'] = d x['techniqueID'] = d
x['color'] = color x['color'] = color
@ -162,8 +172,7 @@ def _map_and_colorize_techniques_for_detections(my_techniques):
{'name': '-Comment', 'value': comment}, {'name': '-Comment', 'value': comment},
{'name': '-Applicable to', 'value': applicable_to}] {'name': '-Applicable to', 'value': applicable_to}]
x['score'] = s x['score'] = s
mapped_techniques.append(x)
mapped_techniques.append(x)
except Exception: except Exception:
print('[!] Possible error in YAML file at: ' + d) print('[!] Possible error in YAML file at: ' + d)
quit() quit()