Added applicable_to parameter in command line arguments and interactive menu, to filter on this field while generating a layer file.
parent
0dd76c68a1
commit
58497e41ce
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue