diff --git a/doc/README.md b/doc/README.md index ae8f33a..d0b0da2 100644 --- a/doc/README.md +++ b/doc/README.md @@ -19,8 +19,10 @@ These instructions have been tested on Ubuntu 18.04 LTS, but should be similar o MISP-Maltego tries to use as much as possible the default Paterva entities, or the most popular from the community. It however comes with a few custom entities: * **MISPEvent**: A representation of an *Event* on MISP, containing *Attributes* (MISP) / *Entities* (Maltego) * **MISPObject**: A way to group associated attributes in a structured way. -* **MISPGalaxy**: A *Tag* containing much more metadata. Please refer to the [MISP Galaxy -](https://github.com/MISP/misp-galaxy) for more information. **MITRE ATT&CK** is for example completely available through MISPGalaxy entities (see use-cases for an example) +* **MISPGalaxy**: A *Tag* containing much more metadata. Please refer to the [MISP Galaxy](https://github.com/MISP/misp-galaxy) for more information. **MITRE ATT&CK** is for example completely available through MISPGalaxy entities (see use-cases for an example) +* **Attack Technique**: Attack patterns or techniques, see [MITRE ATT&CK](https://attack.mitre.org/techniques/enterprise/) for more information. +* **Threat Actor**: Threat actor or intrusion sets. +* **Software**: Software is a generic term for custom or commercial code, operating system utilities, open-source software, or other tools used to conduct behavior modeled in ATT&CK. # Use Cases ## Transform on existing data diff --git a/doc/screenshot.png b/doc/screenshot.png index b8c1299..7901db1 100644 Binary files a/doc/screenshot.png and b/doc/screenshot.png differ diff --git a/setup.py b/setup.py index ac195cc..5110409 100755 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name='MISP_maltego', author='Christophe Vandeplas', # also update version in util.py - version='1.3.7', + version='1.4.0', author_email='christophe@vandeplas.com', maintainer='Christophe Vandeplas', url='https://github.com/MISP/MISP-maltego', diff --git a/src/MISP_maltego/resources/maltego/entities.mtz b/src/MISP_maltego/resources/maltego/entities.mtz index a497f85..77b5eea 100644 Binary files a/src/MISP_maltego/resources/maltego/entities.mtz and b/src/MISP_maltego/resources/maltego/entities.mtz differ diff --git a/src/MISP_maltego/transforms/common/entities.py b/src/MISP_maltego/transforms/common/entities.py index f73a64e..4880ed4 100644 --- a/src/MISP_maltego/transforms/common/entities.py +++ b/src/MISP_maltego/transforms/common/entities.py @@ -13,7 +13,10 @@ __status__ = 'Development' __all__ = [ 'MISPEvent', 'MISPObject', - 'MISPGalaxy' + 'MISPGalaxy', + 'ThreatActor', + 'Software', + 'AttackTechnique' ] @@ -55,3 +58,18 @@ class MISPGalaxy(Entity): cluster_value = StringEntityField('value', display_name='Value', matching_rule=MatchingRule.Loose) synonyms = StringEntityField('synonyms', display_name='Synonyms', matching_rule=MatchingRule.Loose) tag_name = StringEntityField('tag_name', display_name='Tag') + + +class ThreatActor(MISPGalaxy): + _category_ = 'MISP' + _namespace_ = 'misp' + + +class Software(MISPGalaxy): + _category_ = 'MISP' + _namespace_ = 'misp' + + +class AttackTechnique(MISPGalaxy): + _category_ = 'MISP' + _namespace_ = 'misp' diff --git a/src/MISP_maltego/transforms/common/util.py b/src/MISP_maltego/transforms/common/util.py index 1f30792..50cc397 100644 --- a/src/MISP_maltego/transforms/common/util.py +++ b/src/MISP_maltego/transforms/common/util.py @@ -1,7 +1,7 @@ from canari.maltego.entities import Hash, Domain, IPv4Address, URL, DNSName, AS, Website, NSRecord, PhoneNumber, EmailAddress, File, Person, Hashtag, Location, Company, Alias, Port, Twitter from canari.maltego.message import Label, LinkStyle, MaltegoException, Bookmark, LinkDirection, UIMessage, UIMessageType from distutils.version import StrictVersion -from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject, MISPGalaxy +from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject, MISPGalaxy, ThreatActor, Software, AttackTechnique from pymisp import ExpandedPyMISP as PyMISP import json import os @@ -12,7 +12,7 @@ import time # FIXME from galaxy 'to MISP Event' is confusing -__version__ = '1.3.7' # also update version in setup.py +__version__ = '1.4.0' # also update version in setup.py mapping_misp_to_maltego = { 'AS': [AS], @@ -82,6 +82,40 @@ mapping_galaxy_icon = { "user-secret": "threat_actor", } +mapping_galaxy_type = { + # 'amitt-misinformation-pattern': '', + 'android': Software, + 'backdoor': Software, + 'banker': Software, + 'botnet': Software, + # 'branded-vulnerability': '', + # 'cert-eu-govsector': '', + 'cloud-security': AttackTechnique, + 'exploit-kit': Software, + 'financial-fraud': AttackTechnique, + 'guidelines': AttackTechnique, + 'malpedia': Software, + 'microsoft-activity-group': ThreatActor, + 'mitre-attack-pattern': AttackTechnique, + # 'mitre-course-of-action': '', + 'mitre-intrusion-set': ThreatActor, + 'mitre-malware': Software, + 'mitre-tool': Software, + # 'preventive-measure': '', + 'ransomware': Software, + 'rat': Software, + # 'region': '', + # 'sector': '', + 'social-dark-patterns': AttackTechnique, + 'stealer': Software, + 'surveillance-vendor': ThreatActor, + # 'target-information': '', + 'tds': Software, + 'threat-actor': ThreatActor, + 'tool': Software +} + + tag_note_prefixes = ['tlp:', 'PAP:', 'de-vs:', 'euci:', 'fr-classif:', 'nato:'] misp_connection = None @@ -399,15 +433,20 @@ def galaxycluster_to_entity(c, link_label=None, link_direction=LinkDirection.Inp synonyms = '' galaxy_cluster = get_galaxy_cluster(c['uuid']) - icon_url = None - if 'icon' in galaxy_cluster: # map the 'icon' name from the cluster to the icon filename of the intelligence-icons repository - try: - icon_url = mapping_galaxy_icon[galaxy_cluster['icon']] - except Exception: - # it's not in our mapping, just ignore and leave the default Galaxy icon - pass + # map the 'icon' name from the cluster to the icon filename of the intelligence-icons repository + try: + icon_url = mapping_galaxy_icon[galaxy_cluster['icon']] + except KeyError: + icon_url = None + # it's not in our mapping, just ignore and leave the default Galaxy icon + pass - return MISPGalaxy( + # create the right sub-galaxy: ThreatActor, Software, AttackTechnique, ... or MISPGalaxy + try: + galaxy_type = mapping_galaxy_type[galaxy_cluster['type']] + except KeyError: + galaxy_type = MISPGalaxy + return galaxy_type( '{}\n{}'.format(c['type'], c['value']), uuid=c['uuid'], description=c.get('description'), diff --git a/src/MISP_maltego/transforms/galaxytoevent.py b/src/MISP_maltego/transforms/galaxytoevent.py index 9ad608a..92df5ef 100644 --- a/src/MISP_maltego/transforms/galaxytoevent.py +++ b/src/MISP_maltego/transforms/galaxytoevent.py @@ -1,6 +1,6 @@ from canari.maltego.transform import Transform # from canari.framework import EnableDebugWindow -from MISP_maltego.transforms.common.entities import MISPEvent, MISPGalaxy +from MISP_maltego.transforms.common.entities import MISPEvent, MISPGalaxy, ThreatActor, Software, AttackTechnique 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 from canari.maltego.message import UIMessageType, UIMessage, LinkDirection @@ -16,16 +16,6 @@ __email__ = 'christophe@vandeplas.com' __status__ = 'Development' -class GalaxyTo(Transform): - input_type = None - - def __init__(self): - self.request = None - self.response = None - self.config = None - self.misp = None - - # @EnableDebugWindow class GalaxyToEvents(Transform): """Expands a Galaxy to multiple MISP Events.""" @@ -48,14 +38,14 @@ class GalaxyToEvents(Transform): # @EnableDebugWindow -class GalaxyToRelations(Transform): - """Expans a Galaxy to related Galaxies and Clusters""" - input_type = MISPGalaxy +class GalaxyToTransform(Transform): + input_type = None - def do_transform(self, request, response, config): + def do_transform(self, request, response, config, type_filter=MISPGalaxy): response += check_update(config) maltego_misp_galaxy = request.entity + current_cluster = None if maltego_misp_galaxy.uuid: current_cluster = get_galaxy_cluster(uuid=maltego_misp_galaxy.uuid) elif maltego_misp_galaxy.tag_name: @@ -63,7 +53,7 @@ class GalaxyToRelations(Transform): elif maltego_misp_galaxy.name: current_cluster = get_galaxy_cluster(tag=maltego_misp_galaxy.name) - if not current_cluster: + if not current_cluster and maltego_misp_galaxy.name != '-': # maybe the user is searching for a cluster based on a substring. # Search in the list for those that match and return galaxy entities potential_clusters = search_galaxy_cluster(maltego_misp_galaxy.name) @@ -77,8 +67,8 @@ class GalaxyToRelations(Transform): response += UIMessage("Galaxy Cluster UUID not in local mapping. Please update local cache; non-public UUID are not supported yet.", type=UIMessageType.Inform) return response c = current_cluster - # update existing object + # update existing object galaxy_cluster = get_galaxy_cluster(c['uuid']) icon_url = None if 'icon' in galaxy_cluster: # map the 'icon' name from the cluster to the icon filename of the intelligence-icons repository @@ -100,12 +90,15 @@ class GalaxyToRelations(Transform): request.entity.tag_name = c['tag_name'] request.entity.icon_url = icon_url # response += request.entity + # find related objects if 'related' in current_cluster: for related in current_cluster['related']: related_cluster = get_galaxy_cluster(related['dest-uuid']) if related_cluster: - response += galaxycluster_to_entity(related_cluster, link_label=related['type']) + new_entity = galaxycluster_to_entity(related_cluster, link_label=related['type']) + if isinstance(new_entity, type_filter): + response += new_entity # find objects that are relating to this one for related in get_galaxies_relating(current_cluster['uuid']): related_link_label = '' @@ -113,5 +106,39 @@ class GalaxyToRelations(Transform): if rel_in_rel['dest-uuid'] == current_cluster['uuid']: related_link_label = rel_in_rel['type'] break - response += galaxycluster_to_entity(related, link_label=related_link_label, link_direction=LinkDirection.OutputToInput) + new_entity = galaxycluster_to_entity(related, link_label=related_link_label, link_direction=LinkDirection.OutputToInput) + if isinstance(new_entity, type_filter): + response += new_entity return response + + +class GalaxyToRelations(GalaxyToTransform): + """Expands a Galaxy to related Galaxies and Clusters""" + input_type = MISPGalaxy + + def do_transform(self, request, response, config, type_filter=MISPGalaxy): + return super().do_transform(request, response, config, type_filter) + + +class GalaxyToSoftware(GalaxyToTransform): + """Expands a Galaxy to related Software/Tool Galaxies""" + input_type = MISPGalaxy + + def do_transform(self, request, response, config, type_filter=Software): + return super().do_transform(request, response, config, type_filter) + + +class GalaxyToThreatActor(GalaxyToTransform): + """Expands a Galaxy to related ThreatActor Galaxies""" + input_type = MISPGalaxy + + def do_transform(self, request, response, config, type_filter=ThreatActor): + return super().do_transform(request, response, config, type_filter) + + +class GalaxyToAttackTechnique(GalaxyToTransform): + """Expands a Galaxy to related Attack Techniques Galaxies""" + input_type = MISPGalaxy + + def do_transform(self, request, response, config, type_filter=AttackTechnique): + return super().do_transform(request, response, config, type_filter)