CVE-2020-12889 (Python, published 2020-05-15T18:15Z)
Description: value: MISP MISP-maltego 1.4.4 incorrectly shares a MISP connection across users in a remote-transform use case.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/MISP/MISP-maltego
Files changed:
src/MISP_maltego/transforms/attributetoevent.py
src/MISP_maltego/transforms/common/util.py
src/MISP_maltego/transforms/eventtoattributes.py
src/MISP_maltego/transforms/galaxytoevent.py
Commit message: fix: [security] fixes a security issue when used as remote transform

the MISP connection being shared by all the users

Vulnerable code:
from MISP_maltego.transforms.common.util import check_update, get_misp_connection, event_to_entity, object_to_entity, get_attribute_in_event, get_attribute_in_object, attribute_to_entity, get_entity_property, search_galaxy_cluster, galaxycluster_to_entity, tag_matches_note_prefix
            misp = get_misp_connection(config, request.parameters)
                events_json = misp.search(controller='events', eventid=eventid, with_attachments=False)
            events_json = misp.search(controller='events', eventinfo=request.entity.value, with_attachments=False)
                misp = get_misp_connection(config, request.parameters)
                result = misp.direct_call('tags/search', {'name': keyword})
        misp = get_misp_connection(config, request.parameters)
        events_json = misp.search(controller='events', value=request.entity.value, with_attachments=False)
                        response += object_to_entity(o, link_label=link_label)
        misp = get_misp_connection(config, request.parameters)
            events_json = misp.search(controller='events', tags=tag_name, with_attachments=False)
                events_json = misp.search(controller='events', eventid=request.entity.fields.get('event_id').value, with_attachments=False)
            events_json = misp.search(controller='events', tags=tag_name, with_attachments=False)
            events_json = misp.search(controller='events', value=request.entity.value, with_attachments=False)
                        response += object_to_entity(o, link_direction=LinkDirection.OutputToInput)
misp_connection = None
def get_misp_connection(config=None, parameters=None):
    global misp_connection
    if misp_connection:
        return misp_connection
    if not config:
        raise MaltegoException("ERROR: MISP connection not yet established, and config not provided as parameter.")
    misp_verify = True
    misp_debug = False
    misp_url = None
    misp_key = None
    try:
        if is_local_exec_mode():
            misp_url = config['MISP_maltego.local.misp_url']
            misp_key = config['MISP_maltego.local.misp_key']
            if config['MISP_maltego.local.misp_verify'] in ['False', 'false', 0, 'no', 'No']:
                misp_verify = False
            if config['MISP_maltego.local.misp_debug'] in ['True', 'true', 1, 'yes', 'Yes']:
                misp_debug = True
        if is_remote_exec_mode():
                misp_url = parameters['mispurl'].value
                misp_key = parameters['mispkey'].value
            except AttributeError:
                raise MaltegoException("ERROR: mispurl and mispkey need to be set to something valid")
        misp_connection = PyMISP(misp_url, misp_key, misp_verify, 'json', misp_debug, tool='misp_maltego')
    except Exception:
        if is_local_exec_mode():
            raise MaltegoException("ERROR: Cannot connect to MISP server. Please verify your MISP_Maltego.conf settings.")
        if is_remote_exec_mode():
            raise MaltegoException("ERROR: Cannot connect to MISP server. Please verify your settings (MISP URL and API key), and ensure the MISP server is reachable from the internet.")
    return misp_connection
def object_to_entity(o, link_label=None, link_direction=LinkDirection.InputToOutput):
    misp = get_misp_connection()
    # find a nice icon for it
    try:
        icon_url = mapping_object_icon[o['name']]
    except KeyError:
        # it's not in our mapping, just ignore and leave the default icon
        icon_url = None
    # Generate a human readable display-name:
    # - find the first RequiredOneOf that exists
    # - if none, use the first RequiredField
    # LATER further finetune the human readable version of this object
    o_template = misp.get_object_template(o['template_uuid'])
    human_readable = None
    try:
        found = False
        while not found:  # the while loop is broken once something is found, or the requiredOneOf has no elements left
            required_ote_type = o_template['ObjectTemplate']['requirements']['requiredOneOf'].pop(0)
            for ote in o_template['ObjectTemplateElement']:
                if ote['object_relation'] == required_ote_type:
                    required_a_type = ote['type']
                    break
            for a in o['Attribute']:
                if a['type'] == required_a_type:
                    human_readable = '{}:\n{}'.format(o['name'], a['value'])
                    found = True
                    break
    except Exception:
        pass
    if not human_readable:
        try:
            found = False
            parts = []
            for required_ote_type in o_template['ObjectTemplate']['requirements']['required']:
                for ote in o_template['ObjectTemplateElement']:
                    if ote['object_relation'] == required_ote_type:
                        required_a_type = ote['type']
                        break
                for a in o['Attribute']:
                    if a['type'] == required_a_type:
                        parts.append(a['value'])
                        break
            human_readable = '{}:\n{}'.format(o['name'], '|'.join(parts))
        except Exception:
            human_readable = o['name']
    return MISPObject(
        human_readable,
        uuid=o['uuid'],
        event_id=int(o['event_id']),
        meta_category=o.get('meta_category'),
        description=o.get('description'),
        comment=o.get('comment'),
        icon_url=icon_url,
        link_label=link_label,
        link_direction=link_direction,
        bookmark=Bookmark.Green
    )


def object_to_relations(o, e):
    # process forward and reverse references, so just loop over all the objects of the event
    if 'Object' in e['Event']:
        for eo in e['Event']['Object']:
            if 'ObjectReference' in eo:
                for ref in eo['ObjectReference']:
                    # we have found original object. Expand to the related object and attributes
                    if eo['uuid'] == o['uuid']:
                        # the reference is an Object
                        if ref.get('Object'):
                            # get the full object in the event, as our objectReference included does not contain everything we need
                            sub_object = get_object_in_event(ref['Object']['uuid'], e)
                            yield object_to_entity(sub_object, link_label=ref['relationship_type'])
                        # the reference is an Attribute
                        if ref.get('Attribute'):
                            ref['Attribute']['event_id'] = ref['event_id']   # LATER remove this ugly workaround - object can't be requested directly from MISP using the uuid, and to find a full object we need the event_id
                            for item in attribute_to_entity(ref['Attribute'], link_label=ref['relationship_type']):
                                yield item

                    # reverse-lookup - this is another objects relating the original object
                    if ref['referenced_uuid'] == o['uuid']:
                        yield object_to_entity(eo, link_label=ref['relationship_type'], link_direction=LinkDirection.OutputToInput)


from MISP_maltego.transforms.common.util import check_update, get_misp_connection, attribute_to_entity, event_to_entity, galaxycluster_to_entity, object_to_entity, object_to_attributes, object_to_relations, tag_matches_note_prefix
# FIXME have a more human readable version of the MISP event value in the graph. change entity + event_to_entity + do_transform
        self.misp = None
        self.misp = get_misp_connection(config, request.parameters)
        search_result = self.misp.search(controller='events', eventid=event_id, with_attachments=False)
            self.response += object_to_entity(o)
        misp = get_misp_connection(config, request.parameters)
        event_json = misp.get_event(maltego_object.event_id)
                for entity in object_to_relations(o, event_json):
        misp = get_misp_connection(config, request.parameters)
        event_json = misp.get_event(maltego_object.event_id)
                for entity in object_to_relations(o, event_json):
from MISP_maltego.transforms.common.util import check_update, get_misp_connection, galaxycluster_to_entity, get_galaxy_cluster, get_galaxies_relating, search_galaxy_cluster, mapping_galaxy_icon
        misp = get_misp_connection(config, request.parameters)
        events_json = misp.search(controller='events', tags=tag_name, with_attachments=False)

Fixed code:
from MISP_maltego.transforms.common.util import check_update, MISPConnection, event_to_entity, get_attribute_in_event, get_attribute_in_object, attribute_to_entity, get_entity_property, search_galaxy_cluster, galaxycluster_to_entity
            conn = MISPConnection(config, request.parameters)
                events_json = conn.misp.search(controller='events', eventid=eventid, with_attachments=False)
            events_json = conn.misp.search(controller='events', eventinfo=request.entity.value, with_attachments=False)
                conn = MISPConnection(config, request.parameters)
                result = conn.misp.direct_call('tags/search', {'name': keyword})
        conn = MISPConnection(config, request.parameters)
        events_json = conn.misp.search(controller='events', value=request.entity.value, with_attachments=False)
                        response += conn.object_to_entity(o, link_label=link_label)
        conn = MISPConnection(config, request.parameters)
            events_json = conn.misp.search(controller='events', tags=tag_name, with_attachments=False)
                events_json = conn.misp.search(controller='events', eventid=request.entity.fields.get('event_id').value, with_attachments=False)
            events_json = conn.misp.search(controller='events', tags=tag_name, with_attachments=False)
            events_json = conn.misp.search(controller='events', value=request.entity.value, with_attachments=False)
                        response += conn.object_to_entity(o, link_direction=LinkDirection.OutputToInput)
