Пример #1
0
def main():
    parser = argparse.ArgumentParser(
        description="Test of adding event to MISP")
    parser.add_argument("mwdb_user", help="Mwdb username")
    parser.add_argument("mwdb_pass", help="Mwdb password")
    parser.add_argument("config", help="Config")
    parser.add_argument("misp_url", help="Misp url")
    parser.add_argument("misp_key", help="Misp key")
    args = parser.parse_args()

    mwdb = Malwarecage()
    mwdb.login(args.mwdb_user, args.mwdb_pass)

    try:
        cfg = mwdb.query_config(args.config)
        iocs = parse(cfg.family, cfg.cfg)
    except FamilyNotSupportedYetError:
        logging.info("Family %s not supported yet...", cfg.family)
        return

    if not iocs:
        # Nothing actionable found - skip the config
        return

    event = MISPEvent()
    event.add_tag(f"mwdb:family:{cfg.family}")
    event.info = f"Malware configuration ({cfg.family})"

    for o in iocs.to_misp():
        event.add_object(o)

    misp = PyMISP(args.misp_url, args.misp_key, False)
    misp.add_event(event)
Пример #2
0
 def import_all(self, stations_short_names, interval, data_type):
     object_creator = getattr(self, f'{interval}_flask_{data_type}')
     if data_type == 'co2':
         base_url = 'http://scrippsco2.ucsd.edu/assets/data/atmospheric/stations/flask_co2/'
     elif data_type in ['c13', 'o18']:
         base_url = 'http://scrippsco2.ucsd.edu/assets/data/atmospheric/stations/flask_isotopic/'
     for station in stations_short_names:
         url = f'{base_url}/{interval}/{interval}_flask_{data_type}_{station}.csv'
         infofield = f'[{station.upper()}] {interval} average atmospheric {data_type} concentrations'
         filepath = self.fetch(url)
         if not filepath:
             continue
         update = True
         event = self.get_existing_event_to_update(infofield)
         if event:
             location = event.get_objects_by_name('geolocation')[0]
         if not event:
             event = MISPEvent()
             event.info = infofield
             event.add_tag(getattr(self, f'tag_{station}')())
             location = getattr(self, f'geolocation_{station}')()
             event.add_object(location)
             event.add_attribute('link', f'http://scrippsco2.ucsd.edu/data/atmospheric_co2/{station}')
             update = False
         object_creator(event, location, filepath, update)
         if update:
             self.misp.update_event(event)
         else:
             self.misp.add_event(event)
Пример #3
0
def create_new_event():
    me = MISPEvent()
    me.info = "Fail2Ban blocking"
    me.add_tag(args.tag)
    start = datetime.now()
    me.add_attribute('datetime', start.isoformat(), comment='Start Time')
    return me
Пример #4
0
    def create_complex_event(self):
        event = MISPEvent()
        event.info = 'Complex Event'
        event.distribution = Distribution.all_communities
        event.add_tag('tlp:white')

        event.add_attribute('ip-src', '8.8.8.8')
        event.add_attribute('ip-dst', '8.8.8.9')
        event.add_attribute('domain', 'google.com')
        event.add_attribute('md5', '3c656da41f4645f77e3ec3281b63dd43')

        event.attributes[0].distribution = Distribution.your_organisation_only
        event.attributes[1].distribution = Distribution.this_community_only
        event.attributes[2].distribution = Distribution.connected_communities

        event.attributes[0].add_tag('tlp:red')
        event.attributes[1].add_tag('tlp:amber')
        event.attributes[2].add_tag('tlp:green')

        obj = MISPObject('file')

        obj.distribution = Distribution.connected_communities
        obj.add_attribute('filename', 'testfile')
        obj.add_attribute('md5', '3c656da41f4645f77e3ec3281b63dd44')
        obj.attributes[0].distribution = Distribution.your_organisation_only

        event.add_object(obj)

        return event
Пример #5
0
def create_new_event():
    me = MISPEvent()
    me.info = "Fail2Ban blocking"
    me.add_tag(args.tag)
    start = datetime.now()
    me.add_attribute('datetime', start.isoformat(), comment='Start Time')
    return me
Пример #6
0
 def create_new_event(self, entry):
     attribute = MISPAttribute()
     attribute.type = "malware-sample"
     attribute.value = entry["shasum"]
     attribute.data = Path(entry["outfile"])
     attribute.comment = "File uploaded to Cowrie ({})".format(entry["sensor"])
     attribute.expand = "binary"
     if "url" in entry:
         attributeURL = MISPAttribute()
         attributeURL.type = "url"
         attributeURL.value = entry["url"]
         attributeURL.to_ids = True
     else:
         attributeURL = MISPAttribute()
         attributeURL.type = "text"
         attributeURL.value = "External upload"
     attributeIP = MISPAttribute()
     attributeIP.type = "ip-src"
     attributeIP.value = entry["src_ip"]
     attributeDT = MISPAttribute()
     attributeDT.type = "datetime"
     attributeDT.value = entry["timestamp"]
     event = MISPEvent()
     event.info = "File uploaded to Cowrie ({})".format(entry["sensor"])
     event.add_tag("tlp:white")
     event.attributes = [attribute, attributeURL, attributeIP, attributeDT]
     event.run_expansions()
     if self.publish:
         event.publish()
     result = self.misp_api.add_event(event)
     if self.debug:
         log.msg(f"Event creation result: \n{result}")
Пример #7
0
    def create_daily_event(self):
        today = datetime.date.today()
        # [0-3]
        distribution = 0
        info = "Daily AIL-leaks {}".format(today)
        # [0-2]
        analysis = 0
        # [1-4]
        threat = 3
        published = False
        org_id = None
        orgc_id = None
        sharing_group_id = None
        date = None

        event = MISPEvent()
        event.distribution = distribution
        event.info = info
        event.analysis = analysis
        event.threat = threat
        event.published = published

        event.add_tag('infoleak:output-format="ail-daily"')
        existing_event = self.pymisp.add_event(event)
        return existing_event
Пример #8
0
 def submit_tf_update(misp: ExpandedPyMISP, attributes: list) -> MISPEvent:
     """
         create/update abuse.ch MISP-Event and append the new attributes
     """
     eventinfo = event_info_template.format(
         datetime.now().strftime(info_dateformat))
     # logging.debug(eventinfo)
     events = misp.search(controller='events',
                          eventinfo=eventinfo,
                          org=1,
                          pythonify=True)
     if events:  # current event exists already
         event = events[0]
     else:  # create a new event
         event = MISPEvent()
         event.distribution = event_distribution
         event.threat_level_id = event_threat_level
         event.analysis = 2
         event.info = eventinfo
         for tag in tagging:
             event.add_tag(tag)
         event = misp.add_event(event, pythonify=True)
     for att in attributes:
         event.add_attribute(**att)
     event.published = autopublish
     return misp.update_event(event)
Пример #9
0
    def process(self, task: Task) -> None:  # type: ignore
        config = task.get_payload("config")
        family = task.headers["family"]
        dhash = config_dhash(config)

        # Parse the config using iocextract library
        iocs = parse(family, config)

        if not iocs:
            # Nothing actionable found - skip the config
            return

        # Upload structured data to MISP
        event = MISPEvent()
        event.uuid = str(uuid5(self.CONFIG_NAMESPACE, dhash))
        event.add_tag(f"mwdb:family:{family}")
        event.info = f"Malware configuration ({family})"

        if self.mwdb_url is not None:
            event.add_attribute("link", f"{self.mwdb_url}/config/{dhash}")

        for o in iocs.to_misp():
            event.add_object(o)

        misp = ExpandedPyMISP(self.misp_url, self.misp_key, self.misp_verifycert)
        misp.add_event(event)
Пример #10
0
    def processEvent(self, event):
        mevent = MISPEvent()
        mevent.from_dict(Event=event)
        changed = False

        for attr in mevent.attributes:
            if (attr["type"] == "ip-dst"
                    or attr["type"] == "ip-src") and attr["to_ids"]:
                print("Removing IDS flag in event '{}' on attr '{}'".format(
                    mevent.id, attr["value"]))
                changed = True
                attr["to_ids"] = False

        if changed:
            mevent.add_tag(self.expiredTag)
            res = self.misp.update_event(mevent.id, mevent)
Пример #11
0
 def generate_event(self, modsecLog: ModsecLog) -> MISPEvent:
     event = MISPEvent()
     event.info = modsecLog.generateInfoLine()
     [http_method, url, http_version] = modsecLog.getRequestLine()
     event.add_attribute(type='ip-src|port', value=str(modsecLog.log['transaction']['remote_address']) + "|" + str(modsecLog.log['transaction']['remote_port']), comment="Attacker", pythonify=True)
     event.add_attribute(type='ip-dst|port', value=str(modsecLog.log['transaction']['local_address']) + "|" + str(modsecLog.log['transaction']['local_port']), comment="Server", pythonify=True)
     event.add_attribute(type='http-method', value=http_method, pythonify=True)
     event.add_attribute(type='url', value=url, pythonify=True)
     event.add_attribute(type='datetime', value=modsecLog.log['@timestamp'], pythonify=True)
     event.add_attribute(type='other', value=modsecLog.log['audit_data']['producer'], comment="Producer", pythonify=True)
     event.add_attribute(type='text', value=json.dumps(modsecLog.log, indent=2), comment="Json log", pythonify=True)
     #event.add_attribute(type='vulnerability', value=json_log['transaction']['time'], pythonify=True)
     for tag in self.tags:
         event.add_tag(tag)
     log.debug("elasticsearch data")
     log.debug(json.dumps(modsecLog.log, indent=2))
     return event
Пример #12
0
def make_new_event(misp):
    LOGGER.info('Creating new fixed event...')
    event = MISPEvent()

    timestamp = datetime.utcnow()
    event_date = timestamp.strftime('%Y-%m-%d')
    event.info = MISP_EVENT_TITLE
    event.analysis = Analysis.completed
    event.distribution = Distribution.your_organisation_only
    event.threat_level_id = ThreatLevel.low

    event.add_tag('type:OSINT')
    event.add_tag('tlp:white')

    LOGGER.info('Saving event...')
    time.sleep(1)
    return misp.add_event(event, pythonify=True)
Пример #13
0
    def environment(self):
        first_event = MISPEvent()
        first_event.info = 'First event - org only - low - completed'
        first_event.distribution = Distribution.your_organisation_only
        first_event.threat_level_id = ThreatLevel.low
        first_event.analysis = Analysis.completed
        first_event.set_date("2017-12-31")
        first_event.add_attribute('text', str(uuid4()))
        first_event.attributes[0].add_tag('admin_only')
        first_event.attributes[0].add_tag('tlp:white___test')
        first_event.add_attribute('text', str(uuid4()))
        first_event.attributes[1].add_tag('unique___test')

        second_event = MISPEvent()
        second_event.info = 'Second event - org only - medium - ongoing'
        second_event.distribution = Distribution.your_organisation_only
        second_event.threat_level_id = ThreatLevel.medium
        second_event.analysis = Analysis.ongoing
        second_event.set_date("Aug 18 2018")
        second_event.add_attribute('text', str(uuid4()))
        second_event.attributes[0].add_tag('tlp:white___test')
        second_event.add_attribute('ip-dst', '1.1.1.1')
        # Same value as in first event.
        second_event.add_attribute('text', first_event.attributes[0].value)

        third_event = MISPEvent()
        third_event.info = 'Third event - all orgs - high - initial'
        third_event.distribution = Distribution.all_communities
        third_event.threat_level_id = ThreatLevel.high
        third_event.analysis = Analysis.initial
        third_event.set_date("Jun 25 2018")
        third_event.add_tag('tlp:white___test')
        third_event.add_attribute('text', str(uuid4()))
        third_event.attributes[0].add_tag('tlp:amber___test')
        third_event.attributes[0].add_tag('foo_double___test')
        third_event.add_attribute('ip-src', '8.8.8.8')
        third_event.attributes[1].add_tag('tlp:amber___test')
        third_event.add_attribute('ip-dst', '9.9.9.9')

        # Create first and third event as admin
        # usr won't be able to see the first one
        first = self.admin_misp_connector.add_event(first_event)
        third = self.admin_misp_connector.add_event(third_event)
        # Create second event as user
        second = self.user_misp_connector.add_event(second_event)
        return first, second, third
Пример #14
0
    def _create_event(self, artifact: Type[Artifact]) -> MISPEvent:
        """Create an event in MISP, return an Event object."""
        event = MISPEvent()
        event.info = self.event_info.format(source_name=artifact.source_name)

        # Add tags.
        for tag in self.tags:
            event.add_tag(tag)

        # Add references.
        if artifact.reference_link != "":
            event.add_attribute("link", artifact.reference_link)
        if artifact.reference_text != "":
            event.add_attribute("text", artifact.reference_text)
        if artifact.source_name != "" and self.include_artifact_source_name:
            event.add_attribute("other", f"source:{artifact.source_name}")

        return event
    def _create_new_event(self, org_uuid) -> MISPEvent:
        """ Creates new MISP event. """
        misp_event = MISPEvent()
        # TODO turn on correct organization assignment
        # misp_event.orgc = self.misp_inst.get_organisation(org_uuid, pythonify=True)
        # completed
        misp_event.analysis = 2
        # low
        misp_event.threat_level_id = 3
        # TODO use sharing group instead
        misp_event.distribution = 1
        # misp_event.distribution = 4
        # misp_event.sharing_group_id = 2
        misp_event.uuid = self.uuid_generator.get_misp_event_uuid(org_uuid)

        misp_event.add_tag("rsit:test")
        misp_event.add_tag("tlp:amber")

        misp_event.info = "CTI - IntelMQ feed"
        return self.misp_inst.add_event(misp_event, pythonify=True)
