Example #1
0
def ds2():
    cam = stix2.v20.Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS)
    idy = stix2.v20.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS)
    ind = stix2.v20.Indicator(id=INDICATOR_ID, created_by_ref=idy.id, **INDICATOR_KWARGS)
    indv2 = ind.new_version(
        external_references=[{
            "source_name": "unknown",
            "url": "https://examplewebsite.com/",
        }],
    )
    mal = stix2.v20.Malware(id=MALWARE_ID, created_by_ref=idy.id, **MALWARE_KWARGS)
    malv2 = mal.new_version(
        external_references=[{
            "source_name": "unknown",
            "url": "https://examplewebsite2.com/",
        }],
    )
    rel1 = stix2.v20.Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0])
    rel2 = stix2.v20.Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1])
    rel3 = stix2.v20.Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2])
    stix_objs = [cam, idy, ind, indv2, mal, malv2, rel1, rel2, rel3]
    reprt = stix2.v20.Report(
        created_by_ref=idy.id,
        name="example",
        labels=["campaign"],
        published="2021-04-09T08:22:22Z",
        object_refs=stix_objs,
    )
    stix_objs.append(reprt)
    yield stix2.MemoryStore(stix_objs)
def test_environment_functions():
    env = stix2.Environment(stix2.ObjectFactory(created_by_ref=IDENTITY_ID),
                            stix2.MemoryStore())

    # Create a STIX object
    ind = env.create(stix2.Indicator, id=INDICATOR_ID, **INDICATOR_KWARGS)
    assert ind.created_by_ref == IDENTITY_ID

    # Add objects to datastore
    ind2 = ind.new_version(labels=['benign'])
    env.add([ind, ind2])

    # Get both versions of the object
    resp = env.all_versions(INDICATOR_ID)
    assert len(resp) == 1  # should be 2, but MemoryStore only keeps 1 version of objects

    # Get just the most recent version of the object
    resp = env.get(INDICATOR_ID)
    assert resp['labels'][0] == 'benign'

    # Search on something other than id
    query = [stix2.Filter('type', '=', 'vulnerability')]
    resp = env.query(query)
    assert len(resp) == 0

    # See different results after adding filters to the environment
    env.add_filters([stix2.Filter('type', '=', 'indicator'),
                    stix2.Filter('created_by_ref', '=', IDENTITY_ID)])
    env.add_filter(stix2.Filter('labels', '=', 'benign'))  # should be 'malicious-activity'
    resp = env.get(INDICATOR_ID)
    assert resp['labels'][0] == 'benign'  # should be 'malicious-activity'
Example #3
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 = stix2.MemoryStore(stix_data=stix["objects"])
    # get all techniques in STIX
    techniques = ms.query([
        stix2.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,
    }
def test_environment_datastore_and_sink():
    with pytest.raises(ValueError) as excinfo:
        stix2.Environment(
            factory=stix2.ObjectFactory(),
            store=stix2.MemoryStore(), sink=stix2.MemorySink,
        )
    assert 'Data store already provided' in str(excinfo.value)
def test_creator_of_not_found():
    identity = stix2.Identity(**IDENTITY_KWARGS)
    factory = stix2.ObjectFactory(created_by_ref=identity.id)
    env = stix2.Environment(store=stix2.MemoryStore(), factory=factory)

    ind = env.create(stix2.Indicator, **INDICATOR_KWARGS)
    creator = env.creator_of(ind)
    assert creator is None
def get_old_stix_memory_stores():

    src = {}

    for domain in config.domains:
        src[domain] = stix2.MemoryStore()
        src[domain].load_from_file(config.last_attack_path[domain])

    return src
def ds():
    cam = stix2.Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS)
    idy = stix2.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS)
    ind = stix2.Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS)
    mal = stix2.Malware(id=MALWARE_ID, **MALWARE_KWARGS)
    rel1 = stix2.Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0])
    rel2 = stix2.Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1])
    rel3 = stix2.Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2])
    stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3]
    yield stix2.MemoryStore(stix_objs)
