Merge pull request #972 from cvandeplas/main

chg: [MITRE] Split Matrix view based on OS and more metadata
This commit is contained in:
Alexandre Dulaunoy 2024-05-13 13:47:31 +02:00 committed by GitHub
commit 71f219c9ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 3258 additions and 1427 deletions

10
.vscode/launch.json vendored
View file

@ -1,6 +1,15 @@
{ {
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"name": "gen_mitre",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": "-p ../../MITRE-ATTACK",
"cwd": "${fileDirname}"
},
{ {
"name": "gen_interpol_dwvat", "name": "gen_interpol_dwvat",
"type": "debugpy", "type": "debugpy",
@ -9,7 +18,6 @@
"console": "integratedTerminal", "console": "integratedTerminal",
"args": "-p ../../DW-VA-Taxonomy", "args": "-p ../../DW-VA-Taxonomy",
"cwd": "${fileDirname}" "cwd": "${fileDirname}"
}, },
{ {
"name": "Python Debugger: Current File", "name": "Python Debugger: Current File",

File diff suppressed because it is too large Load diff

View file

@ -2,9 +2,55 @@
"description": "ATT&CK Tactic", "description": "ATT&CK Tactic",
"icon": "map", "icon": "map",
"kill_chain_order": { "kill_chain_order": {
"mitre-attack": [ "attack-Azure-AD": [
"reconnaissance", "initial-access",
"resource-development", "execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"impact"
],
"attack-Containers": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"impact"
],
"attack-Google-Workspace": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"exfiltration",
"impact"
],
"attack-IaaS": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"exfiltration",
"impact"
],
"attack-Linux": [
"initial-access", "initial-access",
"execution", "execution",
"persistence", "persistence",
@ -18,7 +64,79 @@
"exfiltration", "exfiltration",
"impact" "impact"
], ],
"mitre-mobile-attack": [ "attack-Network": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"command-and-control",
"exfiltration",
"impact"
],
"attack-Office-365": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"exfiltration",
"impact"
],
"attack-PRE": [
"reconnaissance",
"resource-development"
],
"attack-SaaS": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"exfiltration",
"impact"
],
"attack-Windows": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"command-and-control",
"exfiltration",
"impact"
],
"attack-macOS": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"command-and-control",
"exfiltration",
"impact"
],
"mobile-attack-Android": [
"initial-access", "initial-access",
"execution", "execution",
"persistence", "persistence",
@ -34,7 +152,23 @@
"network-effects", "network-effects",
"remote-service-effects" "remote-service-effects"
], ],
"mitre-pre-attack": [ "mobile-attack-iOS": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"command-and-control",
"exfiltration",
"impact",
"network-effects",
"remote-service-effects"
],
"pre-attack": [
"priority-definition-planning", "priority-definition-planning",
"priority-definition-direction", "priority-definition-direction",
"target-selection", "target-selection",
@ -49,12 +183,14 @@
"persona-development", "persona-development",
"build-capabilities", "build-capabilities",
"test-capabilities", "test-capabilities",
"stage-capabilities" "stage-capabilities",
"launch",
"compromise"
] ]
}, },
"name": "Attack Pattern", "name": "Attack Pattern",
"namespace": "mitre-attack", "namespace": "mitre-attack",
"type": "mitre-attack-pattern", "type": "mitre-attack-pattern",
"uuid": "c4e851fa-775f-11e7-8163-b774922098cd", "uuid": "c4e851fa-775f-11e7-8163-b774922098cd",
"version": 9 "version": 10
} }

View file

@ -25,6 +25,62 @@ types = {'data-source': 'x-mitre-data-source',
} }
mitre_sources = ['mitre-attack', 'mitre-ics-attack', 'mitre-pre-attack', 'mitre-mobile-attack'] mitre_sources = ['mitre-attack', 'mitre-ics-attack', 'mitre-pre-attack', 'mitre-mobile-attack']
kill_chain_order_sort_order = {
"attack": [
"reconnaissance",
"resource-development",
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"command-and-control",
"exfiltration",
"impact"
],
"mobile-attack": [
"initial-access",
"execution",
"persistence",
"privilege-escalation",
"defense-evasion",
"credential-access",
"discovery",
"lateral-movement",
"collection",
"command-and-control",
"exfiltration",
"impact",
"network-effects",
"remote-service-effects"
],
"pre-attack": [
"priority-definition-planning",
"priority-definition-direction",
"target-selection",
"technical-information-gathering",
"people-information-gathering",
"organizational-information-gathering",
"technical-weakness-identification",
"people-weakness-identification",
"organizational-weakness-identification",
"adversary-opsec",
"establish-&-maintain-infrastructure",
"persona-development",
"build-capabilities",
"test-capabilities",
"stage-capabilities",
"launch", # added manually
"compromise" # added manually
]
}
all_data = {} # variable that will contain everything all_data = {} # variable that will contain everything
# read in the non-MITRE data # read in the non-MITRE data
@ -90,6 +146,12 @@ for domain in domains:
if item['type'] not in types.values(): if item['type'] not in types.values():
continue continue
# skip deprecated and/or revoked
# if 'revoked' in item and item['revoked']:
# continue
# if 'x_mitre_deprecated' in item and item['x_mitre_deprecated']:
# continue
# print(json.dumps(item, indent=2, sort_keys=True, ensure_ascii=False)) # print(json.dumps(item, indent=2, sort_keys=True, ensure_ascii=False))
try: try:
# build the new data structure # build the new data structure
@ -97,6 +159,7 @@ for domain in domains:
uuid = re.search('--(.*)$', item['id']).group(0)[2:] uuid = re.search('--(.*)$', item['id']).group(0)[2:]
# item exist already in the all_data set # item exist already in the all_data set
update = False update = False
if uuid in all_data_uuid: if uuid in all_data_uuid:
value = all_data_uuid[uuid] value = all_data_uuid[uuid]
@ -130,12 +193,28 @@ for domain in domains:
if 'kill_chain_phases' in item: # many (but not all) attack-patterns have this if 'kill_chain_phases' in item: # many (but not all) attack-patterns have this
value['meta']['kill_chain'] = [] value['meta']['kill_chain'] = []
for killchain in item['kill_chain_phases']: for killchain in item['kill_chain_phases']:
value['meta']['kill_chain'].append(killchain['kill_chain_name'] + ':' + killchain['phase_name']) kill_chain_name = killchain['kill_chain_name'][6:]
phase_name = killchain['phase_name']
if 'x_mitre_platforms' in item:
for platform in item['x_mitre_platforms']:
platform = platform.replace(' ', '-')
value['meta']['kill_chain'].append(f"{kill_chain_name}-{platform}:{phase_name}")
else:
value['meta']['kill_chain'].append(f"{kill_chain_name}:{phase_name}")
if 'x_mitre_data_sources' in item: if 'x_mitre_data_sources' in item:
value['meta']['mitre_data_sources'] = item['x_mitre_data_sources'] value['meta']['mitre_data_sources'] = item['x_mitre_data_sources']
if 'x_mitre_platforms' in item: if 'x_mitre_platforms' in item:
value['meta']['mitre_platforms'] = item['x_mitre_platforms'] value['meta']['mitre_platforms'] = item['x_mitre_platforms']
# TODO add the other x_mitre elements dynamically # TODO add the other x_mitre elements dynamically
# x_mitre_fields = [key for key in item.keys() if key.startswith('x_mitre')]
# skip_x_mitre_fields = ['x_mitre_aliases', 'x_mitre_version', 'x_mitre_old_attack_id', 'mitre_attack_spec_version']
# for skip_field in skip_x_mitre_fields:
# try:
# x_mitre_fields.remove(skip_field)
# except ValueError:
# pass
# for x_mitre_field in x_mitre_fields:
# value['meta'][x_mitre_field[2:]] = item[x_mitre_field]
# relationships will be build separately afterwards # relationships will be build separately afterwards
value['type'] = item['type'] # remove this before dump to json value['type'] = item['type'] # remove this before dump to json
@ -144,7 +223,7 @@ for domain in domains:
# FIXME if 'x_mitre_deprecated' , 'revoked' # FIXME if 'x_mitre_deprecated' , 'revoked'
all_data_uuid[uuid] = value all_data_uuid[uuid] = value
except Exception as e: except Exception:
print(json.dumps(item, sort_keys=True, indent=2)) print(json.dumps(item, sort_keys=True, indent=2))
import traceback import traceback
traceback.print_exc() traceback.print_exc()
@ -208,6 +287,8 @@ for domain in domains:
# dump all_data to their respective file # dump all_data to their respective file
for t, meta_t in types.items(): for t, meta_t in types.items():
kill_chain_order = {}
fname = os.path.join(misp_dir, 'clusters', 'mitre-{}.json'.format(t)) fname = os.path.join(misp_dir, 'clusters', 'mitre-{}.json'.format(t))
if not os.path.exists(fname): if not os.path.exists(fname):
exit("File {} does not exist, this is unexpected.".format(fname)) exit("File {} does not exist, this is unexpected.".format(fname))
@ -222,6 +303,11 @@ for t, meta_t in types.items():
item_2 = item.copy() item_2 = item.copy()
item_2.pop('type', None) item_2.pop('type', None)
file_data['values'].append(item_2) file_data['values'].append(item_2)
for kill_chains in item['meta'].get('kill_chain', []):
kill_chain_name, kill_chain_phase = kill_chains.split(':')
if kill_chain_name not in kill_chain_order:
kill_chain_order[kill_chain_name] = set()
kill_chain_order[kill_chain_name].add(kill_chain_phase)
# FIXME the sort algo needs to be further improved, potentially with a recursive deep sort # FIXME the sort algo needs to be further improved, potentially with a recursive deep sort
file_data['values'] = sorted(file_data['values'], key=lambda x: sorted(x['value'])) file_data['values'] = sorted(file_data['values'], key=lambda x: sorted(x['value']))
@ -238,4 +324,36 @@ for t, meta_t in types.items():
json.dump(file_data, f, indent=2, sort_keys=True, ensure_ascii=False) json.dump(file_data, f, indent=2, sort_keys=True, ensure_ascii=False)
f.write('\n') # only needed for the beauty and to be compliant with jq_all_the_things f.write('\n') # only needed for the beauty and to be compliant with jq_all_the_things
# rebuild the galaxies file with kill_chains
# OK, this is really inefficient, but just the easiest way to get it done now
fname_galaxy = os.path.join(misp_dir, 'galaxies', 'mitre-{}.json'.format(t))
if not os.path.exists(fname_galaxy):
exit("File {} does not exist, this is unexpected.".format(fname_galaxy))
with open(fname_galaxy) as f_galaxy:
file_data_galaxy = json.load(f_galaxy)
# sort the kill chain order in the right way, using the kill_chain_order_sort_order
kill_chain_order_sorted = {}
for kill_chain_name, kill_chain_phases in kill_chain_order.items():
for kill_chain_order_sort_order_key in kill_chain_order_sort_order.keys():
if kill_chain_name.startswith(kill_chain_order_sort_order_key):
try:
kill_chain_order_sorted[kill_chain_name] = sorted(
list(kill_chain_phases),
key=kill_chain_order_sort_order[kill_chain_order_sort_order_key].index)
except ValueError as e:
print("ERROR:")
print(f"- Kill chain: {kill_chain_name}")
print(f"- Kill chain phases: {kill_chain_phases}")
print(f"- Kill chain order sort order: {kill_chain_order_sort_order[kill_chain_order_sort_order_key]}")
exit(f"ERROR: kill_chain_order_sort_order does not contain a key for {kill_chain_name} - {e}. Please add it manually in the code.")
if kill_chain_order_sorted:
file_data_galaxy['kill_chain_order'] = dict(sorted(kill_chain_order_sorted.items()))
file_data_galaxy['version'] += 1
with open(fname_galaxy, 'w') as f_galaxy:
json.dump(file_data_galaxy, f_galaxy, indent=2, sort_keys=True, ensure_ascii=False)
f_galaxy.write('\n') # only needed for the beauty and to be compliant with jq_all_the_things
print("All done, please don't forget to ./jq_all_the_things.sh, commit, and then ./validate_all.sh.") print("All done, please don't forget to ./jq_all_the_things.sh, commit, and then ./validate_all.sh.")