Пример #16
0
def register_misp(misp: ExpandedPyMISP, misp_event_dict: dict) -> None:
    """ MISPイベントデータを受け取り、MISPに登録する """

    misp_event = MISPEvent()
    misp_event.from_dict(**misp_event_dict)
    threat_level_id = misp_event.get('threat_level_id')
    threat_level = f'anyrun:threat_level:{threat_level_id}'
    misp_event.add_tag('anyrun')
    misp_event.add_tag(threat_level)

    retry_count = 0
    while True:
        try:
            # pymispをインスタンス化してイベント登録
            event_data = misp.add_event(misp_event)
            if event_data.get('errors'):
                raise Exception(event_data['errors'])

            # MISPに登録されるイベントIDを取得し、出力
            event_id = event_data['Event']['id']
            print(f'新規に登録されたEvent_ID: {event_id}')

            return

        except:
            except_return = traceback.format_exc()

            # インポート済みの場合
            if const.DUPLICATE_EVENT_CONFIRM_WORD in except_return:
                print('Importを行おうとしたイベントは既にMISPに登録されています')
                return

            # リトライ回数チェック
            retry_count += 1
            if retry_count >= const.RETRY_MAXIMUM_LIMIT:
                raise

            # インターバル処理
            print('MISPへのイベントインポートをリトライします')
            time.sleep(const.COMMAND_INTERVAL_TIME)
Пример #17
0
def create_event(FEED, type):
    #'uuid', 'info', 'threat_level_id', 'analysis', 'timestamp','publish_timestamp', 'published', 'date', 'extends_uuid'}
    event = MISPEvent()
    event.info = f"[{datetime.now().date().isoformat()}] RST Cloud Daily {type} feed"

    event.analysis = 2  # 0=initial; 1=ongoing; 2=completed
    event.threat_level_id = 2  #1 = high ; 2 = medium; 3 = low; 4 = undefined
    event.add_tag('tlp:white')

    # add to the database and publish
    event = misp.add_event(event)
    if PUBLISH:
        misp.publish(event)

    # add attributes to the newly created event
    for entry in FEED:
        if type == 'Domain':
            misp.add_attribute(
                event, domain_attribute('Network activity', 'domain', entry))
        if type == 'IP':
            misp.add_attribute(
                event, ip_attribute('Network activity', 'ip-dst', entry))
Пример #18
0
    def inserta_misp(nombre_evento, full_tweet, fverbose):
        #Instancio evento MISP
        event = MISPEvent()

        #Nombre del evento. Se cambiara por cada tweet recibido
        event.info = nombre_evento  # Required
        #Valores por defecto

        event.distribution = 0  # Optional, defaults to MISP.default_event_distribution in MISP config
        event.threat_level_id = 2  # Optional, defaults to MISP.default_event_threat_level in MISP config
        event.analysis = 1  # Optional, defaults to 0 (initial analysis)

        #Inserto el tweet completo
        #event.add_attribute('External analysis', full_tweet)
        event.add_attribute('text', full_tweet)

        event.add_tag('tlp:white')

        add_tweet_atributes(event, full_tweet, fverbose)

        #Inserto el evento MISP
        event = misp.add_event(event, pythonify=True)
Пример #19
0
    def import_all(self, stations_short_names, interval, data_type):
        object_creator = getattr(self, f'{interval}_flask_{data_type}')
        if data_type == 'co2':
            base_url = 'https://scrippsco2.ucsd.edu/assets/data/atmospheric/stations/flask_co2/'
        elif data_type in ['c13', 'o18']:
            base_url = 'https://scrippsco2.ucsd.edu/assets/data/atmospheric/stations/flask_isotopic/'
        for station in stations_short_names:
            url = f'{base_url}/{interval}/{interval}_flask_{data_type}_{station}.csv'
            infofield = f'[{station.upper()}] {interval} average atmospheric {data_type} concentrations'
            filepath = self.fetch(url)
            if not filepath:
                continue
            if infofield in self.scrippts_meta:
                event = MISPEvent()
                event.load_file(str(self.output_dir / self.scrippts_meta[infofield]))
                location = event.get_objects_by_name('geolocation')[0]
                update = True
            else:
                event = MISPEvent()
                event.uuid = str(uuid4())
                event.info = infofield
                event.Orgc = self.misp_org
                event.add_tag(getattr(self, f'tag_{station}')())
                location = getattr(self, f'geolocation_{station}')()
                event.add_object(location)
                event.add_attribute('link', f'https://scrippsco2.ucsd.edu/data/atmospheric_co2/{station}')
                update = False
                with self.scrippts_meta_file.open('a') as f:
                    writer = csv.writer(f)
                    writer.writerow([infofield, f'{event.uuid}.json'])

            object_creator(event, location, filepath, update)
            if update:
                # Bump the publish timestamp
                event.publish_timestamp = datetime.datetime.timestamp(datetime.datetime.now())
            feed_output = event.to_feed(with_meta=False)
            with (self.output_dir / f'{event.uuid}.json').open('w') as f:
                # json.dump(feed_output, f, indent=2, sort_keys=True)  # For testing
                json.dump(feed_output, f)
Пример #20
0
def make_new_event(misp):
    LOGGER.info('Creating new fixed event...')
    event = MISPEvent()
    event_date = datetime.now().strftime('%Y-%m-%d')
    event_title = '{0} {1}'.format(MISP_EVENT_TITLE, event_date)

    event.info = event_title
    event.analysis = Analysis.completed
    event.distribution = Distribution.your_organisation_only
    event.threat_level_id = ThreatLevel.low

    event.add_tag('type:OSINT')
    event.add_tag('tlp:white')

    LOGGER.info('Saving event...')
    time.sleep(1)

    try:
        new_event = misp.add_event(event, pythonify=True)
        return new_event

    except Exception as ex:
        LOGGER.error('Failed to make MISP event: {0}'.format(str(ex)))
        return False