Example #8
0
def get_stix_memory_stores():
    """This function reads the json files for each domain and creates a dict
       that contains the memory stores for each domain.
    """

    src = {}

    for domain in site_config.bundles:
        src[domain] = stix2.MemoryStore()
        src[domain].load_from_file(site_config.attack_path[domain])

    return src
def ds():
    cam = stix2.v21.Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS)
    idy = stix2.v21.Identity(id=IDENTITY_ID, **IDENTITY_KWARGS)
    ind = stix2.v21.Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS)
    mal = stix2.v21.Malware(id=MALWARE_ID, **MALWARE_KWARGS)
    rel1 = stix2.v21.Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0])
    rel2 = stix2.v21.Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1])
    rel3 = stix2.v21.Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2])
    reprt = stix2.v21.Report(
        name="Malware Report", published="2021-05-09T08:22:22Z",
        object_refs=[mal.id, rel1.id, ind.id],
    )
    stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3, reprt]
    yield stix2.MemoryStore(stix_objs)
def get_data_from_branch(domain, branch="master"):
    dest = "https://raw.githubusercontent.com/" \
        "mitre/cti/{}/{}/{}" \
        ".json".format(branch, domain, domain)
    stix_json = requests.get(dest).json()
    return stix2.MemoryStore(stix_data=stix_json["objects"])
def test_creator_of_no_created_by_ref():
    env = stix2.Environment(store=stix2.MemoryStore())
    ind = env.create(stix2.Indicator, **INDICATOR_KWARGS)
    creator = env.creator_of(ind)
    assert creator is None
 def __init__(self, bundle: stix2.Bundle):
     self.store = stix2.MemoryStore(bundle)
     super().__init__()
def test_environment_datastore_and_no_object_factory():
    # Uses a default object factory
    env = stix2.Environment(store=stix2.MemoryStore())
    ind = env.create(stix2.Indicator, id=INDICATOR_ID, **INDICATOR_KWARGS)
    assert ind.id == INDICATOR_ID
Example #14
0
 def reset(self):
     self.store = stix2.MemoryStore()
     self._data = self.store.query()
