Merge branch 'development'

master
Marcus Bakker 2020-11-04 11:10:36 +01:00
commit 9f8630f727
17 changed files with 307 additions and 192 deletions

View File

@ -1,6 +1,6 @@
FROM python:3.8-slim-buster FROM python:3.8-slim-buster
LABEL version="1.4.1" LABEL version="1.4.2"
# copy DeTT&CT and install the requirements # copy DeTT&CT and install the requirements
COPY . /opt/DeTTECT COPY . /opt/DeTTECT

View File

@ -1,7 +1,7 @@
<img src="https://github.com/rabobank-cdc/DeTTECT/wiki/images/logo.png" alt="DeTT&CT" width=30% height=30%> <img src="https://github.com/rabobank-cdc/DeTTECT/wiki/images/logo.png" alt="DeTT&CT" width=30% height=30%>
#### Detect Tactics, Techniques & Combat Threats #### Detect Tactics, Techniques & Combat Threats
Latest version: [1.4.1](https://github.com/rabobank-cdc/DeTTECT/wiki/Changelog#version-141) Latest version: [1.4.2](https://github.com/rabobank-cdc/DeTTECT/wiki/Changelog#version-142)
To get started with DeTT&CT, check out this [page](https://github.com/rabobank-cdc/DeTTECT/wiki/Getting-started), our [talk](https://www.youtube.com/watch?v=_kWpekkhomU) at hack.lu 2019 and our blog on: To get started with DeTT&CT, check out this [page](https://github.com/rabobank-cdc/DeTTECT/wiki/Getting-started), our [talk](https://www.youtube.com/watch?v=_kWpekkhomU) at hack.lu 2019 and our blog on:
- [mbsecure.nl/blog/2019/5/dettact-mapping-your-blue-team-to-mitre-attack](https://www.mbsecure.nl/blog/2019/5/dettact-mapping-your-blue-team-to-mitre-attack) or - [mbsecure.nl/blog/2019/5/dettact-mapping-your-blue-team-to-mitre-attack](https://www.mbsecure.nl/blog/2019/5/dettact-mapping-your-blue-team-to-mitre-attack) or

View File

@ -2,7 +2,7 @@ import re
APP_NAME = 'DeTT&CT' APP_NAME = 'DeTT&CT'
APP_DESC = 'Detect Tactics, Techniques & Combat Threats' APP_DESC = 'Detect Tactics, Techniques & Combat Threats'
VERSION = '1.4.1' VERSION = '1.4.2'
EXPIRE_TIME = 60 * 60 * 24 EXPIRE_TIME = 60 * 60 * 24
@ -182,8 +182,8 @@ EQL_INVALID_RESULT_TECH = '[!] Invalid technique administration content. Check y
HEALTH_ERROR_TXT = '[!] The below YAML file contains possible errors. It\'s recommended to check via the ' \ HEALTH_ERROR_TXT = '[!] The below YAML file contains possible errors. It\'s recommended to check via the ' \
'\'--health\' argument or using the option in the interactive menu: \n - ' '\'--health\' argument or using the option in the interactive menu: \n - '
PLATFORMS = {'windows': 'Windows', 'linux': 'Linux', 'macos': 'macOS', 'aws': 'AWS', 'gcp': 'GCP', 'azure': 'Azure', PLATFORMS = {'windows': 'Windows', 'linux': 'Linux', 'pre': 'PRE', 'macos': 'macOS', 'aws': 'AWS', 'gcp': 'GCP', 'azure': 'Azure',
'azure ad': 'Azure AD', 'office 365': 'Office 365', 'saas': 'SaaS'} 'azure ad': 'Azure AD', 'office 365': 'Office 365', 'saas': 'SaaS', 'network': 'Network'}
# Data sources applicable per platform # Data sources applicable per platform
DATA_SOURCES = {'Windows': ['Access tokens', 'Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Binary file metadata', 'BIOS', 'Browser extensions', DATA_SOURCES = {'Windows': ['Access tokens', 'Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Binary file metadata', 'BIOS', 'Browser extensions',
@ -199,6 +199,7 @@ DATA_SOURCES = {'Windows': ['Access tokens', 'Anti-virus', 'API monitoring', 'Ap
'Network intrusion detection system', 'Network protocol analysis', 'Packet capture', 'PowerShell logs', 'Process command-line parameters', 'Process monitoring', 'Network intrusion detection system', 'Network protocol analysis', 'Packet capture', 'PowerShell logs', 'Process command-line parameters', 'Process monitoring',
'Process use of network', 'Sensor health and status', 'Services', 'SSL/TLS inspection', 'System calls', 'Third-party application logs', 'User interface', 'VBR', 'Process use of network', 'Sensor health and status', 'Services', 'SSL/TLS inspection', 'System calls', 'Third-party application logs', 'User interface', 'VBR',
'Web application firewall logs', 'Web logs', 'Web proxy'], 'Web application firewall logs', 'Web logs', 'Web proxy'],
'PRE': ['Domain registration', 'Email gateway', 'Mail server', 'Network device logs', 'Packet capture', 'Social media monitoring', 'SSL/TLS certificates', 'Web logs'],
'macOS': ['Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Binary file metadata', 'BIOS', 'Browser extensions', 'Component firmware', 'macOS': ['Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Binary file metadata', 'BIOS', 'Browser extensions', 'Component firmware',
'Data loss prevention', 'Detonation chamber', 'Digital certificate logs', 'Disk forensics', 'DNS records', 'EFI', 'Email gateway', 'Environment variable', 'File monitoring', 'Data loss prevention', 'Detonation chamber', 'Digital certificate logs', 'Disk forensics', 'DNS records', 'EFI', 'Email gateway', 'Environment variable', 'File monitoring',
'Host network interface', 'Kernel drivers', 'Mail server', 'Malware reverse engineering', 'MBR', 'Named Pipes', 'Netflow/Enclave netflow', 'Network device logs', 'Host network interface', 'Kernel drivers', 'Mail server', 'Malware reverse engineering', 'MBR', 'Named Pipes', 'Netflow/Enclave netflow', 'Network device logs',
@ -210,7 +211,7 @@ DATA_SOURCES = {'Windows': ['Access tokens', 'Anti-virus', 'API monitoring', 'Ap
'Network intrusion detection system', 'Network protocol analysis', 'Packet capture', 'Sensor health and status', 'SSL/TLS inspection', 'Third-party application logs', 'Network intrusion detection system', 'Network protocol analysis', 'Packet capture', 'Sensor health and status', 'SSL/TLS inspection', 'Third-party application logs',
'Web application firewall logs', 'Web logs', 'Web proxy'], 'Web application firewall logs', 'Web logs', 'Web proxy'],
'GCP': ['Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Binary file metadata', 'Data loss prevention', 'Detonation chamber', 'GCP': ['Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Binary file metadata', 'Data loss prevention', 'Detonation chamber',
'DNS records', 'Email gateway', 'File monitoring', 'Mail server', 'Malware reverse engineering', 'Netflow/Enclave netflow', 'Network device logs', 'DNS records', 'Email gateway', 'File monitoring', 'GCP audit logs', 'Mail server', 'Malware reverse engineering', 'Netflow/Enclave netflow', 'Network device logs',
'Network intrusion detection system', 'Network protocol analysis', 'Packet capture', 'Sensor health and status', 'SSL/TLS inspection', 'Stackdriver logs', 'Network intrusion detection system', 'Network protocol analysis', 'Packet capture', 'Sensor health and status', 'SSL/TLS inspection', 'Stackdriver logs',
'Third-party application logs', 'Web application firewall logs', 'Web logs', 'Web proxy'], 'Third-party application logs', 'Web application firewall logs', 'Web logs', 'Web proxy'],
'Azure': ['Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Azure activity logs', 'Azure OS logs', 'Binary file metadata', 'Azure': ['Anti-virus', 'API monitoring', 'Application logs', 'Asset management', 'Authentication logs', 'Azure activity logs', 'Azure OS logs', 'Binary file metadata',
@ -221,5 +222,7 @@ DATA_SOURCES = {'Windows': ['Access tokens', 'Anti-virus', 'API monitoring', 'Ap
'Office 365': ['Anti-virus', 'API monitoring', 'Authentication logs', 'Azure activity logs', 'Data loss prevention', 'Detonation chamber', 'Email gateway', 'Mail server', 'Office 365': ['Anti-virus', 'API monitoring', 'Authentication logs', 'Azure activity logs', 'Data loss prevention', 'Detonation chamber', 'Email gateway', 'Mail server',
'Malware reverse engineering', 'Office 365 account logs', 'Office 365 audit logs', 'Office 365 trace logs', 'Sensor health and status'], 'Malware reverse engineering', 'Office 365 account logs', 'Office 365 audit logs', 'Office 365 trace logs', 'Sensor health and status'],
'SaaS': ['Anti-virus', 'API monitoring', 'Application logs', 'Authentication logs', 'Data loss prevention', 'Detonation chamber', 'Email gateway', 'Mail server', 'SaaS': ['Anti-virus', 'API monitoring', 'Application logs', 'Authentication logs', 'Data loss prevention', 'Detonation chamber', 'Email gateway', 'Mail server',
'Malware reverse engineering', 'OAuth audit logs', 'Sensor health and status', 'Third-party application logs', 'Web application firewall logs', 'Web logs'] 'Malware reverse engineering', 'OAuth audit logs', 'Sensor health and status', 'Third-party application logs', 'Web application firewall logs', 'Web logs'],
'Network': ['File monitoring', 'Netflow/Enclave netflow', 'Network device command history', 'Network device configuration', 'Network device logs', 'Network device run-time memory',
'Network protocol analysis', 'Packet capture']
} }

View File

@ -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')

View File

@ -14,7 +14,8 @@ def _init_menu():
menu_parser = argparse.ArgumentParser(description='Detect Tactics, Techniques & Combat Threats', menu_parser = argparse.ArgumentParser(description='Detect Tactics, Techniques & Combat Threats',
epilog='Source: https://github.com/rabobank-cdc/DeTTECT') epilog='Source: https://github.com/rabobank-cdc/DeTTECT')
menu_parser.add_argument('--version', action='version', version='%(prog)s ' + VERSION) menu_parser.add_argument('--version', action='version', version='%(prog)s ' + VERSION)
menu_parser.add_argument('-i', '--interactive', help='launch the interactive menu, which has support for all modes', menu_parser.add_argument('-i', '--interactive', help='launch the interactive menu, which has support for all modes but not '
'all of the arguments that are available in the CLI',
action='store_true') action='store_true')
# add subparsers # add subparsers
@ -24,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
@ -68,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'],
@ -106,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'],
@ -146,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'],
@ -158,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. '
@ -179,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 '
@ -193,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.',
@ -215,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
@ -288,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)
@ -334,7 +333,7 @@ def _platform_lookup():
Lookup the platform value with the correct capitalisation. Lookup the platform value with the correct capitalisation.
return: lambda function to be used by argparse type= return: lambda function to be used by argparse type=
""" """
return lambda p: PLATFORMS.get(p.lower(), '') return lambda p: PLATFORMS.get(p.lower(), '') if p.lower() != 'all' else 'all'
def _prepare_folders(): def _prepare_folders():

191
editor/package-lock.json generated
View File

@ -1076,9 +1076,9 @@
} }
}, },
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "0.2.0", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz",
"integrity": "sha512-+cIGPCBdLCzqxdtwppswP+zTsH9BOIGzAeKfBIbtb4gW/giMlfMwP0HUSFfhzh20f9u8uZ8hOp62+4GPquTbwQ==", "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==",
"dev": true, "dev": true,
"requires": { "requires": {
"ajv": "^6.12.4", "ajv": "^6.12.4",
@ -1940,17 +1940,6 @@
"unique-filename": "^1.1.1" "unique-filename": "^1.1.1"
} }
}, },
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"cliui": { "cliui": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@ -2004,41 +1993,12 @@
"path-exists": "^4.0.0" "path-exists": "^4.0.0"
} }
}, },
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true "dev": true
}, },
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"locate-path": { "locate-path": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@ -2132,16 +2092,6 @@
"ansi-regex": "^5.0.0" "ansi-regex": "^5.0.0"
} }
}, },
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"terser-webpack-plugin": { "terser-webpack-plugin": {
"version": "2.3.8", "version": "2.3.8",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
@ -2159,18 +2109,6 @@
"webpack-sources": "^1.4.3" "webpack-sources": "^1.4.3"
} }
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.8",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.8.tgz",
"integrity": "sha512-oouKUQWWHbSihqSD7mhymGPX1OQ4hedzAHyvm8RdyHh6m3oIvoRF+NM45i/bhNOlo8jCnuJhaSUf/6oDjv978g==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
}
},
"wrap-ansi": { "wrap-ansi": {
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@ -5318,13 +5256,13 @@
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
}, },
"eslint": { "eslint": {
"version": "7.12.0", "version": "7.12.1",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.1.tgz",
"integrity": "sha512-n5pEU27DRxCSlOhJ2rO57GDLcNsxO0LPpAbpFdh7xmcDmjmlGUfoyrsB3I7yYdQXO5N3gkSTiDrPSPNFiiirXA==", "integrity": "sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
"@eslint/eslintrc": "^0.2.0", "@eslint/eslintrc": "^0.2.1",
"ajv": "^6.10.0", "ajv": "^6.10.0",
"chalk": "^4.0.0", "chalk": "^4.0.0",
"cross-spawn": "^7.0.2", "cross-spawn": "^7.0.2",
@ -12055,9 +11993,9 @@
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
}, },
"v8-compile-cache": { "v8-compile-cache": {
"version": "2.1.1", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
"dev": true "dev": true
}, },
"validate-npm-package-license": { "validate-npm-package-license": {
@ -12216,6 +12154,97 @@
} }
} }
}, },
"vue-loader-v16": {
"version": "npm:vue-loader@16.0.0-beta.9",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-beta.9.tgz",
"integrity": "sha512-mu9pg6554GbXDSO8LlxkQM6qUJzUkb/A0FJc9LgRqnU9MCnhzEXwCt1Zx5NObvFpzs2mH2dH/uUCDwL8Qaz9sA==",
"dev": true,
"optional": true,
"requires": {
"chalk": "^4.1.0",
"hash-sum": "^2.0.0",
"loader-utils": "^2.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"optional": true
},
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
"integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
"dev": true,
"optional": true,
"requires": {
"minimist": "^1.2.5"
}
},
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"optional": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"optional": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"vue-property-decorator": { "vue-property-decorator": {
"version": "8.5.1", "version": "8.5.1",
"resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.5.1.tgz", "resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.5.1.tgz",
@ -12233,9 +12262,9 @@
} }
}, },
"vue-router": { "vue-router": {
"version": "3.4.7", "version": "3.4.8",
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.7.tgz", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.8.tgz",
"integrity": "sha512-CbHXue5BLrDivOk5O4eZ0WT4Yj8XwdXa4kCnsEIOzYUPF/07ZukayA2jGxDCJxLc9SgVQX9QX0OuGOwGlVB4Qg==" "integrity": "sha512-3BsR84AqarcmweXjItxw3jwQsiYNssYg090yi4rlzTnCJxmHtkyCvhNz9Z7qRSOkmiV485KkUCReTp5AjNY4wg=="
}, },
"vue-router-prefetch": { "vue-router-prefetch": {
"version": "1.6.0", "version": "1.6.0",
@ -12243,9 +12272,9 @@
"integrity": "sha512-mLoh5on0GlO3ag0GWt+J8FkcalSAZJGJ/dNpA8PQ8X7pAoFht683FOJZlqdV7vXwKH7BjP+UfKcK09tHtNeDbw==" "integrity": "sha512-mLoh5on0GlO3ag0GWt+J8FkcalSAZJGJ/dNpA8PQ8X7pAoFht683FOJZlqdV7vXwKH7BjP+UfKcK09tHtNeDbw=="
}, },
"vue-scrollto": { "vue-scrollto": {
"version": "2.19.1", "version": "2.20.0",
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.19.1.tgz", "resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.20.0.tgz",
"integrity": "sha512-4+CCBLm6fTwDkKgqJIOnE4UB9ngYVoFAREJfPhT0YaEKMo6oAjnP5fv4//cuN/DxXCi75N/QOwmpOPn/xwPd5g==", "integrity": "sha512-7i+AGKJTThZnMEkhIPgrZjyAX+fXV7/rGdg+CV283uZZwCxwiMXaBLTmIc5RTA4uwGnT+E6eJle3mXQfM2OD3A==",
"requires": { "requires": {
"bezier-easing": "2.1.0" "bezier-easing": "2.1.0"
} }
@ -12264,9 +12293,9 @@
"integrity": "sha512-5yBGlVOQ5gvrkuoT6TtdVHLXy507EKN7X8vgXhPJsMbcJFuDTqzd6AFElSK7Bv4UwcKSU/o1OPzD9GBi86VuTQ==" "integrity": "sha512-5yBGlVOQ5gvrkuoT6TtdVHLXy507EKN7X8vgXhPJsMbcJFuDTqzd6AFElSK7Bv4UwcKSU/o1OPzD9GBi86VuTQ=="
}, },
"vue-slider-component": { "vue-slider-component": {
"version": "3.2.9", "version": "3.2.10",
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.2.9.tgz", "resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.2.10.tgz",
"integrity": "sha512-b70aa66rb+9G1+RwptDSHvfBy4YXWP3fRk49pfs55WprJEVy5FM8IT5R7Yu5vYQDzgjXNGb8U0M8BZSxWeYRNg==", "integrity": "sha512-SDShSZrvCpAcjTUJzbMEDmDl6+C0vVlNwViMfsgkmWex+lcRf7Z89A9rNghnmux2oJwmz5XKDGLVZ59+q+P9uw==",
"requires": { "requires": {
"core-js": "^3.6.5", "core-js": "^3.6.5",
"vue-property-decorator": "^8.0.0" "vue-property-decorator": "^8.0.0"

View File

@ -27,12 +27,12 @@
"vue-directive-tooltip": "^1.6.3", "vue-directive-tooltip": "^1.6.3",
"vue-js-toggle-button": "^1.3.3", "vue-js-toggle-button": "^1.3.3",
"vue-resource": "^1.5.1", "vue-resource": "^1.5.1",
"vue-router": "^3.4.7", "vue-router": "^3.4.8",
"vue-router-prefetch": "^1.6.0", "vue-router-prefetch": "^1.6.0",
"vue-scrollto": "^2.19.1", "vue-scrollto": "^2.20.0",
"vue-showdown": "^2.4.1", "vue-showdown": "^2.4.1",
"vue-simple-suggest": "^1.10.3", "vue-simple-suggest": "^1.10.3",
"vue-slider-component": "^3.2.9", "vue-slider-component": "^3.2.10",
"vue2-transitions": "^0.3.0", "vue2-transitions": "^0.3.0",
"vuejs-datepicker": "^1.6.2", "vuejs-datepicker": "^1.6.2",
"vuejs-smart-table": "0.0.5", "vuejs-smart-table": "0.0.5",
@ -42,7 +42,7 @@
"@vue/cli-plugin-babel": "^4.5.8", "@vue/cli-plugin-babel": "^4.5.8",
"@vue/cli-plugin-eslint": "^4.5.8", "@vue/cli-plugin-eslint": "^4.5.8",
"@vue/cli-service": "^4.5.8", "@vue/cli-service": "^4.5.8",
"eslint": "^7.12.0", "eslint": "^7.12.1",
"eslint-loader": "^4.0.2", "eslint-loader": "^4.0.2",
"eslint-plugin-vue": "^6.2.2", "eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.12" "vue-template-compiler": "^2.6.12"

View File

@ -4,7 +4,7 @@ from attackcti import attack_client
FILE_DATA_SOURCES = 'data_sources.json' FILE_DATA_SOURCES = 'data_sources.json'
FILE_TECHNIQUES = 'techniques.json' FILE_TECHNIQUES = 'techniques.json'
FILE_SOFTWARE = 'software.json' FILE_SOFTWARE = 'software.json'
PLATFORMS = ['Windows', 'Linux', 'macOS', 'AWS', 'GCP', 'Azure', 'Azure AD', 'Office 365', 'SaaS'] PLATFORMS = ['Windows', 'Linux', 'macOS', 'PRE', 'AWS', 'GCP', 'Azure', 'Azure AD', 'Office 365', 'SaaS', 'Network']
class ATTACKData(): class ATTACKData():

View File

@ -85,16 +85,18 @@ export default {
comment: '', comment: '',
auto_generated: false auto_generated: false
}, },
PLATFORMS: ['all', 'Windows', 'Linux', 'macOS', 'AWS', 'GCP', 'Azure', 'Azure AD', 'Office 365', 'SaaS'], PLATFORMS: ['all', 'Windows', 'Linux', 'PRE', 'macOS', 'AWS', 'GCP', 'Azure', 'Azure AD', 'Office 365', 'SaaS', 'Network'],
PLATFORM_CONVERSION: { PLATFORM_CONVERSION: {
windows: 'Windows', windows: 'Windows',
linux: 'Linux', linux: 'Linux',
pre: 'PRE',
macos: 'macOS', macos: 'macOS',
aws: 'AWS', aws: 'AWS',
gcp: 'GCP', gcp: 'GCP',
azure: 'Azure', azure: 'Azure',
'azure ad': 'Azure AD', 'azure ad': 'Azure AD',
'office 365': 'Office 365', 'office 365': 'Office 365',
saas: 'SaaS' saas: 'SaaS',
network: 'Network'
} }
}; };

View File

@ -380,7 +380,9 @@
"Azure AD", "Azure AD",
"GCP", "GCP",
"Linux", "Linux",
"Network",
"Office 365", "Office 365",
"PRE",
"SaaS", "SaaS",
"Windows", "Windows",
"macOS" "macOS"
@ -644,7 +646,9 @@
"Azure AD", "Azure AD",
"GCP", "GCP",
"Linux", "Linux",
"Network",
"Office 365", "Office 365",
"PRE",
"SaaS", "SaaS",
"Windows", "Windows",
"macOS" "macOS"
@ -1714,7 +1718,9 @@
"Azure AD", "Azure AD",
"GCP", "GCP",
"Linux", "Linux",
"Network",
"Office 365", "Office 365",
"PRE",
"SaaS", "SaaS",
"Windows", "Windows",
"macOS" "macOS"
@ -2823,7 +2829,9 @@
"Azure AD", "Azure AD",
"GCP", "GCP",
"Linux", "Linux",
"Network",
"Office 365", "Office 365",
"PRE",
"SaaS", "SaaS",
"Windows", "Windows",
"macOS" "macOS"
@ -2839,7 +2847,9 @@
"Azure AD", "Azure AD",
"GCP", "GCP",
"Linux", "Linux",
"Network",
"Office 365", "Office 365",
"PRE",
"SaaS", "SaaS",
"Windows", "Windows",
"macOS" "macOS"
@ -3517,7 +3527,9 @@
"Azure AD", "Azure AD",
"GCP", "GCP",
"Linux", "Linux",
"Network",
"Office 365", "Office 365",
"PRE",
"SaaS", "SaaS",
"Windows", "Windows",
"macOS" "macOS"
@ -3596,5 +3608,13 @@
"Windows" "Windows"
], ],
"autosuggest": "S0518 - PolyglotDuke" "autosuggest": "S0518 - PolyglotDuke"
},
{
"software_id": "S0519",
"software_name": "SYNful Knock",
"platforms": [
"Network"
],
"autosuggest": "S0519 - SYNful Knock"
} }
] ]

