def create_markdown_dirs(): config = ATCutils.load_config('scripts/config.yml') base_dir = Path(config.get( 'md_name_of_root_directory', '../docs' )) target_dir_list = ['Response_Actions', 'Response_Playbooks' , 'Response_Stages'] for item in target_dir_list: (base_dir / item).mkdir(parents=True, exist_ok=True)
def save_markdown_file(self, atc_dir=ATCconfig.get('md_name_of_root_directory')): """Write content (md template filled with data) to a file""" base = os.path.basename(self.yaml_file) title = os.path.splitext(base)[0] file_path = atc_dir + self.parent_title + "/" + \ title + ".md" return ATCutils.write_file(file_path, self.content)
def render_template(self, template_type): """Description template_type: - "markdown" """ if template_type not in ["markdown"]: raise Exception("Bad template_type. Available values:" + " [\"markdown\"]") # Point to the templates directory env = Environment(loader=FileSystemLoader('scripts/templates')) template = env.get_template('markdown_responsestage_template.md.j2') self.ra_parsed_file.update( {'description': self.ra_parsed_file.get('description').strip()}) ras, ra_paths = ATCutils.load_yamls_with_paths( ATCconfig.get('response_actions_dir')) ra_filenames = [ ra_path.split('/')[-1].replace('.yml', '') for ra_path in ra_paths ] rs_id = self.ra_parsed_file.get('id') stage_list = [] for i in range(len(ras)): if rs_mapping[rs_id] == ATCutils.normalize_rs_name( ras[i].get('stage')): ra_id = ras[i].get('id') ra_filename = ra_filenames[i] ra_title = ATCutils.normalize_react_title(ras[i].get('title')) ra_description = ras[i].get('description').strip() stage_list.append( (ra_id, ra_filename, ra_title, ra_description)) self.ra_parsed_file.update({'stage_list': sorted(stage_list)}) self.content = template.render(self.ra_parsed_file)
def parse_into_fields(self): """Description""" # self.fields contains parsed fields obtained from yaml file self.cu_fields = ATCutils.read_yaml_file(self.yaml_file) """Fill the fields with values. Put None if key not found""" self.title = self.cu_fields.get('title') self.customer_name = self.cu_fields.get('customer_name') self.description = self.cu_fields.get('description') self.data_needed = self.cu_fields.get('dataneeded') self.logging_policies = self.cu_fields.get('loggingpolicy') self.detection_rules = self.cu_fields.get('detectionrule')
def hardening_policy(self, hp_path): """Populate Hardening Policies""" print("[*] Populating Hardening Policies...") if hp_path: hp_list = glob.glob(hp_path + '*.yml') else: hp_dir = ATCconfig.get('hardening_policies_directory') hp_list = glob.glob(hp_dir + '/*.yml') for hp_file in hp_list: try: hp = HardeningPolicy(hp_file) hp.render_template("confluence") confluence_data = { "title": hp.hp_parsed_file["title"], "spacekey": self.space, "parentid": str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, "Hardening Policies")), "confluencecontent": hp.content, } res = ATCutils.push_to_confluence(confluence_data, self.apipath, self.auth) if res == 'Page updated': print("==> updated page: HP '" + hp.hp_parsed_file['title'] + "'") except Exception as err: print(hp_file + " failed") print("Err message: %s" % err) print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) print("[+] Hardening Policies populated!")
def mitigation_system(self, ms_path): """Populate Mitigation Systems""" print("[*] Populating Mitigation Systems...") if ms_path: ms_list = glob.glob(ms_path + '*.yml') else: ms_dir = ATCconfig.get('mitigation_systems_directory') ms_list = glob.glob(ms_dir + '/*.yml') for ms_file in ms_list: try: ms = MitigationSystem(ms_file) ms.render_template("confluence") confluence_data = { "title": ms.ms_parsed_file["title"], "spacekey": self.space, "parentid": str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, "Mitigation Systems")), "confluencecontent": ms.content, } res = ATCutils.push_to_confluence(confluence_data, self.apipath, self.auth) if res == 'Page updated': print("==> updated page: MS '" + ms.ms_parsed_file['title'] + "'") except Exception as err: print(ms_file + " failed") print("Err message: %s" % err) print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) print("[+] Mitigation Systems populated!")
def logging_policy(self, lp_path): """Desc""" print("[*] Populating Logging Policies...") if lp_path: lp_list = glob.glob(lp_path + '*.yml') else: lp_dir = ATCconfig.get('logging_policies_dir') lp_list = glob.glob(lp_dir + '/*.yml') for lp_file in lp_list: try: lp = LoggingPolicy(lp_file) lp.render_template("confluence") confluence_data = { "title": lp.fields["title"], "spacekey": self.space, "parentid": str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, "Logging Policies")), "confluencecontent": lp.content, } res = ATCutils.push_to_confluence(confluence_data, self.apipath, self.auth) if res == 'Page updated': print("==> updated page: LP '" + lp.fields['title'] + "'") # print("Done: ", lp.fields['title']) except Exception as err: print(lp_file + " failed") print("Err message: %s" % err) print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) print("[+] Logging Policies populated!")
def create_markdown_dirs(): config = ATCutils.load_config('config.yml') base_dir = Path( config.get('md_name_of_root_directory', '../Atomic_Threat_Coverage')) target_dir_list = [ 'Detection_Rules', 'Logging_Policies', 'Data_Needed', 'Triggers', 'Enrichments', 'Customers', 'Mitigation_Systems', 'Mitigation_Policies', 'Hardening_Policies' ] for item in target_dir_list: (base_dir / item).mkdir(parents=True, exist_ok=True)
def render_template(self, template_type): """Description template_type: - "markdown" """ if template_type not in ["markdown"]: raise Exception("Bad template_type. Available values:" + " [\"markdown\"]") # Point to the templates directory env = Environment(loader=FileSystemLoader('scripts/templates')) template = env.get_template('markdown_responseaction_template.md.j2') self.ra_parsed_file.update( {'description': self.ra_parsed_file.get('description').strip()}) self.ra_parsed_file.update({ 'title': ATCutils.normalize_react_title(self.ra_parsed_file.get('title')) }) stage_list = [] stage = self.ra_parsed_file.get('stage') for rs_id, rs_name in rs_mapping.items(): if ATCutils.normalize_rs_name(stage) == rs_name: stage_list.append((rs_id, rs_name)) self.ra_parsed_file.update({'stage': stage_list}) self.ra_parsed_file.update({ 'category': ATCutils.get_ra_category(self.ra_parsed_file.get('id')) }) self.content = template.render(self.ra_parsed_file)
def checkRA(self, stage): if self.rp_rule.get(stage): for rule in self.rp_rule.get(stage): try: rtask = ATCutils.read_yaml_file(self.inputRA + rule + ".yml") except OSError: print("Response Action %s not existing\n" % rule) continue self.task_prefix = int(self.task_prefix) self.task_prefix += 1 task = THC.TheHiveTask(order=self.task_order) self.task_order += 1 task.title = str(self.task_prefix) + " | " + \ str(rtask.get('title')) task.group = rtask.get('stage', 'Unknown stage') task.description = str(rtask.get('workflow')) self.case.tasks.append(task.return_dictionary()) if rtask.get('linked_ra'): self.task_prefix = float(self.task_prefix) for linked_ra in rtask.get('linked_ra'): try: rtask = ATCutils.read_yaml_file(self.inputRA + linked_ra + ".yml") except OSError: print("Response Action %s not existing\n" % rule) continue task = THC.TheHiveTask(order=self.task_order) self.task_order += 1 self.task_prefix += 0.1 task.title = str(round(self.task_prefix, 1)) + \ " | " + str(rtask.get('title')) task.title = str(round(self.task_prefix, 1)) + " | "\ + str(rtask.get("title")) task.group = rtask.get('stage', 'Unknown stage') task.description = str(rtask.get('workflow')) self.case.tasks.append(task.return_dictionary())
def triggers(self, tg_path): """Populate Triggers""" print("[*] Populating Triggers...") if tg_path: tg_list = glob.glob(tg_path + '*.yml') else: tg_list = glob.glob(ATCconfig.get("triggers_directory") + '/T*/*.yaml') for tg_file in tg_list: try: tg = Triggers(tg_file) tg.render_template("confluence") title = tg.fields["attack_technique"] + ": " + \ te_mapping.get(tg.fields["attack_technique"]) confluence_data = { "title": title, "spacekey": self.space, "parentid": str(ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, "Triggers")), "confluencecontent": tg.content, } res = ATCutils.push_to_confluence(confluence_data, self.apipath, self.auth) if res == 'Page updated': print("==> updated page: TR '" + title + "'") # print("Done: ", tg.fields["attack_technique"]) except Exception as err: print(tg_file + " failed") print("Err message: %s" % err) print('-' * 60) traceback.print_exc(file=sys.stdout) print('-' * 60) print("[+] Triggers populated!")
def parse_into_fields(self, yaml_file): """Description""" # self.dn_fields contains parsed fields obtained from yaml file self.dn_fields = ATCutils.read_yaml_file(yaml_file) """Fill the fields with values. Put None if key not found""" self.title = self.dn_fields.get("title") self.description = self.dn_fields.get("description") self.loggingpolicy = self.dn_fields.get("loggingpolicy") self.mitigation_policy = self.dn_fields.get("mitigation_policy") self.platform = self.dn_fields.get("platform") self.type = self.dn_fields.get("type") self.channel = self.dn_fields.get("channel") self.provider = self.dn_fields.get("provider") self.fields = self.dn_fields.get("fields") self.sample = self.dn_fields.get("sample")
def __init__(self): def get_techniques(threats): techniques = [] for threat in threats: if not isinstance(threat.get('tags'), list): continue tags = threat['tags'] # iterate over all tags finding the one which starts from attack and has all digits after attack.t technique_ids = [f'T{tag[8:]}' for tag in tags if tag.startswith('attack') and tag[8:].isdigit()] # iterate again finding all techniques and removing attack. part from them tactics = [tag.replace('attack.', '').replace('_', '-') for tag in tags if tag.startswith('attack') and not tag[8:].isdigit()] for technique_id in technique_ids: for tactic in tactics: techniques.append({ "techniqueID": technique_id, "tactic": tactic, "color": "#fcf26b", "comment": "", "enabled": True }) return techniques dr_dirs = ATCconfig.get('detection_rules_directories') dr_list = [] for path in dr_dirs: dr_list.append(ATCutils.load_yamls(path)) # flat dr_list dr_list = [dr for drs_from_path in dr_list for dr in drs_from_path] techniques = get_techniques(dr_list) NAVIGATOR_TEMPLATE['techniques'] = techniques filename = 'atc_attack_navigator_profile.json' exported_analytics_directory = \ ATCconfig.get('exported_analytics_directory') + "/attack_navigator_profiles" with open(exported_analytics_directory + '/' + filename, 'w') as fp: json.dump(NAVIGATOR_TEMPLATE, fp) print(f'[+] Created {filename}')
def convertRPToTemplate(self, file_input, output_file): self.rp_rule = ATCutils.read_yaml_file(file_input) self.case = THC.TheHiveCase() self.case.name = self.rp_rule.get('title') self.case.description = "Description:\n" + \ str(self.rp_rule.get('description')) + \ '\n\nWorkflow:\n' + str(self.rp_rule.get('workflow')) try: self.case.tags += self.rp_rule.get('tags') except TypeError: pass self.case.tlp = self.checkTLP(self.rp_rule.get('tlp')) self.case.pap = self.checkPAP(self.rp_rule.get('pap')) if self.args.prefix: self.case.prefix = self.args.prefix self.task_prefix = 0.0 self.task_order = 0 stages = [ 'preparation', 'identification', 'containment', 'eradication', 'recovery', 'lessons_learned' ] for stage in stages: if stage in self.rp_rule.keys(): self.checkRA(stage) try: with open(output_file, 'w') as f: f.write(self.case.json()) except OSError: print("ERROR: No such directory %s" % os.path.dirname(os.path.abspath(output_file)))
from scripts.atcutils import ATCutils from scripts.attack_mapping import te_mapping, ta_mapping from scripts.amitt_mapping import amitt_tactic_mapping, amitt_technique_mapping from jinja2 import Environment, FileSystemLoader import os import subprocess import re # ########################################################################### # # ########################### Detection Rule ################################ # # ########################################################################### # ATCconfig = ATCutils.load_config('config.yml') env = Environment(loader=FileSystemLoader('scripts/templates')) class DetectionRule: """Class for the Detection Rule entity""" def __init__(self, yaml_file, apipath=None, auth=None, space=None): """Init method""" # Init vars self.yaml_file = yaml_file # The name of the directory containing future markdown DetectionRules self.parent_title = "Detection_Rules"
def __init__(self): dn_path = ATCconfig.get('data_needed_dir') lp_path = ATCconfig.get('logging_policies_dir') en_path = ATCconfig.get('enrichments_directory') rp_path = ATCconfig.get('response_playbooks_dir') ra_path = ATCconfig.get('response_actions_dir') cu_path = ATCconfig.get('customers_directory') cu_list = ATCutils.load_yamls(cu_path) dn_list = ATCutils.load_yamls(dn_path) lp_list = ATCutils.load_yamls(lp_path) ra_list = ATCutils.load_yamls(ra_path) rp_list = ATCutils.load_yamls(rp_path) enrichments_list = ATCutils.load_yamls(en_path) pivoting = [] analytics = [] result = [] dr_dirs = ATCconfig.get('detection_rules_directories') print("[*] Iterating through Detection Rules") # Iterate through alerts and pathes to them for dr_path in dr_dirs: alerts, path_to_alerts = ATCutils.load_yamls_with_paths(dr_path) for alert, path in zip(alerts, path_to_alerts): if not isinstance(alert.get('tags'), list): continue list_of_customers = [] for specific_customer in cu_list: if alert['title'] in specific_customer[ 'detectionrule'] and specific_customer[ 'customer_name'] not in list_of_customers: list_of_customers.append( specific_customer['customer_name']) if not isinstance(list_of_customers, list) or len(list_of_customers) == 0: list_of_customers = ["None"] customer = ';'.join(list_of_customers) threats = [ tag for tag in alert['tags'] if tag.startswith('attack') ] tactics = [ f'{ta_mapping[threat][1]}: {ta_mapping[threat][0]}' for threat in threats if threat in ta_mapping.keys() ] techniques = [ threat for threat in threats if threat.startswith('attack.t') ] enrichments = [ er for er in enrichments_list if er['title'] in alert.get('enrichment', []) ] dn_titles = ATCutils.main_dn_calculatoin_func(path) alert_dns = [ data for data in dn_list if data['title'] in dn_titles ] logging_policies = [] for dn in alert_dns: if 'loggingpolicy' in dn: # If there are logging policies in DN that we havent added yet - add them logging_policies.extend([ l for l in lp_list if l['title'] in dn['loggingpolicy'] and l not in logging_policies ]) # If there are no logging policices at all - make an empty one just to make one row in csv if not isinstance(logging_policies, list) or len(logging_policies) == 0: logging_policies = [{ 'title': "-", 'eventID': [ -1, ] }] for dn in alert_dns: pivot = [ dn['category'], dn['platform'], dn['type'], dn['channel'], dn['provider'], dn['title'], '', '' ] for tactic in tactics: for technique in techniques: technique_name = technique.replace('attack.t', 'T') + ': ' +\ ATCutils.get_attack_technique_name_by_id( technique.replace('attack.', '')) for lp in logging_policies: rps = [ rp for rp in rp_list if technique in rp['tags'] or tactic in rp['tags'] ] if len(rps) < 1: rps = [{'title': '-'}] for rp in rps: ras_buf = [] [ ras_buf.extend(l) for l in rp.values() if isinstance(l, list) ] ras = [ ra for ra in ras_buf if ra.startswith('RA') ] if len(ras) < 1: ras = ['title'] #if len(rp) > 1: #todo for ra in ras: lp['title'] = lp['title'].replace( '\n', '') result.append([ customer, tactic, technique_name, alert['title'], dn['category'], dn['platform'], dn['type'], dn['channel'], dn['provider'], dn['title'], lp['title'], '', '', rp['title'], ra ]) # pivoting.append(pivot) for field in dn['fields']: analytics.append([field] + pivot) for er in enrichments: for dn in [ dnn for dnn in dn_list if dnn['title'] in er.get('data_to_enrich', []) ]: pivot = [ dn['category'], dn['platform'], dn['type'], dn['channel'], dn['provider'], dn['title'], er['title'], ';'.join(er.get('requirements', [])) ] for tactic in tactics: for technique in techniques: technique_name = technique.replace('attack.t', 'T') + ': ' + \ ATCutils.get_attack_technique_name_by_id( technique.replace('attack.', '')) for lp in logging_policies: lp['title'] = lp['title'].replace('\n', '') result.append([ customer, tactic, technique_name, alert['title'], dn['category'], dn['platform'], dn['type'], dn['channel'], dn['provider'], dn['title'], lp['title'], er['title'], ';'.join(er.get('requirements', [])), '-', '-' ]) # pivoting.append(pivot) for field in er['new_fields']: analytics.append([field] + pivot) analytics = [] for dn in dn_list: if 'category' in dn: dn_category = dn['category'] else: dn_category = "-" if 'platform' in dn: dn_platform = dn['platform'] else: dn_platform = "-" if 'type' in dn: dn_type = dn['type'] else: dn_type = "-" if 'channel' in dn: dn_channel = dn['channel'] else: dn_channel = "-" if 'provider' in dn: dn_provider = dn['provider'] else: dn_provider = "-" if 'title' in dn: dn_title = dn['title'] else: dn_title = "-" pivot = [ dn_category, dn_platform, dn_type, dn_channel, dn_provider, dn_title, '', '' ] for field in dn['fields']: analytics.append([field] + pivot) for er in enrichments_list: for dn in [ dnn for dnn in dn_list if dnn['title'] in er.get('data_to_enrich', []) ]: pivot = [ dn['category'], dn['platform'], dn['type'], dn['channel'], dn['provider'], dn['title'], er['title'], ';'.join(er.get('requirements', [])) ] for field in er['new_fields']: analytics.append([field] + pivot) filename = 'analytics.csv' exported_analytics_directory = ATCconfig.get( 'exported_analytics_directory') with open(exported_analytics_directory + '/' + filename, 'w', newline='') as csvfile: # maybe need some quoting alertswriter = csv.writer(csvfile, delimiter=',') alertswriter.writerow([ 'customer', 'tactic', 'technique', 'detection_rule', 'category', 'platform', 'type', 'channel', 'provider', 'data_needed', 'logging policy', 'enrichment', 'enrichment requirements', 'response playbook', 'response action' ]) for row in result: alertswriter.writerow(row) print(f'[+] Created {filename}') filename = 'pivoting.csv' exported_analytics_directory = ATCconfig.get( 'exported_analytics_directory') with open(exported_analytics_directory + '/' + filename, 'w', newline='') as csvfile: # maybe need some quoting alertswriter = csv.writer(csvfile, delimiter=',') alertswriter.writerow([ 'field', 'category', 'platform', 'type', 'channel', 'provider', 'data_needed', 'enrichment', 'enrichment requirements' ]) for row in analytics: alertswriter.writerow(row) print(f'[+] Created {filename}')
#!/usr/bin/env python3 from scripts.atcutils import ATCutils from scripts.attack_mapping import te_mapping, ta_mapping import csv import sys import getopt from os import listdir from os.path import isfile, join from yaml.scanner import ScannerError ATCconfig = ATCutils.read_yaml_file("config.yml") HELP_MESSAGE = """Usage: python3 yamls2csv.py [OPTIONS]\n\n\n Possible options are --detectionrules_path, --dataneeded_path --loggingpolicies path Defaults are dataneeded_path = ../data_needed/; loggingpolicies_path=../logging_policies/""" class GenerateCSV: def __init__(self): dn_path = ATCconfig.get('data_needed_dir') lp_path = ATCconfig.get('logging_policies_dir') en_path = ATCconfig.get('enrichments_directory') rp_path = ATCconfig.get('response_playbooks_dir') ra_path = ATCconfig.get('response_actions_dir') cu_path = ATCconfig.get('customers_directory')
def render_template(self, template_type): """Description template_type: - "markdown" - "confluence" """ if template_type not in ["markdown", "confluence"]: raise Exception("Bad template_type. Available values:" + " [\"markdown\", \"confluence\"]") # Get proper template if template_type == "markdown": template = env.get_template('markdown_enrichments_template.md.j2') self.en_parsed_file.update({ 'description': self.en_parsed_file.get('description').strip() }) elif template_type == "confluence": template = env.get_template( 'confluence_enrichments_template.html.j2') self.en_parsed_file.update({ 'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url') }) data_needed = self.en_parsed_file.get('data_needed') if data_needed: data_needed_with_id = [] for dn in data_needed: data_needed_id = str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, dn)) dn = (dn, data_needed_id) data_needed_with_id.append(dn) self.en_parsed_file.update( {'data_needed': data_needed_with_id}) data_to_enrich = self.en_parsed_file.get('data_to_enrich') if data_to_enrich: data_to_enrich_with_id = [] for de in data_to_enrich: data_to_enrich_id = str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, de)) de = (de, data_to_enrich_id) data_to_enrich_with_id.append(de) self.en_parsed_file.update( {'data_to_enrich': data_to_enrich_with_id}) requirements = self.en_parsed_file.get('requirements') if requirements: requirements_with_id = [] for req in requirements: requirements_id = str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, req)) req = (req, requirements_id) requirements_with_id.append(req) self.en_parsed_file.update( {'requirements': requirements_with_id}) self.en_parsed_file.update({ 'description': self.en_parsed_file.get('description').strip() }) # Render self.content = template.render(self.en_parsed_file)
def parse_into_fields(self, yaml_file): """Description""" self.en_parsed_file = ATCutils.read_yaml_file(yaml_file)
def get_customers(): """ This fuction should be moved to ATCUtils or other class""" customers = ATCutils.load_yamls( ATCconfig.get('customers_directory')) return customers
#!/usr/bin/env python3 from scripts.atcutils import ATCutils from scripts.attack_mapping import te_mapping, ta_mapping from scripts.amitt_mapping import amitt_tactic_mapping, amitt_technique_mapping, amitt_mitigation_mapping from jinja2 import Environment, FileSystemLoader import os import re # ########################################################################### # # ########################### Response Playboo ############################## # # ########################################################################### # ATCconfig = ATCutils.load_config("scripts/config.yml") class ResponsePlaybook: """Class for the Playbook Actions entity""" def __init__(self, yaml_file): """Init method""" # Init vars self.yaml_file = yaml_file # The name of the directory containing future markdown Response_Playbooks self.parent_title = "Response_Playbooks" # Init methods self.parse_into_fields(self.yaml_file) def parse_into_fields(self, yaml_file):
def __init__(self, ra=False, rp=False, rs=False, auto=False, ra_path=False, rp_path=False, rs_path=False, atc_dir=False, init=False): """Init""" # Check if atc_dir provided if atc_dir: self.atc_dir = atc_dir else: self.atc_dir = ATCconfig.get('md_name_of_root_directory') + '/' # Main logic if auto: self.response_action(ra_path) self.response_playbook(rp_path) self.response_stage(rs_path) if ra: self.response_action(ra_path) if rp: self.response_playbook(rp_path) if rs: self.response_stage(rs_path) if ra_path: ras, ra_paths = ATCutils.load_yamls_with_paths(ra_path) else: ras, ra_paths = ATCutils.load_yamls_with_paths( ATCconfig.get('response_actions_dir')) if rp_path: rps, rp_paths = ATCutils.load_yamls_with_paths(rp_path) else: rps, rp_paths = ATCutils.load_yamls_with_paths( ATCconfig.get('response_playbooks_dir')) if rs_path: rss, rs_paths = ATCutils.load_yamls_with_paths(rs_path) else: rss, rs_paths = ATCutils.load_yamls_with_paths( ATCconfig.get('response_stages_dir')) ra_filenames = [ ra_path.split('/')[-1].replace('.yml', '') for ra_path in ra_paths ] rp_filenames = [ rp_path.split('/')[-1].replace('.yml', '') for rp_path in rp_paths ] rs_filenames = [ rs_path.split('/')[-1].replace('.yml', '') for rs_path in rs_paths ] # Point to the templates directory env = Environment(loader=FileSystemLoader('scripts/templates')) # Get proper template template = env.get_template('mkdocs_config_template.md.j2') preparation = [] identification = [] containment = [] eradication = [] recovery = [] lessons_learned = [] detect = [] deny = [] disrupt = [] degrade = [] deceive = [] destroy = [] deter = [] stages = [('preparation', preparation), ('identification', identification), ('containment', containment), ('eradication', eradication), ('recovery', recovery), ('lessons_learned', lessons_learned), ('detect', detect), ('deny', deny), ('disrupt', disrupt), ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), ('deter', deter)] playbooks = [] data_to_render = {} for i in range(len(ras)): ra_updated_title = ras[i].get('id')\ + ": "\ + ATCutils.normalize_react_title(ras[i].get('title')) if "RA1" in ras[i]['id']: preparation.append((ra_updated_title, ra_filenames[i])) elif "RA2" in ras[i]['id']: identification.append((ra_updated_title, ra_filenames[i])) elif "RA3" in ras[i]['id']: containment.append((ra_updated_title, ra_filenames[i])) elif "RA4" in ras[i]['id']: eradication.append((ra_updated_title, ra_filenames[i])) elif "RA5" in ras[i]['id']: recovery.append((ra_updated_title, ra_filenames[i])) elif "RA6" in ras[i]['id']: lessons_learned.append((ra_updated_title, ra_filenames[i])) stages = [(stage_name.replace('_', ' ').capitalize(), sorted(stage_list)) for stage_name, stage_list in stages] for i in range(len(rps)): rp_updated_title = rps[i].get('id')\ + ": "\ + ATCutils.normalize_react_title(rps[i].get('title')) playbooks.append((rp_updated_title, rp_filenames[i])) rs_list = [] for i in range(len(rss)): rs_title = rss[i].get('title') rs_id = rss[i].get('id') rs_list.append((rs_title, rs_id)) data_to_render.update({'stages': stages}) data_to_render.update({'playbooks': sorted(playbooks)}) data_to_render.update({'rs_list': rs_list}) content = template.render(data_to_render) try: ATCutils.write_file('mkdocs.yml', content) print("[+] Created mkdocs.yml") except: print("[-] Failed to create mkdocs.yml")
def __init__(self): def get_customers(): """ This fuction should be moved to ATCUtils or other class""" customers = ATCutils.load_yamls( ATCconfig.get('customers_directory')) return customers def find_rules_per_customer(customer, dr_list): dr_list_per_customer = [ dr for dr_title in customer.get('detectionrule') for dr in dr_list if dr.get('title') == dr_title ] return dr_list_per_customer def get_techniques(threats): techniques = [] for threat in threats: if not isinstance(threat.get('tags'), list): continue tags = threat['tags'] # iterate over all tags finding the one which starts from attack and has all digits after attack.t technique_ids = [ f'T{tag[8:]}' for tag in tags if tag.startswith('attack') and tag[8:].isdigit() ] # iterate again finding all techniques and removing attack. part from them tactics = [ tag.replace('attack.', '').replace('_', '-') for tag in tags if tag.startswith('attack') and not tag[8:].isdigit() ] for technique_id in technique_ids: for tactic in tactics: techniques.append({ "techniqueID": technique_id, "tactic": tactic, "color": "#fcf26b", "comment": "", "enabled": True }) return techniques dr_dirs = ATCconfig.get('detection_rules_directories') dr_list = [] for path in dr_dirs: dr_list.append(ATCutils.load_yamls(path)) # flat dr_list dr_list = [dr for drs_from_path in dr_list for dr in drs_from_path] list_of_customers = get_customers() for customer in list_of_customers: rules = find_rules_per_customer(customer, dr_list) techniques = get_techniques(rules) tab_name = {"name": customer.get('customer_name')} NAVIGATOR_TEMPLATE.update(tab_name) NAVIGATOR_TEMPLATE['techniques'] = techniques filename = 'atc_attack_navigator_profile_' + \ customer.get('title') + '.json' exported_analytics_directory = \ ATCconfig.get('exported_analytics_directory') + \ "/attack_navigator_profiles" with open(exported_analytics_directory + '/' + filename, 'w') as fp: json.dump(NAVIGATOR_TEMPLATE, fp) print(f'[+] Created {filename}')
def parse_into_fields(self): """Description""" self.mp_parsed_file = ATCutils.read_yaml_file(self.yaml_file)
rs=args.responsestage, init=args.init) elif args.react_stix: GenerateSTIX() elif args.react_navigator: GenerateNavigator() elif args.csv: GenerateCSV() elif args.td_navigator: GenerateDetectionNavigator() elif args.td_navigator_customers: GenerateDetectionNavigatorCustomers() elif args.es: GenerateESIndex() elif args.visualisations: ATCconfig = ATCutils.load_config("config.yml") ATCconfig_default = ATCutils.load_config("scripts/config.default.yml") if not args.vis_output_dir: analytics_generated = ATCconfig.get( "exported_analytics_directory", ATCconfig_default.get("exported_analytics_directory")) analytics_generated = analytics_generated if \ analytics_generated[-1] == "/" else analytics_generated + "/" output_path = analytics_generated + "visualizations/" if not args.vis_output_file_name: output_name = ''.join( random.choices(string.ascii_uppercase + string.ascii_lowercase + string.digits, k=20)) # output_name += ".json"
def render_template(self, template_type): """Description template_type: - "markdown" - "confluence" """ if template_type not in ["markdown", "confluence"]: raise Exception("Bad template_type. Available values:" + " [\"markdown\", \"confluence\"]") # Get proper template if template_type == "markdown": template = env.get_template( 'markdown_mitigationpolicies_template.md.j2') platform = self.mp_parsed_file.get("platform") if isinstance(platform, str): platform = [platform] self.mp_parsed_file.update({'platform': platform}) minimum_version = self.mp_parsed_file.get("minimum_version") if isinstance(minimum_version, str): minimum_version = [minimum_version] self.mp_parsed_file.update({'minimum_version': minimum_version}) mitigation_systems = self.mp_parsed_file.get("mitigation_system") if isinstance(mitigation_systems, str): mitigation_systems = [mitigation_systems] self.mp_parsed_file.update( {'mitigation_system': mitigation_systems}) self.mp_parsed_file.update({ 'configuration': self.mp_parsed_file.get('configuration').strip() }) self.mp_parsed_file.update({ 'description': self.mp_parsed_file.get('description').strip() }) # MITRE ATT&CK Tactics and Techniques tactic = [] tactic_re = re.compile(r'attack\.\w\D+$') technique = [] technique_re = re.compile(r'attack\.t\d{1,5}$') # AM!TT Tactics and Techniques amitt_tactic = [] amitt_tactic_re = re.compile(r'amitt\.\w\D+$') amitt_technique = [] amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') # MITRE ATT&CK Mitigation mitigation = [] mitigation_re = re.compile(r'attack\.m\d{1,5}$') # AM!TT Mitigation amitt_mitigation = [] amitt_mitigation_re = re.compile(r'amitt\.m\d{1,5}$') other_tags = [] if self.mp_parsed_file.get('tags'): for tag in self.mp_parsed_file.get('tags'): if tactic_re.match(tag): if ta_mapping.get(tag): tactic.append(ta_mapping.get(tag)) else: other_tags.append(tag) elif amitt_tactic_re.match(tag): if amitt_tactic_mapping.get(tag): amitt_tactic.append(amitt_tactic_mapping.get(tag)) else: other_tags.append(tag) elif technique_re.match(tag): te = tag.upper()[7:] technique.append((te_mapping.get(te), te)) elif amitt_technique_re.match(tag): te = tag.upper()[6:] technique.append((amitt_technique_mapping.get(te), te)) elif mitigation_re.match(tag): mi = tag.upper()[7:] mitigation.append((mi_mapping.get(mi), mi)) elif amitt_mitigation_re.match(tag): te = tag.upper()[6:] mitigation.append( (amitt_mitigation_mapping.get(te), te)) else: other_tags.append(tag) if not tactic_re.match(tag) and not \ technique_re.match(tag) and not \ mitigation_re.match(tag): other_tags.append(tag) if len(tactic): self.mp_parsed_file.update({'tactics': tactic}) if len(technique): self.mp_parsed_file.update({'techniques': technique}) if len(amitt_tactic): self.mp_parsed_file.update({'amitt_tactics': amitt_tactic}) if len(amitt_technique): self.mp_parsed_file.update( {'amitt_techniques': amitt_technique}) if len(mitigation): self.mp_parsed_file.update({'mitigations': mitigation}) if len(amitt_mitigation): self.mp_parsed_file.update( {'amitt_mitigations': amitt_mitigation}) if len(other_tags): self.mp_parsed_file.update({'other_tags': other_tags}) elif template_type == "confluence": template = env.get_template( 'confluence_mitigationpolicies_template.html.j2') self.mp_parsed_file.update({ 'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url') }) self.mp_parsed_file.update({ 'description': self.mp_parsed_file.get('description').strip() }) platform = self.mp_parsed_file.get("platform") if isinstance(platform, str): platform = [platform] self.mp_parsed_file.update({'platform': platform}) minimum_version = self.mp_parsed_file.get("minimum_version") if isinstance(minimum_version, str): minimum_version = [minimum_version] self.mp_parsed_file.update({'minimum_version': minimum_version}) mitigation_systems = self.mp_parsed_file.get("mitigation_system") if isinstance(mitigation_systems, str): mitigation_systems = [mitigation_systems] if not mitigation_systems: mitigation_systems = [ "None", ] mitigation_systems_with_id = [] if mitigation_systems: for ms in mitigation_systems: mitigation_systems_id = str( ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, ms)) ms = (ms, mitigation_systems_id) mitigation_systems_with_id.append(ms) self.mp_parsed_file.update( {'mitigation_system': mitigation_systems_with_id}) # MITRE ATT&CK Tactics and Techniques tactic = [] tactic_re = re.compile(r'attack\.\w\D+$') technique = [] technique_re = re.compile(r'attack\.t\d{1,5}$') # AM!TT Tactics and Techniques amitt_tactic = [] amitt_tactic_re = re.compile(r'amitt\.\w\D+$') amitt_technique = [] amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') # MITRE ATT&CK Mitigation mitigation = [] mitigation_re = re.compile(r'attack\.m\d{1,5}$') # AM!TT Mitigation amitt_mitigation = [] amitt_mitigation_re = re.compile(r'amitt\.m\d{1,5}$') other_tags = [] if self.mp_parsed_file.get('tags'): for tag in self.mp_parsed_file.get('tags'): if tactic_re.match(tag): if ta_mapping.get(tag): tactic.append(ta_mapping.get(tag)) else: other_tags.append(tag) elif amitt_tactic_re.match(tag): if amitt_tactic_mapping.get(tag): amitt_tactic.append(amitt_tactic_mapping.get(tag)) else: other_tags.append(tag) elif technique_re.match(tag): te = tag.upper()[7:] technique.append((te_mapping.get(te), te)) elif amitt_technique_re.match(tag): te = tag.upper()[6:] technique.append((amitt_technique_mapping.get(te), te)) elif mitigation_re.match(tag): mi = tag.upper()[7:] mitigation.append((mi_mapping.get(mi), mi)) elif amitt_mitigation_re.match(tag): te = tag.upper()[6:] mitigation.append( (amitt_mitigation_mapping.get(te), te)) else: other_tags.append(tag) if not tactic_re.match(tag) and not \ technique_re.match(tag) and not \ mitigation_re.match(tag): other_tags.append(tag) if len(tactic): self.mp_parsed_file.update({'tactics': tactic}) if len(technique): self.mp_parsed_file.update({'techniques': technique}) if len(amitt_tactic): self.mp_parsed_file.update({'amitt_tactics': amitt_tactic}) if len(amitt_technique): self.mp_parsed_file.update( {'amitt_techniques': amitt_technique}) if len(mitigation): self.mp_parsed_file.update({'mitigations': mitigation}) if len(amitt_mitigation): self.mp_parsed_file.update( {'amitt_mitigations': amitt_mitigation}) if len(other_tags): self.mp_parsed_file.update({'other_tags': other_tags}) # Render self.content = template.render(self.mp_parsed_file)
def parse_into_fields(self): """Description""" # self.fields contains parsed fields obtained from yaml file self.fields = ATCutils.read_yaml_file(self.yaml_file)
#!/usr/bin/env python3 from scripts.atcutils import ATCutils from jinja2 import Environment, FileSystemLoader import os # ########################################################################### # # ########################### Enrichments ################################### # # ########################################################################### # ATCconfig = ATCutils.load_config("config.yml") try: env = Environment( loader=FileSystemLoader('data/atc_data/scripts/templates')) except: env = Environment(loader=FileSystemLoader('scripts/templates')) class Enrichment: """Class for the Enrichments entity""" def __init__(self, yaml_file, apipath=None, auth=None, space=None): """Init method""" # Init vars self.yaml_file = yaml_file # The name of the directory containing future markdown LogginPolicy self.parent_title = "Enrichments" self.apipath = apipath
def render_template(self, template_type): """Render template with data in it template_type: - "markdown" - "confluence" """ if template_type not in ["markdown", "confluence"]: raise Exception( "Bad template_type. Available values: " + "[\"markdown\", \"confluence\"]") # Get proper template if template_type == "markdown": template = env.get_template( 'markdown_alert_template.md.j2') # Read raw sigma rule sigma_rule = ATCutils.read_rule_file(self.yaml_file) # Put raw sigma rule into fields var self.fields.update({'sigma_rule': sigma_rule}) # Define which queries we want from Sigma #queries = ["es-qs", "xpack-watcher", "graylog", "splunk", "logpoint", "grep", "fieldlist"] queries = ATCconfig.get('detection_queries').split(",") # dict to store query key + query values det_queries = {} # Convert sigma rule into queries (for instance, graylog query) for query in queries: # prepare command to execute from shell # (yes, we know) if query == "powershell": cmd = ATCconfig.get('sigmac_path') + " -t " + query + \ " --config " + ATCconfig.get('powershell_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif query == "es-qs": cmd = ATCconfig.get('sigmac_path') + " -t " + query + \ " --config " + ATCconfig.get('es-qs_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif query == "xpack-watcher": cmd = ATCconfig.get('sigmac_path') + " -t " + query + \ " --config " + ATCconfig.get('xpack-watcher_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif query == "splunk": cmd = ATCconfig.get('sigmac_path') + " -t " + query + \ " --config " + ATCconfig.get('splunk_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif query == "logpoint": cmd = ATCconfig.get('sigmac_path') + " -t " + query + \ " --config " + ATCconfig.get('logpoint_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file else: cmd = ATCconfig.get('sigmac_path') + ' --shoot-yourself-in-the-foot -t "' + \ query + '" --ignore-backend-errors "' + self.yaml_file + '"' #query + " --ignore-backend-errors " + self.yaml_file + \ #" 2> /dev/null" #p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) query2 = subprocess.getoutput(cmd) # Wait for date to terminate. Get return returncode # p_status = p.wait() #p.wait() """ Had to remove '-' due to problems with Jinja2 variable naming, e.g es-qs throws error 'no es variable' """ det_queries[query] = str(query2)#[2:-3] # Update detection rules self.fields.update({"det_queries": det_queries}) self.fields.update({"queries": queries}) # Data Needed data_needed = ATCutils.main_dn_calculatoin_func(self.yaml_file) # if there is only 1 element in the list, print it as a string, # without quotes # if isistance(data_needed, list) and len(data_needed) == 1: # [data_needed] = data_needed # print("%s || Dataneeded: \n%s\n" % # (self.fields.get("title"), data_needed)) self.fields.update({'data_needed': sorted(data_needed)}) # Enrichments enrichments = self.fields.get("enrichment") if isinstance(enrichments, str): enrichments = [enrichments] self.fields.update({'enrichment': enrichments}) # MITRE ATT&CK Tactics and Techniques tactic = [] tactic_re = re.compile(r'attack\.\w\D+$') technique = [] technique_re = re.compile(r'(?:attack\.t\d{4}$|attack\.t\d{4}\.\d{3}$)') # AM!TT Tactics and Techniques amitt_tactic = [] amitt_tactic_re = re.compile(r'amitt\.\w\D+$') amitt_technique = [] amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') other_tags = [] if self.fields.get('tags'): for tag in self.fields.get('tags'): if tactic_re.match(tag): if ta_mapping.get(tag): tactic.append(ta_mapping.get(tag)) else: other_tags.append(tag) elif amitt_tactic_re.match(tag): if amitt_tactic_mapping.get(tag): amitt_tactic.append(amitt_tactic_mapping.get(tag)) else: other_tags.append(tag) elif technique_re.match(tag): te = tag.upper()[7:] technique.append((te_mapping.get(te), te)) elif amitt_technique_re.match(tag): te = tag.upper()[6:] amitt_technique.append((amitt_technique_mapping.get(te), te)) else: other_tags.append(tag) if len(tactic): self.fields.update({'tactics': tactic}) if len(technique): self.fields.update({'techniques': technique}) if len(amitt_tactic): self.fields.update({'amitt_tactics': amitt_tactic}) if len(amitt_technique): self.fields.update({'amitt_techniques': amitt_technique}) if len(other_tags): self.fields.update({'other_tags': other_tags}) triggers = [] for trigger in technique: if trigger is "None": continue trigger_name, trigger_id = trigger # Check if a directory for a technique exists in atomic red team repo if os.path.isdir(ATCconfig.get('triggers_directory') + '/' + trigger_id): triggers.append(trigger) else: print(trigger_id + ": No atomics trigger for this technique") """ triggers.append( trigger + ": No atomics trigger for this technique" ) """ self.fields.update( {'description': self.fields.get('description').strip()}) self.fields.update({'triggers': triggers}) elif template_type == "confluence": template = env.get_template( 'confluence_alert_template.html.j2') self.fields.update( {'confluence_viewpage_url': ATCconfig.get('confluence_viewpage_url')}) sigma_rule = ATCutils.read_rule_file(self.yaml_file) self.fields.update({'sigma_rule': sigma_rule}) #outputs = ["es-qs", "xpack-watcher", "graylog"] queries = ATCconfig.get('detection_queries').split(",") # dict to store query key + query values det_queries = {} for output in queries: if output == "powershell": cmd = ATCconfig.get('sigmac_path') + " -t " + output + \ " --config " + ATCconfig.get('powershell_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif output == "es-qs": cmd = ATCconfig.get('sigmac_path') + " -t " + output + \ " --config " + ATCconfig.get('es-qs_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif output == "xpack-watcher": cmd = ATCconfig.get('sigmac_path') + " -t " + output + \ " --config " + ATCconfig.get('xpack-watcher_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif output == "splunk": cmd = ATCconfig.get('sigmac_path') + " -t " + output + \ " --config " + ATCconfig.get('splunk_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file elif output == "logpoint": cmd = ATCconfig.get('sigmac_path') + " -t " + output + \ " --config " + ATCconfig.get('logpoint_sigma_config') + \ " --ignore-backend-errors " + self.yaml_file else: cmd = ATCconfig.get('sigmac_path') + ' --shoot-yourself-in-the-foot -t "' + \ output + '" --ignore-backend-errors "' + self.yaml_file + '"' p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) (query, err) = p.communicate() # Wait for date to terminate. Get return returncode ## # p_status = p.wait() p.wait() # have to remove '-' due to problems with # Jinja2 variable naming,e.g es-qs throws error # 'no es variable' #self.fields.update({output.replace("-", ""): str(query)[2:-3]}) det_queries[output] = str(query)[2:-3].replace("\\n", "\n") # Update detection rules self.fields.update({"det_queries": det_queries}) self.fields.update({"queries": queries}) # Data Needed data_needed = ATCutils.main_dn_calculatoin_func(self.yaml_file) data_needed_with_id = [] for data in sorted(data_needed): data_needed_id = str(ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, data)) data = (data, data_needed_id) data_needed_with_id.append(data) self.fields.update({'data_needed': data_needed_with_id}) # Enrichments enrichments = self.fields.get("enrichment") enrichments_with_page_id = [] if isinstance(enrichments, str): enrichments = [enrichments] if enrichments: for enrichment_name in enrichments: enrichment_page_id = str(ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, enrichment_name)) enrichment_data = (enrichment_name, enrichment_page_id) enrichments_with_page_id.append(enrichment_data) self.fields.update({'enrichment': enrichments_with_page_id}) # MITRE ATT&CK Tactics and Techniques tactic = [] tactic_re = re.compile(r'attack\.\w\D+$') technique = [] technique_re = re.compile(r'attack\.t\d{1,5}(\.\d{3})?$') # AM!TT Tactics and Techniques amitt_tactic = [] amitt_tactic_re = re.compile(r'amitt\.\w\D+$') amitt_technique = [] amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') other_tags = [] if self.fields.get('tags'): for tag in self.fields.get('tags'): if tactic_re.match(tag): if ta_mapping.get(tag): tactic.append(ta_mapping.get(tag)) else: other_tags.append(tag) elif amitt_tactic_re.match(tag): if amitt_tactic_mapping.get(tag): amitt_tactic.append(amitt_tactic_mapping.get(tag)) else: other_tags.append(tag) elif technique_re.match(tag): te = tag.upper()[7:] technique.append((te_mapping.get(te), te)) elif amitt_technique_re.match(tag): te = tag.upper()[6:] amitt_technique.append((amitt_technique_mapping.get(te), te)) else: other_tags.append(tag) if len(tactic): self.fields.update({'tactics': tactic}) if len(technique): self.fields.update({'techniques': technique}) if len(amitt_tactic): self.fields.update({'amitt_tactics': amitt_tactic}) if len(technique): self.fields.update({'amitt_techniques': amitt_technique}) if len(other_tags): self.fields.update({'other_tags': other_tags}) triggers = [] for trigger_name, trigger_id in technique: if trigger_id is "None": continue try: page_name = trigger_id + ": " + trigger_name trigger_page_id = str(ATCutils.confluence_get_page_id( self.apipath, self.auth, self.space, page_name)) trigger = (trigger_name, trigger_id, trigger_page_id) triggers.append(trigger) except FileNotFoundError: print(trigger + ": No atomics trigger for this technique") self.fields.update({'triggers': triggers}) self.content = template.render(self.fields) # Need to convert ampersand into HTML "save" format # Otherwise confluence throws an error # self.content = self.content.replace("&", "&") # Done in the template itself return True
def render_template(self, template_type): """Description template_type: - "markdown" """ if template_type not in ["markdown"]: raise Exception("Bad template_type. Available values:" + " [\"markdown\"]") # Point to the templates directory env = Environment(loader=FileSystemLoader('scripts/templates')) # Get proper template template = env.get_template('markdown_responseplaybook_template.md.j2') self.rp_parsed_file.update({ 'title': ATCutils.normalize_react_title(self.rp_parsed_file.get('title')) }) # MITRE ATT&CK Tactics and Techniques tactic = [] tactic_re = re.compile(r'attack\.\w\D+$') technique = [] technique_re = re.compile(r'attack\.t\d{1,5}$') # AM!TT Tactics and Techniques amitt_tactic = [] amitt_tactic_re = re.compile(r'amitt\.\w\D+$') amitt_technique = [] amitt_technique_re = re.compile(r'amitt\.t\d{1,5}$') other_tags = [] for tag in self.rp_parsed_file.get('tags'): if tactic_re.match(tag): tactic.append(ta_mapping.get(tag)) elif technique_re.match(tag): te = tag.upper()[7:] technique.append((te_mapping.get(te), te)) elif amitt_tactic_re.match(tag): amitt_tactic.append(amitt_tactic_mapping.get(tag)) elif amitt_technique_re.match(tag): te = tag.upper()[6:] amitt_technique.append((amitt_technique_mapping.get(te), te)) else: other_tags.append(tag) # Add MITRE ATT&CK Tactics and Techniques to J2 self.rp_parsed_file.update({'tactics': tactic}) self.rp_parsed_file.update({'techniques': technique}) # Add AM!TT Tactics and Techniques to J2 self.rp_parsed_file.update({'amitt_tactics': amitt_tactic}) self.rp_parsed_file.update({'amitt_techniques': amitt_technique}) self.rp_parsed_file.update({'other_tags': other_tags}) preparation = [] identification = [] containment = [] eradication = [] recovery = [] lessons_learned = [] detect = [] deny = [] disrupt = [] degrade = [] deceive = [] destroy = [] deter = [] stages = [('preparation', preparation), ('identification', identification), ('containment', containment), ('eradication', eradication), ('recovery', recovery), ('lessons_learned', lessons_learned), ('detect', detect), ('deny', deny), ('disrupt', disrupt), ('degrade', degrade), ('deceive', deceive), ('destroy', destroy), ('deter', deter)] # grab workflow per action in each IR stages # error handling for playbooks with empty stages for stage_name, stage_list in stages: try: for task in self.rp_parsed_file.get(stage_name): action = ATCutils.read_yaml_file( ATCconfig.get('response_actions_dir') + '/' + task + '.yml') action_title = action.get('id')\ + ": "\ + ATCutils.normalize_react_title(action.get('title')) stage_list.append( (action_title, task, action.get('description'), action.get('workflow'))) except TypeError: pass # change stages name to more pretty format stages = [(stage_name.replace('_', ' ').capitalize(), stage_list) for stage_name, stage_list in stages] self.rp_parsed_file.update({'stages': stages}) self.rp_parsed_file.update( {'description': self.rp_parsed_file.get('description').strip()}) # Render self.content = template.render(self.rp_parsed_file)