Skip to content

Commit

Permalink
fix: [security] fixes a security issue when used as remote transform
Browse files Browse the repository at this point in the history
the MISP connection being shared by all the users
  • Loading branch information
cvandeplas committed May 15, 2020
1 parent 32ccad3 commit 3ccde66
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 144 deletions.
30 changes: 15 additions & 15 deletions src/MISP_maltego/transforms/attributetoevent.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from canari.maltego.entities import Unknown, Hashtag
from canari.maltego.transform import Transform
from MISP_maltego.transforms.common.entities import MISPGalaxy
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
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
from canari.maltego.message import LinkDirection, Bookmark

__author__ = 'Christophe Vandeplas'
Expand All @@ -26,20 +26,20 @@ def do_transform(self, request, response, config):
link_label = 'Search result'

if 'properties.mispevent' in request.entity.fields:
misp = get_misp_connection(config, request.parameters)
conn = MISPConnection(config, request.parameters)
# if event_id
try:
if request.entity.value == '0':
return response
eventid = int(request.entity.value)
events_json = misp.search(controller='events', eventid=eventid, with_attachments=False)
events_json = conn.misp.search(controller='events', eventid=eventid, with_attachments=False)
for e in events_json:
response += event_to_entity(e, link_label=link_label, link_direction=LinkDirection.OutputToInput)
return response
except ValueError:
pass
# if event_info string as value
events_json = misp.search(controller='events', eventinfo=request.entity.value, with_attachments=False)
events_json = conn.misp.search(controller='events', eventinfo=request.entity.value, with_attachments=False)
for e in events_json:
response += event_to_entity(e, link_label=link_label, link_direction=LinkDirection.OutputToInput)
return response
Expand Down Expand Up @@ -68,8 +68,8 @@ def do_transform(self, request, response, config):
keyword = get_entity_property(request.entity, 'Temp')
if not keyword:
keyword = request.entity.value
misp = get_misp_connection(config, request.parameters)
result = misp.direct_call('tags/search', {'name': keyword})
conn = MISPConnection(config, request.parameters)
result = conn.misp.direct_call('tags/search', {'name': keyword})
for t in result:
# skip misp-galaxies as we have processed them earlier on
if t['Tag']['name'].startswith('misp-galaxy'):
Expand All @@ -80,8 +80,8 @@ def do_transform(self, request, response, config):
return response

# for all other normal entities
misp = get_misp_connection(config, request.parameters)
events_json = misp.search(controller='events', value=request.entity.value, with_attachments=False)
conn = MISPConnection(config, request.parameters)
events_json = conn.misp.search(controller='events', value=request.entity.value, with_attachments=False)
# we need to do really rebuild the Entity from scratch as request.entity is of type Unknown
for e in events_json:
# find the value as attribute
Expand All @@ -93,7 +93,7 @@ def do_transform(self, request, response, config):
if 'Object' in e['Event']:
for o in e['Event']['Object']:
if get_attribute_in_object(o, attribute_value=request.entity.value, substring=True).get('value'):
response += object_to_entity(o, link_label=link_label)
response += conn.object_to_entity(o, link_label=link_label)

return response

Expand Down Expand Up @@ -137,20 +137,20 @@ def do_transform(self, request, response, config):
# placeholder for https://github.com/MISP/MISP-maltego/issues/11
pass

misp = get_misp_connection(config, request.parameters)
conn = MISPConnection(config, request.parameters)
# from Galaxy
if 'properties.mispgalaxy' in request.entity.fields:
tag_name = get_entity_property(request.entity, 'tag_name')
if not tag_name:
tag_name = request.entity.value
events_json = misp.search(controller='events', tags=tag_name, with_attachments=False)
events_json = conn.misp.search(controller='events', tags=tag_name, with_attachments=False)
for e in events_json:
response += event_to_entity(e, link_direction=LinkDirection.OutputToInput)
return response
# from Object
elif 'properties.mispobject' in request.entity.fields:
if request.entity.fields.get('event_id'):
events_json = misp.search(controller='events', eventid=request.entity.fields.get('event_id').value, with_attachments=False)
events_json = conn.misp.search(controller='events', eventid=request.entity.fields.get('event_id').value, with_attachments=False)
for e in events_json:
response += event_to_entity(e, link_direction=LinkDirection.OutputToInput)
return response
Expand All @@ -161,13 +161,13 @@ def do_transform(self, request, response, config):
tag_name = get_entity_property(request.entity, 'Temp')
if not tag_name:
tag_name = request.entity.value
events_json = misp.search(controller='events', tags=tag_name, with_attachments=False)
events_json = conn.misp.search(controller='events', tags=tag_name, with_attachments=False)
for e in events_json:
response += event_to_entity(e, link_direction=LinkDirection.OutputToInput)
return response
# standard Entities (normal attributes)
else:
events_json = misp.search(controller='events', value=request.entity.value, with_attachments=False)
events_json = conn.misp.search(controller='events', value=request.entity.value, with_attachments=False)

# return the MISPEvent or MISPObject of the attribute
for e in events_json:
Expand All @@ -179,6 +179,6 @@ def do_transform(self, request, response, config):
if 'Object' in e['Event']:
for o in e['Event']['Object']:
if get_attribute_in_object(o, attribute_value=request.entity.value).get('value'):
response += object_to_entity(o, link_direction=LinkDirection.OutputToInput)
response += conn.object_to_entity(o, link_direction=LinkDirection.OutputToInput)

return response
223 changes: 109 additions & 114 deletions src/MISP_maltego/transforms/common/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

tag_note_prefixes = ['tlp:', 'PAP:', 'de-vs:', 'euci:', 'fr-classif:', 'nato:']

misp_connection = None
update_url = 'https://raw.githubusercontent.com/MISP/MISP-maltego/master/setup.py'
local_path_root = os.path.join(tempfile.gettempdir(), 'MISP-maltego')
local_path_version = os.path.join(local_path_root, 'versioncheck')
Expand Down Expand Up @@ -64,37 +63,116 @@ def check_update(config):
return 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():
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:
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")
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
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)


def entity_obj_to_entity(entity_obj, v, t, **kwargs):
Expand Down Expand Up @@ -176,65 +254,6 @@ def attribute_to_entity(a, link_label=None, event_tags=[], only_self=False):
# LATER : relationships from attributes - not yet supported by MISP yet, but there are references in the datamodel


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_attributes(o, e):
# first process attributes from an object that belong together (eg: first-name + last-name), and remove them from the list
if o['name'] == 'person':
Expand All @@ -248,30 +267,6 @@ def object_to_attributes(o, e):
yield item


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)


def get_object_in_event(uuid, e):
for o in e['Event']['Object']:
if o['uuid'] == uuid:
Expand Down

0 comments on commit 3ccde66

Please sign in to comment.