Example #15
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 = stix2.MemoryStore(stix_data=stix["objects"])

    groups = ms.query([stix2.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",
        "version":
        "3.0",
        "description":
        "All techniques used by an APT group with phrase 'bear' in the group aliases",
        "domain":
        "mitre-enterprise",
        "techniques":
        techniques_list,
        "legendItems": [{
            "label": "Used by a group the phrase 'bear' in the group aliases",
            "color": "#ff6666"
        }]
    }
def generate(show_nodetect=False):
    """
        generate and return a layer dict showing techniques used by APT3 and APT29 as well as software used by those groups
        param show_nodetect, if true, causes techniques that have no data-sources to be highlighted as well
    """
    stix = requests.get("https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json").json()
    ms = stix2.MemoryStore(stix_data=stix["objects"])
    apt3 = ms.get("intrusion-set--0bbdf25b-30ff-4894-a1cd-49260d0dd2d9")
    apt29 = ms.get("intrusion-set--899ce53f-13a0-479b-a0e4-67d46e241542")
    
    techniques_used = {} # attackID => {apt3: boolean, apt29: boolean, software: Set, detection: boolean}

    for apt in [apt3, apt29]:

        def use_technique(technique, software=None):
            """helper function to record a technique as used"""
            techniqueID = technique["external_references"][0]["external_id"]
            # init struct if the technique has not been seen before
            if not techniqueID in techniques_used:
                techniques_used[techniqueID] = {
                    "APT3": False,
                    "APT29": False,
                    "software": set(),
                    "datasources": []
                }
            # record new data
            techniques_used[techniqueID][apt["name"]] = True
            if "x_mitre_data_sources" in technique and len(technique["x_mitre_data_sources"]) > 0: 
                techniques_used[techniqueID]["datasources"] = technique["x_mitre_data_sources"]
            if software:
                techniques_used[techniqueID]["software"].add(software["name"])
        
        # traverse relationships
        for relationship in ms.relationships(apt["id"]):
            target_obj = ms.get(relationship["target_ref"])
            # skip relationships with deprecated objects
            if ("x_mitre_deprecated" in target_obj and target_obj["x_mitre_deprecated"]) or ("revoked" in target_obj and target_obj["revoked"]): continue
            # technique type relationship
            if target_obj["type"] == "attack-pattern": 
                # record technique usage
                use_technique(target_obj)
            # software type relationship, traverse to find software-used techniques
            if target_obj["type"] == "malware" or target_obj["type"] == "tool":
                software = target_obj
                for software_relationship in ms.relationships(software["id"]):
                    software_target_obj = ms.get(software_relationship["target_ref"])
                    # skip relationships with deprecated objects
                    if ("x_mitre_deprecated" in software_target_obj and software_target_obj["x_mitre_deprecated"]) or ("revoked" in software_target_obj and software_target_obj["revoked"]): continue
                    if software_target_obj["type"] == "attack-pattern": 
                        # record technique usage
                        use_technique(software_target_obj, software)

    # format the techniques for the output layer
    techniques_list = []

    def color_lookup(usage): 
        if show_nodetect and not len(usage["datasources"]) > 0:
            return "#fc3b3b"
        if usage["APT3"] and usage["APT29"]: 
            return "#74c476"
        if usage["APT3"]: return "#6baed6"
        if usage["APT29"]: return "#fce93b"

    for techniqueID in techniques_used:
        # determine the number of used techniques for the score
        comment = ""
        if show_nodetect:
            if len(techniques_used[techniqueID]["datasources"]) > 0:
                comment = f"considered detectable by a notional organization because it has data-sources {', '.join(techniques_used[techniqueID]['datasources'])}"
            else:
                comment = "considered undetectable by a notional organization because it has no data-sources"
        else:
            used = []
            if techniques_used[techniqueID]["APT3"]: used.append("APT3") 
            if techniques_used[techniqueID]["APT29"]: used.append("APT29") 
            used += list(techniques_used[techniqueID]["software"])
            comment = f"used by {', '.join(used)}"
        # append technique struct to list of layer-formatted techniques
        techniques_list.append({
            "techniqueID": techniqueID,
            "color": color_lookup(techniques_used[techniqueID]),
            "comment": comment,
        })

    # construct and return the layer as a dict
    # set up layer information according to show_nodetect
    name = "APT3 + APT29 with software"
    description = "This layer shows techniques (including techniques from software used by the groups) used by APT3 only in blue, APT29 only in yellow, and both APT3 and APT29 in green."
    legend = [
        {
            "label": "Used by APT3 or a software APT3 uses",
            "color": color_lookup({"APT3": True, "APT29": False, "datasources": ["placeholder"]})
        },
        {
            "label": "Used by APT29 or a software APT29 uses",
            "color": color_lookup({"APT3": False, "APT29": True, "datasources": ["placeholder"]})
        },
        {
            "label": "Used by both APT3 or a softare APT3 uses and APT29 or a software APT29 uses",
            "color": color_lookup({"APT3": True, "APT29": True, "datasources": ["placeholder"]})
        }
    ]
    # additional formatting when displaying notional detectability
    if show_nodetect: 
        name += " and notional no detection"
        description += " The techniques in red denote techniques considered undetectable by a notional organization because they have no data-sources. Disclaimer: Data-sources in ATT&CK are sources of information that COULD be used to identify adversary actions, however the exactness of that evidence varies greatly. Therefore the presence of a data source for technique should only be considered a potential metric for detectability."
        legend.append({
            "label": "Used by either APT3 or APT29 but considered undetectable by a notional organization because it has no data-sources",
            "color": color_lookup({"APT3": True, "APT29": True, "datasources": []})
        })
        
    # layer struct
    return {
        "name": name,
        "version": "3.0",
        "description": description,
        "domain": "mitre-enterprise",
        "techniques": techniques_list,
        "legendItems": legend
    }
Example #17
0
def ds2(ds2_objects):
    yield stix2.MemoryStore(ds2_objects)
Example #18
0
 def __init__(self, path=None):
     super().__init__()
     self.path = path
     self.store = stix2.MemoryStore()
     self._data = self.store.query()
Example #19
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 = stix2.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([stix2.Filter('type', '=', 'malware')])
    if softwaretype == "tool" or softwaretype == "software":
        software_filters.append([stix2.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
        },
    }