added support for object relationships

pull/12/head
Christophe Vandeplas 2018-12-01 20:02:15 +01:00
parent 7e13696d1b
commit 9afb83bd4b
3 changed files with 38 additions and 19 deletions

View File

@ -1,7 +1,7 @@
# Quick start guide # Quick start guide
This is a Maltego MISP integration tool allowing you to view (read-only) data from a MISP instance. This is a Maltego MISP integration tool allowing you to view (read-only) data from a MISP instance.
Currently supported MISP elements are : Event, Attribute, Object, Tag, Taxonomy, Galaxy and relations. Currently supported MISP elements are : Event, Attribute, Object (incl relations), Tag, Taxonomy, Galaxy (incl relations).
Once installed you can start by creating a `MISPEvent` entity, then load the transform `EventToAttributes`. Once installed you can start by creating a `MISPEvent` entity, then load the transform `EventToAttributes`.

View File

@ -37,11 +37,11 @@ class MISPObject(Entity):
icon_url = 'file://MISP_maltego/resources/images/MISPObject.png' icon_url = 'file://MISP_maltego/resources/images/MISPObject.png'
uuid = StringEntityField('uuid', display_name='UUID') uuid = StringEntityField('uuid', display_name='UUID')
event_id = IntegerEntityField('event_id', display_name='Event ID') event_id = IntegerEntityField('event_id', display_name='Event ID') # FIXME remove this once MISP provides objects correctly when requesting only the object. See https://github.com/MISP/MISP/issues/3801
name = StringEntityField('name', display_name='Name', is_value=True) name = StringEntityField('name', display_name='Name', is_value=True)
meta_category = StringEntityField('meta_category', display_name='Meta Category') meta_category = StringEntityField('meta_category', display_name='Meta Category', matching_rule=MatchingRule.Loose)
description = StringEntityField('description', display_name='Description') description = StringEntityField('description', display_name='Description', matching_rule=MatchingRule.Loose)
comment = StringEntityField('comment', display_name='Comment') comment = StringEntityField('comment', display_name='Comment', matching_rule=MatchingRule.Loose)
class MISPGalaxy(Entity): class MISPGalaxy(Entity):

View File

