예제 #1
0
def rel_fs_store():
    cam = Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS)
    idy = Identity(id=IDENTITY_ID, **IDENTITY_KWARGS)
    ind = Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS)
    mal = Malware(id=MALWARE_ID, **MALWARE_KWARGS)
    rel1 = Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0])
    rel2 = Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1])
    rel3 = Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2])
    stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3]
    fs = FileSystemStore(FS_PATH)
    for o in stix_objs:
        fs.add(o)
    yield fs

    for o in stix_objs:
        filepath = os.path.join(FS_PATH, o.type, o.id,
                                _timestamp2filename(o.modified) + '.json')

        # Some test-scoped fixtures (e.g. fs_store) delete all campaigns, so by
        # the time this module-scoped fixture tears itself down, it may find
        # its campaigns already gone, which causes not-found errors.
        try:
            os.remove(filepath)
        except OSError as e:
            # 3 is the ERROR_PATH_NOT_FOUND windows error code.  Which has an
            # errno symbolic value, but not the windows meaning...
            if e.errno in (errno.ENOENT, 3):
                continue
            raise
예제 #2
0
 def get_technique_by_name(self, name):
     dir_path = os.path.dirname(os.path.realpath(__file__))
     from stix2 import FileSystemStore
     fs = FileSystemStore(dir_path + "/../enterprise-attack")
     filt = [
         Filter('type', '=', 'attack-pattern'),
         Filter('name', '=', name)
     ]
     return fs.query(filt)
예제 #3
0
def rel_fs_store():
    cam = Campaign(id=CAMPAIGN_ID, **CAMPAIGN_KWARGS)
    idy = Identity(id=IDENTITY_ID, **IDENTITY_KWARGS)
    ind = Indicator(id=INDICATOR_ID, **INDICATOR_KWARGS)
    mal = Malware(id=MALWARE_ID, **MALWARE_KWARGS)
    rel1 = Relationship(ind, 'indicates', mal, id=RELATIONSHIP_IDS[0])
    rel2 = Relationship(mal, 'targets', idy, id=RELATIONSHIP_IDS[1])
    rel3 = Relationship(cam, 'uses', mal, id=RELATIONSHIP_IDS[2])
    stix_objs = [cam, idy, ind, mal, rel1, rel2, rel3]
    fs = FileSystemStore(FS_PATH)
    for o in stix_objs:
        fs.add(o)
    yield fs

    for o in stix_objs:
        os.remove(os.path.join(FS_PATH, o.type, o.id + '.json'))
def test_filesystem_store_add_as_bundle():
    fs_store = FileSystemStore(FS_PATH, bundlify=True)

    camp1 = Campaign(name="Great Heathen Army",
                     objective="Targeting the government of United Kingdom and insitutions affiliated with the Church Of England",
                     aliases=["Ragnar"])
    fs_store.add(camp1)

    with open(os.path.join(FS_PATH, "campaign", camp1.id + ".json")) as bundle_file:
        assert '"type": "bundle"' in bundle_file.read()

    camp1_r = fs_store.get(camp1.id)
    assert camp1_r.id == camp1.id
    assert camp1_r.name == camp1.name

    shutil.rmtree(os.path.join(FS_PATH, "campaign"), True)
예제 #5
0
def fs_store():
    # create
    yield FileSystemStore(FS_PATH)

    # remove campaign dir
    shutil.rmtree(os.path.join(FS_PATH, "campaign"), True)
