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
Example #2
0
    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
Example #3
0
 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
Example #4
0
    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
Example #5
0
    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
Example #6
0
 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
Example #7
0
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}.')
Example #8
0
    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]
Example #9
0
 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
Example #10
0
 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
Example #13
0
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
Example #14
0
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
Example #16
0
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
Example #17
0
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
Example #18
0
 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
Example #20
0
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
Example #24
0
 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
Example #26
0
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
Example #27
0
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
Example #28
0
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
Example #29
0
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)
Example #30
0
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,
    }