Ejemplo n.º 1
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",
        "version": "3.0",
        "sorting": 3,  # descending order of score
        "description":
        "An example layer where all techniques have a randomized score",
        "domain": "mitre-enterprise",
        "techniques": techniques_list,
    }
Ejemplo n.º 2
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"])]):
        tool_name = software.name.lower()

        # Tool category
        handle_fact(client.fact("category",
                                software.type).source("tool", tool_name),
                    output_format=output_format)

        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 tool_name != alias.lower():
                # Tool category (alias)
                handle_fact(client.fact("category", software.type).source(
                    "tool", alias.lower()),
                            output_format=output_format)
                handle_fact(client.fact("alias").bidirectional(
                    "tool", tool_name, "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
Ejemplo n.º 3
0
 def __init__(self,
              name=None,
              parentApp=None,
              framed=None,
              help=None,
              color='FORMDEFAULT',
              widget_list=None,
              cycle_widgets=False,
              *args,
              store: stix2.MemoryStore,
              **keywords):
     indicators = store.query([stix2.Filter('type', '=', 'indicator')])
     self._provided_indicators = tuple(indicators)
     super().__init__(name, parentApp, framed, help, color, widget_list,
                      cycle_widgets, *args, **keywords)
Ejemplo n.º 4
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
Ejemplo n.º 5
0
def generate():
    """parse the STIX on MITRE/CTI and return a layer dict showing all techniques used by an APT group with phrase 'bear' in the group aliases."""
    # 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"])

    groups = ms.query([Filter("type", "=", "intrusion-set")])

    # find bear groups
    bear_groups = []  #list of groups with bear in name
    for group in groups:
        # filter out deprecated and revoked groups
        if ("x_mitre_deprecated" in group
                and group["x_mitre_deprecated"]) or ("revoked" in group
                                                     and group["revoked"]):
            continue
        # check all aliases for bear
        for alias in group["aliases"]:
            if re.match(".*bear.*", alias, re.IGNORECASE) is not None:
                bear_groups.append(group)
                break  # don't match the same group multiple times

    # find techniques used by bear groups
    techniques_used = {}  #attackID => using bear groups
    for bear in bear_groups:
        # construct the "bear" name for the comment
        # if bear occurs in multiple aliases, list them all
        bearnames = []
        for alias in bear["aliases"]:
            if re.match(".*bear.*", alias, re.IGNORECASE) is not None:
                bearnames.append(alias)
        bearname = bearnames[0]
        if len(bearnames) > 1:
            bearname += " (AKA " + ",".join(bearnames[1:]) + ")"

        # get techniques used by this group
        relationships = ms.relationships(bear["id"])
        for relationship in relationships:
            # skip all non-technique relationships
            if "attack-pattern" not in relationship["target_ref"]: continue
            technique = ms.get(relationship["target_ref"])
            # filter out deprecated and revoked techniques
            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"]
            # store usage in techniques_used struct
            if techniqueID in techniques_used:
                techniques_used[techniqueID].append(bearname)
            else:
                techniques_used[techniqueID] = [bearname]

    # format the techniques for the output layer
    techniques_list = []
    for techniqueID in techniques_used:
        techniques_list.append({
            "techniqueID":
            techniqueID,
            "comment":
            "used by " + ", ".join(techniques_used[techniqueID]),
            "color":
            "#ff6666"
        })
    # construct and return the layer as a dict
    return {
        "name":
        "*Bear APTs",
        "versions": {
            "layer": "4.1",
            "navigator": "4.1"
        },
        "description":
        "All techniques used by an APT group with phrase 'bear' in the group aliases",
        "domain":
        "enterprise-attack",
        "techniques":
        techniques_list,
        "legendItems": [{
            "label": "Used by a group the phrase 'bear' in the group aliases",
            "color": "#ff6666"
        }]
    }
Ejemplo n.º 6
0
def generate(softwaretype="software"):
    """ generate and return a layer dict showing techniques used by software
        If softwaretype is specified as "malware" or "tool", only shows software of that type. If softwaretype is specified as "software" output layer shows both malware and tools
    """
    # 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"])
    # software includes malware and tool types so perform two queries and merge the results
    software_filters = []
    if softwaretype == "malware" or softwaretype == "software":
        software_filters.append([Filter('type', '=', 'malware')])
    if softwaretype == "tool" or softwaretype == "software":
        software_filters.append([Filter('type', '=', 'tool')])

    software = list(chain.from_iterable(ms.query(f) for f in software_filters))

    # build a list of techniques used by software
    techniques_used = {}  #attackID => using software names
    for thesoftware in software:
        # filter out revoked and deprecated software
        if ("x_mitre_deprecated" in thesoftware
                and thesoftware["x_mitre_deprecated"]) or (
                    "revoked" in thesoftware and thesoftware["revoked"]):
            continue
        for relationship in ms.relationships(thesoftware["id"]):
            # skip all non-technique relationships
            if "attack-pattern" not in relationship["target_ref"]: continue
            technique = ms.get(relationship["target_ref"])
            # filter out deprecated and revoked techniques
            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"]
            # store usage in techniques_used struct
            if techniqueID in techniques_used:
                techniques_used[techniqueID].append(thesoftware["name"])
            else:
                techniques_used[techniqueID] = [thesoftware["name"]]

    # format the techniques for the output layer
    techniques_list = []
    highest_usage = 0
    lowest_usage = 1
    for techniqueID in techniques_used:
        # determine the number of used techniques for the score
        count = len(techniques_used[techniqueID])
        highest_usage = max(highest_usage, count)
        lowest_usage = min(lowest_usage, count)
        # append technique struct to list of layer-formatted techniques
        techniques_list.append({
            "techniqueID":
            techniqueID,
            "comment":
            "executed by " + ", ".join(techniques_used[techniqueID]),
            "score":
            count,
        })
    # set up layer name and desc according to softwaretype
    if softwaretype != "software":
        plural = "tools" if softwaretype == "tool" else "malware"
        layername = f"Software ({softwaretype}) Execution"
        layerdescription = f"All techniques that can be executed by software of subtype {softwaretype}, where the score is the count of {plural} using the technique"
    else:
        layername = "Software Execution"
        layerdescription = f"All techniques that can be executed by software, where the score is the count of software using the technique"

    # construct and return the layer as a dict
    return {
        "name": layername,
        "description": layerdescription,
        "version": "3.0",
        "domain": "mitre-enterprise",
        "techniques": techniques_list,
        "sorting": 3,  # order in descending order of score (count)
        "gradient": {
            "colors": [
                "#fff7b3",  # low counts are yellow
                "#ff6666",  # high counts are red
            ],
            "minValue": lowest_usage,
            "maxValue": highest_usage
        },
    }
Ejemplo n.º 7
0
def add_groups(client,
               attack: MemoryStore,
               output_format: Text = "json") -> List[stix2.AttackPattern]:
    """
        extract objects/facts related to ATT&CK Groups

    Args:
        attack (stix2):       Stix attack instance

    """

    notify = []

    # ATT&CK concept    STIX Object type        ACT object
    # =========================================================
    # Group	        intrusion-set           threatActor
    #
    # Filter out ATT&CK groups (intrusion-set) from bundle

    for group in attack.query([Filter("type", "=", "intrusion-set")]):
        if getattr(group, "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(group, "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(group)

        for alias in getattr(group, "aliases", []):
            if group.name != alias:
                handle_fact(client.fact("alias").bidirectional(
                    "threatActor", group.name, "threatActor", alias),
                            output_format=output_format)

        #   ATT&CK concept   STIX Properties
        #   ==========================================================================
        #   Software         relationship where relationship_type == "uses",
        #                    points to a target object with type== "malware" or "tool"

        for tool in attack.related_to(group, relationship_type="uses"):
            if tool.type not in ("malware", "tool"):
                continue

            chain = act.api.fact.fact_chain(
                client.fact("classifiedAs").source("content", "*").destination(
                    "tool", tool.name.lower()),
                client.fact("observedIn").source("content", "*").destination(
                    "event", "*"),
                client.fact("attributedTo").source("event", "*").destination(
                    "incident", "*"),
                client.fact("attributedTo").source(
                    "incident", "*").destination("threatActor", group.name))

            for fact in chain:
                handle_fact(fact, 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(group, relationship_type="uses"):
            if technique.type != "attack-pattern":
                continue

            chain = act.api.fact.fact_chain(
                client.fact("classifiedAs").source("event", "*").destination(
                    "technique", technique.name),
                client.fact("attributedTo").source("event", "*").destination(
                    "incident", "*"),
                client.fact("attributedTo").source(
                    "incident", "*").destination("threatActor", group.name))

            for fact in chain:
                handle_fact(fact, output_format=output_format)

    return notify
Ejemplo n.º 8
0
class MitreExtractor:
    """
    This class extract Mitre techniques and sub techniques that are represented as "attack-pattern" in STIX format.
    The STIX data is collected in JSON format by requesting the specified URL.

    url: must point to json stix location
    kill_chain_name: mitre-attack, mitre-mbc...
    """

    url = ""
    kill_chain_name = ""

    def __init__(self):
        """Download and store in memory the STIX data on instantiation."""
        if self.kill_chain_name == "":
            raise ValueError(
                f"Kill chain name not specified in class {self.__class__.__name__}"
            )

        if self.url == "":
            raise ValueError(
                f"URL not specified in class {self.__class__.__name__}")

        logging.info(f"Downloading STIX data at: {self.url}")
        stix_json = requests.get(self.url).json()
        self._memory_store = MemoryStore(stix_data=stix_json["objects"])

    @staticmethod
    def _remove_deprecated_objetcs(stix_objects) -> List[AttackPattern]:
        """Remove any revoked or deprecated objects from queries made to the data source."""
        return list(
            filter(
                lambda x: x.get("x_mitre_deprecated", False) is False and x.
                get("revoked", False) is False,
                stix_objects,
            ))

    def _get_tactics(self) -> List[Dict]:
        """Get tactics IDs from Mitre matrix."""
        # Only one matrix for enterprise att&ck framework
        matrix = self._remove_deprecated_objetcs(
            self._memory_store.query([
                Filter("type", "=", "x-mitre-matrix"),
            ]))[0]
        return list(map(self._memory_store.get, matrix["tactic_refs"]))

    def _get_techniques_from_tactic(self, tactic: str) -> List[AttackPattern]:
        """Get techniques and sub techniques from a Mitre tactic (kill_chain_phases->phase_name)"""
        techniques = self._remove_deprecated_objetcs(
            self._memory_store.query([
                Filter("type", "=", "attack-pattern"),
                Filter("kill_chain_phases.phase_name", "=", tactic),
                Filter("kill_chain_phases.kill_chain_name", "=",
                       self.kill_chain_name),
            ]))
        return techniques

    def _get_parent_technique_from_subtechnique(
            self, technique: AttackPattern) -> AttackPattern:
        """Get parent technique of a sub technique using the technique ID TXXXX.YYY"""
        sub_id = technique["external_references"][0]["external_id"].split(
            ".")[0]
        parent_technique = self._remove_deprecated_objetcs(
            self._memory_store.query([
                Filter("type", "=", "attack-pattern"),
                Filter("external_references.external_id", "=", sub_id),
            ]))[0]
        return parent_technique

    def run(self) -> Dict[str, Dict[str, str]]:
        """Iterate over every technique over every tactic. If the technique is a sub technique, then
        we also search for the parent technique name.
        """
        logging.info("Starting extraction...")
        data: Dict[str, Dict[str, str]] = {}
        for tactic in self._get_tactics():
            data[tactic["name"]] = {}
            for technique in self._get_techniques_from_tactic(
                    tactic["x_mitre_shortname"]):
                tid = technique["external_references"][0]["external_id"]
                technique_name = technique["name"].split("::")[0]
                if technique["x_mitre_is_subtechnique"]:
                    parent_technique = self._get_parent_technique_from_subtechnique(
                        technique)
                    data[tactic["name"]][
                        tid] = f"{parent_technique['name']}::{technique_name}"
                else:
                    data[tactic["name"]][tid] = technique_name
        return data