@ -1,6 +1,6 @@
from canari.maltego.entities import Unknown, Hash, Domain, IPv4Address, URL, DNSName, AS, Website, NSRecord, PhoneNumber, EmailAddress, File, Person, Hashtag, Location, Company, Alias, Port, Twitter from canari.maltego.entities import Unknown, Hash, Domain, IPv4Address, URL, DNSName, AS, Website, NSRecord, PhoneNumber, EmailAddress, File, Person, Hashtag, Location, Company, Alias, Port, Twitter
from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject, MISPGalaxy from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject, MISPGalaxy
from canari.maltego.message import UIMessageType, UIMessage, Label from canari.maltego.message import UIMessageType, UIMessage, Label, LinkStyle
from pymisp import PyMISP from pymisp import PyMISP
import json import json
import os import os
@ -120,19 +120,23 @@ def entity_obj_to_entity(entity_obj, v, t, **kwargs):
return entity_obj(v, **kwargs) return entity_obj(v, **kwargs)
def attribute_to_entity(a): def attribute_to_entity(a, link_label=None):
# prepare some attributes to a better form # prepare some attributes to a better form
a['data'] = None # empty the file content as we really don't need this here # FIXME feature request for misp.get_event() to not get attachment content a['data'] = None # empty the file content as we really don't need this here # FIXME feature request for misp.get_event() to not get attachment content
if a['type'] == 'malware-sample': if a['type'] == 'malware-sample':
a['type'] = 'filename|md5' a['type'] = 'filename|md5'
if a['type'] == 'regkey|value': if a['type'] == 'regkey|value': # FIXME regkey|value => needs to be a special non-combined object
a['type'] = 'regkey' a['type'] = 'regkey'
# FIXME regkey|value => needs to be a special non-combined object
# special cases
if a['type'] in ('url', 'uri'):
yield(URL(url=a['value'], link_label=link_label))
return
# attribute is from an object, and a relation gives better understanding of the type of attribute # attribute is from an object, and a relation gives better understanding of the type of attribute
if a.get('object_relation') and mapping_misp_to_maltego.get(a['object_relation']): if a.get('object_relation') and mapping_misp_to_maltego.get(a['object_relation']):
entity_obj = mapping_misp_to_maltego[a['object_relation']][0] entity_obj = mapping_misp_to_maltego[a['object_relation']][0]
yield entity_obj(a['value'], labels=[Label('comment', a['comment'])]) yield entity_obj(a['value'], labels=[Label('comment', a.get('comment'))], link_label=link_label)
# combined attributes # combined attributes
elif '|' in a['type']: elif '|' in a['type']:
@ -140,29 +144,29 @@ def attribute_to_entity(a):
v_1, v_2 = a['value'].split('|') v_1, v_2 = a['value'].split('|')
if t_1 in mapping_misp_to_maltego: if t_1 in mapping_misp_to_maltego:
entity_obj = mapping_misp_to_maltego[t_1][0] entity_obj = mapping_misp_to_maltego[t_1][0]
labels = [Label('comment', a['comment'])] labels = [Label('comment', a.get('comment'))]
if entity_obj == File: if entity_obj == File:
labels.append(Label('hash', v_2)) labels.append(Label('hash', v_2))
yield entity_obj_to_entity(entity_obj, v_1, t_1, labels=labels) # TODO change the comment to include the second part of the regkey yield entity_obj_to_entity(entity_obj, v_1, t_1, labels=labels, link_label=link_label) # TODO change the comment to include the second part of the regkey
else: else:
yield UIMessage("Type {} of combined type {} not supported for attribute: {}".format(t_1, a['type'], a), type=UIMessageType.Inform) yield UIMessage("Type {} of combined type {} not supported for attribute: {}".format(t_1, a['type'], a), type=UIMessageType.Inform)
if t_2 in mapping_misp_to_maltego: if t_2 in mapping_misp_to_maltego:
entity_obj = mapping_misp_to_maltego[t_2][0] entity_obj = mapping_misp_to_maltego[t_2][0]
labels = [Label('comment', a['comment'])] labels = [Label('comment', a.get('comment'))]
if entity_obj == Hash: if entity_obj == Hash:
labels.append(Label('filename', v_1)) labels.append(Label('filename', v_1))
yield entity_obj_to_entity(entity_obj, v_2, t_2, labels=labels) # TODO change the comment to include the first part of the regkey yield entity_obj_to_entity(entity_obj, v_2, t_2, labels=labels, link_label=link_label) # TODO change the comment to include the first part of the regkey
else: else:
yield UIMessage("Type {} of combined type {} not supported for attribute: {}".format(t_2, a['type'], a), type=UIMessageType.Inform) yield UIMessage("Type {} of combined type {} not supported for attribute: {}".format(t_2, a['type'], a), type=UIMessageType.Inform)
# normal attributes # normal attributes
elif a['type'] in mapping_misp_to_maltego: elif a['type'] in mapping_misp_to_maltego:
entity_obj = mapping_misp_to_maltego[a['type']][0] entity_obj = mapping_misp_to_maltego[a['type']][0]
yield entity_obj_to_entity(entity_obj, a['value'], a['type'], labels=[Label('comment', a['comment'])]) yield entity_obj_to_entity(entity_obj, a['value'], a['type'], labels=[Label('comment', a.get('comment'))], link_label=link_label)
# not supported in our maltego mapping # not supported in our maltego mapping
else: else:
yield Unknown(a['value'], type=a['type'], labels=[Label('comment', a['comment'])]) yield Unknown(a['value'], type=a['type'], labels=[Label('comment', a.get('comment'))], link_label=link_label)
yield UIMessage("Type {} not fully supported for attribute: {}".format(a['type'], a), type=UIMessageType.Inform) yield UIMessage("Type {} not fully supported for attribute: {}".format(a['type'], a), type=UIMessageType.Inform)
if 'Galaxy' in a: if 'Galaxy' in a:
@ -176,16 +180,18 @@ def attribute_to_entity(a):
if t['name'].startswith('misp-galaxy'): if t['name'].startswith('misp-galaxy'):
continue continue
yield Hashtag(t['name']) yield Hashtag(t['name'])
# TODO : relationships from attributes - not yet supported by MISP yet, but there are references in the datamodel
def object_to_entity(o): def object_to_entity(o, link_label=None):
return MISPObject( return MISPObject(
o['name'], o['name'],
uuid=o['uuid'], uuid=o['uuid'],
event_id=int(o['event_id']), event_id=int(o['event_id']),
meta_category=o.get('meta_category'), meta_category=o.get('meta_category'),
description=o['description'], description=o.get('description'),
comment=o['comment'] comment=o.get('comment'),
link_label=link_label
) )
@ -201,6 +207,19 @@ def object_to_attributes(o):
for item in attribute_to_entity(a): for item in attribute_to_entity(a):
yield item yield item
# process relationships between objects and attributes
if 'ObjectReference' in o:
for ref in o['ObjectReference']:
# the reference is an Object
if ref.get('Object'):
ref['Object']['event_id'] = ref['event_id'] # FIXME remove this ugly workaround - object can't be requested directly from MISP, and to find a full object we need the event_id
yield object_to_entity(ref['Object'], link_label=ref['relationship_type'])
# the reference is an Attribute
if ref.get('Attribute'):
ref['Attribute']['event_id'] = ref['event_id'] # FIXME remove this ugly workaround - object can't be requested directly from MISP, 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
def get_attribute_in_object(o, attribute_type, drop=False): def get_attribute_in_object(o, attribute_type, drop=False):
'''Gets the first attribute of a specific type within an object''' '''Gets the first attribute of a specific type within an object'''