def get_attack_map(composite_ds): print("Parsing attack patterns ...") att_filter = Filter('type', '=', 'attack-pattern') attack_map = {} revoke_map = {} for item in composite_ds.query(att_filter): name = item['name'] attack_id = None for er in item['external_references']: if er['source_name'] in [ "mitre-attack", "mobile-mitre-attack", "mitre-mobile-attack" ]: attack_id = er['external_id'] break if attack_id: if not item['revoked']: categories = [ x['phase_name'] for x in item['kill_chain_phases'] ] desc = item['description'] platforms = item['x_mitre_platforms'] attack_map[attack_id] = { "name": name, "categories": categories, "description": desc, "platforms": platforms, "attack_id": attack_id } print(f"\tAdding {name.upper()} as ID: {attack_id}") else: revoke_id = get_revoked_by(item['id'], composite_ds) if revoke_id is None: print( f"\t[WARN] {name.upper()} ({attack_id}) has been revoked without being replaced." ) else: revoke_map[attack_id] = revoke_id print( f"\tAdding revoked {name.upper()} to the revoked map: {attack_id} => {revoke_id}" ) else: print(f"[ERR] Ignored {name.upper()}: No attack ID found.") return attack_map, revoke_map
def _get_tactic_listing(self, domain='enterprise'): """ INTERNAL - retrieves tactics for the associated domain :param domain: The domain to draw from """ tactics = {} t_filt = [] matrix = self.collections[domain].query( [Filter('type', '=', 'x-mitre-matrix')]) for i in range(len(matrix)): tactics[matrix[i]['name']] = [] for tactic_id in matrix[i]['tactic_refs']: tactics[matrix[i]['name']].append( self.collections[domain].query( [Filter('id', '=', tactic_id)])[0]) for entry in tactics[matrix[0]['name']]: self.convert_data[entry['x_mitre_shortname']] = entry['name'] self.convert_data[entry['name']] = entry['x_mitre_shortname'] t_filt.append( MatrixEntry(id=entry['external_references'][0]['external_id'], name=entry['name'])) return t_filt
def get_all_groups(self): filt = Filter('type', '=', 'intrusion-set') groups = self.src.query([filt]) dict_groups = [] for group in groups: string_report = group.serialize() dict_report = json.loads(string_report) hash_report = hashlib.sha1( string_report.encode('utf-8')).hexdigest() dict_report["hash"] = hash_report dict_groups.append(dict_report) group_df = pd.DataFrame(dict_groups) group_df = group_df.fillna("") return group_df
def __find_relationships(cls, rel_type=None, source_id=None, target_id=None): if cls.relations is None: cls.relations = cls.fs.query([ Filter('type', '=', 'relationship') ]) relations = [] for rel in cls.relations: if rel_type is None or rel_type == rel.relationship_type: if source_id is None or source_id == rel.source_ref: if target_id is None or target_id == rel.target_ref: relations.append(rel) return relations
def _get_technique_listing(self, tactic, domain='enterprise'): """ INTERNAL - retrieves techniques for a given tactic and domain :param tactic: The tactic to grab techniques from :param domain: The domain to draw from """ techniques = [] subtechs = {} techs = self._search(domain, [ Filter('type', '=', 'attack-pattern'), Filter('kill_chain_phases.phase_name', '=', tactic) ]) for entry in techs: if entry['kill_chain_phases'][0]['kill_chain_name'] == 'mitre-attack' or \ entry['kill_chain_phases'][0]['kill_chain_name'] == 'mitre-mobile-attack': tid = [ t['external_id'] for t in entry['external_references'] if 'attack' in t['source_name'] ] platform_tags = [] if 'x_mitre_platforms' in entry: platform_tags = entry['x_mitre_platforms'] if '.' not in tid[0]: techniques.append( MatrixEntry(id=tid[0], name=entry['name'], platforms=platform_tags)) else: parent = tid[0].split('.')[0] if parent not in subtechs: subtechs[parent] = [] subtechs[parent].append( MatrixEntry(id=tid[0], name=entry['name'], platforms=platform_tags)) return techniques, subtechs
def get_object_by_attack_id(self, object_type, attack_id): valid_objects = {'attack-pattern','course-of-action','intrusion-set','malware','tool'} if object_type not in valid_objects: raise ValueError("ERROR: Valid object must be one of %r" % valid_objects) else: dictionary = { "attack-pattern": "techniques", "course-of-action": "mitigations", "intrusion-set": "groups", "malware": "malware", "tool": "tools" } filter_objects = [ Filter('type', '=', object_type), Filter('external_references.external_id', '=', attack_id) ] enterprise_stix_objects = self.TC_ENTERPRISE_SOURCE.query(filter_objects) enterprise_stix_objects = self.parse_stix_objects(enterprise_stix_objects, dictionary[object_type]) pre_stix_objects = self.TC_PRE_SOURCE.query(filter_objects) pre_stix_objects = self.parse_stix_objects(pre_stix_objects, dictionary[object_type]) mobile_stix_objects = self.TC_MOBILE_SOURCE.query(filter_objects) mobile_stix_objects = self.parse_stix_objects(mobile_stix_objects, dictionary[object_type]) all_stix_objects = enterprise_stix_objects + pre_stix_objects + mobile_stix_objects return all_stix_objects
def get_mitre_value_from_id(client, args): attack_ids = argToList(args.get('attack_ids', [])) attack_values = [] for attack_id in attack_ids: collection_id = f"stix/collections/{ENTERPRISE_COLLECTION_ID}/" collection_url = urljoin(client.base_url, collection_id) collection_data = Collection(collection_url, verify=client.verify, proxies=client.proxies) tc_source = TAXIICollectionSource(collection_data) attack_pattern_obj = tc_source.query([ Filter("external_references.external_id", "=", attack_id), Filter("type", "=", "attack-pattern") ]) attack_pattern_name = attack_pattern_obj[0]['name'] if attack_pattern_obj else None if attack_pattern_name and len(attack_id) > 5: # sub-technique parent_name = tc_source.query([ Filter("external_references.external_id", "=", attack_id[:5]), Filter("type", "=", "attack-pattern") ])[0]['name'] attack_pattern_name = f'{parent_name}: {attack_pattern_name}' if attack_pattern_name: attack_values.append({'id': attack_id, 'value': attack_pattern_name}) if attack_values: return CommandResults( outputs=attack_values, outputs_key_field='id', outputs_prefix='MITREATTACK', readable_output=tableToMarkdown('MITRE ATTACK Attack Patterns values:', attack_values) ) return CommandResults(readable_output=f'MITRE ATTACK Attack Patterns values: ' f'No Attack Patterns found for {attack_ids}.')
def get_group_from_alias(alias): """ Maps any alias to the potential MITRE ATT&CK group name, if the provided name is a known alias. :param alias: The alias to check :return: Either returns the MITRE ATT&CK group name or returns "" if the query returns null """ group_from_alias = Helper.fs.query([ Filter('type', '=', 'intrusion-set'), FilterCasefold('aliases', 'casefold', alias) ]) if not group_from_alias: return "" return group_from_alias[0][MITRE_GROUP_NAME]
def get_techniques_used_by_group(self, stix_object, stix_format=True): relationships = self.get_relationships_by_object(stix_object) filter_objects = [ Filter('type', '=', 'attack-pattern'), Filter('id', '=', [r.target_ref for r in relationships]) ] try: enterprise_stix_objects = self.TC_ENTERPRISE_SOURCE.query( filter_objects) except: enterprise_stix_objects = [] try: pre_stix_objects = self.TC_PRE_SOURCE.query(filter_objects) except: pre_stix_objects = [] try: mobile_stix_objects = self.TC_MOBILE_SOURCE.query(filter_objects) except: mobile_stix_objects = [] all_techniques_list = enterprise_stix_objects + pre_stix_objects + mobile_stix_objects if not stix_format: all_techniques_list = self.translate_stix_objects( all_techniques_list) return all_techniques_list
def get_group_by_alias(self, group_alias, all_objects=None): if all_objects is None: filter_objs = [ Filter('type', '=', 'intrusion-set'), Filter('aliases', '=', group_alias) ] enterprise_stix_objects = self.TC_ENTERPRISE_SOURCE.query( filter_objs) enterprise_stix_objects = self.parse_stix_objects( enterprise_stix_objects, 'groups') pre_stix_objects = self.TC_PRE_SOURCE.query(filter_objs) pre_stix_objects = self.parse_stix_objects(pre_stix_objects, 'groups') mobile_stix_objects = self.TC_MOBILE_SOURCE.query(filter_objs) mobile_stix_objects = self.parse_stix_objects( mobile_stix_objects, 'groups') all_stix_objects = enterprise_stix_objects + pre_stix_objects + mobile_stix_objects for o in all_stix_objects: return o else: for o in all_objects['groups']: for a in o['group aliases']: if group_alias.lower() in a.lower(): return o
def test_parse_taxii_filters(): query = [ Filter("added_after", "=", "2016-02-01T00:00:01.000Z"), Filter("id", "=", "taxii stix object ID"), Filter("type", "=", "taxii stix object ID"), Filter("version", "=", "first"), Filter("created_by_ref", "=", "Bane"), ] taxii_filters_expected = [ Filter("added_after", "=", "2016-02-01T00:00:01.000Z"), Filter("id", "=", "taxii stix object ID"), Filter("type", "=", "taxii stix object ID"), Filter("version", "=", "first") ] ds = taxii.TAXIICollectionSource(collection) taxii_filters = ds._parse_taxii_filters(query) assert taxii_filters == taxii_filters_expected
def getLayersByProperty(controls, mappings, attackdata, domain, frameworkname, x_mitre, version): """get layers grouping the mappings according to values of the given property""" propertyname = x_mitre.split("x_mitre_")[1] # remove prefix familyIDToControls, familyIDToName, idToFamily = parseFamilyData(controls) # group controls by the property propertyValueToControls = {} def addToDict(value, control): if value in propertyValueToControls: propertyValueToControls[value].append(control) else: propertyValueToControls[value] = [control] # iterate through controls, grouping by property isListType = False for control in controls.query([Filter("type", "=", "course-of-action")]): value = control.get(x_mitre) if not value: continue if isinstance(value, list): isListType = True for v in value: addToDict(v, control) else: addToDict(value, control) outlayers = [] for value in propertyValueToControls: # controls for the corresponding values controlsOfValue = MemoryStore(stix_data=propertyValueToControls[value]) techniques = toTechniquelist(controlsOfValue, mappings, attackdata, familyIDToControls, familyIDToName, idToFamily) if len(techniques) > 0: # build layer for this technique set outlayers.append({ "outfile": os.path.join(f"by_{propertyname}", f"{value}.json"), "layer": layer( f"{propertyname}={value} mappings", f"techniques where the {propertyname} of associated controls {'includes' if isListType else 'is'} {value}", domain, techniques, version) }) return outlayers
def groupsToDf(src, domain): """ Parse STIX groups from the given data and return corresponding pandas dataframes. :param src: MemoryStore or other stix2 DataSource object holding the domain data :param domain: domain of ATT&CK src corresponds to, e.g "enterprise-attack" :returns: a lookup of labels (descriptors/names) to dataframes """ groups = src.query([Filter("type", "=", "intrusion-set")]) groups = remove_revoked_deprecated(groups) group_rows = [] for group in tqdm(groups, desc="parsing groups"): row = parseBaseStix(group) # add group aliases if "aliases" in group: associated_groups = [] associated_group_citations = [] for alias in sorted(group["aliases"][1:]): # find the reference for the alias associated_groups.append(alias) for ref in group["external_references"]: if ref["source_name"] == alias: associated_group_citations.append(ref["description"]) break # aliases.append(alias) row["associated groups"] = ", ".join(associated_groups) row["associated groups citations"] = ", ".join( associated_group_citations) group_rows.append(row) citations = get_citations(groups) dataframes = { "groups": pd.DataFrame(group_rows).sort_values("name"), } # add relationships dataframes.update(relationshipsToDf(src, relatedType="group")) # add/merge citations if not citations.empty: if "citations" in dataframes: # append to existing citations from references dataframes["citations"] = dataframes["citations"].append(citations) else: # add citations dataframes["citations"] = citations dataframes["citations"].sort_values("reference") return dataframes
def data_sources(): """returns all data sources in Enterprise ATT&CK""" all_data_srcs = [] # Get all techniques in Enterprise ATT&CK techniques = tc_src.query([Filter("type", "=", "attack-pattern")]) # Get all data sources in Enterprise ATT&CK for tech in techniques: if 'x_mitre_data_sources' in tech: all_data_srcs += [ data_src for data_src in tech.x_mitre_data_sources if data_src not in all_data_srcs ] return all_data_srcs
def get_map(base=CTI_BASE): from stix2 import FileSystemSource, CompositeDataSource, Filter print("Loading CTI attack-pattern map...") enterprise_attack_fs = FileSystemSource( os.path.join(base, "enterprise-attack")) mobile_attack_fs = FileSystemSource(os.path.join(base, "mobile-attack")) composite_ds = CompositeDataSource() composite_ds.add_data_sources([enterprise_attack_fs, mobile_attack_fs]) filt = Filter('type', '=', 'attack-pattern') attack_map = {} for item in composite_ds.query(filt): name = item['name'] if item['revoked']: print( f"[WARN] Ignored {name.upper()}: This attack-pattern has been revoked." ) continue categories = [x['phase_name'] for x in item['kill_chain_phases']] desc = item['description'] platforms = item['x_mitre_platforms'] attack_id = None for er in item['external_references']: if er['source_name'] in [ "mitre-attack", "mobile-mitre-attack", "mitre-mobile-attack" ]: attack_id = er['external_id'] if attack_id: attack_map[attack_id] = { "name": name, "categories": categories, "description": desc, "platforms": platforms, "attack_id": attack_id } print(f"\tAdding {name.upper()} as ID: {attack_id}") else: print(f"[ERR] Ignored {name.upper()}: No attack ID found.") return attack_map
def test_filesytem_source_query_multiple(fs_source): # query intrusion_sets = fs_source.query([Filter("type", '=', "intrusion-set")]) assert len(intrusion_sets) == 2 assert "intrusion-set--a653431d-6a5e-4600-8ad3-609b5af57064" in [ is_.id for is_ in intrusion_sets ] assert "intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a" in [ is_.id for is_ in intrusion_sets ] is_1 = [ is_ for is_ in intrusion_sets if is_.id == "intrusion-set--f3bdec95-3d62-42d9-a840-29630f6cdc1a" ][0] assert "DragonOK" in is_1.aliases assert len(is_1.external_references) == 4
def add_software(client, attack: MemoryStore, output_format: Text = "json") -> List[stix2.AttackPattern]: """ extract objects/facts related to ATT&CK Software Insert to ACT if client.baseurl is set, if not, print to stdout Args: attack (stix2): Stix attack instance """ notify = [] for software in attack.query([Filter("type", "in", ["tool", "malware"])]): if getattr(software, "revoked", None): # Object is revoked, add to notification list but do not add to facts that should be added to the platform notify.append(software) continue if getattr(software, "x_mitre_deprecated", None): # Object is revoked, add to notification list AND continue to add to facts that should be added to the platform notify.append(software) for alias in getattr(software, "x_mitre_aliases", []): if software.name.lower() != alias.lower(): handle_fact(client.fact("alias").bidirectional( "tool", software.name.lower(), "tool", alias.lower()), output_format=output_format) # ATT&CK concept STIX Properties # ========================================================================== # Technqiues relationship where relationship_type == "uses", points to # a target object with type == "attack-pattern" for technique in attack.related_to(software, relationship_type="uses"): if technique.type != "attack-pattern": continue handle_fact(client.fact("implements").source( "tool", software.name.lower()).destination("technique", technique.name), output_format=output_format) return notify
def get_pre(self, stix_format=True): pre_filter_objects = { "techniques": Filter("type", "=", "attack-pattern"), "groups": Filter("type", "=", "intrusion-set"), "relationships": Filter("type", "=", "relationship"), "tactics": Filter("type", "=", "x-mitre-tactic"), "matrix": Filter("type", "=", "x-mitre-matrix"), "identity": Filter("type", "=", "identity"), "marking-definition": Filter("type", "=", "marking-definition") } pre_stix_objects = {} for key in pre_filter_objects: pre_stix_objects[key] = self.TC_PRE_SOURCE.query(pre_filter_objects[key]) if not stix_format: pre_stix_objects[key] = self.translate_stix_objects(pre_stix_objects[key]) return pre_stix_objects
def test_filters7(): # Test filtering on embedded property stix_objects = list(STIX_OBJS2) + [{ "type": "observed-data", "id": "observed-data--b67d30ff-02ac-498a-92f9-32f845f448cf", "created_by_ref": "identity--f431f809-377b-45e0-aa1c-6a4751cae5ff", "created": "2016-04-06T19:58:16.000Z", "modified": "2016-04-06T19:58:16.000Z", "first_observed": "2015-12-21T19:00:00Z", "last_observed": "2015-12-21T19:00:00Z", "number_observed": 50, "objects": { "0": { "type": "file", "hashes": { "SHA-256": "35a01331e9ad96f751278b891b6ea09699806faedfa237d40513d92ad1b7100f" }, "extensions": { "pdf-ext": { "version": "1.7", "document_info_dict": { "Title": "Sample document", "Author": "Adobe Systems Incorporated", "Creator": "Adobe FrameMaker 5.5.3 for Power Macintosh", "Producer": "Acrobat Distiller 3.01 for Power Macintosh", "CreationDate": "20070412090123-02" }, "pdfid0": "DFCE52BD827ECF765649852119D", "pdfid1": "57A1E0F9ED2AE523E313C" } } } } }] resp = list( apply_common_filters( stix_objects, [Filter("objects.0.extensions.pdf-ext.version", ">", "1.2")])) assert resp[0]['id'] == stix_objects[3]['id'] assert len(resp) == 1
def get_software(attack): """ extract objects/facts related to ATT&CK Software Insert to ACT if client.baseurl is set, if not, print to stdout Args: attack (stix2): Stix attack instance """ notify = [] facts = [] for software in attack.query([Filter("type", "in", ["tool", "malware"])]): if getattr(software, "revoked", None): # Object is revoked, add to notification list but do not add to facts that should be added to the platform notify.append(group) continue if getattr(software, "x_mitre_deprecated", None): # Object is revoked, add to notification list AND continue to add to facts that should be added to the platform notify.append(software) if hasattr(software, "x_mitre_aliases"): aliases = [tool.lower() for tool in software.x_mitre_aliases] facts.append(("tool", software.name.lower(), "toolAlias", "tool", aliases, "bidirectional")) # ATT&CK concept STIX Properties # ========================================================================== # Technqiues relationship where relationship_type == "uses", points to # a target object with type == "attack-pattern" uses_techniques = [ tech.name for tech in attack.related_to(software, relationship_type="uses") if tech.type in ("attack-pattern") ] facts.append(("tool", software.name, "usesTechnique", "technique", uses_techniques, "linked")) return (facts, notify)
def get_all_mobile(self): mobile_filter_objects = { "techniques": Filter("type", "=", "attack-pattern"), "mitigations": Filter("type", "=", "course-of-action"), "groups": Filter("type", "=", "intrusion-set"), "malware": Filter("type", "=", "malware"), "tools": Filter("type", "=", "tool"), "relationships": Filter("type", "=", "relationship") } mobile_stix_objects = {} for key in mobile_filter_objects: mobile_stix_objects[key] = self.TC_MOBILE_SOURCE.query(mobile_filter_objects[key]) mobile_stix_objects[key] = self.parse_stix_objects(mobile_stix_objects[key], key) return mobile_stix_objects
def get_all_enterprise(self): enterprise_filter_objects = { "techniques": Filter("type", "=", "attack-pattern"), "mitigations": Filter("type", "=", "course-of-action"), "groups": Filter("type", "=", "intrusion-set"), "malware": Filter("type", "=", "malware"), "tools": Filter("type", "=", "tool"), "relationships": Filter("type", "=", "relationship") } enterprise_stix_objects = {} for key in enterprise_filter_objects: enterprise_stix_objects[key] = self.TC_ENTERPRISE_SOURCE.query(enterprise_filter_objects[key]) enterprise_stix_objects[key] = self.parse_stix_objects(enterprise_stix_objects[key], key) return enterprise_stix_objects
def get_group_map(composite_ds): print("Parsing intrusion sets ...") group_filter = Filter('type', '=', 'intrusion-set') group_map = {} revoke_map = {} for item in composite_ds.query(group_filter): name = item['name'] group_id = None for er in item['external_references']: if er['source_name'] in [ "mitre-attack", "mobile-mitre-attack", "mitre-mobile-attack" ]: group_id = er['external_id'] break if group_id: if not item['revoked']: group_map[group_id] = { "name": name, "description": item['description'], "group_id": group_id } print(f"\tAdding {name.upper()} as ID: {group_id}") else: revoke_id = get_revoked_by(item['id'], composite_ds) if revoke_id is None: print( f"\t[WARN] {name.upper()} ({group_id}) has been revoked without being replaced." ) else: revoke_map[group_id] = revoke_id print( f"\tAdding revoked {name.upper()} to the revoked map: {group_id} => {revoke_id}" ) else: print(f"[ERR] Ignored {name.upper()}: No group ID found.") return group_map, revoke_map
def get_all_techniques(self): technique_query = self.src.query(Filter('type', '=', 'attack-pattern')) dict_techniques = [] for current_technique in technique_query: string_report = current_technique.serialize() dict_report = json.loads(string_report) dict_techniques.append(dict_report) techniques_df = pd.DataFrame(dict_techniques) techniques_df = techniques_df.fillna("") techniques_df["kill_chain_phases"] = techniques_df[ "kill_chain_phases"].apply(lambda x: [ y["phase_name"] for y in x if 'mitre' in y['kill_chain_name'] ]) techniques_df = techniques_df.rename( index=str, columns={ "x_mitre_permissions_required": "permissions_required", "x_mitre_platforms": "platforms", "id": "mitre_id" }) return techniques_df[self.columns_list["techniques"]]
def parseFamilyData(controls): """ingest control data to return familyIDToControls mapping and familyIDToName mapping""" idToFamily = re.compile("(\w+)-.*") familyIDToControls = {} # family ID to control object familyIDToName = {} for control in controls.query([Filter("type", "=", "course-of-action")]): # parse family ID from control external ID familyID = idToFamily.search( control["external_references"][0]["external_id"]).groups()[0] if familyID not in familyIDToControls: familyIDToControls[familyID] = [control] else: familyIDToControls[familyID].append(control) # parse family name if possible, or just use family ID if not if "x_mitre_family" in control: familyIDToName[familyID] = control["x_mitre_family"] else: familyIDToName[familyID] = familyID return familyIDToControls, familyIDToName, idToFamily
def add_techniques(client, attack: MemoryStore, output_format: Text = "json") -> List[stix2.AttackPattern]: """ extract objects/facts related to ATT&CK techniques Args: attack (stix2): Stix attack instance """ notify = [] # ATT&CK concept STIX Object type ACT object # ========================================================= # Technique attack-pattern technique # Filter out ATT&CK techniques (attack-pattern) from bundle for technique in attack.query([Filter("type", "=", "attack-pattern")]): if getattr(technique, "revoked", None): # Object is revoked, add to notification list but do not add to facts that should be added to the platform notify.append(technique) continue if getattr(technique, "x_mitre_deprecated", None): # Object is revoked, add to notification list AND continue to add to facts that should be added to the platform notify.append(technique) # Mitre ATT&CK Tactics are implemented in STIX as kill chain phases with kill_chain_name "mitre-attack" for tactic in technique.kill_chain_phases: if tactic.kill_chain_name != "mitre-attack": continue handle_fact(client.fact("accomplishes").source( "technique", technique.name).destination("tactic", tactic.phase_name), output_format=output_format) return notify
def get_attack_map(composite_ds): print("Parsing attack patterns ...") att_filter = Filter('type', '=', 'attack-pattern') attack_map = {} for item in composite_ds.query(att_filter): name = item['name'] if item['revoked']: print( f"\t[WARN] Ignored {name.upper()}: This attack-pattern has been revoked." ) continue categories = [x['phase_name'] for x in item['kill_chain_phases']] desc = item['description'] platforms = item['x_mitre_platforms'] attack_id = None for er in item['external_references']: if er['source_name'] in [ "mitre-attack", "mobile-mitre-attack", "mitre-mobile-attack" ]: attack_id = er['external_id'] break if attack_id: attack_map[attack_id] = { "name": name, "categories": categories, "description": desc, "platforms": platforms, "attack_id": attack_id } print(f"\tAdding {name.upper()} as ID: {attack_id}") else: print(f"[ERR] Ignored {name.upper()}: No attack ID found.") return attack_map
def tacticsToDf(src, domain): """ Parse STIX tactics from the given data and return corresponding pandas dataframes. :param src: MemoryStore or other stix2 DataSource object holding the domain data :param domain: domain of ATT&CK src corresponds to, e.g "enterprise-attack" :returns: a lookup of labels (descriptors/names) to dataframes """ tactics = src.query([Filter("type", "=", "x-mitre-tactic")]) tactics = remove_revoked_deprecated(tactics) tactic_rows = [] for tactic in tqdm(tactics, desc="parsing mitigations"): tactic_rows.append(parseBaseStix(tactic)) citations = get_citations(tactics) dataframes = { "tactics": pd.DataFrame(tactic_rows).sort_values("name"), } if not citations.empty: dataframes["citations"] = citations.sort_values("reference") return dataframes
def get_techniques(attack): """ extract objects/facts related to ATT&CK techniques Args: attack (stix2): Stix attack instance """ notify = [] facts = [] # ATT&CK concept STIX Object type ACT object # ========================================================= # Technique attack-pattern technique # Filter out ATT&CK techniques (attack-pattern) from bundle for technique in attack.query([Filter("type", "=", "attack-pattern")]): if getattr(technique, "revoked", None): # Object is revoked, add to notification list but do not add to facts that should be added to the platform notify.append(technique) continue if getattr(technique, "x_mitre_deprecated", None): # Object is revoked, add to notification list AND continue to add to facts that should be added to the platform notify.append(technique) # Mitre ATT&CK Tactics are implemented in STIX as kill chain phases with kill_chain_name "mitre-attack" tactics = [ tactic.phase_name for tactic in technique.kill_chain_phases if tactic.kill_chain_name == "mitre-attack" ] facts.append(("tactic", tactics, "usesTechnique", "technique", technique.name, "linked")) return (facts, notify)
def generate(): """parse the STIX on MITRE/CTI and return a layer dict with techniques with randomized scores""" # import the STIX data from MITRE/CTI stix = requests.get( "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json" ).json() ms = MemoryStore(stix_data=stix["objects"]) # get all techniques in STIX techniques = ms.query([Filter("type", "=", "attack-pattern")]) # parse techniques into layer format techniques_list = [] for technique in techniques: # skip deprecated and revoked if ("x_mitre_deprecated" in technique and technique["x_mitre_deprecated"]) or ("revoked" in technique and technique["revoked"]): continue techniqueID = technique["external_references"][0][ "external_id"] # get the attackID techniques_list.append({ "techniqueID": techniqueID, "score": random.randint(1, 100) # random score }) # return the techniques in a layer dict return { "name": "heatmap example", "versions": { "layer": "4.0", "navigator": "4.0" }, "sorting": 3, # descending order of score "description": "An example layer where all techniques have a randomized score", "domain": "enterprise-attack", "techniques": techniques_list, }