View File

@ -206,28 +206,23 @@ 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
""" """
layer = dict() layer = dict()
layer['name'] = name layer['name'] = name
layer['version'] = '3.0' layer['versions'] = {'navigator': '4.0', 'layer': '4.0'}
layer['domain'] = 'mitre-enterprise' layer['domain'] = 'enterprise-attack'
layer['description'] = description layer['description'] = description
if stage == 'attack': layer['filters'] = {'platforms': platform}
layer['filters'] = {'stages': ['act'], 'platforms': platform}
else:
layer['filters'] = {'stages': ['prepare'], 'platforms': platform}
layer['sorting'] = sorting layer['sorting'] = sorting
layer['layout'] = {"layout": "flat", "showName": True, "showID": False} layer['layout'] = {"layout": "flat", "showName": True, "showID": False}
layer['hideDisable'] = False layer['hideDisable'] = False
@ -240,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})
@ -289,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},
@ -312,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},
@ -334,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},
@ -355,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},
@ -1111,7 +1101,7 @@ def get_platform_from_yaml(yaml_content):
platform = [platform] platform = [platform]
platform = [p.lower() for p in platform if p is not None] platform = [p.lower() for p in platform if p is not None]
if platform == ['all']: if 'all' in platform:
platform = list(PLATFORMS.values()) platform = list(PLATFORMS.values())
else: else:
valid_platform_list = [] valid_platform_list = []

View File

@ -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)

View File

@ -94,23 +94,22 @@ def check_health_data_sources(filename, ds_content, health_is_called, no_print=F
ATT&CK Platform is not part of the EQL search result ATT&CK Platform is not part of the EQL search result
:return: False if no errors have been found, otherwise True :return: False if no errors have been found, otherwise True
""" """
from generic import get_applicable_data_sources_platform from generic import get_applicable_data_sources_platform, get_platform_from_yaml
has_error = False has_error = False
platform = ds_content.get('platform', None) platform = get_platform_from_yaml(ds_content)
if not src_eql: if not src_eql:
if platform != 'all' and platform != ['all']: if isinstance(platform, str):
if isinstance(platform, str): platform = [platform]
platform = [platform] if platform is None or len(platform) == 0 or platform == '':
if platform is None or len(platform) == 0 or platform == '': platform = ['empty']
platform = ['empty'] for p in platform:
for p in platform: if p.lower() not in PLATFORMS.keys():
if p.lower() not in PLATFORMS.keys(): has_error = _print_error_msg(
has_error = _print_error_msg( '[!] EMPTY or INVALID value for \'platform\' within the data source admin. '
'[!] EMPTY or INVALID value for \'platform\' within the data source admin. ' 'file: %s (should be value(s) of: [%s] or all)' % (p, ', '.join(list(PLATFORMS.values()))),
'file: %s (should be value(s) of: [%s] or all)' % (p, ', '.join(list(PLATFORMS.values()))), health_is_called)
health_is_called)
ds_list = [kv['data_source_name'].lower() for kv in ds_content['data_sources']] ds_list = [kv['data_source_name'].lower() for kv in ds_content['data_sources']]
@ -275,7 +274,7 @@ def _check_health_techniques(filename, technique_content, health_is_called):
for p in platform: for p in platform:
if p.lower() not in PLATFORMS.keys(): if p.lower() not in PLATFORMS.keys():
has_error = _print_error_msg( has_error = _print_error_msg(
'[!] EMPTY or INVALID value for \'platform\' within the data source admin. ' '[!] EMPTY or INVALID value for \'platform\' within the technique admin. '
'file: %s (should be value(s) of: [%s] or all)' % (p, ', '.join(list(PLATFORMS.values()))), 'file: %s (should be value(s) of: [%s] or all)' % (p, ', '.join(list(PLATFORMS.values()))),
health_is_called) health_is_called)

View File

@ -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()

View File

@ -1,7 +1,7 @@
attackcti==0.3.3 attackcti==0.3.3
simplejson==3.17.2 simplejson==3.17.2
plotly==4.12.0 plotly==4.12.0
pandas==1.1.3 pandas==1.1.4
xlsxwriter==1.3.7 xlsxwriter==1.3.7
ruamel.yaml==0.16.12 ruamel.yaml==0.16.12
eql==0.9.5 eql==0.9.5

View File

@ -4,7 +4,7 @@ version: 1.0
file_type: data-source-administration file_type: data-source-administration
name: empty-data-source-admin-file name: empty-data-source-admin-file
# Fill in the correct MITRE ATT&CK enterprise platform(s). Multiple can be included using a list # Fill in the correct MITRE ATT&CK enterprise platform(s). Multiple can be included using a list
# - (Windows, Linux, macOS, AWS, GCP, Azure, Azure AD, Office 365, SaaS) # - (Windows, Linux, macOS, PRE, AWS, GCP, Azure, Azure AD, Office 365, SaaS, Network)
# Also, take into account which data sources are applicable per platform. For more info see: # Also, take into account which data sources are applicable per platform. For more info see:
# - https://github.com/rabobank-cdc/DeTTECT/wiki/Data-sources-per-platform # - https://github.com/rabobank-cdc/DeTTECT/wiki/Data-sources-per-platform
platform: platform:
@ -35,6 +35,18 @@ data_sources:
timeliness: 0 timeliness: 0
consistency: 0 consistency: 0
retention: 0 retention: 0
- data_source_name: GCP audit logs
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: Process command-line parameters - data_source_name: Process command-line parameters
date_registered: date_registered:
date_connected: date_connected:
@ -119,6 +131,42 @@ data_sources:
timeliness: 0 timeliness: 0
consistency: 0 consistency: 0
retention: 0 retention: 0
- data_source_name: Network device command history
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: Network device configuration
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: Network device run-time memory
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: Windows event logs - data_source_name: Windows event logs
date_registered: date_registered:
date_connected: date_connected:
@ -203,6 +251,18 @@ data_sources:
timeliness: 0 timeliness: 0
consistency: 0 consistency: 0
retention: 0 retention: 0
- data_source_name: SSL/TLS certificates
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: SSL/TLS inspection - data_source_name: SSL/TLS inspection
date_registered: date_registered:
date_connected: date_connected:
@ -371,6 +431,18 @@ data_sources:
timeliness: 0 timeliness: 0
consistency: 0 consistency: 0
retention: 0 retention: 0
- data_source_name: Social media monitoring
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: Web logs - data_source_name: Web logs
date_registered: date_registered:
date_connected: date_connected:
@ -491,6 +563,18 @@ data_sources:
timeliness: 0 timeliness: 0
consistency: 0 consistency: 0
retention: 0 retention: 0
- data_source_name: Domain registration
date_registered:
date_connected:
products: []
available_for_data_analytics: False
comment: ''
data_quality:
device_completeness: 0
data_field_completeness: 0
timeliness: 0
consistency: 0
retention: 0
- data_source_name: Browser extensions - data_source_name: Browser extensions
date_registered: date_registered:
date_connected: date_connected:

View File

@ -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)