예제 #6
0
def main(args, config):
    """
    Wrapper to run all components of CDAS

    Calls context, agents, and asset builders to create simulation componenets.
    Passes resulting components to simulation module. Manages output.

    Parameters
    ----------
    args : list
        The arguments passed in from argparse or read from the configuration 
        file in the arguments method
    config : dict
        The configuration file opened and loaded from json
    """

    # Set up the Output directory
    if os.path.isdir(args.output):
        q = (f"Overwrite the output folder {os.getcwd() + '/' + args.output}? "
             f"(y/n) ")
    else:
        q = f"Output path {os.getcwd() + '/' + args.output} does not exist.\n\
            Create this directory? (y/n) "

    if not args.overwrite_output:
        answer = ""
        while answer not in ['y', 'n']:
            answer = input(q)
    else:
        answer = 'y'
    if answer == 'n':
        sys.exit(f"CDAS exited without completing")
    else:
        if os.path.isdir(args.output):
            for filename in os.listdir(args.output):
                file_path = os.path.join(args.output, filename)
                try:
                    if os.path.isfile(file_path) or os.path.islink(file_path):
                        os.unlink(file_path)
                    elif os.path.isdir(file_path):
                        shutil.rmtree(file_path)
                except Exception as e:
                    print('Failed to delete %s. %s' % (file_path, e))
        else:
            os.mkdir(args.output)

    # Set up the STIX data stores
    # Check if it's okay to overwrite the contents of the temporary data store
    temp_path = pkg_resources.resource_filename(__name__, config['temp_path'])
    if os.path.isdir(temp_path):
        q = f"Overwrite temporary stix data folder ({temp_path})? (y/n) "
        overwrite = input(q)
        if overwrite == 'n':
            print(f"Rename the 'temp path' variable in config file and \
                restart the simulation.")
            sys.exit()
        elif overwrite == 'y':
            shutil.rmtree(temp_path)
            os.mkdir(temp_path)
        else:
            overwrite = input(q)
    else:
        os.mkdir(temp_path)
    fs_gen = FileSystemStore(temp_path)
    fs_real = FileSystemSource(
        pkg_resources.resource_filename(__name__, "assets/mitre_cti/"))

    # Load or create country data
    countries = []
    if args.randomize_geopol is True:
        print("Creating countries...")
        with open(args.random_geodata, encoding='utf-8') as f:
            context_options = json.load(f)  # seed file
        f.close()
        map_matrix = context.Map(args.num_countries)
        for c in range(0, args.num_countries):
            countries.append(
                context.Country(fs_gen, context_options, map_matrix.map))
        for c in countries:
            # This loop is used mainly to convert references to other countries
            # to the names of those countries instead of their ID numbers,
            # since, during the generation of each country it only has access
            # to map_matrix with ID numbers of the other countries

            # Convert the neighbors listed by id# to neighbor country names
            neighbors = {}
            for n in c.neighbors:
                n_name = next((x.name for x in countries if x.id == n), None)
                neighbors[n_name] = c.neighbors[n]
            c.neighbors = neighbors
            if len(c.neighbors) == 0:
                c.neighbors = "None (island nation)"

            # if country is a terrority, find its owner
            if c.government_type == "non-self-governing territory":
                gdps = [(int(gdp.gdp[1:].replace(',', '')), gdp.name)
                        for gdp in countries]
                gdps.sort()
                # Territory owners are most likely to be high GDP countries
                # pick a random one from the top three GDP
                owner_name = np.random.choice([gdp[1] for gdp in gdps][-3:])
                if c.name in [gdp[1] for gdp in gdps][-3:]:
                    # if the territory itself is in the top three GDP, change
                    # its gov type to a republic instead of a territory
                    c.government_type = "federal parliamentary republic"
                else:
                    c.government_type += f" of {str(owner_name)}"
                    # update ethnic groups to include owner instead of random
                    owner = next(
                        (x.id for x in countries if x.name == owner_name),
                        None)
                    if str(owner) not in c.ethnic_groups:
                        egs = {}
                        for eg in c.ethnic_groups:
                            try:
                                int(eg)
                                if str(owner) not in egs:
                                    egs[str(owner)] = c.ethnic_groups[eg]
                                else:
                                    egs[eg] = c.ethnic_groups[eg]
                            except ValueError:
                                egs[eg] = c.ethnic_groups[eg]
                        c.ethnic_groups = egs
                    # update forces to include owner name if necessary
                    msf = c.military_and_security_forces
                    c.military_and_security_forces = msf.replace(
                        "[COUNTRY]", owner_name)
                    # update languages to include owner instead of random
                    if str(owner) not in c.languages:
                        langs = {}
                        for eg in c.languages:
                            try:
                                int(eg)
                                if str(owner) not in langs:
                                    langs[str(owner)] = c.languages[eg]
                                else:
                                    langs[eg] = c.languages[eg]
                            except ValueError:
                                langs[eg] = c.languages[eg]
                        c.languages = langs

            # Apply nationalities to ethnic groups listed by id#
            egs = {}
            for eg in c.ethnic_groups:
                try:
                    egs[next(
                        (x.nationality for x in countries if x.id == int(eg)),
                        None)] = c.ethnic_groups[eg]
                except ValueError:
                    egs[eg] = c.ethnic_groups[eg]
            c.ethnic_groups = egs

            # Convert languges listed by id# to country names
            egs = {}
            for eg in c.languages:
                try:
                    eg_name = next(
                        (x.name for x in countries if x.id == int(eg)), None)
                    if eg_name.endswith(('a', 'e', 'i', 'o', 'u')):
                        eg_name += "nese"
                    else:
                        eg_name += 'ish'
                    egs[eg_name] = c.languages[eg]
                except ValueError:
                    egs[eg] = c.languages[eg]
            c.languages = egs
    else:
        # Using country data files instead of random generation
        print("Loading countries...")
        for fn in os.listdir(args.country_data):
            with open(args.country_data + fn, 'r') as f:
                country_data = json.load(f)
            f.close()
            countries.append(context.Country(fs_gen, **country_data))

    # Load or create actor data
    print("Creating threat actors...")
    with open(pkg_resources.resource_filename(__name__,
                                              "assets/stix_vocab.json"),
              encoding='utf-8') as json_file:
        stix_vocab = json.load(json_file)
    json_file.close()
    if config['agents']['randomize_threat_actors'] is True:
        apt_store = fs_gen
        with open(pkg_resources.resource_filename(
                __name__,
                config['agents']['random_variables']['actor_name_1']),
                  encoding='utf-8') as f:
            adjectives = [line.rstrip() for line in f]
        f.close()
        with open(pkg_resources.resource_filename(
                __name__,
                config['agents']['random_variables']['actor_name_2']),
                  encoding='utf-8') as f:
            nouns = [line.rstrip() for line in f]
        f.close()
        actors = 1
        while actors <= config['agents']['random_variables']['num_agents']:
            agents.create_threatactor(stix_vocab, nouns, adjectives, countries,
                                      apt_store)
            actors += 1
    else:
        # no randomization - use provided data set
        if config['agents']['non_random_vars']['apt_data'] == "mitre_cti":
            apt_store = fs_real
        else:
            apt_store = FileSystemStore(
                config['agents']['non_random_vars']['apt_data'])

    # Create organizations
    print('Creating organizations...')
    with open(
            pkg_resources.resource_filename(
                __name__,
                config['agents']['org_variables']['org_names'])) as f:
        org_names = f.read().splitlines()  # organization name possibilities
    f.close()
    with open(pkg_resources.resource_filename(__name__,
                                              'assets/NIST_assess.json'),
              encoding='utf-8') as json_file:
        assessment = json.load(json_file)
    json_file.close()
    for c in countries:
        orgs = 0
        while orgs < config['agents']['org_variables']["orgs_per_country"]:
            agents.create_organization(stix_vocab, fs_gen, c, org_names,
                                       assessment)
            orgs += 1

    # Run simulation
    print('Running simulation...')
    start = datetime.strptime(config["simulation"]['time_range'][0],
                              '%Y-%m-%d')
    end = datetime.strptime(config["simulation"]['time_range'][1], '%Y-%m-%d')
    td = end - start
    actors = apt_store.query(Filter("type", "=", "intrusion-set"))
    orgs = fs_gen.query([
        Filter("type", "=", "identity"),
        Filter("identity_class", "=", "organization")
    ])
    tools = fs_real.query(Filter('type', '=', 'tool'))
    malwares = fs_real.query(Filter('type', '=', 'malware'))
    for r in range(1, int(config["simulation"]['number_of_rounds']) + 1):
        print(f'\tRound {r}')
        simulator.simulate(
            actors, orgs, tools, malwares, fs_gen, start,
            td.days / (config["simulation"]['number_of_rounds'] * len(actors)))
        start += timedelta(days=td.days /
                           config["simulation"]['number_of_rounds'])

    # Create output files
    print('Saving output...')
    # Map
    country_names = {}
    for country in countries:
        country_names[str(country.id)] = country.name
    try:
        map_matrix.plot_map(args.output, **country_names)
    except NameError:
        pass

    for ot in args.output_types:
        print(f'\t{ot}')
        path = args.output + "/" + ot
        if ot == "stix":
            shutil.copytree(temp_path, path)
        else:
            os.mkdir(path)
            os.mkdir(path + '/countries/')
            os.mkdir(path + '/actors/')
            os.mkdir(path + '/reports/')
            os.mkdir(path + '/organizations/')
            for country in countries:
                country.save(path + '/countries/', ot)
            apts = apt_store.query(Filter("type", "=", "intrusion-set"))
            for apt in apts:
                agents.save(apt, path + '/actors/', ot, fs_gen, fs_real)
            events = fs_gen.query(Filter("type", "=", "sighting"))
            for e in events:
                simulator.save(e, apt_store, fs_real, path + '/reports/', ot)
            for org in orgs:
                agents.save_org(org, path + '/organizations/', ot, assessment)
        if ot == "html":
            html_src = pkg_resources.resource_filename(
                __name__, 'assets/html_templates')
            html_templates = os.listdir(html_src)
            for f in html_templates:
                shutil.copy(html_src + '/' + f, path)
            f = open(path + '/COUNTRY.html', 'r')
            c_template = f.read()
            f.close()
            for country in countries:
                f = open(path + '/countries/' + country.name + '.html', 'w')
                f.write(c_template.replace('COUNTRY', country.name))
                f.close()
            os.remove(path + '/COUNTRY.html')

    shutil.rmtree(temp_path)

    print('Done')