class MISPConnection():
    def __init__(self, config=None, parameters=None):
        self.misp = None

        if not config:
            raise MaltegoException("ERROR: MISP connection not yet established, and config not provided as parameter.")
        misp_verify = True
        misp_debug = False
        misp_url = None
        misp_key = None
        try:
            if is_local_exec_mode():
                misp_url = config['MISP_maltego.local.misp_url']
                misp_key = config['MISP_maltego.local.misp_key']
                if config['MISP_maltego.local.misp_verify'] in ['False', 'false', 0, 'no', 'No']:
                    misp_verify = False
                if config['MISP_maltego.local.misp_debug'] in ['True', 'true', 1, 'yes', 'Yes']:
                    misp_debug = True
            else:
                try:
                    misp_url = parameters['mispurl'].value
                    misp_key = parameters['mispkey'].value
                except AttributeError:
                    raise MaltegoException("ERROR: mispurl and mispkey need to be set to something valid")
            self.misp = PyMISP(misp_url, misp_key, misp_verify, 'json', misp_debug, tool='misp_maltego')
        except Exception:
            if is_local_exec_mode():
                raise MaltegoException("ERROR: Cannot connect to MISP server. Please verify your MISP_Maltego.conf settings.")
            else:
                raise MaltegoException("ERROR: Cannot connect to MISP server. Please verify your settings (MISP URL and API key), and ensure the MISP server is reachable from the internet.")

    def object_to_entity(self, o, link_label=None, link_direction=LinkDirection.InputToOutput):
        # find a nice icon for it
        try:
            icon_url = mapping_object_icon[o['name']]
        except KeyError:
            # it's not in our mapping, just ignore and leave the default icon
            icon_url = None
        # Generate a human readable display-name:
        # - find the first RequiredOneOf that exists
        # - if none, use the first RequiredField
        # LATER further finetune the human readable version of this object
        o_template = self.misp.get_object_template(o['template_uuid'])
        human_readable = None
        try:
            found = False
            while not found:  # the while loop is broken once something is found, or the requiredOneOf has no elements left
                required_ote_type = o_template['ObjectTemplate']['requirements']['requiredOneOf'].pop(0)
                for ote in o_template['ObjectTemplateElement']:
                    if ote['object_relation'] == required_ote_type:
                        required_a_type = ote['type']
                        break
                for a in o['Attribute']:
                    if a['type'] == required_a_type:
                        human_readable = '{}:\n{}'.format(o['name'], a['value'])
                        found = True
                        break
        except Exception:
            pass
        if not human_readable:
                found = False
                parts = []
                for required_ote_type in o_template['ObjectTemplate']['requirements']['required']:
                    for ote in o_template['ObjectTemplateElement']:
                        if ote['object_relation'] == required_ote_type:
                            required_a_type = ote['type']
                            break
                    for a in o['Attribute']:
                        if a['type'] == required_a_type:
                            parts.append(a['value'])
                            break
                human_readable = '{}:\n{}'.format(o['name'], '|'.join(parts))
            except Exception:
                human_readable = o['name']
        return MISPObject(
            human_readable,
            uuid=o['uuid'],
            event_id=int(o['event_id']),
            meta_category=o.get('meta_category'),
            description=o.get('description'),
            comment=o.get('comment'),
            icon_url=icon_url,
            link_label=link_label,
            link_direction=link_direction,
            bookmark=Bookmark.Green
        )

    def object_to_relations(self, o, e):
        # process forward and reverse references, so just loop over all the objects of the event
        if 'Object' in e['Event']:
            for eo in e['Event']['Object']:
                if 'ObjectReference' in eo:
                    for ref in eo['ObjectReference']:
                        # we have found original object. Expand to the related object and attributes
                        if eo['uuid'] == o['uuid']:
                            # the reference is an Object
                            if ref.get('Object'):
                                # get the full object in the event, as our objectReference included does not contain everything we need
                                sub_object = get_object_in_event(ref['Object']['uuid'], e)
                                yield self.object_to_entity(sub_object, link_label=ref['relationship_type'])
                            # the reference is an Attribute
                            if ref.get('Attribute'):
                                ref['Attribute']['event_id'] = ref['event_id']   # LATER remove this ugly workaround - object can't be requested directly from MISP using the uuid, and to find a full object we need the event_id
                                for item in attribute_to_entity(ref['Attribute'], link_label=ref['relationship_type']):
                                    yield item

                        # reverse-lookup - this is another objects relating the original object
                        if ref['referenced_uuid'] == o['uuid']:
                            yield self.object_to_entity(eo, link_label=ref['relationship_type'], link_direction=LinkDirection.OutputToInput)
from MISP_maltego.transforms.common.util import check_update, MISPConnection, attribute_to_entity, event_to_entity, galaxycluster_to_entity, object_to_attributes, tag_matches_note_prefix
# TODO have a more human readable version of the MISP event value in the graph. change entity + event_to_entity + do_transform
        self.conn = None
        self.conn = MISPConnection(config, request.parameters)
        search_result = self.conn.misp.search(controller='events', eventid=event_id, with_attachments=False)
            self.response += self.conn.object_to_entity(o)
        conn = MISPConnection(config, request.parameters)
        event_json = conn.misp.get_event(maltego_object.event_id)
                for entity in conn.object_to_relations(o, event_json):
        conn = MISPConnection(config, request.parameters)
        event_json = conn.misp.get_event(maltego_object.event_id)
                for entity in conn.object_to_relations(o, event_json):
from MISP_maltego.transforms.common.util import check_update, MISPConnection, galaxycluster_to_entity, get_galaxy_cluster, get_galaxies_relating, search_galaxy_cluster, mapping_galaxy_icon
        conn = MISPConnection(config, request.parameters)
        events_json = conn.misp.search(controller='events', tags=tag_name, with_attachments=False)