Removed support for PRE-ATT&CK from the Group mode
parent
b3705c782e
commit
94e8b5e4b5
|
@ -26,7 +26,7 @@ def generate_data_sources_layer(filename, output_filename, layer_name, platform=
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = 'Data sources ' + name
|
layer_name = 'Data sources ' + name
|
||||||
|
|
||||||
layer = get_layer_template_data_sources(layer_name, 'description', 'attack', platform)
|
layer = get_layer_template_data_sources(layer_name, 'description', platform)
|
||||||
layer['techniques'] = my_techniques
|
layer['techniques'] = my_techniques
|
||||||
|
|
||||||
json_string = simplejson.dumps(layer).replace('}, ', '},\n')
|
json_string = simplejson.dumps(layer).replace('}, ', '},\n')
|
||||||
|
|
26
dettect.py
26
dettect.py
|
@ -25,7 +25,7 @@ def _init_menu():
|
||||||
'group, generic} --help', metavar='', dest='subparser')
|
'group, generic} --help', metavar='', dest='subparser')
|
||||||
|
|
||||||
parser_editor = subparsers.add_parser('editor', aliases=['e'], help='DeTT&CT Editor',
|
parser_editor = subparsers.add_parser('editor', aliases=['e'], help='DeTT&CT Editor',
|
||||||
description='Start the DeTT&CT Editor for easy editing the YAML administration files.')
|
description='Start the DeTT&CT Editor for easy editing the YAML administration files')
|
||||||
parser_editor.add_argument('-p', '--port', help='port where the webserver listens on (default is 8080)', required=False, default=8080)
|
parser_editor.add_argument('-p', '--port', help='port where the webserver listens on (default is 8080)', required=False, default=8080)
|
||||||
|
|
||||||
# create the data source parser
|
# create the data source parser
|
||||||
|
@ -69,9 +69,9 @@ def _init_menu():
|
||||||
parser_data_sources.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
parser_data_sources.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
||||||
parser_data_sources.add_argument('--health', help='check the YAML file(s) for errors', action='store_true')
|
parser_data_sources.add_argument('--health', help='check the YAML file(s) for errors', action='store_true')
|
||||||
parser_data_sources.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
parser_data_sources.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
||||||
'or to use a specific version of STIX objects.')
|
'or to use a specific version of STIX objects')
|
||||||
parser_data_sources.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
parser_data_sources.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
||||||
'to ATT&CK with sub-techniques.', action='store_true')
|
'to ATT&CK with sub-techniques', action='store_true')
|
||||||
|
|
||||||
# create the visibility parser
|
# create the visibility parser
|
||||||
parser_visibility = subparsers.add_parser('visibility', aliases=['v'],
|
parser_visibility = subparsers.add_parser('visibility', aliases=['v'],
|
||||||
|
@ -107,9 +107,9 @@ def _init_menu():
|
||||||
parser_visibility.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
parser_visibility.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
||||||
parser_visibility.add_argument('--health', help='check the YAML file for errors', action='store_true')
|
parser_visibility.add_argument('--health', help='check the YAML file for errors', action='store_true')
|
||||||
parser_visibility.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
parser_visibility.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
||||||
'or to use a specific version of STIX objects.')
|
'or to use a specific version of STIX objects')
|
||||||
parser_visibility.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
parser_visibility.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
||||||
'to ATT&CK with sub-techniques.', action='store_true')
|
'to ATT&CK with sub-techniques', action='store_true')
|
||||||
|
|
||||||
# create the detection parser
|
# create the detection parser
|
||||||
parser_detection = subparsers.add_parser('detection', aliases=['d'],
|
parser_detection = subparsers.add_parser('detection', aliases=['d'],
|
||||||
|
@ -147,9 +147,9 @@ def _init_menu():
|
||||||
parser_detection.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
parser_detection.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
||||||
parser_detection.add_argument('--health', help='check the YAML file(s) for errors', action='store_true')
|
parser_detection.add_argument('--health', help='check the YAML file(s) for errors', action='store_true')
|
||||||
parser_detection.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
parser_detection.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
||||||
'or to use a specific version of STIX objects.')
|
'or to use a specific version of STIX objects')
|
||||||
parser_detection.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
parser_detection.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
||||||
'to ATT&CK with sub-techniques.', action='store_true')
|
'to ATT&CK with sub-techniques', action='store_true')
|
||||||
|
|
||||||
# create the group parser
|
# create the group parser
|
||||||
parser_group = subparsers.add_parser('group', aliases=['g'],
|
parser_group = subparsers.add_parser('group', aliases=['g'],
|
||||||
|
@ -159,7 +159,7 @@ def _init_menu():
|
||||||
parser_group.add_argument('-g', '--groups', help='specify the ATT&CK Groups to include. Group can be its ID, '
|
parser_group.add_argument('-g', '--groups', help='specify the ATT&CK Groups to include. Group can be its ID, '
|
||||||
'name or alias (default is all groups). Multiple Groups can be '
|
'name or alias (default is all groups). Multiple Groups can be '
|
||||||
'provided with extra \'-g/--group\' arguments. Another option is '
|
'provided with extra \'-g/--group\' arguments. Another option is '
|
||||||
'to provide a YAML file with a custom group(s).',
|
'to provide a YAML file with a custom group(s)',
|
||||||
default=None, action='append')
|
default=None, action='append')
|
||||||
parser_group.add_argument('-o', '--overlay', help='specify what to overlay on the group(s) (provided using the '
|
parser_group.add_argument('-o', '--overlay', help='specify what to overlay on the group(s) (provided using the '
|
||||||
'arguments \-g/--groups\): group(s), visibility or detection. '
|
'arguments \-g/--groups\): group(s), visibility or detection. '
|
||||||
|
@ -180,8 +180,6 @@ def _init_menu():
|
||||||
'can be provided with extra \'-p/--platform\' arguments',
|
'can be provided with extra \'-p/--platform\' arguments',
|
||||||
choices=['all'] + list(PLATFORMS.values()), default=None, action='append',
|
choices=['all'] + list(PLATFORMS.values()), default=None, action='append',
|
||||||
type=_platform_lookup())
|
type=_platform_lookup())
|
||||||
parser_group.add_argument('-s', '--stage', help='specify the stage (default = attack)',
|
|
||||||
choices=['attack', 'pre-attack'], default='attack')
|
|
||||||
parser_group.add_argument('-sd', '--search-detection', help='only include detection objects which match the '
|
parser_group.add_argument('-sd', '--search-detection', help='only include detection objects which match the '
|
||||||
'provided EQL query')
|
'provided EQL query')
|
||||||
parser_group.add_argument('-sv', '--search-visibility', help='only include visibility objects which match the '
|
parser_group.add_argument('-sv', '--search-visibility', help='only include visibility objects which match the '
|
||||||
|
@ -194,9 +192,9 @@ def _init_menu():
|
||||||
parser_group.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
parser_group.add_argument('-ln', '--layer-name', help='set the name of the Navigator layer')
|
||||||
parser_group.add_argument('--health', help='check the YAML file(s) for errors', action='store_true')
|
parser_group.add_argument('--health', help='check the YAML file(s) for errors', action='store_true')
|
||||||
parser_group.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
parser_group.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
||||||
'or to use a specific version of STIX objects.')
|
'or to use a specific version of STIX objects')
|
||||||
parser_group.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
parser_group.add_argument('--update-to-sub-techniques', help='Update the technique administration YAML file '
|
||||||
'to ATT&CK with sub-techniques.', action='store_true')
|
'to ATT&CK with sub-techniques', action='store_true')
|
||||||
|
|
||||||
# create the generic parser
|
# create the generic parser
|
||||||
parser_generic = subparsers.add_parser('generic', description='Generic functions which will output to stdout.',
|
parser_generic = subparsers.add_parser('generic', description='Generic functions which will output to stdout.',
|
||||||
|
@ -216,7 +214,7 @@ def _init_menu():
|
||||||
'date (default = modified)', choices=['modified', 'created'],
|
'date (default = modified)', choices=['modified', 'created'],
|
||||||
default='modified')
|
default='modified')
|
||||||
parser_generic.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
parser_generic.add_argument('--local-stix-path', help='path to a local STIX repository to use DeTT&CT offline '
|
||||||
'or to use a specific version of STIX objects.')
|
'or to use a specific version of STIX objects')
|
||||||
|
|
||||||
return menu_parser
|
return menu_parser
|
||||||
|
|
||||||
|
@ -289,7 +287,7 @@ def _menu(menu_parser):
|
||||||
|
|
||||||
# TODO add search capabilities
|
# TODO add search capabilities
|
||||||
elif args.subparser in ['group', 'g']:
|
elif args.subparser in ['group', 'g']:
|
||||||
generate_group_heat_map(args.groups, args.overlay, args.overlay_type, args.stage, args.platform,
|
generate_group_heat_map(args.groups, args.overlay, args.overlay_type, args.platform,
|
||||||
args.software_group, args.search_visibility, args.search_detection, args.health,
|
args.software_group, args.search_visibility, args.search_detection, args.health,
|
||||||
args.output_filename, args.layer_name, include_all_score_objs=args.all_scores)
|
args.output_filename, args.layer_name, include_all_score_objs=args.all_scores)
|
||||||
|
|
||||||
|
|
28
generic.py
28
generic.py
|
@ -206,13 +206,12 @@ def init_yaml():
|
||||||
return _yaml
|
return _yaml
|
||||||
|
|
||||||
|
|
||||||
def _get_base_template(name, description, stage, platform, sorting):
|
def _get_base_template(name, description, platform, sorting):
|
||||||
"""
|
"""
|
||||||
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
||||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||||
:param name: name
|
:param name: name
|
||||||
:param description: description
|
:param description: description
|
||||||
:param stage: stage (act | prepare)
|
|
||||||
:param platform: platform
|
:param platform: platform
|
||||||
:param sorting: sorting
|
:param sorting: sorting
|
||||||
:return: layer template dictionary
|
:return: layer template dictionary
|
||||||
|
@ -236,19 +235,18 @@ def _get_base_template(name, description, stage, platform, sorting):
|
||||||
return layer
|
return layer
|
||||||
|
|
||||||
|
|
||||||
def get_layer_template_groups(name, max_count, description, stage, platform, overlay_type):
|
def get_layer_template_groups(name, max_count, description, platform, overlay_type):
|
||||||
"""
|
"""
|
||||||
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
||||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||||
:param name: name
|
:param name: name
|
||||||
:param max_count: the sum of all count values
|
:param max_count: the sum of all count values
|
||||||
:param description: description
|
:param description: description
|
||||||
:param stage: stage (act | prepare)
|
|
||||||
:param platform: platform
|
:param platform: platform
|
||||||
:param overlay_type: group, visibility or detection
|
:param overlay_type: group, visibility or detection
|
||||||
:return: layer template dictionary
|
:return: layer template dictionary
|
||||||
"""
|
"""
|
||||||
layer = _get_base_template(name, description, stage, platform, 3)
|
layer = _get_base_template(name, description, platform, 3)
|
||||||
layer['gradient'] = {'colors': [COLOR_GRADIENT_MIN, COLOR_GRADIENT_MAX], 'minValue': 0, 'maxValue': max_count}
|
layer['gradient'] = {'colors': [COLOR_GRADIENT_MIN, COLOR_GRADIENT_MAX], 'minValue': 0, 'maxValue': max_count}
|
||||||
layer['legendItems'] = []
|
layer['legendItems'] = []
|
||||||
layer['legendItems'].append({'label': 'Tech. not often used', 'color': COLOR_GRADIENT_MIN})
|
layer['legendItems'].append({'label': 'Tech. not often used', 'color': COLOR_GRADIENT_MIN})
|
||||||
|
@ -285,17 +283,16 @@ def get_layer_template_groups(name, max_count, description, stage, platform, ove
|
||||||
return layer
|
return layer
|
||||||
|
|
||||||
|
|
||||||
def get_layer_template_detections(name, description, stage, platform):
|
def get_layer_template_detections(name, description, platform):
|
||||||
"""
|
"""
|
||||||
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
||||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||||
:param name: name
|
:param name: name
|
||||||
:param description: description
|
:param description: description
|
||||||
:param stage: stage (act | prepare)
|
|
||||||
:param platform: platform
|
:param platform: platform
|
||||||
:return: layer template dictionary
|
:return: layer template dictionary
|
||||||
"""
|
"""
|
||||||
layer = _get_base_template(name, description, stage, platform, 0)
|
layer = _get_base_template(name, description, platform, 0)
|
||||||
layer['legendItems'] = \
|
layer['legendItems'] = \
|
||||||
[
|
[
|
||||||
{'label': 'Detection score 0: Forensics/Context', 'color': COLOR_D_0},
|
{'label': 'Detection score 0: Forensics/Context', 'color': COLOR_D_0},
|
||||||
|
@ -308,17 +305,16 @@ def get_layer_template_detections(name, description, stage, platform):
|
||||||
return layer
|
return layer
|
||||||
|
|
||||||
|
|
||||||
def get_layer_template_data_sources(name, description, stage, platform):
|
def get_layer_template_data_sources(name, description, platform):
|
||||||
"""
|
"""
|
||||||
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
||||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||||
:param name: name
|
:param name: name
|
||||||
:param description: description
|
:param description: description
|
||||||
:param stage: stage (act | prepare)
|
|
||||||
:param platform: platform
|
:param platform: platform
|
||||||
:return: layer template dictionary
|
:return: layer template dictionary
|
||||||
"""
|
"""
|
||||||
layer = _get_base_template(name, description, stage, platform, 0)
|
layer = _get_base_template(name, description, platform, 0)
|
||||||
layer['legendItems'] = \
|
layer['legendItems'] = \
|
||||||
[
|
[
|
||||||
{'label': '1-25% of data sources available', 'color': COLOR_DS_25p},
|
{'label': '1-25% of data sources available', 'color': COLOR_DS_25p},
|
||||||
|
@ -330,17 +326,16 @@ def get_layer_template_data_sources(name, description, stage, platform):
|
||||||
return layer
|
return layer
|
||||||
|
|
||||||
|
|
||||||
def get_layer_template_visibility(name, description, stage, platform):
|
def get_layer_template_visibility(name, description, platform):
|
||||||
"""
|
"""
|
||||||
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
||||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||||
:param name: name
|
:param name: name
|
||||||
:param description: description
|
:param description: description
|
||||||
:param stage: stage (act | prepare)
|
|
||||||
:param platform: platform
|
:param platform: platform
|
||||||
:return: layer template dictionary
|
:return: layer template dictionary
|
||||||
"""
|
"""
|
||||||
layer = _get_base_template(name, description, stage, platform, 0)
|
layer = _get_base_template(name, description, platform, 0)
|
||||||
layer['legendItems'] = \
|
layer['legendItems'] = \
|
||||||
[
|
[
|
||||||
{'label': 'Visibility score 1: Minimal', 'color': COLOR_V_1},
|
{'label': 'Visibility score 1: Minimal', 'color': COLOR_V_1},
|
||||||
|
@ -351,17 +346,16 @@ def get_layer_template_visibility(name, description, stage, platform):
|
||||||
return layer
|
return layer
|
||||||
|
|
||||||
|
|
||||||
def get_layer_template_layered(name, description, stage, platform):
|
def get_layer_template_layered(name, description, platform):
|
||||||
"""
|
"""
|
||||||
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
Prepares a base template for the json layer file that can be loaded into the MITRE ATT&CK Navigator.
|
||||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||||
:param name: name
|
:param name: name
|
||||||
:param description: description
|
:param description: description
|
||||||
:param stage: stage (act | prepare)
|
|
||||||
:param platform: platform
|
:param platform: platform
|
||||||
:return: layer template dictionary
|
:return: layer template dictionary
|
||||||
"""
|
"""
|
||||||
layer = _get_base_template(name, description, stage, platform, 0)
|
layer = _get_base_template(name, description, platform, 0)
|
||||||
layer['legendItems'] = \
|
layer['legendItems'] = \
|
||||||
[
|
[
|
||||||
{'label': 'Visibility and detection', 'color': COLOR_OVERLAY_BOTH},
|
{'label': 'Visibility and detection', 'color': COLOR_OVERLAY_BOTH},
|
||||||
|
|
|
@ -44,19 +44,18 @@ def _are_groups_found(groups_found, argument_groups):
|
||||||
if not group_id: # the group that has been provided through the command line cannot be found in ATT&CK
|
if not group_id: # the group that has been provided through the command line cannot be found in ATT&CK
|
||||||
print('[!] Unknown ATT&CK group: ' + group_arg)
|
print('[!] Unknown ATT&CK group: ' + group_arg)
|
||||||
group_found = False
|
group_found = False
|
||||||
elif group_id not in groups_found: # group not present in filtered data sate (i.e. platform and stage)
|
elif group_id not in groups_found: # group not present in filtered (platform) data set
|
||||||
print('[!] Group not part of the data set: ' + group_arg)
|
print('[!] Group not part of the data set: ' + group_arg)
|
||||||
group_found = False
|
group_found = False
|
||||||
|
|
||||||
return group_found
|
return group_found
|
||||||
|
|
||||||
|
|
||||||
def _get_software_techniques(groups, stage, platform):
|
def _get_software_techniques(groups, platform):
|
||||||
"""
|
"""
|
||||||
Get all techniques (in a dict) from the provided list of groups in relation to the software these groups use,
|
Get all techniques (in a dict) from the provided list of groups in relation to the software these groups use,
|
||||||
and hence techniques they support.
|
and hence techniques they support.
|
||||||
:param groups: ATT&CK groups
|
:param groups: ATT&CK groups
|
||||||
:param stage: attack or pre-attack
|
|
||||||
:param platform: one or multiple values from PLATFORMS constant
|
:param platform: one or multiple values from PLATFORMS constant
|
||||||
:return: dictionary with info on groups
|
:return: dictionary with info on groups
|
||||||
"""
|
"""
|
||||||
|
@ -108,7 +107,7 @@ def _get_software_techniques(groups, stage, platform):
|
||||||
# software matches the ATT&CK Matrix and platform
|
# software matches the ATT&CK Matrix and platform
|
||||||
# and the group is a group we are interested in
|
# and the group is a group we are interested in
|
||||||
if s['x_mitre_platforms']: # there is software that do not have a platform, skip those
|
if s['x_mitre_platforms']: # there is software that do not have a platform, skip those
|
||||||
if s['matrix'] == 'mitre-' + stage and len(set(s['x_mitre_platforms']).intersection(set(platform))) > 0 and \
|
if s['matrix'] == 'mitre-attack' and len(set(s['x_mitre_platforms']).intersection(set(platform))) > 0 and \
|
||||||
(groups[0] == 'all' or s['group_id'].lower() in groups or _is_in_group(s['aliases'], groups)):
|
(groups[0] == 'all' or s['group_id'].lower() in groups or _is_in_group(s['aliases'], groups)):
|
||||||
if s['group_id'] not in groups_dict:
|
if s['group_id'] not in groups_dict:
|
||||||
groups_dict[s['group_id']] = {'group_name': s['name']}
|
groups_dict[s['group_id']] = {'group_name': s['name']}
|
||||||
|
@ -149,11 +148,10 @@ def _generate_group_id(group_name, campaign):
|
||||||
return CG_GROUPS[group_name + campaign]
|
return CG_GROUPS[group_name + campaign]
|
||||||
|
|
||||||
|
|
||||||
def _get_group_techniques(groups, stage, platform, file_type):
|
def _get_group_techniques(groups, platform, file_type):
|
||||||
"""
|
"""
|
||||||
Get all techniques (in a dict) from the provided list of groups
|
Get all techniques (in a dict) from the provided list of groups
|
||||||
:param groups: group ID, group name/alias or a YAML file with group(s) data
|
:param groups: group ID, group name/alias or a YAML file with group(s) data
|
||||||
:param stage: attack or pre-attack
|
|
||||||
:param platform: one or multiple values from PLATFORMS constant
|
:param platform: one or multiple values from PLATFORMS constant
|
||||||
:param file_type: the file type of the YAML file as present in the key 'file_type'
|
:param file_type: the file type of the YAML file as present in the key 'file_type'
|
||||||
:return: returns dictionary with all techniques from the provided list of groups or -1 when group is not found
|
:return: returns dictionary with all techniques from the provided list of groups or -1 when group is not found
|
||||||
|
@ -196,7 +194,7 @@ def _get_group_techniques(groups, stage, platform, file_type):
|
||||||
platforms = ['Windows']
|
platforms = ['Windows']
|
||||||
|
|
||||||
# group matches the: matrix/stage, platform and the group(s) we are interested in
|
# group matches the: matrix/stage, platform and the group(s) we are interested in
|
||||||
if gr['matrix'] == 'mitre-' + stage and len(set(platforms).intersection(set(platform))) > 0 and \
|
if gr['matrix'] == 'mitre-attack' and len(set(platforms).intersection(set(platform))) > 0 and \
|
||||||
(groups[0] == 'all' or gr['group_id'].lower() in groups or _is_in_group(gr['aliases'], groups)):
|
(groups[0] == 'all' or gr['group_id'].lower() in groups or _is_in_group(gr['aliases'], groups)):
|
||||||
if gr['group_id'] not in groups_dict:
|
if gr['group_id'] not in groups_dict:
|
||||||
groups_found.add(gr['group_id'])
|
groups_found.add(gr['group_id'])
|
||||||
|
@ -477,7 +475,7 @@ def _get_group_list(groups, file_type):
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
|
|
||||||
def generate_group_heat_map(groups, overlay, overlay_type, stage, platform, software_groups, search_visibility,
|
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):
|
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.
|
Calls all functions that are necessary for the generation of the heat map and write a json layer to disk.
|
||||||
|
@ -485,7 +483,6 @@ def generate_group_heat_map(groups, overlay, overlay_type, stage, platform, soft
|
||||||
:param overlay: group(s), visibility or detections to overlay (group ID, group name/alias, YAML file with
|
:param overlay: group(s), visibility or detections to overlay (group ID, group name/alias, YAML file with
|
||||||
group(s), detections or visibility)
|
group(s), detections or visibility)
|
||||||
:param overlay_type: group, visibility or detection
|
:param overlay_type: group, visibility or detection
|
||||||
:param stage: attack or pre-attack
|
|
||||||
:param platform: one or multiple the values from PLATFORMS constant or None (default = Windows)
|
: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 software_groups: specify if techniques from related software should be included
|
||||||
:param search_visibility: visibility EQL search query
|
:param search_visibility: visibility EQL search query
|
||||||
|
@ -567,11 +564,11 @@ def generate_group_heat_map(groups, overlay, overlay_type, stage, platform, soft
|
||||||
|
|
||||||
# we are not overlaying visibility or detection, overlay group will therefore contain information on another group
|
# we are not overlaying visibility or detection, overlay group will therefore contain information on another group
|
||||||
elif len(overlay) > 0:
|
elif len(overlay) > 0:
|
||||||
overlay_dict = _get_group_techniques(overlay, stage, platform, overlay_file_type)
|
overlay_dict = _get_group_techniques(overlay, platform, overlay_file_type)
|
||||||
if overlay_dict == -1:
|
if overlay_dict == -1:
|
||||||
return None # returns None when the provided Group(s) to be overlaid, contains Groups not part of ATT&CK
|
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, stage, platform, groups_file_type)
|
groups_dict = _get_group_techniques(groups, platform, groups_file_type)
|
||||||
if groups_dict == -1:
|
if groups_dict == -1:
|
||||||
return None # returns None when the provided Group contains Groups not part of ATT&CK
|
return None # returns None when the provided Group contains Groups not part of ATT&CK
|
||||||
if len(groups_dict) == 0:
|
if len(groups_dict) == 0:
|
||||||
|
@ -582,9 +579,9 @@ def generate_group_heat_map(groups, overlay, overlay_type, stage, platform, soft
|
||||||
if software_groups and overlay:
|
if software_groups and overlay:
|
||||||
if overlay_type not in [OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION]:
|
if overlay_type not in [OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION]:
|
||||||
# if a group overlay is provided, get the software techniques for the overlay
|
# if a group overlay is provided, get the software techniques for the overlay
|
||||||
groups_software_dict = _get_software_techniques(overlay, stage, platform)
|
groups_software_dict = _get_software_techniques(overlay, platform)
|
||||||
elif software_groups:
|
elif software_groups:
|
||||||
groups_software_dict = _get_software_techniques(groups, stage, platform)
|
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_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,
|
technique_layer = _get_technique_layer(technique_count, groups_dict, overlay_dict, groups_software_dict,
|
||||||
|
@ -597,26 +594,24 @@ def generate_group_heat_map(groups, overlay, overlay_type, stage, platform, soft
|
||||||
groups_list = _get_group_list(groups_dict, groups_file_type)
|
groups_list = _get_group_list(groups_dict, groups_file_type)
|
||||||
overlay_list = _get_group_list(overlay_dict, overlay_file_type)
|
overlay_list = _get_group_list(overlay_dict, overlay_file_type)
|
||||||
|
|
||||||
desc = 'stage: ' + stage + ' | platform(s): ' + platform_to_name(platform, separator=', ') + ' | group(s): ' \
|
desc = 'stage: attack | platform(s): ' + platform_to_name(platform, separator=', ') + ' | group(s): ' \
|
||||||
+ ', '.join(groups_list) + ' | overlay group(s): ' + ', '.join(overlay_list)
|
+ ', '.join(groups_list) + ' | overlay group(s): ' + ', '.join(overlay_list)
|
||||||
|
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = stage[0].upper() + stage[1:] + ' - ' + platform_to_name(platform, separator=', ')
|
layer_name = 'Attack - ' + platform_to_name(platform, separator=', ')
|
||||||
|
|
||||||
layer = get_layer_template_groups(layer_name, max_count, desc, stage, platform, overlay_type)
|
layer = get_layer_template_groups(layer_name, max_count, desc, platform, overlay_type)
|
||||||
layer['techniques'] = technique_layer
|
layer['techniques'] = technique_layer
|
||||||
|
|
||||||
json_string = simplejson.dumps(layer).replace('}, ', '},\n')
|
json_string = simplejson.dumps(layer).replace('}, ', '},\n')
|
||||||
|
|
||||||
if not output_filename:
|
if not output_filename:
|
||||||
if stage == 'pre-attack':
|
if overlay:
|
||||||
filename = '_'.join(groups_list)
|
|
||||||
elif overlay:
|
|
||||||
filename = platform_to_name(platform) + '_' + '_'.join(groups_list) + '-overlay_' + '_'.join(overlay_list)
|
filename = platform_to_name(platform) + '_' + '_'.join(groups_list) + '-overlay_' + '_'.join(overlay_list)
|
||||||
else:
|
else:
|
||||||
filename = platform_to_name(platform) + '_' + '_'.join(groups_list)
|
filename = platform_to_name(platform) + '_' + '_'.join(groups_list)
|
||||||
|
|
||||||
filename = create_output_filename(stage, filename)
|
filename = create_output_filename('attack', filename)
|
||||||
write_file(filename, json_string)
|
write_file(filename, json_string)
|
||||||
else:
|
else:
|
||||||
write_file(output_filename, json_string)
|
write_file(output_filename, json_string)
|
||||||
|
|
|
@ -7,7 +7,6 @@ from eql_yaml import *
|
||||||
groups = 'all'
|
groups = 'all'
|
||||||
software_group = False
|
software_group = False
|
||||||
default_platform = ['Windows']
|
default_platform = ['Windows']
|
||||||
default_stage = 'attack'
|
|
||||||
default_matrix = 'enterprise'
|
default_matrix = 'enterprise'
|
||||||
groups_overlay = ''
|
groups_overlay = ''
|
||||||
overlay_type = 'group'
|
overlay_type = 'group'
|
||||||
|
@ -460,7 +459,7 @@ def _menu_groups():
|
||||||
Prints and handles the Threat actor group mapping functionality.
|
Prints and handles the Threat actor group mapping functionality.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
global groups, software_group, default_platform, default_stage, groups_overlay, overlay_type, eql_all_scores, \
|
global groups, software_group, default_platform, groups_overlay, overlay_type, eql_all_scores, \
|
||||||
eql_query_detection, eql_query_visibility
|
eql_query_detection, eql_query_visibility
|
||||||
_clear()
|
_clear()
|
||||||
print('Menu: %s' % MENU_NAME_THREAT_ACTOR_GROUP_MAPPING)
|
print('Menu: %s' % MENU_NAME_THREAT_ACTOR_GROUP_MAPPING)
|
||||||
|
@ -468,12 +467,11 @@ def _menu_groups():
|
||||||
print('Options:')
|
print('Options:')
|
||||||
print('1. Software group: %s' % str(software_group))
|
print('1. Software group: %s' % str(software_group))
|
||||||
print('2. Platform: %s' % ','.join(default_platform))
|
print('2. Platform: %s' % ','.join(default_platform))
|
||||||
print('3. Stage: %s' % default_stage)
|
print('3. Groups: %s' % groups)
|
||||||
print('4. Groups: %s' % groups)
|
print('4. Overlay: ')
|
||||||
print('5. Overlay: ')
|
|
||||||
print(' - %s: %s' % ('File' if os.path.exists(groups_overlay) else 'Groups', groups_overlay))
|
print(' - %s: %s' % ('File' if os.path.exists(groups_overlay) else 'Groups', groups_overlay))
|
||||||
print(' - Type: %s' % overlay_type)
|
print(' - Type: %s' % overlay_type)
|
||||||
print('6. EQL search: ')
|
print('5. EQL search: ')
|
||||||
eql_d_str = '' if not eql_query_detection else eql_query_detection
|
eql_d_str = '' if not eql_query_detection else eql_query_detection
|
||||||
eql_v_str = '' if not eql_query_visibility else eql_query_visibility
|
eql_v_str = '' if not eql_query_visibility else eql_query_visibility
|
||||||
print(' - Only include detection objects which match the EQL query: ' + eql_d_str)
|
print(' - Only include detection objects which match the EQL query: ' + eql_d_str)
|
||||||
|
@ -481,7 +479,7 @@ def _menu_groups():
|
||||||
print(' - Include all \'score\' objects from the \'score_logbook\' in the EQL search: ' + str(eql_all_scores))
|
print(' - Include all \'score\' objects from the \'score_logbook\' in the EQL search: ' + str(eql_all_scores))
|
||||||
print('')
|
print('')
|
||||||
print('Select what you want to do:')
|
print('Select what you want to do:')
|
||||||
print('7. Generate a heat map layer.')
|
print('6. Generate a heat map layer.')
|
||||||
print('9. Back to main menu.')
|
print('9. Back to main menu.')
|
||||||
choice = _ask_input()
|
choice = _ask_input()
|
||||||
if choice == '1':
|
if choice == '1':
|
||||||
|
@ -491,15 +489,11 @@ def _menu_groups():
|
||||||
p = _ask_input().lower()
|
p = _ask_input().lower()
|
||||||
default_platform = [PLATFORMS[p]] if p in PLATFORMS.keys() else ['all']
|
default_platform = [PLATFORMS[p]] if p in PLATFORMS.keys() else ['all']
|
||||||
elif choice == '3':
|
elif choice == '3':
|
||||||
print('Specify stage (pre-attack, attack):')
|
|
||||||
s = _ask_input().lower()
|
|
||||||
default_stage = 'pre-attack' if s == 'pre-attack' else 'attack'
|
|
||||||
elif choice == '4':
|
|
||||||
print('Specify the groups to include separated using commas. Group can be their ID, name or alias '
|
print('Specify the groups to include separated using commas. Group can be their ID, name or alias '
|
||||||
'(default is all groups). Other option is to provide a YAML file with a custom group(s)')
|
'(default is all groups). Other option is to provide a YAML file with a custom group(s)')
|
||||||
g = _ask_input()
|
g = _ask_input()
|
||||||
groups = g if g != '' else 'all'
|
groups = g if g != '' else 'all'
|
||||||
elif choice == '5':
|
elif choice == '4':
|
||||||
print('')
|
print('')
|
||||||
print('1. Overlay with groups.')
|
print('1. Overlay with groups.')
|
||||||
print('2. Overlay with detections.')
|
print('2. Overlay with detections.')
|
||||||
|
@ -521,7 +515,7 @@ def _menu_groups():
|
||||||
elif choice == '4':
|
elif choice == '4':
|
||||||
overlay_type = ''
|
overlay_type = ''
|
||||||
groups_overlay = ''
|
groups_overlay = ''
|
||||||
elif choice == '6':
|
elif choice == '5':
|
||||||
print('')
|
print('')
|
||||||
print('1. Only include detection objects which match the EQL query: ' + eql_d_str)
|
print('1. Only include detection objects which match the EQL query: ' + eql_d_str)
|
||||||
print('2. Only include visibility objects which match the EQL query: ' + eql_v_str)
|
print('2. Only include visibility objects which match the EQL query: ' + eql_v_str)
|
||||||
|
@ -537,8 +531,8 @@ def _menu_groups():
|
||||||
elif choice == '3':
|
elif choice == '3':
|
||||||
eql_all_scores = not eql_all_scores
|
eql_all_scores = not eql_all_scores
|
||||||
|
|
||||||
elif choice == '7':
|
elif choice == '6':
|
||||||
generate_group_heat_map(groups, groups_overlay, overlay_type, default_stage, default_platform,
|
generate_group_heat_map(groups, groups_overlay, overlay_type, default_platform,
|
||||||
software_group, eql_query_visibility, eql_query_detection, False,
|
software_group, eql_query_visibility, eql_query_detection, False,
|
||||||
None, None, include_all_score_objs=eql_all_scores)
|
None, None, include_all_score_objs=eql_all_scores)
|
||||||
_wait()
|
_wait()
|
||||||
|
|
|
@ -23,14 +23,14 @@ def generate_detection_layer(filename_techniques, filename_data_sources, overlay
|
||||||
mapped_techniques_detection = _map_and_colorize_techniques_for_detections(my_techniques)
|
mapped_techniques_detection = _map_and_colorize_techniques_for_detections(my_techniques)
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = 'Detections ' + name
|
layer_name = 'Detections ' + name
|
||||||
layer_detection = get_layer_template_detections(layer_name, 'description', 'attack', platform)
|
layer_detection = get_layer_template_detections(layer_name, 'description', platform)
|
||||||
_write_layer(layer_detection, mapped_techniques_detection, 'detection', name, output_filename)
|
_write_layer(layer_detection, mapped_techniques_detection, 'detection', name, output_filename)
|
||||||
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_overlaid(my_techniques, my_data_sources, platform)
|
mapped_techniques_both = _map_and_colorize_techniques_for_overlaid(my_techniques, my_data_sources, platform)
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = 'Visibility and Detection ' + name
|
layer_name = 'Visibility and Detection ' + name
|
||||||
layer_both = get_layer_template_layered(layer_name, 'description', 'attack', platform)
|
layer_both = get_layer_template_layered(layer_name, 'description', platform)
|
||||||
_write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', name, output_filename)
|
_write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', name, output_filename)
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,13 +53,13 @@ def generate_visibility_layer(filename_techniques, filename_data_sources, overla
|
||||||
mapped_techniques_visibility = _map_and_colorize_techniques_for_visibility(my_techniques, my_data_sources, platform)
|
mapped_techniques_visibility = _map_and_colorize_techniques_for_visibility(my_techniques, my_data_sources, platform)
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = 'Visibility ' + name
|
layer_name = 'Visibility ' + name
|
||||||
layer_visibility = get_layer_template_visibility(layer_name, 'description', 'attack', platform)
|
layer_visibility = get_layer_template_visibility(layer_name, 'description', platform)
|
||||||
_write_layer(layer_visibility, mapped_techniques_visibility, 'visibility', name, output_filename)
|
_write_layer(layer_visibility, mapped_techniques_visibility, 'visibility', name, output_filename)
|
||||||
else:
|
else:
|
||||||
mapped_techniques_both = _map_and_colorize_techniques_for_overlaid(my_techniques, my_data_sources, platform)
|
mapped_techniques_both = _map_and_colorize_techniques_for_overlaid(my_techniques, my_data_sources, platform)
|
||||||
if not layer_name:
|
if not layer_name:
|
||||||
layer_name = 'Visibility and Detection ' + name
|
layer_name = 'Visibility and Detection ' + name
|
||||||
layer_both = get_layer_template_layered(layer_name, 'description', 'attack', platform)
|
layer_both = get_layer_template_layered(layer_name, 'description', platform)
|
||||||
_write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', name, output_filename)
|
_write_layer(layer_both, mapped_techniques_both, 'visibility_and_detection', name, output_filename)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue