Merge branch 'development'
commit
9f8630f727
|
@ -1,6 +1,6 @@
|
|||
FROM python:3.8-slim-buster
|
||||
|
||||
LABEL version="1.4.1"
|
||||
LABEL version="1.4.2"
|
||||
|
||||
# copy DeTT&CT and install the requirements
|
||||
COPY . /opt/DeTTECT
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<img src="https://github.com/rabobank-cdc/DeTTECT/wiki/images/logo.png" alt="DeTT&CT" width=30% height=30%>
|
||||
|
||||
#### 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:
|
||||
- [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
|
||||
|
|
13
constants.py
13
constants.py
|
@ -2,7 +2,7 @@ import re
|
|||
|
||||
APP_NAME = 'DeTT&CT'
|
||||
APP_DESC = 'Detect Tactics, Techniques & Combat Threats'
|
||||
VERSION = '1.4.1'
|
||||
VERSION = '1.4.2'
|
||||
|
||||
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\' argument or using the option in the interactive menu: \n - '
|
||||
|
||||
PLATFORMS = {'windows': 'Windows', 'linux': 'Linux', 'macos': 'macOS', 'aws': 'AWS', 'gcp': 'GCP', 'azure': 'Azure',
|
||||
'azure ad': 'Azure AD', 'office 365': 'Office 365', 'saas': 'SaaS'}
|
||||
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', 'network': 'Network'}
|
||||
|
||||
# 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',
|
||||
|
@ -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',
|
||||
'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'],
|
||||
'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',
|
||||
'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',
|
||||
|
@ -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',
|
||||
'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',
|
||||
'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',
|
||||
'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',
|
||||
|
@ -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',
|
||||
'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',
|
||||
'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']
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ def generate_data_sources_layer(filename, output_filename, layer_name, platform=
|
|||
if not layer_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
|
||||
|
||||
json_string = simplejson.dumps(layer).replace('}, ', '},\n')
|
||||
|
|
31
dettect.py
31
dettect.py
|
@ -14,7 +14,8 @@ def _init_menu():
|
|||
menu_parser = argparse.ArgumentParser(description='Detect Tactics, Techniques & Combat Threats',
|
||||
epilog='Source: https://github.com/rabobank-cdc/DeTTECT')
|
||||
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')
|
||||
|
||||
# add subparsers
|
||||
|
@ -24,7 +25,7 @@ def _init_menu():
|
|||
'group, generic} --help', metavar='', dest='subparser')
|
||||
|
||||
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)
|
||||
|
||||
# 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('--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 '
|
||||
'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 '
|
||||
'to ATT&CK with sub-techniques.', action='store_true')
|
||||
'to ATT&CK with sub-techniques', action='store_true')
|
||||
|
||||
# create the visibility parser
|
||||
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('--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 '
|
||||
'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 '
|
||||
'to ATT&CK with sub-techniques.', action='store_true')
|
||||
'to ATT&CK with sub-techniques', action='store_true')
|
||||
|
||||
# create the detection parser
|
||||
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('--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 '
|
||||
'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 '
|
||||
'to ATT&CK with sub-techniques.', action='store_true')
|
||||
'to ATT&CK with sub-techniques', action='store_true')
|
||||
|
||||
# create the group parser
|
||||
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, '
|
||||
'name or alias (default is all groups). Multiple Groups can be '
|
||||
'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')
|
||||
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. '
|
||||
|
@ -179,8 +180,6 @@ def _init_menu():
|
|||
'can be provided with extra \'-p/--platform\' arguments',
|
||||
choices=['all'] + list(PLATFORMS.values()), default=None, action='append',
|
||||
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 '
|
||||
'provided EQL query')
|
||||
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('--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 '
|
||||
'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 '
|
||||
'to ATT&CK with sub-techniques.', action='store_true')
|
||||
'to ATT&CK with sub-techniques', action='store_true')
|
||||
|
||||
# create the generic parser
|
||||
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'],
|
||||
default='modified')
|
||||
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
|
||||
|
||||
|
@ -288,7 +287,7 @@ def _menu(menu_parser):
|
|||
|
||||
# TODO add search capabilities
|
||||
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.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.
|
||||
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():
|
||||
|
|
|
@ -1076,9 +1076,9 @@
|
|||
}
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.0.tgz",
|
||||
"integrity": "sha512-+cIGPCBdLCzqxdtwppswP+zTsH9BOIGzAeKfBIbtb4gW/giMlfMwP0HUSFfhzh20f9u8uZ8hOp62+4GPquTbwQ==",
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz",
|
||||
"integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.12.4",
|
||||
|
@ -1940,17 +1940,6 @@
|
|||
"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": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||
|
@ -2004,41 +1993,12 @@
|
|||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"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": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||
|
@ -2132,16 +2092,6 @@
|
|||
"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": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
|
||||
|
@ -2159,18 +2109,6 @@
|
|||
"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": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||
|
@ -5318,13 +5256,13 @@
|
|||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
|
||||
},
|
||||
"eslint": {
|
||||
"version": "7.12.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.0.tgz",
|
||||
"integrity": "sha512-n5pEU27DRxCSlOhJ2rO57GDLcNsxO0LPpAbpFdh7xmcDmjmlGUfoyrsB3I7yYdQXO5N3gkSTiDrPSPNFiiirXA==",
|
||||
"version": "7.12.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-7.12.1.tgz",
|
||||
"integrity": "sha512-HlMTEdr/LicJfN08LB3nM1rRYliDXOmfoO4vj39xN6BLpFzF00hbwBoqHk8UcJ2M/3nlARZWy/mslvGEuZFvsg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"@eslint/eslintrc": "^0.2.0",
|
||||
"@eslint/eslintrc": "^0.2.1",
|
||||
"ajv": "^6.10.0",
|
||||
"chalk": "^4.0.0",
|
||||
"cross-spawn": "^7.0.2",
|
||||
|
@ -12055,9 +11993,9 @@
|
|||
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||
},
|
||||
"v8-compile-cache": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz",
|
||||
"integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==",
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz",
|
||||
"integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
|
||||
"dev": true
|
||||
},
|
||||
"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": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-property-decorator/-/vue-property-decorator-8.5.1.tgz",
|
||||
|
@ -12233,9 +12262,9 @@
|
|||
}
|
||||
},
|
||||
"vue-router": {
|
||||
"version": "3.4.7",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.7.tgz",
|
||||
"integrity": "sha512-CbHXue5BLrDivOk5O4eZ0WT4Yj8XwdXa4kCnsEIOzYUPF/07ZukayA2jGxDCJxLc9SgVQX9QX0OuGOwGlVB4Qg=="
|
||||
"version": "3.4.8",
|
||||
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.8.tgz",
|
||||
"integrity": "sha512-3BsR84AqarcmweXjItxw3jwQsiYNssYg090yi4rlzTnCJxmHtkyCvhNz9Z7qRSOkmiV485KkUCReTp5AjNY4wg=="
|
||||
},
|
||||
"vue-router-prefetch": {
|
||||
"version": "1.6.0",
|
||||
|
@ -12243,9 +12272,9 @@
|
|||
"integrity": "sha512-mLoh5on0GlO3ag0GWt+J8FkcalSAZJGJ/dNpA8PQ8X7pAoFht683FOJZlqdV7vXwKH7BjP+UfKcK09tHtNeDbw=="
|
||||
},
|
||||
"vue-scrollto": {
|
||||
"version": "2.19.1",
|
||||
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.19.1.tgz",
|
||||
"integrity": "sha512-4+CCBLm6fTwDkKgqJIOnE4UB9ngYVoFAREJfPhT0YaEKMo6oAjnP5fv4//cuN/DxXCi75N/QOwmpOPn/xwPd5g==",
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-scrollto/-/vue-scrollto-2.20.0.tgz",
|
||||
"integrity": "sha512-7i+AGKJTThZnMEkhIPgrZjyAX+fXV7/rGdg+CV283uZZwCxwiMXaBLTmIc5RTA4uwGnT+E6eJle3mXQfM2OD3A==",
|
||||
"requires": {
|
||||
"bezier-easing": "2.1.0"
|
||||
}
|
||||
|
@ -12264,9 +12293,9 @@
|
|||
"integrity": "sha512-5yBGlVOQ5gvrkuoT6TtdVHLXy507EKN7X8vgXhPJsMbcJFuDTqzd6AFElSK7Bv4UwcKSU/o1OPzD9GBi86VuTQ=="
|
||||
},
|
||||
"vue-slider-component": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.2.9.tgz",
|
||||
"integrity": "sha512-b70aa66rb+9G1+RwptDSHvfBy4YXWP3fRk49pfs55WprJEVy5FM8IT5R7Yu5vYQDzgjXNGb8U0M8BZSxWeYRNg==",
|
||||
"version": "3.2.10",
|
||||
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-3.2.10.tgz",
|
||||
"integrity": "sha512-SDShSZrvCpAcjTUJzbMEDmDl6+C0vVlNwViMfsgkmWex+lcRf7Z89A9rNghnmux2oJwmz5XKDGLVZ59+q+P9uw==",
|
||||
"requires": {
|
||||
"core-js": "^3.6.5",
|
||||
"vue-property-decorator": "^8.0.0"
|
||||
|
|
|
@ -27,12 +27,12 @@
|
|||
"vue-directive-tooltip": "^1.6.3",
|
||||
"vue-js-toggle-button": "^1.3.3",
|
||||
"vue-resource": "^1.5.1",
|
||||
"vue-router": "^3.4.7",
|
||||
"vue-router": "^3.4.8",
|
||||
"vue-router-prefetch": "^1.6.0",
|
||||
"vue-scrollto": "^2.19.1",
|
||||
"vue-scrollto": "^2.20.0",
|
||||
"vue-showdown": "^2.4.1",
|
||||
"vue-simple-suggest": "^1.10.3",
|
||||
"vue-slider-component": "^3.2.9",
|
||||
"vue-slider-component": "^3.2.10",
|
||||
"vue2-transitions": "^0.3.0",
|
||||
"vuejs-datepicker": "^1.6.2",
|
||||
"vuejs-smart-table": "0.0.5",
|
||||
|
@ -42,7 +42,7 @@
|
|||
"@vue/cli-plugin-babel": "^4.5.8",
|
||||
"@vue/cli-plugin-eslint": "^4.5.8",
|
||||
"@vue/cli-service": "^4.5.8",
|
||||
"eslint": "^7.12.0",
|
||||
"eslint": "^7.12.1",
|
||||
"eslint-loader": "^4.0.2",
|
||||
"eslint-plugin-vue": "^6.2.2",
|
||||
"vue-template-compiler": "^2.6.12"
|
||||
|
|
|
@ -4,7 +4,7 @@ from attackcti import attack_client
|
|||
FILE_DATA_SOURCES = 'data_sources.json'
|
||||
FILE_TECHNIQUES = 'techniques.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():
|
||||
|
|
|
@ -85,16 +85,18 @@ export default {
|
|||
comment: '',
|
||||
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: {
|
||||
windows: 'Windows',
|
||||
linux: 'Linux',
|
||||
pre: 'PRE',
|
||||
macos: 'macOS',
|
||||
aws: 'AWS',
|
||||
gcp: 'GCP',
|
||||
azure: 'Azure',
|
||||
'azure ad': 'Azure AD',
|
||||
'office 365': 'Office 365',
|
||||
saas: 'SaaS'
|
||||
saas: 'SaaS',
|
||||
network: 'Network'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -380,7 +380,9 @@
|
|||
"Azure AD",
|
||||
"GCP",
|
||||
"Linux",
|
||||
"Network",
|
||||
"Office 365",
|
||||
"PRE",
|
||||
"SaaS",
|
||||
"Windows",
|
||||
"macOS"
|
||||
|
@ -644,7 +646,9 @@
|
|||
"Azure AD",
|
||||
"GCP",
|
||||
"Linux",
|
||||
"Network",
|
||||
"Office 365",
|
||||
"PRE",
|
||||
"SaaS",
|
||||
"Windows",
|
||||
"macOS"
|
||||
|
@ -1714,7 +1718,9 @@
|
|||
"Azure AD",
|
||||
"GCP",
|
||||
"Linux",
|
||||
"Network",
|
||||
"Office 365",
|
||||
"PRE",
|
||||
"SaaS",
|
||||
"Windows",
|
||||
"macOS"
|
||||
|
@ -2823,7 +2829,9 @@
|
|||
"Azure AD",
|
||||
"GCP",
|
||||
"Linux",
|
||||
"Network",
|
||||
"Office 365",
|
||||
"PRE",
|
||||
"SaaS",
|
||||
"Windows",
|
||||
"macOS"
|
||||
|
@ -2839,7 +2847,9 @@
|
|||
"Azure AD",
|
||||
"GCP",
|
||||
"Linux",
|
||||
"Network",
|
||||
"Office 365",
|
||||
"PRE",
|
||||
"SaaS",
|
||||
"Windows",
|
||||
"macOS"
|
||||
|
@ -3517,7 +3527,9 @@
|
|||
"Azure AD",
|
||||
"GCP",
|
||||
"Linux",
|
||||
"Network",
|
||||
"Office 365",
|
||||
"PRE",
|
||||
"SaaS",
|
||||
"Windows",
|
||||
"macOS"
|
||||
|
@ -3596,5 +3608,13 @@
|
|||
"Windows"
|
||||
],
|
||||
"autosuggest": "S0518 - PolyglotDuke"
|
||||
},
|
||||
{
|
||||
"software_id": "S0519",
|
||||
"software_name": "SYNful Knock",
|
||||
"platforms": [
|
||||
"Network"
|
||||
],
|
||||
"autosuggest": "S0519 - SYNful Knock"
|
||||
}
|
||||
]
|
40
generic.py
40
generic.py
|
@ -206,28 +206,23 @@ def init_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.
|
||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||
:param name: name
|
||||
:param description: description
|
||||
:param stage: stage (act | prepare)
|
||||
:param platform: platform
|
||||
:param sorting: sorting
|
||||
:return: layer template dictionary
|
||||
"""
|
||||
layer = dict()
|
||||
layer['name'] = name
|
||||
layer['version'] = '3.0'
|
||||
layer['domain'] = 'mitre-enterprise'
|
||||
layer['versions'] = {'navigator': '4.0', 'layer': '4.0'}
|
||||
layer['domain'] = 'enterprise-attack'
|
||||
layer['description'] = description
|
||||
|
||||
if stage == 'attack':
|
||||
layer['filters'] = {'stages': ['act'], 'platforms': platform}
|
||||
else:
|
||||
layer['filters'] = {'stages': ['prepare'], 'platforms': platform}
|
||||
|
||||
layer['filters'] = {'platforms': platform}
|
||||
layer['sorting'] = sorting
|
||||
layer['layout'] = {"layout": "flat", "showName": True, "showID": False}
|
||||
layer['hideDisable'] = False
|
||||
|
@ -240,19 +235,18 @@ def _get_base_template(name, description, stage, platform, sorting):
|
|||
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.
|
||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||
:param name: name
|
||||
:param max_count: the sum of all count values
|
||||
:param description: description
|
||||
:param stage: stage (act | prepare)
|
||||
:param platform: platform
|
||||
:param overlay_type: group, visibility or detection
|
||||
: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['legendItems'] = []
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||
:param name: name
|
||||
:param description: description
|
||||
:param stage: stage (act | prepare)
|
||||
:param platform: platform
|
||||
:return: layer template dictionary
|
||||
"""
|
||||
layer = _get_base_template(name, description, stage, platform, 0)
|
||||
layer = _get_base_template(name, description, platform, 0)
|
||||
layer['legendItems'] = \
|
||||
[
|
||||
{'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
|
||||
|
||||
|
||||
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.
|
||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||
:param name: name
|
||||
:param description: description
|
||||
:param stage: stage (act | prepare)
|
||||
:param platform: platform
|
||||
:return: layer template dictionary
|
||||
"""
|
||||
layer = _get_base_template(name, description, stage, platform, 0)
|
||||
layer = _get_base_template(name, description, platform, 0)
|
||||
layer['legendItems'] = \
|
||||
[
|
||||
{'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
|
||||
|
||||
|
||||
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.
|
||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||
:param name: name
|
||||
:param description: description
|
||||
:param stage: stage (act | prepare)
|
||||
:param platform: platform
|
||||
:return: layer template dictionary
|
||||
"""
|
||||
layer = _get_base_template(name, description, stage, platform, 0)
|
||||
layer = _get_base_template(name, description, platform, 0)
|
||||
layer['legendItems'] = \
|
||||
[
|
||||
{'label': 'Visibility score 1: Minimal', 'color': COLOR_V_1},
|
||||
|
@ -355,17 +346,16 @@ def get_layer_template_visibility(name, description, stage, platform):
|
|||
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.
|
||||
More information on the layer format can be found here: https://github.com/mitre/attack-navigator/blob/master/layers/
|
||||
:param name: name
|
||||
:param description: description
|
||||
:param stage: stage (act | prepare)
|
||||
:param platform: platform
|
||||
:return: layer template dictionary
|
||||
"""
|
||||
layer = _get_base_template(name, description, stage, platform, 0)
|
||||
layer = _get_base_template(name, description, platform, 0)
|
||||
layer['legendItems'] = \
|
||||
[
|
||||
{'label': 'Visibility and detection', 'color': COLOR_OVERLAY_BOTH},
|
||||
|
@ -1111,7 +1101,7 @@ def get_platform_from_yaml(yaml_content):
|
|||
platform = [platform]
|
||||
platform = [p.lower() for p in platform if p is not None]
|
||||
|
||||
if platform == ['all']:
|
||||
if 'all' in platform:
|
||||
platform = list(PLATFORMS.values())
|
||||
else:
|
||||
valid_platform_list = []
|
||||
|
|
|
@ -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
|
||||
print('[!] Unknown ATT&CK group: ' + group_arg)
|
||||
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)
|
||||
group_found = False
|
||||
|
||||
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,
|
||||
and hence techniques they support.
|
||||
:param groups: ATT&CK groups
|
||||
:param stage: attack or pre-attack
|
||||
:param platform: one or multiple values from PLATFORMS constant
|
||||
: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
|
||||
# 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['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)):
|
||||
if s['group_id'] not in groups_dict:
|
||||
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]
|
||||
|
||||
|
||||
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
|
||||
: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 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
|
||||
|
@ -196,7 +194,7 @@ def _get_group_techniques(groups, stage, platform, file_type):
|
|||
platforms = ['Windows']
|
||||
|
||||
# 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)):
|
||||
if gr['group_id'] not in groups_dict:
|
||||
groups_found.add(gr['group_id'])
|
||||
|
@ -477,7 +475,7 @@ def _get_group_list(groups, file_type):
|
|||
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):
|
||||
"""
|
||||
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
|
||||
group(s), detections or visibility)
|
||||
: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 software_groups: specify if techniques from related software should be included
|
||||
: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
|
||||
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:
|
||||
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:
|
||||
return None # returns None when the provided Group contains Groups not part of ATT&CK
|
||||
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 overlay_type not in [OVERLAY_TYPE_VISIBILITY, OVERLAY_TYPE_DETECTION]:
|
||||
# if a group overlay is provided, get the software techniques for the overlay
|
||||
groups_software_dict = _get_software_techniques(overlay, stage, platform)
|
||||
groups_software_dict = _get_software_techniques(overlay, platform)
|
||||
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_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)
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
json_string = simplejson.dumps(layer).replace('}, ', '},\n')
|
||||
|
||||
if not output_filename:
|
||||
if stage == 'pre-attack':
|
||||
filename = '_'.join(groups_list)
|
||||
elif overlay:
|
||||
if overlay:
|
||||
filename = platform_to_name(platform) + '_' + '_'.join(groups_list) + '-overlay_' + '_'.join(overlay_list)
|
||||
else:
|
||||
filename = platform_to_name(platform) + '_' + '_'.join(groups_list)
|
||||
|
||||
filename = create_output_filename(stage, filename)
|
||||
filename = create_output_filename('attack', filename)
|
||||
write_file(filename, json_string)
|
||||
else:
|
||||
write_file(output_filename, json_string)
|
||||
|
|
27
health.py
27
health.py
|
@ -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
|
||||
: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
|
||||
|
||||
platform = ds_content.get('platform', None)
|
||||
platform = get_platform_from_yaml(ds_content)
|
||||
|
||||
if not src_eql:
|
||||
if platform != 'all' and platform != ['all']:
|
||||
if isinstance(platform, str):
|
||||
platform = [platform]
|
||||
if platform is None or len(platform) == 0 or platform == '':
|
||||
platform = ['empty']
|
||||
for p in platform:
|
||||
if p.lower() not in PLATFORMS.keys():
|
||||
has_error = _print_error_msg(
|
||||
'[!] 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()))),
|
||||
health_is_called)
|
||||
if isinstance(platform, str):
|
||||
platform = [platform]
|
||||
if platform is None or len(platform) == 0 or platform == '':
|
||||
platform = ['empty']
|
||||
for p in platform:
|
||||
if p.lower() not in PLATFORMS.keys():
|
||||
has_error = _print_error_msg(
|
||||
'[!] 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()))),
|
||||
health_is_called)
|
||||
|
||||
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:
|
||||
if p.lower() not in PLATFORMS.keys():
|
||||
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()))),
|
||||
health_is_called)
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ from eql_yaml import *
|
|||
groups = 'all'
|
||||
software_group = False
|
||||
default_platform = ['Windows']
|
||||
default_stage = 'attack'
|
||||
default_matrix = 'enterprise'
|
||||
groups_overlay = ''
|
||||
overlay_type = 'group'
|
||||
|
@ -460,7 +459,7 @@ def _menu_groups():
|
|||
Prints and handles the Threat actor group mapping functionality.
|
||||
: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
|
||||
_clear()
|
||||
print('Menu: %s' % MENU_NAME_THREAT_ACTOR_GROUP_MAPPING)
|
||||
|
@ -468,12 +467,11 @@ def _menu_groups():
|
|||
print('Options:')
|
||||
print('1. Software group: %s' % str(software_group))
|
||||
print('2. Platform: %s' % ','.join(default_platform))
|
||||
print('3. Stage: %s' % default_stage)
|
||||
print('4. Groups: %s' % groups)
|
||||
print('5. Overlay: ')
|
||||
print('3. Groups: %s' % groups)
|
||||
print('4. Overlay: ')
|
||||
print(' - %s: %s' % ('File' if os.path.exists(groups_overlay) else 'Groups', groups_overlay))
|
||||
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_v_str = '' if not eql_query_visibility else eql_query_visibility
|
||||
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('')
|
||||
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.')
|
||||
choice = _ask_input()
|
||||
if choice == '1':
|
||||
|
@ -491,15 +489,11 @@ def _menu_groups():
|
|||
p = _ask_input().lower()
|
||||
default_platform = [PLATFORMS[p]] if p in PLATFORMS.keys() else ['all']
|
||||
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 '
|
||||
'(default is all groups). Other option is to provide a YAML file with a custom group(s)')
|
||||
g = _ask_input()
|
||||
groups = g if g != '' else 'all'
|
||||
elif choice == '5':
|
||||
elif choice == '4':
|
||||
print('')
|
||||
print('1. Overlay with groups.')
|
||||
print('2. Overlay with detections.')
|
||||
|
@ -521,7 +515,7 @@ def _menu_groups():
|
|||
elif choice == '4':
|
||||
overlay_type = ''
|
||||
groups_overlay = ''
|
||||
elif choice == '6':
|
||||
elif choice == '5':
|
||||
print('')
|
||||
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)
|
||||
|
@ -537,8 +531,8 @@ def _menu_groups():
|
|||
elif choice == '3':
|
||||
eql_all_scores = not eql_all_scores
|
||||
|
||||
elif choice == '7':
|
||||
generate_group_heat_map(groups, groups_overlay, overlay_type, default_stage, default_platform,
|
||||
elif choice == '6':
|
||||
generate_group_heat_map(groups, groups_overlay, overlay_type, default_platform,
|
||||
software_group, eql_query_visibility, eql_query_detection, False,
|
||||
None, None, include_all_score_objs=eql_all_scores)
|
||||
_wait()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
attackcti==0.3.3
|
||||
simplejson==3.17.2
|
||||
plotly==4.12.0
|
||||
pandas==1.1.3
|
||||
pandas==1.1.4
|
||||
xlsxwriter==1.3.7
|
||||
ruamel.yaml==0.16.12
|
||||
eql==0.9.5
|
||||
|
|
|
@ -4,7 +4,7 @@ version: 1.0
|
|||
file_type: data-source-administration
|
||||
name: empty-data-source-admin-file
|
||||
# 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:
|
||||
# - https://github.com/rabobank-cdc/DeTTECT/wiki/Data-sources-per-platform
|
||||
platform:
|
||||
|
@ -35,6 +35,18 @@ data_sources:
|
|||
timeliness: 0
|
||||
consistency: 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
|
||||
date_registered:
|
||||
date_connected:
|
||||
|
@ -119,6 +131,42 @@ data_sources:
|
|||
timeliness: 0
|
||||
consistency: 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
|
||||
date_registered:
|
||||
date_connected:
|
||||
|
@ -203,6 +251,18 @@ data_sources:
|
|||
timeliness: 0
|
||||
consistency: 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
|
||||
date_registered:
|
||||
date_connected:
|
||||
|
@ -371,6 +431,18 @@ data_sources:
|
|||
timeliness: 0
|
||||
consistency: 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
|
||||
date_registered:
|
||||
date_connected:
|
||||
|
@ -491,6 +563,18 @@ data_sources:
|
|||
timeliness: 0
|
||||
consistency: 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
|
||||
date_registered:
|
||||
date_connected:
|
||||
|
|
|
@ -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)
|
||||
if not layer_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)
|
||||
else:
|
||||
my_data_sources = _load_data_sources(filename_data_sources)
|
||||
mapped_techniques_both = _map_and_colorize_techniques_for_overlaid(my_techniques, my_data_sources, platform)
|
||||
if not layer_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)
|
||||
|
||||
|
||||
|
@ -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)
|
||||
if not layer_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)
|
||||
else:
|
||||
mapped_techniques_both = _map_and_colorize_techniques_for_overlaid(my_techniques, my_data_sources, platform)
|
||||
if not layer_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)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue