2024-03-16 22:02:59 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
#
|
|
|
|
#
|
|
|
|
# A simple convertor of the Threat Matrix for storage services to a MISP Galaxy datastructure.
|
|
|
|
# Copyright (C) 2022 Christophe Vandeplas
|
|
|
|
#
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU Affero General Public License as
|
|
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
|
|
# License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU Affero General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
import yaml
|
|
|
|
import os
|
|
|
|
import uuid
|
|
|
|
import re
|
|
|
|
import argparse
|
2024-06-25 11:16:48 +00:00
|
|
|
from pymispgalaxies import Cluster, Galaxy
|
2024-03-16 22:02:59 +00:00
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='Create/update the Threat Matrix for storage services based on Markdown files.')
|
|
|
|
parser.add_argument("-p", "--path", required=True, help="Path of the 'Threat Matrix for storage services' git clone folder")
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
if not os.path.exists(args.path):
|
|
|
|
exit("ERROR: Threat Matrix for storage services folder incorrect")
|
|
|
|
|
|
|
|
with open(os.path.join(args.path, 'mkdocs.yml'), 'r') as f:
|
|
|
|
mkdocs_data = yaml.load(f, Loader=yaml.BaseLoader)
|
|
|
|
|
|
|
|
tactics = []
|
|
|
|
clusters = {}
|
|
|
|
|
2024-06-25 11:16:48 +00:00
|
|
|
|
|
|
|
mitre_attack_pattern = Cluster('mitre-attack-pattern')
|
|
|
|
|
|
|
|
|
2024-03-16 22:02:59 +00:00
|
|
|
def find_mitre_uuid_from_technique_id(technique_id):
|
2024-06-25 11:16:48 +00:00
|
|
|
try:
|
|
|
|
return mitre_attack_pattern.get_by_external_id(technique_id).uuid
|
|
|
|
except KeyError:
|
|
|
|
print("No MITRE UUID found for technique_id: ", technique_id)
|
|
|
|
return None
|
|
|
|
|
2024-03-16 22:02:59 +00:00
|
|
|
|
|
|
|
for nav_item in mkdocs_data['nav']:
|
|
|
|
try:
|
|
|
|
for tact_item in nav_item['Tactics']:
|
|
|
|
try:
|
|
|
|
tactic = next(iter(tact_item.keys()))
|
|
|
|
tactics.append(tactic)
|
|
|
|
for techn_items in tact_item[tactic]:
|
|
|
|
try:
|
|
|
|
# for techn_fname in techn_items['Techniques']:
|
|
|
|
for technique_name, fname in techn_items.items():
|
|
|
|
description_lst = []
|
|
|
|
with open(os.path.join(args.path, 'docs', fname), 'r') as technique_f:
|
|
|
|
# find the short description, residing between the main title (#) and next title (!!!) or table (|)
|
|
|
|
technique_f_lines = technique_f.read()
|
|
|
|
description = technique_f_lines.split('\n')[-2].strip()
|
|
|
|
technique_id = re.search(r'ID: (MS-T[0-9]+)', technique_f_lines).group(1)
|
|
|
|
try:
|
|
|
|
# make relationship to MITRE ATT&CK
|
|
|
|
mitre_technique_id = re.search(r'MITRE technique: \[(T[0-9]+)\]', technique_f_lines).group(1)
|
|
|
|
mitre_technique_uuid = find_mitre_uuid_from_technique_id(mitre_technique_id)
|
|
|
|
related = [
|
|
|
|
{
|
2024-06-25 11:16:48 +00:00
|
|
|
"dest-uuid": mitre_technique_uuid,
|
|
|
|
"type": "related-to"
|
2024-03-16 22:02:59 +00:00
|
|
|
}
|
|
|
|
]
|
|
|
|
except AttributeError:
|
|
|
|
mitre_technique_uuid = None
|
|
|
|
pass
|
|
|
|
# print(f"{tactic} / {technique} / {description}")
|
|
|
|
technique = f'{technique_id} - {technique_name}'
|
|
|
|
if technique not in clusters:
|
|
|
|
clusters[technique] = {
|
|
|
|
'value': technique,
|
|
|
|
'description': description,
|
|
|
|
'uuid': str(uuid.uuid5(uuid.UUID("9319371e-2504-4128-8410-3741cebbcfd3"), technique)),
|
|
|
|
'meta': {
|
|
|
|
'kill_chain': [],
|
2024-03-16 22:09:14 +00:00
|
|
|
'refs': [f"https://microsoft.github.io/Threat-matrix-for-storage-services/{fname[:-3]}"],
|
|
|
|
'external_id': technique_id
|
2024-03-16 22:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if mitre_technique_uuid:
|
|
|
|
clusters[technique]['related'] = related
|
|
|
|
clusters[technique]['meta']['kill_chain'].append(f"TMSS-tactics:{tactic}")
|
|
|
|
except KeyError:
|
|
|
|
continue
|
|
|
|
except AttributeError:
|
|
|
|
continue
|
|
|
|
except AttributeError: # skip lines that have no field/value
|
|
|
|
continue
|
|
|
|
break
|
|
|
|
except KeyError:
|
|
|
|
continue
|
|
|
|
|
|
|
|
galaxy_type = "tmss"
|
|
|
|
galaxy_name = "Threat Matrix for storage services"
|
|
|
|
galaxy_description = 'Microsoft Defender for Cloud threat matrix for storage services contains attack tactics, techniques and mitigations relevant storage services delivered by cloud providers.'
|
|
|
|
galaxy_source = 'https://github.com/microsoft/Threat-matrix-for-storage-services'
|
2024-06-25 11:16:48 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
galaxy = Galaxy('tmss')
|
|
|
|
except (KeyError, FileNotFoundError):
|
|
|
|
galaxy = Galaxy({
|
|
|
|
'icon': "map",
|
|
|
|
'kill_chain_order': {
|
|
|
|
'TMSS-tactics': tactics
|
|
|
|
},
|
|
|
|
'name': galaxy_name,
|
|
|
|
'description': galaxy_description,
|
|
|
|
'namespace': "microsoft",
|
|
|
|
'type': galaxy_type,
|
|
|
|
'uuid': "d6532b58-99e0-44a9-93c8-affe055e4443",
|
|
|
|
'version': 1
|
|
|
|
})
|
|
|
|
|
|
|
|
galaxy.save('tmss')
|
|
|
|
|
|
|
|
try:
|
|
|
|
cluster = Cluster('tmss')
|
|
|
|
except (KeyError, FileNotFoundError):
|
|
|
|
cluster = Cluster({
|
|
|
|
'authors': ["Microsoft"],
|
|
|
|
'category': 'tmss',
|
|
|
|
'name': galaxy_name,
|
|
|
|
'description': galaxy_description,
|
|
|
|
'source': galaxy_source,
|
|
|
|
'type': galaxy_type,
|
|
|
|
'uuid': "aaf033a6-7f1e-45ab-beef-20a52b75b641",
|
|
|
|
'version': 0
|
|
|
|
})
|
|
|
|
|
2024-03-16 22:02:59 +00:00
|
|
|
# add authors based on the Acknowledgements page
|
2024-03-18 09:44:09 +00:00
|
|
|
authors = ('Evgeny Bogokovsky', 'Ram Pliskin')
|
2024-03-16 22:02:59 +00:00
|
|
|
for author in authors:
|
2024-06-25 11:16:48 +00:00
|
|
|
cluster.authors.add(author)
|
2024-03-16 22:02:59 +00:00
|
|
|
|
2024-06-25 11:16:48 +00:00
|
|
|
for cluster_value in clusters.values():
|
|
|
|
cluster.append(cluster_value)
|
2024-03-16 22:02:59 +00:00
|
|
|
|
2024-06-25 11:16:48 +00:00
|
|
|
cluster.save('tmss')
|
2024-03-16 22:02:59 +00:00
|
|
|
|
2024-06-25 11:16:48 +00:00
|
|
|
print("All done, please don't forget to ./jq_all_the_things.sh, commit, and then ./validate_all.sh, and update_README.")
|