Пример #21
0
class TestMISPEvent(unittest.TestCase):
    def setUp(self):
        self.maxDiff = None
        self.mispevent = MISPEvent()

    def init_event(self):
        self.mispevent.info = 'This is a test'
        self.mispevent.distribution = 1
        self.mispevent.threat_level_id = 1
        self.mispevent.analysis = 1
        self.mispevent.set_date("2017-12-31")  # test the set date method

    def test_simple(self):
        with open('tests/mispevent_testfiles/simple.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_event(self):
        self.init_event()
        self.mispevent.publish()
        with open('tests/mispevent_testfiles/event.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_loadfile(self):
        self.mispevent.load_file('tests/mispevent_testfiles/event.json')
        with open('tests/mispevent_testfiles/event.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_event_tag(self):
        self.init_event()
        self.mispevent.add_tag('bar')
        self.mispevent.add_tag(name='baz')
        new_tag = MISPTag()
        new_tag.from_dict(name='foo')
        self.mispevent.add_tag(new_tag)
        with open('tests/mispevent_testfiles/event_tags.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_attribute(self):
        self.init_event()
        a = self.mispevent.add_attribute('filename', 'bar.exe')
        del a.uuid
        a = self.mispevent.add_attribute_tag('osint', 'bar.exe')
        attr_tags = self.mispevent.get_attribute_tag('bar.exe')
        self.assertEqual(self.mispevent.attributes[0].tags[0].name, 'osint')
        self.assertEqual(attr_tags[0].name, 'osint')
        with open('tests/mispevent_testfiles/attribute.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))
        # Fake setting an attribute ID for testing
        self.mispevent.attributes[0].id = 42
        self.mispevent.delete_attribute(42)
        with open('tests/mispevent_testfiles/attribute_del.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_object_tag(self):
        self.mispevent.add_object(name='file', strict=True)
        a = self.mispevent.objects[0].add_attribute('filename', value='')
        self.assertEqual(a, None)
        a = self.mispevent.objects[0].add_attribute('filename', value=None)
        self.assertEqual(a, None)
        a = self.mispevent.objects[0].add_attribute('filename',
                                                    value='bar',
                                                    Tag=[{
                                                        'name': 'blah'
                                                    }])
        del a.uuid
        self.assertEqual(self.mispevent.objects[0].attributes[0].tags[0].name,
                         'blah')
        self.assertTrue(self.mispevent.objects[0].has_attributes_by_relation(
            ['filename']))
        self.assertEqual(
            len(self.mispevent.objects[0].get_attributes_by_relation(
                'filename')), 1)
        self.mispevent.add_object(name='url', strict=True)
        a = self.mispevent.objects[1].add_attribute(
            'url', value='https://www.circl.lu')
        del a.uuid
        self.mispevent.objects[0].uuid = 'a'
        self.mispevent.objects[1].uuid = 'b'
        reference = self.mispevent.objects[0].add_reference(
            self.mispevent.objects[1], 'baz', comment='foo')
        del reference.uuid
        self.assertEqual(
            self.mispevent.objects[0].references[0].relationship_type, 'baz')
        with open('tests/mispevent_testfiles/event_obj_attr_tag.json',
                  'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    @unittest.skip(
        "Not supported on MISP: https://github.com/MISP/MISP/issues/2638 - https://github.com/MISP/PyMISP/issues/168"
    )
    def test_object_level_tag(self):
        self.mispevent.add_object(name='file', strict=True)
        self.mispevent.objects[0].add_attribute('filename', value='bar')
        self.mispevent.objects[0].add_tag('osint')
        self.mispevent.objects[0].uuid = 'a'
        with open('tests/mispevent_testfiles/event_obj_tag.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_malware(self):
        with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
            pseudofile = BytesIO(f.read())
        self.init_event()
        a = self.mispevent.add_attribute('malware-sample',
                                         'bar.exe',
                                         data=pseudofile)
        del a.uuid
        attribute = self.mispevent.attributes[0]
        self.assertEqual(attribute.malware_binary, pseudofile)
        with open('tests/mispevent_testfiles/malware.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_existing_malware(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/malware_exist.json')
        with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
            pseudofile = BytesIO(f.read())
        self.assertEqual(
            self.mispevent.objects[0].get_attributes_by_relation(
                'malware-sample')[0].malware_binary.read(), pseudofile.read())

    def test_sighting(self):
        sighting = MISPSighting()
        sighting.from_dict(value='1', type='bar', timestamp=11111111)
        with open('tests/mispevent_testfiles/sighting.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(sighting.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_existing_event(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        with open('tests/mispevent_testfiles/existing_event.json', 'r') as f:
            ref_json = json.load(f)

        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_shadow_attributes_existing(self):
        self.mispevent.load_file('tests/mispevent_testfiles/shadow.json')
        with open('tests/mispevent_testfiles/shadow.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    @unittest.skip("Not supported on MISP.")
    def test_shadow_attributes(self):
        self.init_event()
        p = self.mispevent.add_proposal(type='filename', value='baz.jpg')
        del p.uuid
        a = self.mispevent.add_attribute('filename', 'bar.exe')
        del a.uuid
        p = self.mispevent.attributes[0].add_proposal(type='filename',
                                                      value='bar.pdf')
        del p.uuid
        with open('tests/mispevent_testfiles/proposals.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_default_attributes(self):
        self.mispevent.add_object(name='file', strict=True)
        a = self.mispevent.objects[0].add_attribute('filename',
                                                    value='bar',
                                                    Tag=[{
                                                        'name': 'blah'
                                                    }])
        del a.uuid
        a = self.mispevent.objects[0].add_attribute('pattern-in-file',
                                                    value='baz')
        self.assertEqual(a.category, 'Artifacts dropped')
        del a.uuid
        self.mispevent.add_object(name='file',
                                  strict=False,
                                  default_attributes_parameters=self.mispevent.
                                  objects[0].attributes[0])
        a = self.mispevent.objects[1].add_attribute('filename', value='baz')
        del a.uuid
        self.mispevent.objects[0].uuid = 'a'
        self.mispevent.objects[1].uuid = 'b'
        with open('tests/mispevent_testfiles/event_obj_def_param.json',
                  'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_obj_default_values(self):
        self.init_event()
        self.mispevent.add_object(name='whois', strict=True)
        a = self.mispevent.objects[0].add_attribute(
            'registrar', value='registar.example.com')
        del a.uuid
        a = self.mispevent.objects[0].add_attribute('domain',
                                                    value='domain.example.com')
        del a.uuid
        a = self.mispevent.objects[0].add_attribute('nameserver',
                                                    value='ns1.example.com')
        del a.uuid
        a = self.mispevent.objects[0].add_attribute(
            'nameserver',
            value='ns2.example.com',
            disable_correlation=False,
            to_ids=True,
            category='External analysis')
        del a.uuid
        self.mispevent.objects[0].uuid = 'a'
        with open('tests/mispevent_testfiles/def_param.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_event_not_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)

    def test_event_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.mispevent.info = 'blah'
        self.assertTrue(self.mispevent.edited)

    def test_event_tag_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.add_tag('foo')
        self.assertTrue(self.mispevent.edited)

    def test_event_attribute_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.mispevent.attributes[0].value = 'blah'
        self.assertTrue(self.mispevent.attributes[0].edited)
        self.assertFalse(self.mispevent.attributes[1].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_attribute_tag_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.attributes[0].tags[0].name = 'blah'
        self.assertTrue(self.mispevent.attributes[0].tags[0].edited)
        self.assertFalse(self.mispevent.attributes[0].tags[1].edited)
        self.assertTrue(self.mispevent.attributes[0].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_attribute_tag_edited_second(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.attributes[0].add_tag(name='blah')
        self.assertTrue(self.mispevent.attributes[0].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_object_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.objects[0].comment = 'blah'
        self.assertTrue(self.mispevent.objects[0].edited)
        self.assertFalse(self.mispevent.objects[1].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_object_attribute_edited(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.objects[0].attributes[0].comment = 'blah'
        self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
        self.assertTrue(self.mispevent.objects[0].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_object_attribute_edited_tag(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.objects[0].attributes[0].add_tag('blah')
        self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
        self.assertTrue(self.mispevent.objects[0].edited)
        self.assertTrue(self.mispevent.edited)
        with open('tests/mispevent_testfiles/existing_event_edited.json',
                  'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))

    def test_obj_by_id(self):
        self.mispevent.load_file(
            'tests/mispevent_testfiles/existing_event.json')
        misp_obj = self.mispevent.get_object_by_id(1556)
        self.assertEqual(misp_obj.uuid, '5a3cd604-e11c-4de5-bbbf-c170950d210f')

    def test_userdefined_object(self):
        self.init_event()
        self.mispevent.add_object(
            name='test_object_template',
            strict=True,
            misp_objects_path_custom='tests/mispevent_testfiles')
        with self.assertRaises(InvalidMISPObject) as e:
            # Fail on required
            self.mispevent.to_json(sort_keys=True, indent=2)
        if sys.version_info >= (3, ):
            self.assertEqual(e.exception.message,
                             '{\'member3\'} are required.')
        else:
            # Python2 bullshit
            self.assertEqual(e.exception.message,
                             'set([u\'member3\']) are required.')

        a = self.mispevent.objects[0].add_attribute('member3', value='foo')
        del a.uuid
        with self.assertRaises(InvalidMISPObject) as e:
            # Fail on requiredOneOf
            self.mispevent.to_json(sort_keys=True, indent=2)
        self.assertEqual(
            e.exception.message,
            'At least one of the following attributes is required: member1, member2'
        )

        a = self.mispevent.objects[0].add_attribute('member1', value='bar')
        del a.uuid
        a = self.mispevent.objects[0].add_attribute('member1', value='baz')
        del a.uuid
        with self.assertRaises(InvalidMISPObject) as e:
            # member1 is not a multiple
            self.mispevent.to_json(sort_keys=True, indent=2)
        self.assertEqual(e.exception.message,
                         'Multiple occurrences of member1 is not allowed')

        self.mispevent.objects[0].attributes = self.mispevent.objects[
            0].attributes[:2]
        self.mispevent.objects[0].uuid = 'a'
        with open('tests/mispevent_testfiles/misp_custom_obj.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(sort_keys=True, indent=2),
                         json.dumps(ref_json, sort_keys=True, indent=2))
Пример #22
0
class StixParser():
    def __init__(self):
        super(StixParser, self).__init__()
        self.misp_event = MISPEvent()
        self.misp_event['Galaxy'] = []

    def load_data(self, filename, version, event, args):
        self.filename = filename
        self.stix_version = version
        self.event = event
        if args and args[0] is not None:
            self.add_original_file(args[0])
        try:
            event_distribution = args[1]
            if not isinstance(event_distribution, int):
                event_distribution = int(
                    event_distribution) if event_distribution.isdigit() else 5
        except IndexError:
            event_distribution = 5
        try:
            attribute_distribution = args[2]
            if attribute_distribution != 'event' and not isinstance(
                    attribute_distribution, int):
                attribute_distribution = int(
                    attribute_distribution) if attribute_distribution.isdigit(
                    ) else 5
        except IndexError:
            attribute_distribution = 5
        self.misp_event.distribution = event_distribution
        self._attribute_distribution = event_distribution if attribute_distribution == 'event' else attribute_distribution

    def add_original_file(self, original_filename):
        with open(self.filename, 'rb') as f:
            sample = b64encode(f.read()).decode('utf-8')
        original_file = MISPObject('original-imported-file')
        original_file.add_attribute(
            **{
                'type': 'attachment',
                'value': original_filename,
                'object_relation': 'imported-sample',
                'data': sample
            })
        original_file.add_attribute(
            **{
                'type': 'text',
                'object_relation': 'format',
                'value': self.stix_version
            })
        self.misp_event.add_object(**original_file)

    def general_handler(self):
        self.outputname = '{}.stix2'.format(self.filename)
        self.buildMISPDict()
        self.set_distribution()

    def buildMISPDict(self):
        report_attributes = defaultdict(set)
        for _, report in self.event['report'].items():
            report_attributes['orgs'].add(
                report['created_by_ref'].split('--')[1])
            report_attributes['name'].add(report['name'])
            if report.get('published'):
                report_attributes['published'].add(report['published'])
            if 'labels' in report:
                report_attributes['labels'].update(
                    [l for l in report['labels']])
            if 'external_references' in report:
                self.add_links(report['external_references'])
            for ref in report['object_refs']:
                if 'relationship' not in ref:
                    object_type, uuid = ref.split('--')
                    object2parse = self.event[object_type][uuid]
                    self.parsing_process(object2parse, object_type)
        if len(report_attributes['orgs']) == 1:
            identity = self.event['identity'][report_attributes['orgs'].pop()]
            self.misp_event['Org'] = {'name': identity['name']}
        if len(report_attributes['published']) == 1:
            self.misp_event.publish_timestamp = self.getTimestampfromDate(
                report_attributes['published'].pop())
        if len(report_attributes['name']) == 1:
            self.misp_event.info = report_attributes['name'].pop()
        else:
            self.misp_event.info = "Imported with MISP import script for {}.".format(
                self.stix_version)
        for l in report_attributes['labels']:
            self.misp_event.add_tag(l)

    def set_distribution(self):
        for attribute in self.misp_event.attributes:
            attribute.distribution = self._attribute_distribution
        for misp_object in self.misp_event.objects:
            misp_object.distribution = self._attribute_distribution
            for attribute in misp_object.attributes:
                attribute.distribution = self._attribute_distribution

    def saveFile(self):
        eventDict = self.misp_event.to_json()
        outputfile = '{}.stix2'.format(self.filename)
        with open(outputfile, 'w') as f:
            f.write(eventDict)

    def add_links(self, refs):
        for e in refs:
            link = {"type": "link"}
            comment = e.get('source_name')
            try:
                comment = comment.split('url - ')[1]
            except IndexError:
                pass
            if comment:
                link['comment'] = comment
            link['value'] = e.get('url')
            self.misp_event.add_attribute(**link)

    @staticmethod
    def getTimestampfromDate(stix_date):
        try:
            return int(stix_date.timestamp())
        except AttributeError:
            return int(
                time.mktime(
                    time.strptime(
                        stix_date.split('+')[0], "%Y-%m-%d %H:%M:%S")))

    @staticmethod
    def get_misp_type(labels):
        return labels[0].split('=')[1][1:-1]

    @staticmethod
    def get_misp_category(labels):
        return labels[1].split('=')[1][1:-1]

    def parse_course_of_action(self, o):
        misp_object = MISPObject('course-of-action')
        if 'name' in o:
            attribute = {
                'type': 'text',
                'object_relation': 'name',
                'value': o.get('name')
            }
            misp_object.add_attribute(**attribute)
        else:
            return
        if 'description' in o:
            attribute = {
                'type': 'text',
                'object_relation': 'description',
                'value': o.get('description')
            }
            misp_object.add_attribute(**attribute)
        self.misp_event.add_object(**misp_object)

    @staticmethod
    def parse_observable(observable, attribute_type):
        return misp_types_mapping[attribute_type](observable, attribute_type)

    @staticmethod
    def parse_pattern(pattern):
        if ' AND ' in pattern:
            pattern_parts = pattern.split(' AND ')
            if len(pattern_parts) == 3:
                _, value1 = pattern_parts[2].split(' = ')
                _, value2 = pattern_parts[0].split(' = ')
                return '{}|{}'.format(value1[1:-2], value2[1:-1])
            else:
                _, value1 = pattern_parts[0].split(' = ')
                _, value2 = pattern_parts[1].split(' = ')
                if value1 in ("'ipv4-addr'", "'ipv6-addr'"):
                    return value2[1:-2]
                return '{}|{}'.format(value1[1:-1], value2[1:-2])
        else:
            return pattern.split(' = ')[1][1:-2]

    def parse_pattern_with_data(self, pattern):
        if 'artifact:payload_bin' not in pattern:
            return self.parse_pattern(pattern)
        pattern_parts = pattern.split(' AND ')
        if len(pattern_parts) == 3:
            filename = pattern_parts[0].split(' = ')[1]
            md5 = pattern_parts[1].split(' = ')[1]
            return "{}|{}".format(
                filename[1:-1],
                md5[1:-1]), pattern_parts[2].split(' = ')[1][1:-2]
        return pattern_parts[0].split(' = ')[1][1:-1], pattern_parts[1].split(
            ' = ')[1][1:-2]
Пример #23
0
class AssemblyLineParser():
    def __init__(self):
        self.misp_event = MISPEvent()
        self.results = {}
        self.attribute = {'to_ids': True}
        self._results_mapping = {
            'NET_DOMAIN_NAME': 'domain',
            'NET_FULL_URI': 'url',
            'NET_IP': 'ip-dst'
        }
        self._file_mapping = {
            'entropy': {
                'type': 'float',
                'object_relation': 'entropy'
            },
            'md5': {
                'type': 'md5',
                'object_relation': 'md5'
            },
            'mime': {
                'type': 'mime-type',
                'object_relation': 'mimetype'
            },
            'sha1': {
                'type': 'sha1',
                'object_relation': 'sha1'
            },
            'sha256': {
                'type': 'sha256',
                'object_relation': 'sha256'
            },
            'size': {
                'type': 'size-in-bytes',
                'object_relation': 'size-in-bytes'
            },
            'ssdeep': {
                'type': 'ssdeep',
                'object_relation': 'ssdeep'
            }
        }

    def get_submission(self, attribute, client):
        sid = attribute['value'].split('=')[-1]
        try:
            if not client.submission.is_completed(sid):
                self.results[
                    'error'] = 'Submission not completed, please try again later.'
                return
        except Exception as e:
            self.results[
                'error'] = f'Something went wrong while trying to check if the submission in AssemblyLine is completed: {e.__str__()}'
            return
        try:
            submission = client.submission.full(sid)
        except Exception as e:
            self.results[
                'error'] = f"Something went wrong while getting the submission from AssemblyLine: {e.__str__()}"
            return
        self._parse_report(submission)

    def finalize_results(self):
        if 'error' in self.results:
            return self.results
        event = json.loads(self.misp_event.to_json())
        results = {
            key: event[key]
            for key in ('Attribute', 'Object', 'Tag')
            if (key in event and event[key])
        }
        return {'results': results}

    def _create_attribute(self, result, attribute_type):
        attribute = MISPAttribute()
        attribute.from_dict(type=attribute_type,
                            value=result['value'],
                            **self.attribute)
        if result['classification'] != 'UNCLASSIFIED':
            attribute.add_tag(result['classification'].lower())
        self.misp_event.add_attribute(**attribute)
        return {
            'referenced_uuid': attribute.uuid,
            'relationship_type': '-'.join(result['context'].lower().split(' '))
        }

    def _create_file_object(self, file_info):
        file_object = MISPObject('file')
        filename_attribute = {'type': 'filename'}
        filename_attribute.update(self.attribute)
        if file_info['classification'] != "UNCLASSIFIED":
            tag = {'Tag': [{'name': file_info['classification'].lower()}]}
            filename_attribute.update(tag)
            for feature, attribute in self._file_mapping.items():
                attribute.update(tag)
                file_object.add_attribute(value=file_info[feature],
                                          **attribute)
            return filename_attribute, file_object
        for feature, attribute in self._file_mapping.items():
            file_object.add_attribute(value=file_info[feature], **attribute)
        return filename_attribute, file_object

    @staticmethod
    def _get_results(submission_results):
        results = defaultdict(list)
        for k, values in submission_results.items():
            h = k.split('.')[0]
            for t in values['result']['tags']:
                if t['context'] is not None:
                    results[h].append(t)
        return results

    def _get_scores(self, file_tree):
        scores = {}
        for h, f in file_tree.items():
            score = f['score']
            if score > 0:
                scores[h] = {'name': f['name'], 'score': score}
            if f['children']:
                scores.update(self._get_scores(f['children']))
        return scores

    def _parse_report(self, submission):
        if submission['classification'] != 'UNCLASSIFIED':
            self.misp_event.add_tag(submission['classification'].lower())
        filtered_results = self._get_results(submission['results'])
        scores = self._get_scores(submission['file_tree'])
        for h, results in filtered_results.items():
            if h in scores:
                attribute, file_object = self._create_file_object(
                    submission['file_infos'][h])
                print(file_object)
                for filename in scores[h]['name']:
                    file_object.add_attribute('filename',
                                              value=filename,
                                              **attribute)
                for reference in self._parse_results(results):
                    file_object.add_reference(**reference)
                self.misp_event.add_object(**file_object)

    def _parse_results(self, results):
        references = []
        for result in results:
            try:
                attribute_type = self._results_mapping[result['type']]
            except KeyError:
                continue
            references.append(self._create_attribute(result, attribute_type))
        return references
Пример #24
0
class TestMISPEvent(unittest.TestCase):

    def setUp(self):
        self.maxDiff = None
        self.mispevent = MISPEvent()

    def init_event(self):
        self.mispevent.info = 'This is a test'
        self.mispevent.distribution = 1
        self.mispevent.threat_level_id = 1
        self.mispevent.analysis = 1
        self.mispevent.set_date("2017-12-31")  # test the set date method

    def test_simple(self):
        with open('tests/mispevent_testfiles/simple.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_event(self):
        self.init_event()
        self.mispevent.publish()
        with open('tests/mispevent_testfiles/event.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_loadfile(self):
        self.mispevent.load_file('tests/mispevent_testfiles/event.json')
        with open('tests/mispevent_testfiles/event.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_event_tag(self):
        self.init_event()
        self.mispevent.add_tag('bar')
        self.mispevent.add_tag(name='baz')
        new_tag = MISPTag()
        new_tag.from_dict(name='foo')
        self.mispevent.add_tag(new_tag)
        with open('tests/mispevent_testfiles/event_tags.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_attribute(self):
        self.init_event()
        self.mispevent.add_attribute('filename', 'bar.exe')
        self.mispevent.add_attribute_tag('osint', 'bar.exe')
        attr_tags = self.mispevent.get_attribute_tag('bar.exe')
        self.assertEqual(self.mispevent.attributes[0].tags[0].name, 'osint')
        self.assertEqual(attr_tags[0].name, 'osint')
        with open('tests/mispevent_testfiles/attribute.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))
        # Fake setting an attribute ID for testing
        self.mispevent.attributes[0].id = 42
        self.mispevent.delete_attribute(42)
        with open('tests/mispevent_testfiles/attribute_del.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_object_tag(self):
        self.mispevent.add_object(name='file', strict=True)
        self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}])
        self.assertEqual(self.mispevent.objects[0].attributes[0].tags[0].name, 'blah')
        self.assertTrue(self.mispevent.objects[0].has_attributes_by_relation(['filename']))
        self.assertEqual(len(self.mispevent.objects[0].get_attributes_by_relation('filename')), 1)
        self.mispevent.add_object(name='url', strict=True)
        self.mispevent.objects[1].add_attribute('url', value='https://www.circl.lu')
        self.mispevent.objects[0].uuid = 'a'
        self.mispevent.objects[1].uuid = 'b'
        self.mispevent.objects[0].add_reference('b', 'baz', comment='foo')
        self.assertEqual(self.mispevent.objects[0].references[0].relationship_type, 'baz')
        with open('tests/mispevent_testfiles/event_obj_attr_tag.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    @unittest.skip("Not supported on MISP: https://github.com/MISP/MISP/issues/2638 - https://github.com/MISP/PyMISP/issues/168")
    def test_object_level_tag(self):
        self.mispevent.add_object(name='file', strict=True)
        self.mispevent.objects[0].add_attribute('filename', value='bar')
        self.mispevent.objects[0].add_tag('osint')
        self.mispevent.objects[0].uuid = 'a'
        with open('tests/mispevent_testfiles/event_obj_tag.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_malware(self):
        with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
            pseudofile = BytesIO(f.read())
        self.init_event()
        self.mispevent.add_attribute('malware-sample', 'bar.exe', data=pseudofile)
        attribute = self.mispevent.attributes[0]
        self.assertEqual(attribute.malware_binary, pseudofile)
        with open('tests/mispevent_testfiles/malware.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_existing_malware(self):
        self.mispevent.load_file('tests/mispevent_testfiles/malware_exist.json')
        with open('tests/mispevent_testfiles/simple.json', 'rb') as f:
            pseudofile = BytesIO(f.read())
        self.assertEqual(
            self.mispevent.objects[0].get_attributes_by_relation('malware-sample')[0].malware_binary.read(),
            pseudofile.read())

    def test_sighting(self):
        sighting = MISPSighting()
        sighting.from_dict(value='1', type='bar', timestamp=11111111)
        with open('tests/mispevent_testfiles/sighting.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(sighting.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_existing_event(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        with open('tests/mispevent_testfiles/existing_event.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_shadow_attributes_existing(self):
        self.mispevent.load_file('tests/mispevent_testfiles/shadow.json')
        with open('tests/mispevent_testfiles/shadow.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_shadow_attributes(self):
        self.init_event()
        self.mispevent.add_proposal(type='filename', value='baz.jpg')
        self.mispevent.add_attribute('filename', 'bar.exe')
        self.mispevent.attributes[0].add_proposal(type='filename', value='bar.pdf')
        with open('tests/mispevent_testfiles/proposals.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_default_attributes(self):
        self.mispevent.add_object(name='file', strict=True)
        self.mispevent.objects[0].add_attribute('filename', value='bar', Tag=[{'name': 'blah'}])
        self.mispevent.add_object(name='file', strict=False, default_attributes_parameters=self.mispevent.objects[0].attributes[0])
        self.mispevent.objects[1].add_attribute('filename', value='baz')
        self.mispevent.objects[0].uuid = 'a'
        self.mispevent.objects[1].uuid = 'b'
        with open('tests/mispevent_testfiles/event_obj_def_param.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_obj_default_values(self):
        self.init_event()
        self.mispevent.add_object(name='whois', strict=True)
        self.mispevent.objects[0].add_attribute('registrar', value='registar.example.com')
        self.mispevent.objects[0].add_attribute('domain', value='domain.example.com')
        self.mispevent.objects[0].add_attribute('nameserver', value='ns1.example.com')
        self.mispevent.objects[0].add_attribute('nameserver', value='ns2.example.com', disable_correlation=False, to_ids=True, category='External analysis')
        self.mispevent.objects[0].uuid = 'a'
        with open('tests/mispevent_testfiles/def_param.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_event_not_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)

    def test_event_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.mispevent.info = 'blah'
        self.assertTrue(self.mispevent.edited)

    def test_event_tag_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.add_tag('foo')
        self.assertTrue(self.mispevent.edited)

    def test_event_attribute_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.mispevent.attributes[0].value = 'blah'
        self.assertTrue(self.mispevent.attributes[0].edited)
        self.assertFalse(self.mispevent.attributes[1].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_attribute_tag_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.attributes[0].tags[0].name = 'blah'
        self.assertTrue(self.mispevent.attributes[0].tags[0].edited)
        self.assertFalse(self.mispevent.attributes[0].tags[1].edited)
        self.assertTrue(self.mispevent.attributes[0].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_attribute_tag_edited_second(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.attributes[0].add_tag(name='blah')
        self.assertTrue(self.mispevent.attributes[0].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_object_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.objects[0].comment = 'blah'
        self.assertTrue(self.mispevent.objects[0].edited)
        self.assertFalse(self.mispevent.objects[1].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_object_attribute_edited(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.objects[0].attributes[0].comment = 'blah'
        self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
        self.assertTrue(self.mispevent.objects[0].edited)
        self.assertTrue(self.mispevent.edited)

    def test_event_object_attribute_edited_tag(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        self.assertFalse(self.mispevent.edited)
        self.mispevent.objects[0].attributes[0].add_tag('blah')
        self.assertTrue(self.mispevent.objects[0].attributes[0].edited)
        self.assertTrue(self.mispevent.objects[0].edited)
        self.assertTrue(self.mispevent.edited)
        with open('tests/mispevent_testfiles/existing_event_edited.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))

    def test_obj_by_id(self):
        self.mispevent.load_file('tests/mispevent_testfiles/existing_event.json')
        misp_obj = self.mispevent.get_object_by_id(1556)
        self.assertEqual(misp_obj.uuid, '5a3cd604-e11c-4de5-bbbf-c170950d210f')

    def test_userdefined_object(self):
        self.init_event()
        self.mispevent.add_object(name='test_object_template', strict=True, misp_objects_path_custom='tests/mispevent_testfiles')
        with self.assertRaises(InvalidMISPObject) as e:
            # Fail on required
            self.mispevent.to_json()
        if sys.version_info >= (3, ):
            self.assertEqual(e.exception.message, '{\'member3\'} are required.')
        else:
            # Python2 bullshit
            self.assertEqual(e.exception.message, 'set([u\'member3\']) are required.')

        self.mispevent.objects[0].add_attribute('member3', value='foo')
        with self.assertRaises(InvalidMISPObject) as e:
            # Fail on requiredOneOf
            self.mispevent.to_json()
        self.assertEqual(e.exception.message, 'At least one of the following attributes is required: member1, member2')

        self.mispevent.objects[0].add_attribute('member1', value='bar')
        self.mispevent.objects[0].add_attribute('member1', value='baz')
        with self.assertRaises(InvalidMISPObject) as e:
            # member1 is not a multiple
            self.mispevent.to_json()
        self.assertEqual(e.exception.message, 'Multiple occurrences of member1 is not allowed')

        self.mispevent.objects[0].attributes = self.mispevent.objects[0].attributes[:2]
        self.mispevent.objects[0].uuid = 'a'
        with open('tests/mispevent_testfiles/misp_custom_obj.json', 'r') as f:
            ref_json = json.load(f)
        self.assertEqual(self.mispevent.to_json(), json.dumps(ref_json, sort_keys=True, indent=2))
Пример #25
0
class Mail2MISP():
    def __init__(self,
                 misp_url,
                 misp_key,
                 verifycert,
                 config,
                 offline=False,
                 urlsonly=False):
        self.offline = offline
        if not self.offline:
            self.misp = ExpandedPyMISP(misp_url,
                                       misp_key,
                                       verifycert,
                                       debug=config.debug)
        self.config = config
        self.urlsonly = urlsonly
        if not hasattr(self.config, 'enable_dns'):
            setattr(self.config, 'enable_dns', True)
        if self.urlsonly is False:
            setattr(self.config, 'enable_dns', False)
        self.debug = self.config.debug
        self.config_from_email_body = {}
        # Init Faup
        self.f = Faup()
        self.sightings_to_add = []

    def load_email(self, pseudofile):
        self.pseudofile = pseudofile
        self.original_mail = message_from_bytes(self.pseudofile.getvalue(),
                                                policy=policy.default)
        self.subject = self.original_mail.get('Subject')
        try:
            self.sender = self.original_mail.get('From')
        except:
            self.sender = "<unknown sender>"

        # Remove words from subject
        for removeword in self.config.removelist:
            self.subject = re.sub(removeword, "", self.subject).strip()

        # Initialize the MISP event
        self.misp_event = MISPEvent()
        self.misp_event.info = f'{self.config.email_subject_prefix} - {self.subject}'
        self.misp_event.distribution = self.config.default_distribution
        self.misp_event.threat_level_id = self.config.default_threat_level
        self.misp_event.analysis = self.config.default_analysis

    def sighting(self, value, source):
        if self.offline:
            raise Exception('The script is running in offline mode, ')
        '''Add a sighting'''
        s = MISPSighting()
        s.from_dict(value=value, source=source)
        self.misp.add_sighting(s)

    def _find_inline_forward(self):
        '''Does the body contains a forwarded email?'''
        for identifier in self.config.forward_identifiers:
            if identifier in self.clean_email_body:
                self.clean_email_body, fw_email = self.clean_email_body.split(
                    identifier)
                return self.forwarded_email(
                    pseudofile=BytesIO(fw_email.encode()))

    def _find_attached_forward(self):
        forwarded_emails = []
        for attachment in self.original_mail.iter_attachments():
            attachment_content = attachment.get_content()
            # Search for email forwarded as attachment
            # I could have more than one, attaching everything.
            if isinstance(attachment_content, message.EmailMessage):
                forwarded_emails.append(
                    self.forwarded_email(
                        pseudofile=BytesIO(attachment_content.as_bytes())))
            else:
                if isinstance(attachment_content, str):
                    attachment_content = attachment_content.encode()
                filename = attachment.get_filename()
                if not filename:
                    filename = 'missing_filename'
                if self.config_from_email_body.get(
                        'attachment'
                ) == self.config.m2m_benign_attachment_keyword:
                    # Attach sane file
                    self.misp_event.add_attribute(
                        'attachment',
                        value=filename,
                        data=BytesIO(attachment_content))
                else:
                    f_object, main_object, sections = make_binary_objects(
                        pseudofile=BytesIO(attachment_content),
                        filename=filename,
                        standalone=False)
                    self.misp_event.add_object(f_object)
                    if main_object:
                        self.misp_event.add_object(main_object)
                        [
                            self.misp_event.add_object(section)
                            for section in sections
                        ]
        return forwarded_emails

    def email_from_spamtrap(self):
        '''The email comes from a spamtrap and should be attached as-is.'''
        raw_body = self.original_mail.get_body(preferencelist=('html',
                                                               'plain'))
        if raw_body:
            self.clean_email_body = html.unescape(
                raw_body.get_payload(decode=True).decode(
                    'utf8', 'surrogateescape'))
        else:
            self.clean_email_body = ''
        return self.forwarded_email(self.pseudofile)

    def forwarded_email(self, pseudofile: BytesIO):
        '''Extracts all possible indicators out of an email and create a MISP event out of it.
        * Gets all relevant Headers
        * Attach the body
        * Create MISP file objects (uses lief if possible)
        * Set all references
        '''
        email_object = EMailObject(pseudofile=pseudofile,
                                   attach_original_mail=True,
                                   standalone=False)
        if email_object.attachments:
            # Create file objects for the attachments
            for attachment_name, attachment in email_object.attachments:
                if not attachment_name:
                    attachment_name = 'NameMissing.txt'
                if self.config_from_email_body.get(
                        'attachment'
                ) == self.config.m2m_benign_attachment_keyword:
                    a = self.misp_event.add_attribute('attachment',
                                                      value=attachment_name,
                                                      data=attachment)
                    email_object.add_reference(a.uuid, 'related-to',
                                               'Email attachment')
                else:
                    f_object, main_object, sections = make_binary_objects(
                        pseudofile=attachment,
                        filename=attachment_name,
                        standalone=False)
                    if self.config.vt_key:
                        try:
                            vt_object = VTReportObject(
                                self.config.vt_key,
                                f_object.get_attributes_by_relation(
                                    'sha256')[0].value,
                                standalone=False)
                            self.misp_event.add_object(vt_object)
                            f_object.add_reference(vt_object.uuid,
                                                   'analysed-with')
                        except InvalidMISPObject as e:
                            print(e)
                            pass
                    self.misp_event.add_object(f_object)
                    if main_object:
                        self.misp_event.add_object(main_object)
                        for section in sections:
                            self.misp_event.add_object(section)
                    email_object.add_reference(f_object.uuid, 'related-to',
                                               'Email attachment')
        self.process_body_iocs(email_object)
        if self.config.spamtrap or self.config.attach_original_mail or self.config_from_email_body.get(
                'attach_original_mail'):
            self.misp_event.add_object(email_object)
        return email_object

    def process_email_body(self):
        mail_as_bytes = self.original_mail.get_body(
            preferencelist=('html', 'plain')).get_payload(decode=True)
        if mail_as_bytes:
            self.clean_email_body = html.unescape(
                mail_as_bytes.decode('utf8', 'surrogateescape'))
            # Check if there are config lines in the body & convert them to a python dictionary:
            #   <config.body_config_prefix>:<key>:<value> => {<key>: <value>}
            self.config_from_email_body = {
                k.strip(): v.strip()
                for k, v in re.findall(
                    f'{self.config.body_config_prefix}:(.*):(.*)',
                    self.clean_email_body)
            }
            if self.config_from_email_body:
                # ... remove the config lines from the body
                self.clean_email_body = re.sub(
                    rf'^{self.config.body_config_prefix}.*\n?',
                    '',
                    html.unescape(
                        self.original_mail.get_body(
                            preferencelist=('html', 'plain')).get_payload(
                                decode=True).decode('utf8',
                                                    'surrogateescape')),
                    flags=re.MULTILINE)
            # Check if autopublish key is present and valid
            if self.config_from_email_body.get(
                    'm2mkey') == self.config.m2m_key:
                if self.config_from_email_body.get('distribution') is not None:
                    self.misp_event.distribution = self.config_from_email_body.get(
                        'distribution')
                if self.config_from_email_body.get('threat_level') is not None:
                    self.misp_event.threat_level_id = self.config_from_email_body.get(
                        'threat_level')
                if self.config_from_email_body.get('analysis') is not None:
                    self.misp_event.analysis = self.config_from_email_body.get(
                        'analysis')
                if self.config_from_email_body.get('publish'):
                    self.misp_event.publish()

            self._find_inline_forward()
        else:
            self.clean_email_body = ''
        self._find_attached_forward()

    def process_body_iocs(self, email_object=None):
        if email_object:
            body = html.unescape(
                email_object.email.get_body(
                    preferencelist=('html',
                                    'plain')).get_payload(decode=True).decode(
                                        'utf8', 'surrogateescape'))
        else:
            body = self.clean_email_body

        # Cleanup body content
        # Depending on the source of the mail, there is some cleanup to do. Ignore lines in body of message
        for ignoreline in self.config.ignorelist:
            body = re.sub(rf'^{ignoreline}.*\n?', '', body, flags=re.MULTILINE)

        # Remove everything after the stopword from the body
        body = body.split(self.config.stopword, 1)[0]

        # Add tags to the event if keywords are found in the mail
        for tag in self.config.tlptags:
            for alternativetag in self.config.tlptags[tag]:
                if alternativetag in body.lower():
                    self.misp_event.add_tag(tag)

        # Prepare extraction of IOCs
        # Refang email data
        body = refang(body)

        # Extract and add hashes
        contains_hash = False
        for h in set(re.findall(hashmarker.MD5_REGEX, body)):
            contains_hash = True
            attribute = self.misp_event.add_attribute(
                'md5', h, enforceWarninglist=self.config.enforcewarninglist)
            if email_object:
                email_object.add_reference(attribute.uuid, 'contains')
            if self.config.sighting:
                self.sightings_to_add.append((h, self.config.sighting_source))
        for h in set(re.findall(hashmarker.SHA1_REGEX, body)):
            contains_hash = True
            attribute = self.misp_event.add_attribute(
                'sha1', h, enforceWarninglist=self.config.enforcewarninglist)
            if email_object:
                email_object.add_reference(attribute.uuid, 'contains')
            if self.config.sighting:
                self.sightings_to_add.append((h, self.config.sighting_source))
        for h in set(re.findall(hashmarker.SHA256_REGEX, body)):
            contains_hash = True
            attribute = self.misp_event.add_attribute(
                'sha256', h, enforceWarninglist=self.config.enforcewarninglist)
            if email_object:
                email_object.add_reference(attribute.uuid, 'contains')
            if self.config.sighting:
                self.sightings_to_add.append((h, self.config.sighting_source))

        if contains_hash:
            [
                self.misp_event.add_tag(tag)
                for tag in self.config.hash_only_tags
            ]

        # # Extract network IOCs
        urllist = []
        urllist += re.findall(urlmarker.WEB_URL_REGEX, body)
        urllist += re.findall(urlmarker.IP_REGEX, body)
        if self.debug:
            syslog.syslog(str(urllist))

        hostname_processed = []

        # Add IOCs and expanded information to MISP
        for entry in set(urllist):
            ids_flag = True
            self.f.decode(entry)

            domainname = self.f.get_domain()
            if domainname in self.config.excludelist:
                # Ignore the entry
                continue

            hostname = self.f.get_host()

            scheme = self.f.get_scheme()
            if scheme:
                scheme = scheme

            resource_path = self.f.get_resource_path()
            if resource_path:
                resource_path = resource_path

            if self.debug:
                syslog.syslog(domainname)

            if domainname in self.config.internallist and self.urlsonly is False:  # Add link to internal reference unless in urlsonly mode
                attribute = self.misp_event.add_attribute(
                    'link',
                    entry,
                    category='Internal reference',
                    to_ids=False,
                    enforceWarninglist=False)
                if email_object:
                    email_object.add_reference(attribute.uuid, 'contains')
            elif domainname in self.config.externallist or self.urlsonly is False:  # External analysis
                attribute = self.misp_event.add_attribute(
                    'link',
                    entry,
                    category='External analysis',
                    to_ids=False,
                    enforceWarninglist=False)
                if email_object:
                    email_object.add_reference(attribute.uuid, 'contains')
            elif domainname in self.config.externallist or self.urlsonly:  # External analysis
                if self.urlsonly:
                    comment = self.subject + " (from: " + self.sender + ")"
                else:
                    comment = ""
                attribute = self.misp.add_attribute(
                    self.urlsonly, {
                        "type": 'link',
                        "value": entry,
                        "category": 'External analysis',
                        "to_ids": False,
                        "comment": comment
                    })
                for tag in self.config.tlptags:
                    for alternativetag in self.config.tlptags[tag]:
                        if alternativetag in self.subject.lower():
                            self.misp.tag(attribute["uuid"], tag)
                            new_subject = comment.replace(alternativetag, '')
                            self.misp.change_comment(attribute["uuid"],
                                                     new_subject)

            else:  # The URL is probably an indicator.
                comment = ""
                if (domainname in self.config.noidsflaglist) or (
                        hostname in self.config.noidsflaglist):
                    ids_flag = False
                    comment = "Known host (mostly for connectivity test or IP lookup)"
                if self.debug:
                    syslog.syslog(str(entry))

                if scheme:
                    if is_ip(hostname):
                        attribute = self.misp_event.add_attribute(
                            'url',
                            entry,
                            to_ids=False,
                            enforceWarninglist=self.config.enforcewarninglist)
                        if email_object:
                            email_object.add_reference(attribute.uuid,
                                                       'contains')
                    else:
                        if resource_path:  # URL has path, ignore warning list
                            attribute = self.misp_event.add_attribute(
                                'url',
                                entry,
                                to_ids=ids_flag,
                                enforceWarninglist=False,
                                comment=comment)
                            if email_object:
                                email_object.add_reference(
                                    attribute.uuid, 'contains')
                        else:  # URL has no path
                            attribute = self.misp_event.add_attribute(
                                'url',
                                entry,
                                to_ids=ids_flag,
                                enforceWarninglist=self.config.
                                enforcewarninglist,
                                comment=comment)
                            if email_object:
                                email_object.add_reference(
                                    attribute.uuid, 'contains')
                    if self.config.sighting:
                        self.sightings_to_add.append(
                            (entry, self.config.sighting_source))

                if hostname in hostname_processed:
                    # Hostname already processed.
                    continue

                hostname_processed.append(hostname)
                if self.config.sighting:
                    self.sightings_to_add.append(
                        (hostname, self.config.sighting_source))

                if self.debug:
                    syslog.syslog(hostname)

                comment = ''
                port = self.f.get_port()
                if port:
                    port = port
                    comment = f'on port: {port}'

                if is_ip(hostname):
                    attribute = self.misp_event.add_attribute(
                        'ip-dst',
                        hostname,
                        to_ids=ids_flag,
                        enforceWarninglist=self.config.enforcewarninglist,
                        comment=comment)
                    if email_object:
                        email_object.add_reference(attribute.uuid, 'contains')
                else:
                    related_ips = []
                    if HAS_DNS and self.config.enable_dns:
                        try:
                            syslog.syslog(hostname)
                            for rdata in dns.resolver.query(hostname, 'A'):
                                if self.debug:
                                    syslog.syslog(str(rdata))
                                related_ips.append(rdata.to_text())
                        except Exception as e:
                            if self.debug:
                                syslog.syslog(str(e))

                    if related_ips:
                        hip = MISPObject(name='ip-port')
                        hip.add_attribute(
                            'hostname',
                            value=hostname,
                            to_ids=ids_flag,
                            enforceWarninglist=self.config.enforcewarninglist,
                            comment=comment)
                        for ip in set(related_ips):
                            hip.add_attribute('ip',
                                              type='ip-dst',
                                              value=ip,
                                              to_ids=False,
                                              enforceWarninglist=self.config.
                                              enforcewarninglist)
                        self.misp_event.add_object(hip)
                        if email_object:
                            email_object.add_reference(hip.uuid, 'contains')
                    else:
                        if self.urlsonly is False:
                            attribute = self.misp_event.add_attribute(
                                'hostname',
                                value=hostname,
                                to_ids=ids_flag,
                                enforceWarninglist=self.config.
                                enforcewarninglist,
                                comment=comment)
                        if email_object:
                            email_object.add_reference(attribute.uuid,
                                                       'contains')

    def add_event(self):
        '''Add event on the remote MISP instance.'''

        # Add additional tags depending on others
        tags = []
        for tag in [t.name for t in self.misp_event.tags]:
            if self.config.dependingtags.get(tag):
                tags += self.config.dependingtags.get(tag)

        # Add additional tags according to configuration
        for malware in self.config.malwaretags:
            if malware.lower() in self.subject.lower():
                tags += self.config.malwaretags.get(malware)
        if tags:
            [self.misp_event.add_tag(tag) for tag in tags]

        has_tlp_tag = False
        for tag in [t.name for t in self.misp_event.tags]:
            if tag.lower().startswith('tlp'):
                has_tlp_tag = True
        if not has_tlp_tag:
            self.misp_event.add_tag(self.config.tlptag_default)

        if self.offline:
            return self.misp_event.to_json()
        event = self.misp.add_event(self.misp_event, pythonify=True)
        if self.config.sighting:
            for value, source in self.sightings_to_add:
                self.sighting(value, source)
        return event
Пример #26
0
        customerUserId = event.add_attribute(
            'other',
            alert["identity"]["customerUserId"],
            comment="Customer User Id")

    if alert["identity"]["department"] is not None:
        department = event.add_attribute(alert['other',
                                               "identity"]["department"],
                                         comment="Department")

    if alert["identity"]["location"] is not None:
        location = event.add_attribute('other',
                                       alert["identity"]["location"],
                                       comment="Location")

    if alert["identity"]["name"] is not None:

        name = event.add_attribute('target-user',
                                   alert["identity"]["name"],
                                   comment="Name")

    if alert["identity"]["title"] is not None:

        title = event.add_attribute('other',
                                    alert["identity"]["title"],
                                    comment="Title")

    event.add_tag("VAP")

    misp.add_event(event.to_json())
Пример #27
0
# -*- coding: utf-8 -*-

from pymisp import ExpandedPyMISP, MISPEvent
from pymisp import MISPObject
from keys import misp_url, misp_key, misp_verifycert
from datetime import date

misp = ExpandedPyMISP(misp_url, misp_key, misp_verifycert)

event = MISPEvent()
event.info = 'IoT malware'  # Event Title
event.distribution = 1  # 0 = Your Organisation Only, 1 = Community
event.threat_level_id = 2  # 1 = High, 2 = Medium, 3 = Low
event.analysis = 2  # 0 (initial analysis), 1 (On-Going), 2 (Complete)

event.add_tag('malware_classification:malware-category="Botnet"')
event.add_tag('tlp:amber')

d = date.today()
event.set_date(d)

attribute_second = event.add_attribute('url',
                                       'http://1.2.3.4/example',
                                       disable_correlation=False,
                                       comment="Botnet example text",
                                       to_ids=False)

event = misp.add_event(event, pythonify=True)

# Publish event
event.publish()
Пример #28
0
    # initialize and set MISPOrganisation()
    orgc = MISPOrganisation()
    orgc.name = 'RH-ISAC'
    orgc.id = '#{ORGC_ID}'  # organisation id
    orgc.uuid = '#{ORGC_UUID}'  # organisation uuid
    # initialize and set MISPEvent()
    event = MISPEvent()
    event.Orgc = orgc
    event.info = report.title
    event.distribution = 0  # Optional, defaults to MISP.default_event_distribution in MISP config
    event.threat_level_id = 2  # Optional, defaults to MISP.default_event_threat_level in MISP config
    event.analysis = 0  # Optional, defaults to 0 (initial analysis)

    # get tags for report
    for tag in tru.get_enclave_tags(report.id):
        event.add_tag(tag.name)

    # get indicators for report
    for indicator in tru.get_indicators_for_report(report.id):

        # map trustar indicator type to MISP format
        indicator_type = {
            "MD5": "md5",
            "SHA1": "sha1",
            "SHA256": "sha256",
            "SOFTWARE": "filename",
            "URL": "link",
            "EMAIL_ADDRESS": "email-src",
            "IP": "ip-dst",
            "MALWARE": "malware-type",
            "CIDR_BLOCK": "ip-src",
Пример #29
0
if make_feed:
    org = MISPOrganisation()
    org.name = 'CIRCL'
    org.uuid = "55f6ea5e-2c60-40e5-964f-47a8950d210f"
else:
    from covid_key import url, key
    misp = PyMISP(url, key)

for p in path.glob('*_json/current_china.json'):
    d = parse(p.parent.name[:-5])
    event = MISPEvent()
    event.info = f"[{d.isoformat()}] DXY COVID-19 live report"
    event.date = d
    event.distribution = 3
    event.add_tag('tlp:white')
    if make_feed:
        event.orgc = org
    else:
        e = misp.search(eventinfo=event.info, metadata=True, pythonify=True)
        if e:
            # Already added.
            continue
    event.add_attribute('attachment',
                        p.name,
                        data=BytesIO(p.open('rb').read()))
    with p.open() as f:
        data = json.load(f)
    for province in data:
        obj_province = event.add_object(name='covid19-dxy-live-province',
                                        standalone=False)
Пример #30
0
    "reportname":"top-attacks",
    "key":"#{API_KEY}"  # put your Panorama api key here
}

# try with "..., verify=False)" if you get an SSL error
response = requests.request("GET", url, params=querystring)

resp_text = response.text

json_data = json.loads(json.dumps(xmltodict.parse(resp_text)))

# initialize and set MISPOrganisation
orgc = MISPOrganisation()
orgc.name = 'Palo Alto'
orgc.id = '#{ORGC_ID}'  # organisation id
orgc.uuid = '#{ORGC_UUID}'  # organisation uuid
# initialize and set MISPEvent()
event = MISPEvent()
event.Orgc = orgc
event.info = json_data['report']['result']['@name'] + " | " + json_data['report']['result']['@range']
event.distribution = 0  # Optional, defaults to MISP.default_event_distribution in MISP config
event.threat_level_id = 0  # Optional, defaults to MISP.default_event_threat_level in MISP config
event.analysis = 0  # Optional, defaults to 0 (initial analysis)
event.add_tag('firewall threats')

for threatid in json_data['report']['result']['entry']:
    attribute = event.add_attribute('comment', threatid['threatid'])
    attribute.comment = threatid['count']

misp.add_event(event.to_json())
Пример #31
0
class StixParser():
    def __init__(self):
        self.misp_event = MISPEvent()
        self.event = []
        self.misp_event['Galaxy'] = []

    def loadEvent(self, args):
        try:
            filename = os.path.join(os.path.dirname(args[0]), args[1])
            tempFile = open(filename, 'r', encoding='utf-8')
            self.filename = filename
            event = json.loads(tempFile.read())
            self.stix_version = 'stix {}'.format(event.get('spec_version'))
            for o in event.get('objects'):
                try:
                    try:
                        self.event.append(stix2.parse(o, allow_custom=True))
                    except:
                        self.parse_custom(o)
                except:
                    pass
            if not self.event:
                print(
                    json.dumps({
                        'success':
                        0,
                        'message':
                        'There is no valid STIX object to import'
                    }))
                sys.exit(1)
            try:
                event_distribution = args[2]
                if not isinstance(event_distribution, int):
                    event_distribution = int(
                        event_distribution) if event_distribution.isdigit(
                        ) else 5
            except:
                event_distribution = 5
            try:
                attribute_distribution = args[3]
                if attribute_distribution != 'event' and not isinstance(
                        attribute_distribution, int):
                    attribute_distribution = int(
                        attribute_distribution
                    ) if attribute_distribution.isdigit() else 5
            except:
                attribute_distribution = 5
            self.misp_event.distribution = event_distribution
            self.__attribute_distribution = event_distribution if attribute_distribution == 'event' else attribute_distribution
            self.load_mapping()
        except:
            print(
                json.dumps({
                    'success': 0,
                    'message': 'The STIX file could not be read'
                }))
            sys.exit(1)

    def parse_custom(self, obj):
        custom_object_type = obj.pop('type')
        labels = obj['labels']
        try:

            @stix2.CustomObject(
                custom_object_type,
                [('id', stix2.properties.StringProperty(required=True)),
                 ('x_misp_timestamp',
                  stix2.properties.StringProperty(required=True)),
                 ('labels', stix2.properties.ListProperty(labels,
                                                          required=True)),
                 ('x_misp_value',
                  stix2.properties.StringProperty(required=True)),
                 ('created_by_ref',
                  stix2.properties.StringProperty(required=True)),
                 ('x_misp_comment', stix2.properties.StringProperty()),
                 ('x_misp_category', stix2.properties.StringProperty())])
            class Custom(object):
                def __init__(self, **kwargs):
                    return

            custom = Custom(**obj)
        except:

            @stix2.CustomObject(
                custom_object_type,
                [('id', stix2.properties.StringProperty(required=True)),
                 ('x_misp_timestamp',
                  stix2.properties.StringProperty(required=True)),
                 ('labels', stix2.properties.ListProperty(labels,
                                                          required=True)),
                 ('x_misp_values',
                  stix2.properties.DictionaryProperty(required=True)),
                 ('created_by_ref',
                  stix2.properties.StringProperty(required=True)),
                 ('x_misp_comment', stix2.properties.StringProperty()),
                 ('x_misp_category', stix2.properties.StringProperty())])
            class Custom(object):
                def __init__(self, **kwargs):
                    return

            custom = Custom(**obj)
        self.event.append(stix2.parse(custom))

    def load_mapping(self):
        self.objects_mapping = {
            'asn': {
                'observable': observable_asn,
                'pattern': pattern_asn
            },
            'domain-ip': {
                'observable': observable_domain_ip,
                'pattern': pattern_domain_ip
            },
            'email': {
                'observable': self.observable_email,
                'pattern': self.pattern_email
            },
            'file': {
                'observable': observable_file,
                'pattern': self.pattern_file
            },
            'ip-port': {
                'observable': observable_ip_port,
                'pattern': pattern_ip_port
            },
            'network-socket': {
                'observable': observable_socket,
                'pattern': pattern_socket
            },
            'process': {
                'observable': observable_process,
                'pattern': pattern_process
            },
            'registry-key': {
                'observable': observable_regkey,
                'pattern': pattern_regkey
            },
            'url': {
                'observable': observable_url,
                'pattern': pattern_url
            },
            'WindowsPEBinaryFile': {
                'observable': self.observable_pe,
                'pattern': self.pattern_pe
            },
            'x509': {
                'observable': observable_x509,
                'pattern': pattern_x509
            }
        }

    def handler(self):
        self.outputname = '{}.stix2'.format(self.filename)
        if self.from_misp():
            self.buildMispDict()
        else:
            self.version_attribute = {
                'type': 'text',
                'object_relation': 'version',
                'value': self.stix_version
            }
            self.buildExternalDict()
        self.set_distribution()

    def from_misp(self):
        for o in self.event:
            if o._type == 'report' and 'misp:tool="misp2stix2"' in o.get(
                    'labels'):
                index = self.event.index(o)
                self.report = self.event.pop(index)
                return True
        return False

    def buildMispDict(self):
        self.parse_identity()
        self.parse_report()
        for o in self.event:
            try:
                object_type = o._type
            except:
                object_type = o['type']
            labels = o.get('labels')
            if object_type in galaxy_types:
                self.parse_galaxy(o, labels)
            elif object_type == 'course-of-action':
                self.parse_course_of_action(o)
            elif 'x-misp-object' in object_type:
                if 'from_object' in labels:
                    self.parse_custom_object(o)
                else:
                    self.parse_custom_attribute(o, labels)
            else:
                if 'from_object' in labels:
                    self.parse_object(o, labels)
                else:
                    self.parse_attribute(o, labels)

    def parse_identity(self):
        identity = self.event.pop(0)
        org = {'name': identity.get('name')}
        self.misp_event['Org'] = org

    def parse_report(self):
        report = self.report
        self.misp_event.info = report.get('name')
        if report.get('published'):
            self.misp_event.publish_timestamp = self.getTimestampfromDate(
                report.get('published'))
        if hasattr(report, 'labels'):
            labels = report['labels']
            for l in labels:
                self.misp_event.add_tag(l)
        if hasattr(report, 'external_references'):
            ext_refs = report['external_references']
            for e in ext_refs:
                link = {"type": "link"}
                comment = e.get('source_name')
                try:
                    comment = comment.split('url - ')[1]
                except:
                    pass
                if comment:
                    link['comment'] = comment
                link['value'] = e.get('url')
                self.misp_event.add_attribute(**link)

    def parse_galaxy(self, o, labels):
        galaxy_type = self.get_misp_type(labels)
        tag = labels[1]
        value = tag.split(':')[1].split('=')[1]
        galaxy_description, cluster_description = o.get('description').split(
            '|')
        galaxy = {
            'type':
            galaxy_type,
            'name':
            o.get('name'),
            'description':
            galaxy_description,
            'GalaxyCluster': [{
                'type': galaxy_type,
                'value': value,
                'tag_name': tag,
                'description': cluster_description
            }]
        }
        self.misp_event['Galaxy'].append(galaxy)

    def parse_course_of_action(self, o):
        misp_object = MISPObject('course-of-action')
        if 'name' in o:
            attribute = {
                'type': 'text',
                'object_relation': 'name',
                'value': o.get('name')
            }
            misp_object.add_attribute(**attribute)
        else:
            return
        if 'description' in o:
            attribute = {
                'type': 'text',
                'object_relation': 'description',
                'value': o.get('description')
            }
            misp_object.add_attribute(**attribute)
        self.misp_event.add_object(**misp_object)

    def parse_custom_object(self, o):
        name = o.get('type').split('x-misp-object-')[1]
        timestamp = self.getTimestampfromDate(o.get('x_misp_timestamp'))
        category = o.get('category')
        attributes = []
        values = o.get('x_misp_values')
        for v in values:
            attribute_type, object_relation = v.split('_')
            attribute = {
                'type': attribute_type,
                'value': values.get(v),
                'object_relation': object_relation
            }
            attributes.append(attribute)
        misp_object = {
            'name': name,
            'timestamp': timestamp,
            'meta-category': category,
            'Attribute': attributes
        }
        self.misp_event.add_object(**misp_object)

    def parse_custom_attribute(self, o, labels):
        attribute_type = o.get('type').split('x-misp-object-')[1]
        if attribute_type not in misp_types:
            attribute_type = attribute_type.replace('-', '|')
        timestamp = self.getTimestampfromDate(o.get('x_misp_timestamp'))
        to_ids = bool(labels[1].split('=')[1])
        value = o.get('x_misp_value')
        category = self.get_misp_category(labels)
        attribute = {
            'type': attribute_type,
            'timestamp': timestamp,
            'to_ids': to_ids,
            'value': value,
            'category': category
        }
        self.misp_event.add_attribute(**attribute)

    def parse_object(self, o, labels):
        object_type = self.get_misp_type(labels)
        name = 'file' if object_type == 'WindowsPEBinaryFile' else object_type
        object_category = self.get_misp_category(labels)
        stix_type = o._type
        misp_object = MISPObject(name)
        misp_object['meta-category'] = object_category
        if stix_type == 'indicator':
            pattern = o.get('pattern').replace('\\\\', '\\').split(' AND ')
            pattern[0] = pattern[0][1:]
            pattern[-1] = pattern[-1][:-1]
            attributes = self.objects_mapping[object_type]['pattern'](pattern)
        if stix_type == 'observed-data':
            observable = o.get('objects')
            attributes = self.objects_mapping[object_type]['observable'](
                observable)
        if isinstance(attributes, tuple):
            attributes, pe_uuid = attributes
            misp_object.add_reference(pe_uuid, 'included-in')
        for attribute in attributes:
            misp_object.add_attribute(**attribute)
        misp_object.to_ids = bool(labels[1].split('=')[1])
        self.misp_event.add_object(**misp_object)

    def parse_attribute(self, o, labels):
        attribute_type = self.get_misp_type(labels)
        attribute_category = self.get_misp_category(labels)
        attribute = {'type': attribute_type, 'category': attribute_category}
        stix_type = o._type
        if stix_type == 'vulnerability':
            value = o.get('name')
        else:
            if stix_type == 'indicator':
                o_date = o.get('valid_from')
                pattern = o.get('pattern').replace('\\\\', '\\')
                value = self.parse_pattern_with_data(
                    pattern) if attribute_type in (
                        'malware-sample',
                        'attachment') else self.parse_pattern(pattern)
                attribute['to_ids'] = True
            else:
                o_date = o.get('first_observed')
                observable = o.get('objects')
                try:
                    value = self.parse_observable(observable, attribute_type)
                except:
                    print('{}: {}'.format(attribute_type, observable))
                attribute['to_ids'] = False
            attribute['timestamp'] = self.getTimestampfromDate(o_date)
        if 'description' in o:
            attribute['comment'] = o.get('description')
        if isinstance(value, tuple):
            value, data = value
            attribute['data'] = io.BytesIO(data.encode())
        attribute['value'] = value
        self.misp_event.add_attribute(**attribute)

    @staticmethod
    def observable_email(observable):
        attributes = []
        addresses = {}
        files = {}
        for o_key, o_dict in observable.items():
            part_type = o_dict._type
            if part_type == 'email-addr':
                addresses[o_key] = o_dict.get('value')
            elif part_type == 'file':
                files[o_key] = o_dict.get('name')
            else:
                message = dict(o_dict)
        attributes.append({
            'type': 'email-src',
            'object_relation': 'from',
            'value': addresses[message.pop('from_ref')],
            'to_ids': False
        })
        for ref in ('to_refs', 'cc_refs'):
            if ref in message:
                for item in message.pop(ref):
                    mapping = email_mapping[ref]
                    attributes.append({
                        'type': mapping['type'],
                        'object_relation': mapping['relation'],
                        'value': addresses[item],
                        'to_ids': False
                    })
        if 'body_multipart' in message:
            for f in message.pop('body_multipart'):
                attributes.append({
                    'type': 'email-attachment',
                    'object_relation': 'attachment',
                    'value': files[f.get('body_raw_ref')],
                    'to_ids': False
                })
        for m_key, m_value in message.items():
            if m_key == 'additional_header_fields':
                for field_key, field_value in m_value.items():
                    mapping = email_mapping[field_key]
                    if field_key == 'Reply-To':
                        for rt in field_value:
                            attributes.append({
                                'type':
                                mapping['type'],
                                'object_relation':
                                mapping['relation'],
                                'value':
                                rt,
                                'to_ids':
                                False
                            })
                    else:
                        attributes.append({
                            'type':
                            mapping['type'],
                            'object_relation':
                            mapping['relation'],
                            'value':
                            field_value,
                            'to_ids':
                            False
                        })
            else:
                try:
                    mapping = email_mapping[m_key]
                    attributes.append({
                        'type': mapping['type'],
                        'object_relation': mapping['relation'],
                        'value': m_value,
                        'to_ids': False
                    })
                except:
                    if m_key.startswith("x_misp_attachment_"):
                        attribute_type, relation = m_key.split(
                            "x_misp_")[1].split("_")
                        attributes.append({
                            'type':
                            attribute_type,
                            'object_relation':
                            relation,
                            'to_ids':
                            False,
                            'value':
                            m_value['value'],
                            'data':
                            io.BytesIO(m_value['data'].encode())
                        })
                    elif "x_misp_" in m_key:
                        attribute_type, relation = m_key.split(
                            "x_misp_")[1].split("_")
                        attributes.append({
                            'type': attribute_type,
                            'object_relation': relation,
                            'value': m_value,
                            'to_ids': False
                        })
        return attributes

    @staticmethod
    def pattern_email(pattern):
        attributes = []
        attachments = defaultdict(dict)
        for p in pattern:
            p_type, p_value = p.split(' = ')
            try:
                mapping = email_mapping[p_type]
                attributes.append({
                    'type': mapping['type'],
                    'object_relation': mapping['relation'],
                    'value': p_value[1:-1],
                    'to_ids': True
                })
            except KeyError:
                if p_type.startswith("email-message:'x_misp_attachment_"):
                    relation, field = p_type.split('.')
                    relation = relation.split(':')[1][1:-1]
                    attachments[relation][field] = p_value[1:-1]
                elif "x_misp_" in p_type:
                    attribute_type, relation = p_type.split(
                        "x_misp_")[1][:-1].split("_")
                    attributes.append({
                        'type': attribute_type,
                        'object_relation': relation,
                        'value': p_value[1:-1],
                        'to_ids': True
                    })
        for a_key, a_dict in attachments.items():
            _, _, attribute_type, relation = a_key.split('_')
            attributes.append({
                'type': attribute_type,
                'object_relation': relation,
                'to_ids': True,
                'value': a_dict['value'],
                'data': io.BytesIO(a_dict['data'].encode())
            })
        return attributes

    @staticmethod
    def pattern_file(pattern):
        attributes = []
        malware_sample = {}
        for p in pattern:
            p_type, p_value = p.split(' = ')
            if p_type == 'artifact:payload_bin':
                malware_sample['data'] = p_value
            elif p_type in ("file:name", "file:hashes.'md5'"):
                try:
                    mapping = file_mapping[p_type]
                    attributes.append({
                        'type': mapping['type'],
                        'object_relation': mapping['relation'],
                        'value': p_value[1:-1],
                        'to_ids': True
                    })
                    malware_sample['filename'] = p_value[1:-1]
                except KeyError:
                    attributes.append({
                        'type': 'md5',
                        'object_relation': 'md5',
                        'value': p_value[1:-1],
                        'to_ids': True
                    })
                    malware_sample['md5'] = p_value[1:-1]
            elif 'file:hashes.' in p_type:
                _, h = p_type.split('.')
                h = h[1:-1]
                attributes.append({
                    'type': h,
                    'object_relation': h,
                    'value': p_value[1:-1]
                })
            else:
                try:
                    mapping = file_mapping[p_type]
                    attributes.append({
                        'type': mapping['type'],
                        'object_relation': mapping['relation'],
                        'value': p_value[1:-1],
                        'to_ids': True
                    })
                except KeyError:
                    if "x_misp_" in p_type:
                        attribute_type, relation = p_type.split(
                            "x_misp_")[1][:-1].split("_")
                        attributes.append({
                            'type': attribute_type,
                            'object_relation': relation,
                            'value': p_value[1:-1],
                            'to_ids': True
                        })
        if 'data' in malware_sample:
            value = "{}|{}".format(malware_sample['filename'],
                                   malware_sample['md5'])
            attributes.append({
                'type':
                'malware-sample',
                'object_relation':
                'malware-sample',
                'value':
                value,
                'to_ids':
                True,
                'data':
                io.BytesIO(malware_sample['data'].encode())
            })
        return attributes

    def observable_pe(self, observable):
        extension = observable['1']['extensions']['windows-pebinary-ext']
        sections = extension['sections']
        pe = MISPObject('pe')
        pe_uuid = str(uuid.uuid4())
        pe.uuid = pe_uuid
        self.fill_object_attributes_observable(pe, pe_mapping, extension)
        for section in sections:
            pe_section = MISPObject('pe-section')
            if 'hashes' in section:
                for h_type, h_value in section['hashes'].items():
                    h_type = h_type.lower().replace('-', '')
                    pe_section.add_attribute(
                        **{
                            'type': h_type,
                            'object_relation': h_type,
                            'value': h_value,
                            'to_ids': False
                        })
            self.fill_object_attributes_observable(pe_section,
                                                   pe_section_mapping, section)
            section_uuid = str(uuid.uuid4())
            pe_section.uuid = section_uuid
            pe.add_reference(section_uuid, 'included-in')
            self.misp_event.add_object(**pe_section)
        self.misp_event.add_object(**pe)
        return observable_file(observable), pe_uuid

    @staticmethod
    def fill_object_attributes_observable(misp_object, mapping_dict,
                                          stix_object):
        for stix_type, value in stix_object.items():
            try:
                mapping = mapping_dict[stix_type]
                misp_object.add_attribute(
                    **{
                        'type': mapping['type'],
                        'object_relation': mapping['relation'],
                        'value': value,
                        'to_ids': False
                    })
            except KeyError:
                if stix_type.startswith("x_misp_"):
                    attribute_type, relation = parse_custom_property(stix_type)
                    misp_object.add_attribute(
                        **{
                            'type': attribute_type,
                            'object_relation': relation[:-1],
                            'value': value,
                            'to_ids': False
                        })

    def pattern_pe(self, pattern):
        attributes = []
        sections = defaultdict(dict)
        pe = MISPObject('pe')
        pe_uuid = str(uuid.uuid4())
        pe.uuid = pe_uuid
        for p in pattern:
            p_type, p_value = p.split(' = ')
            p_value = p_value[1:-1]
            if ':extensions.' in p_type:
                if '.sections[' in p_type:
                    p_type_list = p_type.split('.')
                    stix_type = "hashes.{}".format(
                        p_type_list[4]
                        [1:-1]) if '.hashes.' in p_type else p_type_list[3]
                    sections[p_type_list[2]][stix_type] = p_value
                else:
                    stix_type = p_type.split('.')[-1]
                    try:
                        mapping = pe_mapping[stix_type]
                        pe.add_attribute(
                            **{
                                'type': mapping['type'],
                                'object_relation': mapping['relation'],
                                'value': p_value,
                                'to_ids': True
                            })
                    except KeyError:
                        if stix_type.startswith("x_misp_"):
                            attribute_type, relation = parse_custom_property(
                                stix_type)
                            pe.add_attribute(
                                **{
                                    'type': attribute_type,
                                    'object_relation': relation[:-2],
                                    'value': p_value,
                                    'to_ids': False
                                })
            else:
                if 'file:hashes.' in p_type:
                    _, h = p_type.split('.')
                    h = h[1:-1]
                    attributes.append({
                        'type': h,
                        'object_relation': h,
                        'value': p_value,
                        'to_ids': True
                    })
                else:
                    try:
                        mapping = file_mapping[p_type]
                        attributes.append({
                            'type':
                            mapping['type'],
                            'object_relation':
                            mapping['relation'],
                            'value':
                            p_value,
                            'to_ids':
                            True
                        })
                    except KeyError:
                        if "x_misp_" in p_type:
                            attribute_type, relation = p_type.split(
                                "x_misp_")[1][:-1].split("_")
                            attributes.append({
                                'type': attribute_type,
                                'object_relation': relation,
                                'value': p_value,
                                'to_ids': True
                            })
        for _, section in sections.items():
            pe_section = MISPObject('pe-section')
            for stix_type, value in section.items():
                if 'hashes.' in stix_type:
                    h_type = stix_type.split('.')[1]
                    pe_section.add_attribute(
                        **{
                            'type': h_type,
                            'object_relation': h_type,
                            'value': value,
                            'to_ids': True
                        })
                else:
                    try:
                        mapping = pe_section_mapping[stix_type]
                        pe_section.add_attribute(
                            **{
                                'type': mapping['type'],
                                'object_relation': mapping['relation'],
                                'value': value,
                                'to_ids': True
                            })
                    except KeyError:
                        if "x_misp_" in stix_type:
                            attribute_type, relation = stix_type.split(
                                "x_misp_")[1][:-1].split("_")
                            attributes.append({
                                'type': attribute_type,
                                'object_relation': relation,
                                'value': value,
                                'to_ids': True
                            })
            section_uuid = str(uuid.uuid4())
            pe_section.uuid = pe_uuid
            pe.add_reference(section_uuid, 'included-in')
            self.misp_event.add_object(**pe_section)
        self.misp_event.add_object(**pe)
        return attributes, pe_uuid

    def buildExternalDict(self):
        self.fetch_report()
        for o in self.event:
            object_type = o._type
            if object_type in ('relationship', 'report'):
                continue
            if object_type in galaxy_types:
                self.parse_external_galaxy(o)
            elif object_type == 'vulnerability':
                attribute = {'type': 'vulnerability', 'value': o.get('name')}
                if 'description' in o:
                    attribute['comment'] = o.get('description')
                self.misp_event.add_attribute(**attribute)
            elif object_type == 'course-of-action':
                self.parse_course_of_action(o)
            elif object_type == 'indicator':
                pattern = o.get('pattern')
                self.parse_external_pattern(pattern)
                attribute = {
                    'type': 'stix2-pattern',
                    'object_relation': 'stix2-pattern',
                    'value': pattern
                }
                misp_object = {
                    'name': 'stix2-pattern',
                    'meta-category': 'stix2-pattern',
                    'Attribute': [self.version_attribute, attribute]
                }
                self.misp_event.add_object(**misp_object)

    def fetch_report(self):
        reports = []
        for o in self.event:
            if o._type == 'report':
                reports.append(o)
        if len(reports) == 1:
            self.report = reports[0]
            self.parse_report()

    def parse_external_galaxy(self, o):
        galaxy = {'name': galaxy_types[o._type]}
        if 'kill_chain_phases' in o:
            galaxy['type'] = o['kill_chain_phases'][0].get('phase_name')
        cluster = defaultdict(dict)
        cluster['value'] = o.get('name')
        cluster['description'] = o.get('description')
        if 'aliases' in o:
            aliases = []
            for a in o.get('aliases'):
                aliases.append(a)
            cluster['meta']['synonyms'] = aliases
        galaxy['GalaxyCluster'] = [cluster]
        self.misp_event['Galaxy'].append(galaxy)

    def parse_external_pattern(self, pattern):
        if ' OR ' in pattern and ' AND ' not in pattern:
            pattern = pattern.split('OR')
            for p in pattern:
                attribute = self.attribute_from_external_pattern(p)
                self.misp_event.add_attribute(**attribute)
        elif ' OR ' not in pattern and ' LIKE ' not in pattern:
            pattern = pattern.split('AND')
            if len(pattern) == 1:
                attribute = self.attribute_from_external_pattern(pattern[0])
                self.misp_event.add_attribute(**attribute)

    @staticmethod
    def attribute_from_external_pattern(pattern):
        pattern_type, pattern_value = pattern.split(' = ')
        pattern_type, pattern_value = pattern_type[1:].strip(
        ), pattern_value[1:-2].strip()
        stix_type, value_type = pattern_type.split(':')
        if 'hashes' in value_type and 'x509' not in stix_type:
            h_type = value_type.split('.')[1]
            return {'type': h_type, 'value': pattern_value}
        else:
            # Might cause some issues, need more examples to test
            return {
                'type':
                external_pattern_mapping[stix_type][value_type].get('type'),
                'value': pattern_value
            }

    def set_distribution(self):
        for attribute in self.misp_event.attributes:
            attribute.distribution = self.__attribute_distribution
        for misp_object in self.misp_event.objects:
            misp_object.distribution = self.__attribute_distribution
            for attribute in misp_object.attributes:
                attribute.distribution = self.__attribute_distribution

    def saveFile(self):
        eventDict = self.misp_event.to_json()
        outputfile = '{}.stix2'.format(self.filename)
        with open(outputfile, 'w') as f:
            f.write(eventDict)

    @staticmethod
    def getTimestampfromDate(stix_date):
        try:
            return int(stix_date.timestamp())
        except:
            return int(
                time.mktime(
                    time.strptime(
                        stix_date.split('+')[0], "%Y-%m-%d %H:%M:%S")))

    @staticmethod
    def get_misp_type(labels):
        return labels[0].split('=')[1][1:-1]

    @staticmethod
    def get_misp_category(labels):
        return labels[1].split('=')[1][1:-1]

    @staticmethod
    def parse_observable(observable, attribute_type):
        return misp_types_mapping[attribute_type](observable, attribute_type)

    @staticmethod
    def parse_pattern(pattern):
        if ' AND ' in pattern:
            pattern_parts = pattern.split(' AND ')
            if len(pattern_parts) == 3:
                _, value1 = pattern_parts[2].split(' = ')
                _, value2 = pattern_parts[0].split(' = ')
                return '{}|{}'.format(value1[1:-2], value2[1:-1])
            else:
                _, value1 = pattern_parts[0].split(' = ')
                _, value2 = pattern_parts[1].split(' = ')
                if value1 in ("'ipv4-addr'", "'ipv6-addr'"):
                    return value2[1:-2]
                return '{}|{}'.format(value1[1:-1], value2[1:-2])
        else:
            return pattern.split(' = ')[1][1:-2]

    def parse_pattern_with_data(self, pattern):
        if 'artifact:payload_bin' not in pattern:
            return self.parse_pattern(pattern)
        pattern_parts = pattern.split(' AND ')
        if len(pattern_parts) == 3:
            filename = pattern_parts[0].split(' = ')[1]
            md5 = pattern_parts[1].split(' = ')[1]
            return "{}|{}".format(
                filename[1:-1],
                md5[1:-1]), pattern_parts[2].split(' = ')[1][1:-2]
        else:
            return pattern_parts[0].split(
                ' = ')[1][1:-1], pattern_parts[1].split(' = ')[1][1:-2]
Пример #32
0
    def add_misp_fingerprint(self, fingerprint_json: dict):
        """
        Upload fingerprint to MISP
        :param fingerprint_json: fingerprint to upload
        :return:
        """

        LOGGER.info('Uploading the fingerprint to MISP')
        start = time.time()

        # Maximum number of source IPs to include
        # 0 means all (no limit)
        source_ips_limit = 10

        # Possible dicts in each attack_vector of the fingerprint
        # that will be added as comments (with the dict as value) to the event (not the ddos objects)
        attack_vector_dicts = [
            'ttl',
            'tcp_flags',
            'fragmentation_offset',
            'ethernet_type',
            'frame_len',
            'dns_query_name',
            'dns_query_type',
            'ICMP type',
            'ntp_requestcode',
            'http_uri',
            'http_method',
            'http_user_agent',
        ]

        # Possible fields in each attack_vector of the fingerprint
        # that will be added as comments to the event (not the ddos objects)
        attack_vector_fields = [
            'service',
            'fraction_of_attack',
            'nr_flows',
            'nr_packets',
            'nr_megabytes',
            'time_start',
            'duration_seconds',
        ]

        # Possible fields in the fingerprint
        # that will be added as comments to the event
        fingerprint_fields = [
            'time_start',
            'time_end',
            'duration_seconds',
            'total_flows',
            'total_megabytes',
            'total_packets',
            'total_ips',
            'avg_bps',
            'avg_pps',
            'avg_Bpp',
        ]

        # Create the DDoSCH tag (returns existing one if already present)
        ddosch_tag = self.get_misp_tag(self.ddosch_tag_name)
        if ddosch_tag is None:
            ddosch_tag = self.add_misp_tag(self.ddosch_tag_name,
                                           self.ddosch_tag_colour)
        LOGGER.debug(ddosch_tag)

        # Retrieve (or create) the sharing group if specified
        misp_sharing_group = None
        if self.sharing_group:
            misp_sharing_group = [
                sh_grp for sh_grp in self.misp.sharing_groups(pythonify=True)
                if sh_grp.name == self.sharing_group
            ]
            if len(misp_sharing_group) == 0:
                misp_sharing_group = self.misp.add_sharing_group(
                    {'name': self.sharing_group}, pythonify=True)
            else:
                misp_sharing_group = misp_sharing_group[0]

        # Create an event to link everything to
        LOGGER.debug('Creating a new event for the fingerprint')
        event = MISPEvent()
        event.info = fingerprint_json['key']

        # TARGET
        event.add_attribute(category='Network activity',
                            type='ip-dst',
                            value=fingerprint_json['target'],
                            comment='target')
        # KEY
        event.add_attribute(category='Network activity',
                            type='md5',
                            value=fingerprint_json['key'],
                            comment='attack key')

        LOGGER.debug('Adding fingerprint fields')
        for fp_field in fingerprint_fields:
            if fp_field in fingerprint_json:
                event.add_attribute(category='Network activity',
                                    type='comment',
                                    value=fingerprint_json[fp_field],
                                    comment=fp_field)

        # TAGS
        if 'tags' in fingerprint_json:
            LOGGER.debug('Adding fingerprint tags')
            for tag in fingerprint_json['tags']:
                event.add_tag(tag=tag)
        event.add_tag(tag='validated')
        if ddosch_tag is not None:
            event.add_tag(tag=self.ddosch_tag_name)

        # Add each attack vector as a MISP object to the MISP event
        for v_i, attack_vector in enumerate(
                fingerprint_json['attack_vectors']):
            LOGGER.debug(f'Processing Attack Vector #{v_i}')
            ddos_object = MISPObject(name='ddos')
            # ATTACK VECTOR PROTOCOL
            ddos_object.add_attribute('protocol',
                                      attack_vector['protocol'],
                                      comment=f'vector {v_i}')

            for av_dict in attack_vector_dicts:
                if av_dict in attack_vector and type(
                        attack_vector[av_dict]) == dict:
                    LOGGER.debug(f'Adding dict {av_dict}')
                    event.add_attribute(
                        category='Network activity',
                        type='comment',
                        value=json.dumps(attack_vector[av_dict]),
                        comment=f'vector {v_i} {av_dict} ({av_dict}:fraction)')

            for av_field in attack_vector_fields:
                if av_field in attack_vector and attack_vector[
                        av_field] is not None:
                    LOGGER.debug(f'Adding field {av_field}')
                    event.add_attribute(category='Network activity',
                                        type='comment',
                                        value=attack_vector[av_field],
                                        comment=f'vector {v_i} {av_field}')

            # ATTACK VECTOR SOURCE_PORT
            if type(attack_vector['source_port']) == int:
                LOGGER.debug('Adding source ports')
                ddos_object.add_attribute('src-port',
                                          attack_vector['source_port'],
                                          comment=f'vector {v_i} src-port')

            # ATTACK VECTOR DESTINATION PORTS
            if type(attack_vector['destination_ports']) == dict:
                LOGGER.debug('Adding destination ports')
                for port in attack_vector['destination_ports'].keys():
                    ddos_object.add_attribute(
                        'dst-port',
                        int(port),
                        comment=f'vector {v_i} destination port '
                        f'(fraction:{attack_vector["destination_ports"][port]}'
                    )

            # ATTACK VECTOR DNS
            if 'dns_query_name' in attack_vector or 'dns_query_type' in attack_vector:
                ddos_object.add_attribute(
                    'type',
                    'dns',
                    comment=f'vector {v_i}  type of attack vector')
                ddos_object.add_attribute(
                    'type',
                    'dns-amplification',
                    comment=f'vector {v_i} type of attack vector')

            # ATTACK VECTOR ICMP
            if 'ICMP type' in attack_vector:
                ddos_object.add_attribute(
                    'type',
                    'icmp',
                    comment=f'vector {v_i} type of attack vector')

            # ATTACK VECTOR NTP
            if 'ntp_requestcode' in attack_vector:
                ddos_object.add_attribute(
                    'type',
                    'ntp-amplification',
                    comment=f'vector {v_i} type of attack vector')

            # ATTACK VECTOR SOURCE IPS
            if 'source_ips' in attack_vector:
                for i, src_ip in enumerate(attack_vector['source_ips'],
                                           start=1):
                    ddos_object.add_attribute(
                        'ip-src', src_ip, comment=f'vector {v_i}  source IP')
                    if i >= source_ips_limit > 0:
                        break

            event.add_object(ddos_object, pythonify=True)

        if misp_sharing_group:
            self.misp.change_sharing_group_on_entity(event,
                                                     misp_sharing_group.id,
                                                     pythonify=True)

        if self.publish:
            event.publish()

        event = self.misp.add_event(event, pythonify=True)

        LOGGER.info(f'event: {event}')

        LOGGER.debug('That took {} seconds'.format(time.time() - start))