Compare commits

..

No commits in common. "master" and "v1.4.0" have entirely different histories.

33 changed files with 427 additions and 1848 deletions

2
.gitignore vendored
View File

@ -10,5 +10,3 @@
# Locally genenerated mtz
/*.mtz
ansible/inventory.txt

View File

@ -1,47 +0,0 @@
# Install MISP-maltego remote transform as docker image.
#
# DO NOT USE THIS UNLESS YOU REALLY KNOW YOU NEED THIS
# - Most people usually probably want to use the local transforms
# - Others the 'ATT&CK - MISP' form the Transform Hub
#
# To build: "docker build MISP-maltego -t misp-maltego"
# To run: "docker run -p 8080:8080/tcp misp-maltego" if you want to run and enable portforwarding
# To stop: "docker ps" and "docker stop <instance_name>"
#
# Then configure your iTDS server
# - to create all the transforms and seeds and point to your docker.
# - export the objects, icons and machines to a mtz and associate to the seed
# Paired Configurations:
# - in Maltego > Export Config, and select
# -- Entities > MISP
# -- Icons > MISP + intelligence icons
# -- Machines
# Save as "paired_config.mtz", upload on TDS
# TODO
# - run the service with TLS, but that makes stuff more complex to automate
FROM python:3
RUN pip install PyMISP canari
# keep this for normal install
RUN pip install MISP-maltego
# use this for install from your own local git repo
# - first run "python setup.py sdist" to build the package
# - change the version number below
#COPY dist/MISP_maltego-1.4.1.tar.gz /usr/local/src/
#RUN pip install /usr/local/src/MISP_maltego-1.4.1.tar.gz
ENV LC_ALL='C.UTF-8'
ENV LANG='C.UTF-8'
ENV PLUME_ROOT='/var/plume'
RUN addgroup nobody
RUN canari install-plume --accept-defaults
RUN canari load-plume-package MISP_maltego --plume-dir /var/plume --accept-defaults
EXPOSE 8080/tcp
CMD ["/etc/init.d/plume", "start-docker"]

120
README.md
View File

@ -1,13 +1,8 @@
![logo](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/logo.png)
# Quick start guide
This is a [Maltego](https://www.paterva.com/web7/) [MISP](https://www.misp-project.org) integration tool allowing you to view (read-only) data from a MISP instance.
It also allows browsing through the [MITRE ATT&CK](https://attack.mitre.org/) entities. (no MISP connection needed)
This user guide should help you through the [installation](#installation) of **MISP-Maltego**, and should guide you how to use it through a few [use-cases](#use-cases). As this is a collaborative project, do not hesitate to propose changes, write other use-cases or raise [feature requests](https://github.com/MISP/MISP-maltego/issues) for missing features.
It also allows browsing through the [MITRE ATT&CK](https://attack.mitre.org/) entities.
## Quick start
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 Machine `EventToAll` or the transform `EventToAttributes`.
@ -15,121 +10,24 @@ Once installed you can start by creating a `MISPEvent` entity, then load the Mac
Alternatively initiate a transform on an existing Maltego entity.
The currently supported entities are: `AS`, `DNSName`, `Domain`, `EmailAddress`, `File`, `Hash`, `IPv4Address`, `NSRecord`, `Person`, `PhoneNumber`, `URL`, `Website`
For MITRE ATT&CK pivoting, feel free to start with an `Attack Technique`, `Software`, `Threat Actor`, or `MISPGalaxy`. Create your entity, enter a keyword such as `%gama%` and use the `Search in MISP` transform to get started.
## Installation
### Transform Hub
Open the Transform Hub, locate **ATT&CK - MISP** and press the **Install** button.
Your transforms will go through Paterva's servers and ours. See the [Transform Hub Disclaimer](https://github.com/MISP/MISP-maltego/blob/master/TRANSFORM_HUB_DISCLAIMER.md) for more information.
- ATT&CK transforms do not require a MISP server or API key to be configured.
- MISP transforms requires your MISP server to be reachable from the internet! To enter your MISP server URL and key click **Details** on the Transform Hub item and then **Settings** at the bottom right.
### Local Transform Installation
If you trust nobody, or just want to connect to your local MISP server you can install everything as local transforms.
These instructions have been tested on Ubuntu 18.04 LTS, but should be similar on other systems.
1. Download and install [Maltego](https://www.paterva.com/web7/downloads.php)
2. Install using pip: `sudo pip3 install MISP-maltego`
3. Generate the Maltego bundle: `canari create-profile MISP_maltego`
4. Import this bundle in Maltego.
1. Open Maltego
2. Click on the home button (Maltego icon, top-left corner).
3. Click on 'Import'
4. Click on 'Import Configuration'.
5. Load the `MISP_maltego.mtz` file and follow the prompts.
5. Edit `$HOME/.canari/MISP_maltego.conf` and enter your `misp_url` and `misp_key`
## Custom Entities
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)
* **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
In this use case we will be using already existing entities and will initiate a transform using MISP. The currently supported entities are: `AS`, `DNSName`, `Domain`, `EmailAddress`, `File`, `Hash`, `IPv4Address`, `NSRecord`, `Person`, `PhoneNumber`, `URL`, `Website`.
Example:
* create an entity `domain` with the value `1dnscontrol.com`.
* right click and choose *Local Transforms* > *MISP_maltego* > *Domain To Event*
![animated screenshot](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase1-transform.gif)
* continue loading transforms on the *MISP Event*
## Transform from MISP Event ID
While MISP already has a graphing capability we would like to use the power of Maltego to look at the data and expand the work.
* Create a *MISP Event* and give it an `event id`, or `UUID`
* One **manual** way is to right click and choose *Local Transforms* > *MISP_maltego* > *Event To Attributes*
* Notice the event is transformed to *Attributes*, *Objects*, *Tags*, *Galaxies* and related *MISP Events*
* You can now further transform on an *Object* > *Object To Attributes* and see the content of the object
![machine transforms](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase2-manual.gif)
* Alternatively you can also use the **Maltego Machine** to speed up things.
* Click on the *MISP Event* and in the left menu choose *Event to All* in the *Machines* section.
![machine transforms](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase2-machine-menu.png)
* Notice that the whole event, objects and such will get expanded with data from your MISP instance.
![animated screenshot](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase2-machine.gif)
* You can now further transform on any data.
## Which data is already in MISP?
If you use MISP as central database it can be quite convenient to know which data is present in MISP, and which data is not; especially after using a number of other transforms.
To permit this MISP-Maltego will always add a green bookmark to all the data that is present in MISP.
![green bookmark](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase3-bookmark.png)
## Searching in MISP using keywords
As with the MISP attribute search through the MISP Web UI you can use `%` wildcards at the front and end to specify the substring. You might be tempted to always use `%keyword%`, but bare in mind how databases indexes work; a search for `keyword%` will always be much faster than `%keyword`.
![Search in MISP](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/search_in_misp.gif)
## Transform from Galaxy
Galaxies are actually tags with much more contextual data. Examples are threat actors, malware families, but also the whole MITRE ATT&CK data is available as Galaxy. All this data comes from the [MISP Galaxy](https://github.com/MISP/misp-galaxy) repository. Today the integration is not done using a MISP server because of limitations in MISP.
You might encounter Galaxies when transforming from MISP Events or Attributes. An alternative use-case is by starting immediately from a Galaxy.
There are 3 ways to manually create a good Galaxy Entity.
1. Using a find capability (see below)
2. Create the Galaxy and set the UUID. You can find the UUIDs in the [MISP Galaxy](https://github.com/MISP/misp-galaxy) repository.
3. Create the Galaxy with the right tag name; for example: `misp-galaxy:`
To use the magical search feature:
* Create a *MISP Galaxy* and type the keyword as value.
* Run the *Galaxy To Relation* transform, notice the search results will appear as connected entities
* Remove the non-relevant entities, including the your search-keyword
![animated galaxy search](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase4-galaxy-search.gif)
## Visualize MITRE ATT&CK
Apply the same steps for MITRE ATT&CK browsing:
![animated ATTACK](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase5-attack.gif)
You might end up with such a graph:
![ATTACK](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase5-attack.png)
## Visualise common ATT&CK patterns
Having access to a large amount of Threat information through MISP Threat Sharing communities gives you outstanding opportunities to aggregate this information and take the process of trying to understand how all this data fits together telling a broader story to the next level. We are transforming technical data or indicators of compromise (IOCs) into cyber threat intelligence. This is where the analytical challenge begins. [[read more](https://www.misp-project.org/2019/10/27/visualising_common_patterns_attack.html)]
## Massively large MISP event? Think before you transform.
In some communities such as the [COVID-19 MISP](https://www.misp-project.org/covid-19-misp/) some events contain tens of thousands attributes. Loading all the attributes from these events might not be a good idea if you do not have Maltego XL.
You can see the amount of attributes and objects in the Event properties, so you can think before you click:
![object count](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/event_count_attr1.png)![attribute count](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/event_count_attr2.png)
## Installation and User Guide:
Installation is fairly easy by using `pip`, just read the steps in the [documentation](https://github.com/MISP/MISP-maltego/blob/master/doc/README.md).
The [User Guide](https://github.com/MISP/MISP-maltego/blob/master/doc/README.md#use-cases) gives some example use-cases.
## Screenshot
![Screenshot](https://github.com/MISP/MISP-maltego/blob/master/doc/screenshot.png)
![ATT&CK](https://github.com/MISP/MISP-maltego/blob/master/doc/attack.jpg)
## License
This software is licensed under [GNU Affero General Public License version 3](http://www.gnu.org/licenses/agpl-3.0.html)
* Copyright (C) 2018-2024 Christophe Vandeplas
* Copyright (C) 2018 Christophe Vandeplas
Note: Before being rewritten from scratch this project was maintained by Emmanuel Bouillon. The code is available in the `v1` branch.
The logo is CC-BY-SA and was designed by Françoise Penninckx
The icons in the intelligence-icons folder are from [intelligence-icons](https://github.com/MISP/intelligence-icons) licensed CC-BY-SA - Françoise Penninckx, Brett Jordan

View File

@ -1 +0,0 @@
For security issues, please refer to the instructions in the main MISP repository: https://github.com/MISP/MISP/

View File

@ -1,21 +0,0 @@
# MISP Maltego Remote Transform Disclaimer
**When using the MISP Maltego transforms using the Transform Hub (not the locally installed version) you need to know you are are sending data, including your MISP URL and API key to 3rd parties.**
The public Transform Distribution Server (TDS) is located on the Internet and is free for all to use. Its a convenient way to immediately start writing remote transforms. Since this server is located on Patervas infrastructure data (entity, and settings) will be flowing from the Maltego GUI to this server. Paterva states they DO NOT store the details of your transforms (entities, MISP URL, API KEY).
Finally it will flow further to a server managed by the MISP-maltego developer(s), where the transform code runs and for MISP transforms connects to YOUR server. We DO NOT store or look at the details of your transforms (entities, MISP URL, API KEY). As you can see in the code (open source), this data is only used live in memory to provide the transform functionality. The only reasons why we would be seeing this data is by accident; while troubleshooting or by unintentional mis-configuration.
We do keep standard HTTP logs for troubleshooting and anonymous statistics, although these contain the IP addresses of Paterva's TDS server, and not yours.
**DO NOT use these Transform Hub transforms if you do not agree or if this is in violation with your MISP community.**
**If so, feel free to use the MISP-Maltego transforms locally, where all the code runs on your own system. Installation instructions can be found [here](https://github.com/MISP/MISP-maltego/blob/master/README.md#installation).**
You can also run this on your own iTDS server if you have the license. Have a look at the [Dockerfile](https://github.com/MISP/MISP-maltego/blob/master/Dockerfile) for more info.
## More info
For more information please read Paterva's and Canari's documentation:
* [http://www.canariproject.com/en/latest/canari.quickstart.html#making-transforms-remote](http://www.canariproject.com/en/latest/canari.quickstart.html#making-transforms-remote)
* [https://docs.maltego.com/support/solutions/articles/15000020198-what-is-itds-](https://docs.maltego.com/support/solutions/articles/15000020198-what-is-itds-)
* [https://www.paterva.com/buy/maltego-servers.php](https://www.paterva.com/buy/maltego-servers.php)

View File

@ -1,10 +0,0 @@
This folder is meant to contain ansible automation scripts to install the MISP-maltego server.
Users normally do not need to use this, except if you're using a private transform server.
Paired Configurations:
- in Maltego > Export Config, and select
-- Entities > MISP
-- Icons > MISP + intelligence icons
-- Machines
Save as "paired_config.mtz", upload on TDS

View File

@ -1,41 +0,0 @@
server {
# dummy server for let's encrypt
listen 80;
listen [::]:80;
root /var/www/html;
server_name _;
try_files $uri @redirect;
location @redirect {
return 302 https://github.com/MISP/MISP-maltego;
}
}
server {
# true reverse proxy for plume
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
ssl_certificate /etc/letsencrypt/live/misp-maltego.misp-project.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/misp-maltego.misp-project.org/privkey.pem;
root /var/www/html;
server_name _;
location /munin/ {
alias /var/cache/munin/www/;
index index.html;
allow 127.0.0.1;
deny all;
}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://127.0.0.1:8080;
proxy_intercept_errors on;
error_page 404 = @redirect; # redirect to the github when page not found
}
location @redirect {
return 302 https://github.com/MISP/MISP-maltego;
}
}

View File

@ -1,255 +0,0 @@
---
# Install MISP-maltego remote transform using ansible.
#
# DO NOT USE THIS UNLESS YOU REALLY KNOW YOU NEED THIS
# - Most people usually probably want to use the local transforms
# - Others the 'ATT&CK - MISP' from the Transform Hub
#
# First install your ubuntu system,
# Then run ansible-playbook -i inventory.txt plume.yaml
#
# Then configure your iTDS server
# - to create all the transforms and seeds and point to your docker.
# - export the objects, icons and machines to a mtz and associate to the seed
# Paired Configurations:
# - in Maltego > Export Config, and select
# -- Entities > MISP
# -- Icons > MISP + intelligence icons
# -- Machines
# Save as "paired_config.mtz", upload on TDS
- hosts: all
become: yes
vars:
misp_maltego_version: 1.4.4 # TODO change this !!!
host_locale: en_US.UTF-8
host_locale_dict: {
LANG: "{{ host_locale }}",
LC_COLLATE: "{{ host_locale }}",
LC_CTYPE: "{{ host_locale }}",
LC_MESSAGES: "{{ host_locale }}",
LC_MONETARY: "{{ host_locale }}",
LC_NUMERIC: "{{ host_locale }}",
LC_TIME: "{{ host_locale }}",
LC_ALL: "{{ host_locale }}",
}
tasks:
- name: install python3-pip
package:
name: python3-pip
state: present
- name: install python libs
pip:
executable: /usr/bin/pip3
name: ['canari', 'pymisp']
state: latest
# NGINX reverse proxy
# ######
- name: install nginx
package:
name: nginx
state: present
- name: letsencrypt certbot ppa
apt_repository:
repo: ppa:certbot/certbot
- name: letsencrypt certbot install
package:
name: ['certbot', 'python-certbot-nginx']
state: present
# FIXME generate the cert automagically, while answering the questions
# creates: /etc/letsencrypt/live/misp-maltego.misp-project.org/privkey.pem
# Requires input:
# - email address
# - agree terms
# - no sharing email with EFF
- name: nginx disable default config
file:
path: /etc/nginx/sites-enabled/default
state: absent
- name: nginx copy config
copy:
src: nginx.conf
dest: /etc/nginx/sites-available/plume
notify: restart nginx
- name: nginx enable plume config
file:
src: /etc/nginx/sites-available/plume
dest: /etc/nginx/sites-enabled/plume
state: link
notify: restart nginx
- name: letsencrypt auto-renew
cron:
name: "letsencrypt auto-renew SSL certificate"
special_time: daily
job: "/usr/sbin/certbot -q renew"
# PLUME
#######
- name: create nobody group - needed by plume
group:
name: nobody
state: present
- name: install canari plume
shell:
cmd: canari install-plume --accept-defaults
creates: /var/plume/canari.conf
environment:
LC_ALL: 'C.UTF-8'
LANG: 'C.UTF-8'
- name: Start service plume at boot
file:
src: /etc/init.d/plume
dest: /etc/rc{{item}}.d/S20plume
state: link
with_items:
- 3
- 4
- 5
# LATER migrate to systemd service
# use the public pip package
- name: install MISP-maltego
pip:
executable: pip3
name: ['MISP-maltego']
state: latest
notify: restart plume
# use local git repo instead, useful for development
# - name: bundle MISP-maltego
# delegate_to: 127.0.0.1
# command:
# cmd: python3 setup.py sdist
# chdir: ../
# become: no
# - name: copy MISP-maltego
# copy:
# src: ../dist/MISP_maltego-{{misp_maltego_version}}.tar.gz
# dest: /usr/local/src/
# - name: install MISP-maltego
# pip:
# executable: /usr/bin/pip3
# name: file:///usr/local/src/MISP_maltego-{{misp_maltego_version}}.tar.gz
# state: forcereinstall
# environment: "{{host_locale_dict}}"
# notify: restart plume
# - name: remove local MISP-maltego bundle
# delegate_to: 127.0.0.1
# file:
# path: ../dist/MISP_maltego-{{misp_maltego_version}}.tar.gz
# state: absent
# become: no
- name: load plume package
command:
cmd: canari load-plume-package MISP_maltego --plume-dir /var/plume --accept-defaults
chdir: /var/plume
creates: /var/plume/MISP_maltego.conf
environment:
LC_ALL: 'C.UTF-8'
LANG: 'C.UTF-8'
PLUME_ROOT: '/var/plume'
notify: restart plume
- name: Start service plume, if not started
service:
name: plume
state: started
# MONITORING
#############
- name: install munin
package:
name: ['munin', 'munin-node', 'munin-plugins-extra']
- name: munin - enabling plugins
file:
state: link
src: '/usr/share/munin/plugins/{{item}}'
dest: '/etc/munin/plugins/{{item}}'
loop:
- nginx_request
- nginx_status
notify: restart munin-node
- name: munin - service active and running
service:
name: munin-node
state: started
enabled: yes
# FIREWALLING
#############
- name: firewall logging
ufw:
logging: 'low'
- name: firewall inbound rate limited
ufw:
rule: limit
port: '2245' # ssh
proto: tcp
direction: in
- name: firewall inbound
ufw:
rule: allow
port: "{{item}}"
proto: tcp
direction: in
loop:
- '80' # nginx
- '443' # nginx plume
- '25324' # monitoring
- name: firewall outbound
ufw:
rule: allow
port: "{{ item.port }}"
proto: "{{ item.proto }}"
direction: out
loop:
- { port: '53', proto: 'udp'}
- { port: '123', proto: 'udp'}
- { port: '53', proto: 'tcp'}
- { port: '80', proto: 'tcp'}
- { port: '443', proto: 'tcp'}
- { port: '32526', proto: 'tcp'} # waagent
- name: firewall default rule
ufw:
state: enabled
default: deny
direction: '{{ item }}'
loop:
- incoming
- outgoing
handlers:
- name: restart plume
service:
name: plume
state: restarted
- name: restart nginx
service:
name: nginx
state: restarted
- name: restart munin-node
service:
name: munin-node
state: restarted

View File

@ -1,59 +0,0 @@
---
# Install MISP-maltego remote transform using ansible.
- hosts: all
become: yes
vars:
misp_maltego_version: 1.4.5 # TODO change this !!!
host_locale: en_US.UTF-8
host_locale_dict: {
LANG: "{{ host_locale }}",
LC_COLLATE: "{{ host_locale }}",
LC_CTYPE: "{{ host_locale }}",
LC_MESSAGES: "{{ host_locale }}",
LC_MONETARY: "{{ host_locale }}",
LC_NUMERIC: "{{ host_locale }}",
LC_TIME: "{{ host_locale }}",
LC_ALL: "{{ host_locale }}",
}
tasks:
# use the public pip package
- name: install MISP-maltego
pip:
executable: pip3
name: ['MISP-maltego']
state: latest
notify: restart plume
# use local git repo instead, useful for development
# - name: bundle MISP-maltego
# delegate_to: 127.0.0.1
# command:
# cmd: python3 setup.py sdist
# chdir: ../
# become: no
# - name: copy MISP-maltego
# copy:
# src: ../dist/MISP_maltego-{{misp_maltego_version}}.tar.gz
# dest: /usr/local/src/
# - name: install MISP-maltego
# pip:
# executable: /usr/bin/pip3
# name: file:///usr/local/src/MISP_maltego-{{misp_maltego_version}}.tar.gz
# state: forcereinstall
# environment: "{{host_locale_dict}}"
# notify: restart plume
# - name: remove local MISP-maltego bundle
# delegate_to: 127.0.0.1
# file:
# path: ../dist/MISP_maltego-{{misp_maltego_version}}.tar.gz
# state: absent
# become: no
handlers:
- name: restart plume
service:
name: plume
state: restarted

78
doc/README.md Normal file
View File

@ -0,0 +1,78 @@
# MISP-Maltego User Guide
This user guide should help you through the installation of **MISP-Maltego**, and should guide you how to use it through a few use-cases. As this is a collaborative project, do not hesitate to propose changes, write other use-cases or raise [feature requests](https://github.com/MISP/MISP-maltego/issues) for missing features.
## Installation
These instructions have been tested on Ubuntu 18.04 LTS, but should be similar on other systems.
1. Download and install [Maltego](https://www.paterva.com/web7/downloads.php)
2. Install using pip: `sudo pip3 install MISP-maltego`
3. Generate the Maltego bundle: `canari create-profile MISP_maltego`
4. Import this bundle in Maltego.
1. Open Maltego
2. Click on the home button (Maltego icon, top-left corner).
3. Click on 'Import'
4. Click on 'Import Configuration'.
5. Load the `MISP_maltego.mtz` file and follow the prompts.
5. Edit `$HOME/.canari/MISP_maltego.conf` and enter your `misp_url` and `misp_key`
## Custom Entities
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)
* **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
In this use case we will be using already existing entities and will initiate a transform using MISP. The currently supported entities are: `AS`, `DNSName`, `Domain`, `EmailAddress`, `File`, `Hash`, `IPv4Address`, `NSRecord`, `Person`, `PhoneNumber`, `URL`, `Website`.
Example:
* create an entity `domain` with the value `1dnscontrol.com`.
* right click and choose *Local Transforms* > *MISP_maltego* > *Domain To Event*
![animated screenshot](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase1-transform.gif)
* continue loading transforms on the *MISP Event*
## Transform from MISP Event ID
While MISP already has a graphing capability we would like to use the power of Maltego to look at the data and expand the work.
* Create a *MISP Event* and give it an `event id`, or `UUID`
* One **manual** way is to right click and choose *Local Transforms* > *MISP_maltego* > *Event To Attributes*
* Notice the event is transformed to *Attributes*, *Objects*, *Tags*, *Galaxies* and related *MISP Events*
* You can now further transform on an *Object* > *Object To Attributes* and see the content of the object
![machine transforms](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase2-manual.gif)
* Alternatively you can also use the **Maltego Machine** to speed up things.
* Click on the *MISP Event* and in the left menu choose *Event to All* in the *Machines* section.
![machine transforms](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase2-machine-menu.png)
* Notice that the whole event, objects and such will get expanded with data from your MISP instance.
![animated screenshot](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase2-machine.gif)
* You can now further transform on any data.
## Which data is already in MISP?
If you use MISP as central database it can be quite convenient to know which data is present in MISP, and which data is not; especially after using a number of other transforms.
To permit this MISP-Maltego will always add a green bookmark to all the data that is present in MISP.
![green bookmark](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase3-bookmark.png)
## Transform from Galaxy
Galaxies are actually tags with much more contextual data. Examples are threat actors, malware families, but also the whole MITRE ATT&CK data is available as Galaxy. All this data comes from the [MISP Galaxy](https://github.com/MISP/misp-galaxy) repository. Today the integration is not done using a MISP server because of limitations in MISP.
You might encounter Galaxies when transforming from MISP Events or Attributes. An alternative use-case is by starting immediately from a Galaxy.
There are 3 ways to manually create a good Galaxy Entity.
1. Using a find capability (see below)
2. Create the Galaxy and set the UUID. You can find the UUIDs in the [MISP Galaxy](https://github.com/MISP/misp-galaxy) repository.
3. Create the Galaxy with the right tag name; for example: `misp-galaxy:`
To use the magical search feature:
* Create a *MISP Galaxy* and type the keyword as value.
* Run the *Galaxy To Relation* transform, notice the search results will appear as connected entities
* Remove the non-relevant entities, including the your search-keyword
![animated galaxy search](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase4-galaxy-search.gif)
## Visualize MITRE ATT&CK
Apply the same steps for MITRE ATT&CK browsing:
![animated ATTACK](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase5-attack.gif)
You might end up with such a graph:
![ATTACK](https://raw.githubusercontent.com/MISP/MISP-maltego/master/doc/img/usecase5-attack.png)

BIN
doc/attack.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 192 KiB

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 KiB

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,105 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1"
id="svg108" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="misp-logo.svg" inkscape:version="0.92.3 (2405546, 2018-03-11)"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="-8.6 13.1 128 128"
enable-background="new -8.6 13.1 128 128" xml:space="preserve">
<sodipodi:namedview fit-margin-top="0" inkscape:pageshadow="2" inkscape:pageopacity="0.0" inkscape:current-layer="layer1" inkscape:window-maximized="1" inkscape:window-y="27" inkscape:window-x="0" inkscape:window-height="1025" inkscape:window-width="1920" fit-margin-right="0" fit-margin-bottom="0" inkscape:document-units="mm" showgrid="false" inkscape:zoom="0.35" borderopacity="1.0" pagecolor="#ffffff" inkscape:cy="96.511905" bordercolor="#666666" fit-margin-left="0" inkscape:cx="-155.06677" id="base">
</sodipodi:namedview>
<g>
<path fill="#C5411E" d="M73.1,104.6c-2.2-2-2.2-3.7-0.8-6.2c0-0.4-0.1-0.7-0.1-1c-0.3-0.3-0.6-0.7-0.9-1.1
c-0.6-1.1-0.9-1.9-1.1-3.1c-0.1-0.4,0-1.6,0-1.6l-13.7,8l2.4-9.3c-2.7-0.7-5.7-2.1-5.7-4.7L53,83.4c-4.2-4-8-7.5-12.3-11.6
c3.4-2.2,6.2-3.8,8.8-5.8c1.4-1,2-1.5,3.2-2.6V63c-0.3-5.7,4.7-8,6.2-8.4c0.6-1,0.6-1.3,1.1-3.8c0.7-3.9,0.7-7.6,0-11H44.9
c0.7,1.7,1.1,3.5,1.1,5.6c0.2,6.6-3.7,10.8-8.9,14c-7,4.3-5.1,4.6-10.4-1.5c-2.4-2.8-4.3-6.3-5.4-9.8c-1-3-0.8-5.8,0.4-8.3h-3.8
l8.3-19.6c-1.9,0.5-3.8,1.2-5.7,2.2C11,26.9,5.8,34.8,5.6,45.6c-0.2,8.4,3.8,15,8.9,21.1c1.2,1.4,2.3,2.8,3.2,3.9
c-5.3,4.1-10.6,7.5-15,11.7c-7.9,7.5-10.8,17-8.5,27.7c3,14,12.4,22.1,26.1,24.8c4.8,1,13.9,0.9,13.9,0.9l0-15.2
c-3.3,0.6-6.9,0.4-10.7-0.8c-7.9-2.4-12.9-9.3-12.9-21.3c0.1-4.9,5.4-10.2,12.1-14.6c5.2-3.4,5.2-3.4,9.6,1
c5.7,5.9,11.4,11.8,17.2,17.5c1.8,1.7,2,3,0.7,5.2c-1.6,2.7-3.3,5-5.1,6.9l2.2,3.6l3.9-6.5h29.3C78.1,109.2,75.6,106.9,73.1,104.6z
"/>
</g>
<g>
<g id="g316" transform="translate(385.579,529.5928)">
<g>
<path fill="#2FA1DB" d="M-270.5-457v20.3c0,2.5-1.3,5.1-4.3,5.1h-0.4l1.4,5.6l-9.5-5.6h-24.9c-1.4,0-2.8-0.6-3.9-1.5
c-0.5-0.4-1-1-1.3-1.5v0c-0.4-0.7-0.6-1.4-0.6-2.1v-0.9h32.6c3.3,0,5-2.6,5-5.4v-18.3h1.7C-271.8-461.4-270.5-459.5-270.5-457z"
/>
</g>
</g>
<g id="g320-4" transform="translate(349.7253,569.1839)">
<g>
<path fill="#2FA1DB" d="M-250.4-513.7h-37.9c-1,0-2.1,0.3-3.1,0.8c-2.2,1.1-4.1,3.1-4.1,5.1v22.9c0,2.6,3.3,4.5,6.4,4.8l-2,7.7
l13-7.6h27.7c3.3,0,6.4-2,6.4-4.9v-22.9C-244-510.7-247.1-513.7-250.4-513.7z M-280.6-494.9c-0.5,0.4-1.2,0.6-1.9,0.6
c-0.3,0-0.6,0-0.9-0.1c-1.4-0.4-2.5-1.7-2.5-3.3c0-1.9,1.5-3.4,3.4-3.4c0.8,0,1.6,0.3,2.2,0.8c0.7,0.6,1.2,1.4,1.2,2.4
c0,0.1,0,0.2,0,0.3C-279.1-496.5-279.7-495.5-280.6-494.9z M-266.3-497.7c0,1.1-0.6,2.1-1.4,2.7c-0.6,0.4-1.3,0.7-2,0.7
c-1.9,0-3.4-1.5-3.4-3.4s1.5-3.4,3.4-3.4C-267.8-501.2-266.3-499.6-266.3-497.7C-266.3-497.7-266.3-497.7-266.3-497.7z
M-256.9-494.3c-1.9,0-3.4-1.5-3.4-3.4s1.5-3.4,3.4-3.4c1.9,0,3.4,1.5,3.4,3.4S-255-494.3-256.9-494.3z"/>
</g>
</g>
</g>
<g>
<g id="path1078" inkscape:connector-curvature="0">
<polygon fill="#5F6062" points="35.5,112.9 42.6,112.9 47.3,120.6 52,112.9 59.1,112.9 59.1,134.2 52.1,134.2 52.1,123.8
47.3,131.3 47.2,131.3 42.4,123.8 42.4,134.2 35.5,134.2 "/>
</g>
<g id="path1080" inkscape:export-ydpi="100" inkscape:export-xdpi="100" inkscape:export-filename="/home/adulau/misp.png" inkscape:connector-curvature="0">
<rect x="62.5" y="112.9" fill="#5F6062" width="7" height="21.3"/>
</g>
<g id="g1084">
<g id="g1090" transform="translate(297.5875,384.2569)">
<path id="path1092" inkscape:connector-curvature="0" fill="#5F6062" d="M-226.3-253.6l3.7-4.5c2.3,1.7,4.9,2.5,7.3,2.5
c1.3,0,1.8-0.3,1.8-0.9v-0.1c0-0.6-0.7-0.9-2.9-1.4c-4.7-1-8.8-2.3-8.8-6.7v-0.1c0-4,3.1-7.1,8.9-7.1c4,0,7,1,9.4,2.9l-3.4,4.7
c-2-1.4-4.3-2.1-6.3-2.1c-1,0-1.5,0.4-1.5,0.9v0.1c0,0.6,0.6,0.9,2.8,1.3c5.4,1,8.9,2.5,8.9,6.8v0.1c0,4.4-3.6,7.1-9.2,7.1
C-219.8-250.1-223.6-251.3-226.3-253.6L-226.3-253.6z"/>
</g>
<g id="g1094" transform="translate(340.0289,418.4302)">
<path id="path1096" inkscape:connector-curvature="0" fill="#5F6062" d="M-237.2-305.6h-9.4v21.2h7v-6h2.3c5.7,0,9.5-2.7,9.5-7.7
v-0.1C-227.8-303.1-231.6-305.6-237.2-305.6z M-234.7-297.6c0,1.5-1.2,2.3-3,2.3h-1.9v-4.7h1.9
C-235.9-300-234.7-299.2-234.7-297.6L-234.7-297.6z"/>
</g>
</g>
</g>
<g>
<g>
<path fill="#C5411E" d="M75.2,32.9c-0.4-0.3-0.4-0.6-0.1-1c0.9-1.6,1.7-3.3,2.6-4.9c0.1-0.2,0.2-0.4,0.3-0.7c-0.7,0-1.4,0-2,0
c-0.6-0.1-0.9,0.2-1.2,0.7c-0.6,1.2-1.2,2.4-1.8,3.5c-1.1-1.1-2.2-2.1-3.4-3.2c0.6-0.4,1.1-0.7,1.5-1c0.9-0.6,1.6-1.5,1.8-2.6
c0.3-1.8-0.2-3.3-1.6-4.4c-1.6-1.2-3.3-1.3-5.1-0.5c-1.6,0.8-2.5,2.1-2.5,4c0,1.4,0.6,2.6,1.5,3.6c0.2,0.2,0.4,0.5,0.5,0.7
c-0.9,0.7-1.8,1.3-2.6,2c-1.3,1.3-1.8,2.9-1.5,4.7c0.5,2.4,2.1,3.8,4.4,4.2c2.5,0.5,4.7,0.1,6.3-2.1c0.3-0.4,0.5-0.8,0.8-1.2
c1.2,1.1,2.3,2.2,3.4,3.4c0.6-0.6,1.2-1.2,1.9-2C77.5,35,76.4,33.9,75.2,32.9z M67.1,21c0.7-0.6,1.5-0.6,2.3-0.2
c0.8,0.4,1.2,1.1,1.2,2c0,1.1-0.6,1.8-1.5,2.4c-1.2,0.7-0.9,0.8-1.8-0.3c-0.4-0.5-0.7-1.1-0.9-1.7C66.1,22.4,66.4,21.6,67.1,21z
M71.3,33.4c-1.1,1.9-2.7,2.7-4.5,2.1c-1.3-0.4-2.2-1.6-2.2-3.6c0-0.8,0.9-1.7,2.1-2.5c0.9-0.6,0.9-0.6,1.6,0.2c1,1,1.9,2,2.9,3
C71.5,32.8,71.5,33,71.3,33.4z"/>
</g>
<g>
<path fill="#C5411E" d="M98.1,32c-0.6,3.5-5.3,6.4-9.9,6.1c-5-0.3-8.9-4.5-9-9.7c-0.1-5.1,3.6-9.4,8.7-10
c4.4-0.5,9.5,2.5,10.2,6.2c-0.9,0-1.9,0-2.8,0c-0.2,0-0.4-0.2-0.5-0.4c-1.9-2.8-5-3.9-8-2.8c-2.9,1-4.9,4-4.7,7.3
c0.3,4.3,3.6,7.3,7.8,6.8c2-0.2,3.6-1.3,4.7-2.9c0.3-0.4,0.5-0.5,1-0.5C96.5,32,97.3,32,98.1,32z"/>
</g>
<g>
<path fill="#C5411E" d="M39.7,37.5c-2.5-6.1-4.9-12.2-7.4-18.4c-0.1-0.2-0.4-0.4-0.6-0.4c-0.6-0.1-1.1,0-1.7,0
c-0.5,0-0.7,0.2-0.8,0.6c-1.6,4-3.3,8.1-5,12.1c-0.9,2.1-1.7,4.2-2.6,6.4c1,0,1.8,0,2.7,0c0.5,0,0.6-0.2,0.8-0.6
c0.4-1.2,0.9-2.3,1.3-3.5c0.1-0.4,0.4-0.5,0.8-0.5c2.4,0,4.7,0,7.1,0c0.4,0,0.6,0.1,0.8,0.5c0.5,1.2,0.9,2.4,1.4,3.5
c0.1,0.2,0.2,0.5,0.4,0.5c1,0,2,0,3,0C39.7,37.6,39.7,37.5,39.7,37.5z M27.4,30.8c1.1-2.8,2.1-5.6,3.1-8.4c0.1,0,0.1,0,0.2,0
c1.1,2.8,2.1,5.5,3.2,8.4H27.4z"/>
</g>
<g>
<path fill="#C5411E" d="M113.8,37.8c-1.3,0-2.4,0-3.5,0c-0.2,0-0.4-0.3-0.6-0.5c-2.1-2.5-4.2-5.1-6.3-7.7
c-0.1-0.2-0.3-0.3-0.5-0.6v8.7h-3c0-0.2,0-0.5,0-0.7c0-5.9,0-11.7,0-17.6c0-0.6,0.2-0.8,0.8-0.7c0.5,0,0.9,0,1.4,0
c0.9,0,0.9,0,0.9,0.9v7.9c0.1,0,0.1,0,0.2,0.1c0.1-0.2,0.3-0.4,0.4-0.5c2-2.6,4-5.2,6-7.8c0.3-0.4,0.6-0.5,1-0.5
c0.8,0,1.6,0,2.5,0c-0.2,0.3-0.4,0.5-0.5,0.7c-2.2,2.8-4.3,5.5-6.5,8.3c-0.3,0.4-0.3,0.7,0,1.1c2.4,2.8,4.8,5.6,7.2,8.4
C113.4,37.3,113.5,37.5,113.8,37.8z"/>
</g>
<g>
<path fill="#C5411E" d="M49.1,19.5c0,0.6,0,1.2,0,1.9c-1.3,0-2.4,0-3.6,0c-0.6,0-0.8,0.2-0.8,0.8c0,5,0,9.9,0,14.9
c0,0.6-0.2,0.8-0.8,0.8c-0.6,0-1.1,0-1.7,0c-0.5,0-0.6-0.2-0.6-0.6c0-3,0-6,0-9c0-2,0-4,0-6.1c0-0.6-0.1-0.8-0.7-0.8c-1,0-2,0-3,0
c-0.5,0-0.7-0.1-0.6-0.6c0.1-0.7-0.2-1.5,0.2-1.9c0.3-0.3,1.2-0.1,1.9-0.1c3,0,6,0,9,0C49,18.7,49.2,18.9,49.1,19.5z"/>
</g>
<g>
<path fill="#C5411E" d="M62,19.4c0,0.6,0,1.2,0,1.9h-1.5c-0.8,0-1.5,0-2.3,0c-0.6,0-0.6,0.3-0.6,0.7c0,3.6,0,7.2,0,10.8
c0,1.4,0,2.9,0,4.3c0,0.4-0.1,0.6-0.6,0.6c-0.8,0-1.6,0-2.4,0v-0.8c0-4.1,0-8.2,0-12.3c0-1.1-0.1-2.2-0.1-3.3
c-1.1,0-2.1-0.1-3.2-0.1c-0.2,0-0.4,0-0.6,0c-0.4,0-0.5-0.2-0.5-0.5c0-0.7,0.1-1.4,0.1-2c0.6,0,1.3-0.1,1.9-0.1c3,0,6.1,0,9.1,0
C61.8,18.7,62.1,18.8,62,19.4z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -2,19 +2,15 @@
from setuptools import setup, find_packages
with open("README.md", "r") as fh:
long_description = fh.read()
setup(
name='MISP_maltego',
author='Christophe Vandeplas',
# also update version in util.py
version='1.4.6',
version='1.4.0',
author_email='christophe@vandeplas.com',
maintainer='Christophe Vandeplas',
url='https://github.com/MISP/MISP-maltego',
description='Maltego transform for interacting with a MISP Threat Sharing community and with MITRE ATT&CK.',
long_description=long_description,
license='AGPLv3',
packages=find_packages('src'),
package_dir={'': 'src'},
@ -36,7 +32,7 @@ setup(
python_requires='>=3.5',
install_requires=[
'canari>=3.3.10,<4',
'PyMISP>=2.4.127'
'PyMISP>=2.4.114'
],
dependency_links=[
# custom links for the install_requires

View File

@ -8,4 +8,3 @@ misp_debug = False
check_updates = True
[MISP_maltego.remote]

View File

@ -3,20 +3,14 @@ machine("misp.MISPEventToAll",
author:"Christophe Vandeplas",
description: "Automatically expands MISP Objects to their attributes") {
start {
paths {
path {
run("MISP_maltego.EventToAttributes")
paths {
path {
run("MISP_maltego.AttributeToEvent")
}
paths {
path {
run("MISP_maltego.ObjectToAttributes")
}
}
// run("MISP_maltego.AttributeToEvent")
}
path {
run("MISP_maltego.EventToTags")
run("MISP_maltego.GalaxyToRelations")
}
}

View File

@ -4,14 +4,11 @@ machine("misp.MISPEventToAttributes",
description: "Also automatically expands MISP Objects to their attributes") {
start {
paths {
run("MISP_maltego.EventToAttributes")
path {
run("MISP_maltego.EventToObjects")
run("MISP_maltego.ObjectToAttributes")
}
path {
run("MISP_maltego.EventToAttributes")
}
}
}
}

View File

@ -1,8 +1,8 @@
from canari.maltego.entities import Unknown, Hashtag
from canari.maltego.entities import Unknown
from canari.maltego.transform import Transform
from MISP_maltego.transforms.common.entities import MISPGalaxy
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
# from canari.framework import EnableDebugWindow
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
from canari.maltego.message import LinkDirection
__author__ = 'Christophe Vandeplas'
__copyright__ = 'Copyright 2018, MISP_maltego Project'
@ -15,108 +15,46 @@ __email__ = 'christophe@vandeplas.com'
__status__ = 'Development'
class SearchInMISP(Transform):
"""Use % at the front/end for wildcard search"""
# @EnableDebugWindow
class AttributeInMISP(Transform):
"""Green bookmark if known in MISP"""
display_name = 'in MISP?'
input_type = Unknown
display_name = 'Search in MISP'
description = "Use % at the front/end for wildcard search"
remote = True
def do_transform(self, request, response, config):
response += check_update(config)
link_label = 'Search result'
if 'properties.mispevent' in request.entity.fields:
conn = MISPConnection(config, request.parameters)
# if event_id
maltego_misp_attribute = request.entity
# skip MISP Events (value = int)
try:
if request.entity.value == '0':
int(maltego_misp_attribute.value)
return response
eventid = int(request.entity.value)
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:
except Exception:
pass
# if event_info string as value
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
# From galaxy or Hashtag
if 'properties.mispgalaxy' in request.entity.fields or 'properties.temp' in request.entity.fields or 'twitter.hashtag' in request.entity.fields:
if request.entity.value == '-':
return response
# First search in galaxies
keyword = get_entity_property(request.entity, 'Temp')
if not keyword:
keyword = request.entity.value
# assume 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(keyword)
# LATER check if duplicates are possible
if potential_clusters:
for potential_cluster in potential_clusters:
new_entity = galaxycluster_to_entity(potential_cluster, link_label=link_label)
# LATER support the type_filter - unfortunately this is not possible, we need Canari to tell us the original entity type
if isinstance(new_entity, MISPGalaxy):
response += new_entity
# from Hashtag search also in tags
if 'properties.temp' in request.entity.fields or 'twitter.hashtag' in request.entity.fields:
keyword = get_entity_property(request.entity, 'Temp')
if not keyword:
keyword = request.entity.value
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'):
continue
# In this case we do not filter away those we add as notes, as people might want to pivot on it explicitly.
response += Hashtag(t['Tag']['name'], link_label=link_label, bookmark=Bookmark.Green)
return response
# for all other normal entities
conn = MISPConnection(config, request.parameters)
misp = get_misp_connection(config)
events_json = misp.search(controller='events', value=maltego_misp_attribute.value, with_attachments=False)
# we need to do really rebuild the Entity from scratch as request.entity is of type Unknown
# TODO First try to build the object, then only attributes (for those that are not in object, or for all?)
# TODO check for the right version of MISP before, it needs to be 2.4.127 or higher.
# obj_json = conn.misp.search(controller='objects', value=request.entity.value, with_attachments=False)
# for o in obj_json:
# for item in attribute_to_entity(attr, only_self=True, link_label=link_label):
# response += item
# # find the value as object, and return the object
# 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 += conn.object_to_entity(o, link_label=link_label)
attr_json = conn.misp.search(controller='attributes', value=request.entity.value, with_attachments=False)
for a in attr_json['Attribute']:
for item in attribute_to_entity(a, only_self=True, link_label=link_label):
for e in events_json:
attr = get_attribute_in_event(e, maltego_misp_attribute.value)
if attr:
for item in attribute_to_entity(attr, only_self=True):
response += item
return response
# placeholder for https://github.com/MISP/MISP-maltego/issues/11
# waiting for support of CIDR search through the REST API
# @EnableDebugWindow
# class NetblockToAttributes(Transform):
# display_name = 'to MISP Attributes'
# input_type = Netblock
# remote = True
# def do_transform(self, request, response, config):
# maltego_misp_attribute = request.entity
# misp = get_misp_connection(config, request.parameters)
# misp = get_misp_connection(config)
# import ipaddress
# ip_start, ip_end = maltego_misp_attribute.value.split('-')
# # LATER make this work with IPv4 and IPv6
# # FIXME make this work with IPv4 and IPv6
# # automagically detect the different CIDRs
# cidrs = ipaddress.summarize_address_range(ipaddress.IPv4Address(ip_start), ipaddress.IPv4Address(ip_end))
# for cidr in cidrs:
@ -126,10 +64,10 @@ class SearchInMISP(Transform):
# return response
# @EnableDebugWindow
class AttributeToEvent(Transform):
display_name = 'to MISP Event'
input_type = Unknown
display_name = 'To MISP Events'
remote = True
def do_transform(self, request, response, config):
response += check_update(config)
@ -143,37 +81,28 @@ class AttributeToEvent(Transform):
# placeholder for https://github.com/MISP/MISP-maltego/issues/11
pass
conn = MISPConnection(config, request.parameters)
misp = get_misp_connection(config)
# 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 = conn.misp.search(controller='events', tags=tag_name, with_attachments=False)
events_json = 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 = conn.misp.search(controller='events', eventid=request.entity.fields.get('event_id').value, with_attachments=False)
events_json = 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
else:
return response
# from Hashtag
elif 'properties.temp' in request.entity.fields or 'twitter.hashtag' in request.entity.fields:
tag_name = get_entity_property(request.entity, 'Temp')
if not tag_name:
tag_name = request.entity.value
events_json = conn.misp.search_index(tags=tag_name)
for e in events_json:
response += event_to_entity({'Event': e}, link_direction=LinkDirection.OutputToInput)
return response
# standard Entities (normal attributes)
else:
events_json = conn.misp.search(controller='events', value=request.entity.value, with_attachments=False)
events_json = misp.search(controller='events', value=request.entity.value, with_attachments=False)
# return the MISPEvent or MISPObject of the attribute
for e in events_json:
@ -185,6 +114,6 @@ class AttributeToEvent(Transform):
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 += conn.object_to_entity(o, link_direction=LinkDirection.OutputToInput)
response += object_to_entity(o, link_direction=LinkDirection.OutputToInput)
return response

View File

@ -32,8 +32,6 @@ class MISPEvent(Entity):
# threat_level = EnumEntityField('type.enum', choices=['Undefined', 'Low', 'Medium', 'High'], display_name='Threat Level')
# analysis = EnumEntityField('type.enum', choices=['Initial', 'Ongoing', 'Completed'])
# org = StringEntityField('type.str', display_name='Organisation')
count_attributes = IntegerEntityField('count_attributes', display_name="# attributes", matching_rule=MatchingRule.Loose)
count_objects = IntegerEntityField('count_objects', display_name="# objects", matching_rule=MatchingRule.Loose)
class MISPObject(Entity):

View File

@ -1,764 +0,0 @@
from canari.maltego.entities import Hash, Domain, IPv4Address, URL, DNSName, AS, Website, NSRecord, PhoneNumber, EmailAddress, File, Location, Company, Alias, Port, Twitter
from MISP_maltego.transforms.common.entities import ThreatActor, Software, AttackTechnique
mapping_misp_to_maltego = {
'AS': [AS],
'domain': [Domain, NSRecord, Website, DNSName],
'email-dst': [EmailAddress],
'email-src': [EmailAddress],
'filename': [File],
'hostname': [Website, NSRecord, Domain, DNSName],
'ip': [IPv4Address],
'ip-dst': [IPv4Address],
'ip-src': [IPv4Address],
'md5': [Hash],
'phone-number': [PhoneNumber],
'sha1': [Hash],
'sha224': [Hash],
'sha256': [Hash],
'sha384': [Hash],
'sha512': [Hash],
'sha512/224': [Hash],
'sha512/256': [Hash],
'ssdeep': [Hash],
'impfuzzy': [Hash],
'uri': [URL],
'url': [URL],
'whois-registrant-email': [EmailAddress],
'country-of-residence': [Location],
'github-organisation': [Company],
'github-username': [Alias],
'imphash': [Hash],
'jabber-id': [Alias],
'passport-country': [Location],
'place-of-birth': [Location],
'port': [Port],
'target-email': [EmailAddress],
'target-location': [Location],
'target-org': [Company],
'target-user': [Alias],
'twitter-id': [Twitter],
# object mappings
'nameserver': [NSRecord],
# custom types created internally for technical reasons
# 'regkey_value': [Unknown]
}
mapping_galaxy_icon = {
# "android": "malware", # "android",
"btc": "ransomware",
"bug": "vulnerability",
# "cart-arrow-down": "malware", #"tds",
"chain": "course_of_action",
"door-open": "backdoor",
"eye": "malware",
"gavel": "tool",
# "globe": "cert-eu-govsector",
# "industry": "sector",
# "internet-explorer": "exploit-kit",
"key": "stealer",
"map": "attack_pattern",
"optin-monster": "malware",
# "shield": "malpedia",
# "shield": "preventive-measure",
"sitemap": "botnet",
"usd": "malware", # "banker",
# "user-secret": "mitre-intrusion-set",
"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
}
mapping_object_icon = {
'ail-leak': '',
'ais-info': '',
'android-permission': '',
'annotation': '',
'anonymisation': 'AffiliationAnonymous',
'asn': '',
'attack-pattern': '',
'authenticode-signerinfo': '',
'av-signature': '',
'bank-account': '',
'bgp-hijack': '',
'blog': 'URL',
'btc-transaction': 'BankCard',
'btc-wallet': 'BankAccount',
'cap-alert': '',
'cap-info': '',
'cap-resource': '',
'coin-address': 'BankAccount',
'command': '',
'command-line': '',
'cookie': 'Cookies',
'cortex': '',
'cortex-taxonomy': '',
'course-of-action': 'course_of_action',
'covid19-csse-daily-report': '',
'covid19-dxy-live-city': '',
'covid19-dxy-live-province': '',
'cowrie': '',
'credential': '',
'credit-card': 'BankCard',
'crypto-material': 'Encrypt',
'cytomic_orion': '',
'cytomic_orion_machine': '',
'dark-pattern': '',
'ddos': '',
'device': '',
'diameter-attack': '',
'dns-record': 'ServerDNS',
'domain-crawled': '',
'domain-ip': 'NetworkGlobal',
'elf': '',
'elf-section': '',
'email': 'Email',
'employee': 'Person',
'exploit-poc': 'Person',
'facial-composite': '',
'fail2ban': '',
'file': 'File',
'forensic-case': '',
'forensic-evidence': '',
'forged-document': '',
'geolocation': 'GPS',
'gtp-attack': '',
'http-request': 'URL',
'ilr-impact': '',
'ilr-notification-incident': '',
'impersonation': 'GangBoss',
'imsi-catcher': 'MobileNet',
'instant-message': 'Form',
'instant-message-group': '',
'intelmq_event': '',
'intelmq_report': '',
'internal-reference': '',
'interpol-notice': '',
'iot-device': 'InternetISP',
'iot-firmware': '',
'ip-api-address': '',
'ip-port': 'NetworkCard',
'irc': '',
'ja3': '',
'leaked-document': 'InternetDocument',
'legal-entity': 'Company',
'lnk': 'File',
'macho': '',
'macho-section': '',
'mactime-timeline-analysis': '',
'malware-config': 'Virus',
'meme-image': '',
'microblog': '',
'mutex': '',
'netflow': '',
'network-connection': 'NetworkSymmetric',
'network-socket': '',
'news-agency': '',
'news-media': '',
'organization': 'Company',
'original-imported-file': 'File',
'passive-dns': 'ServerDNS',
'paste': 'InternetDocument',
'pcap-metadata': '',
'pe': 'File',
'person': 'Person',
'pe-section': '',
'pgp-meta': '',
'phishing': 'InternetDocument',
'phishing-kit': '',
'phone': 'PhoneNumber',
'process': '',
'python-etvx-event-log': '',
'r2graphity': '',
'regexp': '',
'registry-key': 'RegistryErase',
'regripper-NTUser': '',
'regripper-sam-hive-single-user': '',
'regripper-sam-hive-user-group': '',
'regripper-software-hive-appInit-DLLS': '',
'regripper-software-hive-application-paths': '',
'regripper-software-hive-applications-installed': '',
'regripper-software-hive-BHO': '',
'regripper-software-hive-command-shell': '',
'regripper-software-hive-general-windows-info': '',
'regripper-software-hive-software-run': '',
'regripper-software-hive-userprofile-winlogon': '',
'regripper-system-hive-firewall-configuration': '',
'regripper-system-hive-general-configuration': '',
'regripper-system-hive-network-information': '',
'regripper-system-hive-service-drivers': '',
'report': 'Resume',
'research-scanner': '',
'rogue-dns': '',
'rtir': '',
'sandbox-report': 'Resume',
'sb-signature': '',
'scrippsco2-c13-daily': '',
'scrippsco2-c13-monthly': '',
'scrippsco2-co2-daily': '',
'scrippsco2-co2-monthly': '',
'scrippsco2-o18-daily': '',
'scrippsco2-o18-monthly': '',
'script': '',
'shell-commands': '',
'shodan-report': '',
'shortened-link': 'URL',
'short-message-service': '',
'splunk': '',
'ss7-attack': '',
'ssh-authorized-keys': '',
'stix2-pattern': '',
'suricata': '',
'target-system': 'sighting',
'threatgrid-report': '',
'timecode': '',
'timesketch_message': '',
'timesketch-timeline': '',
'timestamp': '',
'tor-hiddenservice': '',
'tor-node': '',
'tracking-id': '',
'transaction': '',
'translation': '',
'trustar_report': '',
'TSK-Chats': '',
'TSK-Web-Bookmark': '',
'TSK-Web-Cookie': '',
'TSK-Web-Downloads': '',
'TSK-Web-History': '',
'TSK-Web-Search-Query': '',
'url': 'URL',
'user-account': 'User',
'vehicle': 'Car',
'victim': 'Victim',
'virustotal-graph': '',
'virustotal-report': '',
'vulnerability': 'vulnerability',
'weakness': 'vulnerability',
'whois': 'VINNumber',
'x509': 'MedicalRecord',
'yabin': '',
'yara': '',
}
# All possible default icons shipped with Maltego - useful for auto_completion
# AccessCard
# AccessPoint
# Accident
# Accountant
# Add
# Admin
# AdultFemale
# AdultMale
# AffiliationAndroid
# AffiliationAnonymous
# AffiliationApple
# AffiliationBebo
# AffiliationBlogger
# AffiliationBuiltWith
# AffiliationCloud
# AffiliationColdfusion
# AffiliationDigg
# AffiliationDropbox
# AffiliationEbay
# AffiliationFacebook
# AffiliationFlickr
# AffiliationGoogleDrive
# AffiliationGooglePlus
# AffiliationInstagram
# AffiliationKik
# AffiliationLinkedIn
# AffiliationLinux
# AffiliationMeetup
# AffiliationMyspace
# AffiliationNewsvine
# AffiliationOrkut
# AffiliationPayPal
# AffiliationPicasa
# AffiliationPinterest
# Affiliation
# AffiliationReddit
# AffiliationRSS
# AffiliationSkype
# AffiliationSnapchat
# AffiliationSpock
# AffiliationTinder
# AffiliationTwitter
# AffiliationWechat
# AffiliationWhatsapp
# AffiliationWiki
# AffiliationWindows
# AffiliationWWF
# AffiliationYammer
# AffiliationYelp
# AffiliationYouTube
# AffiliationZoomInfo
# AircraftBomber
# AircraftCarrier
# AirCrash
# Airport
# Alarm
# Alias
# Alliance
# Ammunition
# Anarchy
# Antenna
# Apartments
# Army
# Artist
# Assemble
# Asteroid
# Atom
# Author
# Baby
# Backbone
# Ballerina
# BandAid
# BankAccount
# BankCard
# Banner
# Bear
# Bee
# Binary
# BioAgent
# Bit
# BlueAura
# Bomb
# BookPDF
# Book
# BorderCheckpoint
# Businessman
# BusinessPhoneSystem
# Bus
# CableUSB
# Camera
# Captive
# Cargo
# Car
# CashInTransit
# Cash
# CellNetwork
# Cemetery
# CEO
# Certificate
# Certification
# Champion
# CheckBox
# Checkpoint
# ChemicalAnalysis
# Child
# Church
# CircularArea
# City
# Clock
# ClusterOrange
# Cluster
# CoffeeShop
# ColoredBall
# Community
# Company
# ConferenceAudio
# Connect
# Contract
# ControlTower
# Cookies
# CrimeScene
# Criminal
# CV
# Dam
# DatabaseConnect
# DatabaseErase
# Database
# DateField
# Deceased
# Degree
# Delete
# Desert
# Desktop
# Destroy
# Diamond
# Diary
# Dictator
# Directions
# Disabled
# Disconnect
# DNACode
# Donation
# Donkey
# Drone
# DrugDealer
# Earthquake
# Elderly
# Elephant
# Email
# Encrypt
# Environment
# Erase
# Event
# Explosion
# Factory
# Farm
# FastFood
# Fax
# FieldDelete
# Field
# File
# FileSharing
# Files
# Filter
# FingerPrint
# FireForest
# Firewall
# Fix
# FlightNumber
# FlightPath
# Flood
# FloppyDisk
# Form
# GamingConsole
# GangBoss
# GangMember
# Gang
# GasStation
# Gateway
# Genealogy
# Genetic
# Geography
# GhostSighting
# GlobalWarming
# Gorilla
# GovermentOfficial
# Government
# GPS
# Green2Grey
# Green2Orange2Turquoise
# Green2Red2Blue
# Group
# Guard
# Gun
# Hacker
# Harbour
# HardDisk
# Harvest
# Hashtag
# Headphones
# Helicopter
# Home
# HospitalLocation
# Hostage
# Hotel
# Hurricane
# HydroPower
# IconManager
# ID
# IED
# ImageField
# Image
# Influencer
# InfoMessage
# InternetDocument
# InternetFastSpeed
# InternetIP
# InternetISP
# InternetMIMEDocs
# InternetMIMEFolder
# InternetMIME
# Internet
# InternetUser
# Invasion
# ISBN
# Island
# Judge
# KeyPrimary
# Keys
# KillerWhale
# Knife
# Last
# LawEnforcementOfficer
# Lawyer
# Leader
# License
# LinkBroke
# Link
# List
# Lobby
# Location
# Log
# MacAddress
# MaltegoGraph
# ManyIn
# Marijuana
# MedicalRecord
# Medicine
# MeetingBusiness
# MeetingSocial
# Memorial
# MergeCells
# Messenger
# MilitaryOfficer
# Mine
# MissileRPG
# MissingPerson
# MobileComputer
# MobileNet
# MobilePhone
# MobileUser
# Modem
# Monitoring
# Moon
# Mosque
# Motorbike
# Movie
# Murder
# MusicAlbum
# MusicSinger
# MusicSongwriter
# MXRecord
# MySQL
# Neighborhood
# NetAdmin
# NetworkAdmin
# NetworkAsymetric
# NetworkCardBlue
# NetworkCard
# NetworkConnector
# NetworkDistribution
# NetworkGlobal
# NetworkHub
# NetworkID
# NetworkIntranet
# NetworkISDN
# NetworkMonitor
# NetworkSoftware
# NetworkSymmetric
# News
# Node
# NSRecord
# NuclearPlant
# Nurse
# Objects
# OilField
# OilSpill
# OilWell
# OnlineGroup
# Orange2Green
# Orange2Purple
# Organization
# OSIModel
# Passport
# PasswordPHP
# Password
# Patient
# Person
# PetrolBomb
# PhoneConversation
# PhoneLandlineOffice
# PhoneLandlineResidential
# PhoneNumber
# Phrase
# Pilot
# Piracy
# Pirate
# Plane
# Planet
# Play
# Poison
# PoliticalParty
# Port
# PowerPlant
# Prescription
# PrisonCamp
# Prisoner
# Prison
# Privilege
# ProgressBar
# Protester
# Protest
# Protocol
# Purple2Turquoise
# PurplePink2Green
# PurplePink2Yellow2Blue
# QRCode
# Quarantine
# QuestionDialog
# Radar
# Radio
# Rain
# Red2Blue
# Red2Green
# Red2Yellow
# RefugeeCamp
# RegistrationPlate
# RegistryErase
# RelationshipModel
# Relationship
# RemoteControl
# Repeater
# Reporter
# Restaurant
# Resume
# Rhino
# Rocket
# Role
# Route
# Router
# Royalty
# RunningWater
# Satellite
# Savings
# School
# Science
# Scientist
# Script
# SecurityCameraMonitoring
# SecurityCheckpoints
# Security
# Seed
# Sentiment
# ServerBackup
# ServerChat
# ServerDNS
# ServerFTP
# ServerMicrosoftSQL
# Server
# ServerProxy
# Service
# SexOffender
# Sharing
# SharkAttack
# ShipContainer
# ShipCruise
# ShipPirate
# Ship
# ShipSpeed
# ShipTanker
# ShipTrawler
# ShipYacht
# Shop
# SIMCard
# SiteFTP
# SizeAllLinks
# SizeInLinks
# SizeOutLinks
# SmileConfused
# SmileMad
# Smile
# SmileSad
# SMS
# Sniffer
# Snow
# Socket
# SoftwareBlocking
# SoftwareCollaborative
# SoftwareFTP
# SoftwareManager
# SoftwareMeeting
# Software
# Soldier
# Solidarity
# Space
# SpaceStation
# Spider
# SplitCells
# Spy
# Spyware
# SQLQuery
# SSLCertificate
# SSL
# SSN
# Star
# Stop
# SuicideBomber
# SUNET
# Suspect
# SuspiciousPerson
# Switch
# Sybase
# SynagogueTemple
# Syndicate
# Table
# TabletTouch
# Tag
# Tank
# TargetPerson
# Target
# Taxi
# Technician
# Temple
# Terminal
# TerroristLeader
# TerroristMember
# TerroristThug
# Terror
# TextField
# Theatre
# Ticket
# TradeUnion
# Train
# TrainStation
# Transform
# Trojan
# Truck
# TsetseFly
# Tsunami
# Turquoise2Orange2Red
# Turquoise2Yellow
# TV
# UFOAbduction
# Underground
# Universe
# UnknownBody
# Unknown
# UPS
# Urgent
# URL
# USB
# UserID
# User
# Victim
# VideoCamera
# Videoconference
# VINNumber
# Virus
# Voice
# VOIP
# VolcanoEruption
# VPN
# WAN
# WebDir
# Website
# WiFi
# WindFarm
# WirelessRouter

View File

@ -1,9 +1,7 @@
from canari.maltego.entities import Hash, URL, File, Person, Hashtag
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 canari.mode import is_local_exec_mode, is_remote_exec_mode
from distutils.version import StrictVersion
from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject, MISPGalaxy
from MISP_maltego.transforms.common.mappings import mapping_object_icon, mapping_misp_to_maltego, mapping_galaxy_icon, mapping_galaxy_type
from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject, MISPGalaxy, ThreatActor, Software, AttackTechnique
from pymisp import ExpandedPyMISP as PyMISP
import json
import os
@ -12,22 +10,123 @@ import requests
import tempfile
import time
__version__ = '1.4.6' # also update version in setup.py
# FIXME from galaxy 'to MISP Event' is confusing
__version__ = '1.4.0' # also update version in setup.py
mapping_misp_to_maltego = {
'AS': [AS],
'domain': [Domain, NSRecord, Website, DNSName],
'email-dst': [EmailAddress],
'email-src': [EmailAddress],
'filename': [File],
'hostname': [Website, NSRecord, Domain, DNSName],
'ip': [IPv4Address],
'ip-dst': [IPv4Address],
'ip-src': [IPv4Address],
'md5': [Hash],
'phone-number': [PhoneNumber],
'sha1': [Hash],
'sha224': [Hash],
'sha256': [Hash],
'sha384': [Hash],
'sha512': [Hash],
'sha512/224': [Hash],
'sha512/256': [Hash],
'ssdeep': [Hash],
'impfuzzy': [Hash],
'uri': [URL],
'url': [URL],
'whois-registrant-email': [EmailAddress],
'country-of-residence': [Location],
'github-organisation': [Company],
'github-username': [Alias],
'imphash': [Hash],
'jabber-id': [Alias],
'passport-country': [Location],
'place-of-birth': [Location],
'port': [Port],
'target-email': [EmailAddress],
'target-location': [Location],
'target-org': [Company],
'target-user': [Alias],
'twitter-id': [Twitter],
# object mappings
'nameserver': [NSRecord],
# TODO add more object mappings
# custom types created internally for technical reasons
# 'rekey_value': [Unknown]
}
mapping_galaxy_icon = {
# "android": "malware", # "android",
"btc": "ransomware",
"bug": "vulnerability",
# "cart-arrow-down": "malware", #"tds",
"chain": "course_of_action",
"door-open": "backdoor",
"eye": "malware",
"gavel": "tool",
# "globe": "cert-eu-govsector",
# "industry": "sector",
# "internet-explorer": "exploit-kit",
"key": "stealer",
"map": "attack_pattern",
"optin-monster": "malware",
# "shield": "malpedia",
# "shield": "preventive-measure",
"sitemap": "botnet",
"usd": "malware", # "banker",
# "user-secret": "mitre-intrusion-set",
"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
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')
if not os.path.exists(local_path_root):
os.mkdir(local_path_root)
os.chmod(local_path_root, mode=0o777) # temporary workaround - see https://github.com/redcanari/canari3/issues/61
def check_update(config):
# Do not check updates if running as remote transform
if is_remote_exec_mode():
return None
# only raise the alert once a day/reboot to the user.
try:
if time.time() - os.path.getmtime(local_path_version) > 60 * 60 * 24: # check the timestamp of the file
@ -61,116 +160,25 @@ def check_update(config):
return None
class MISPConnection():
def __init__(self, config=None, parameters=None):
self.misp = None
def get_misp_connection(config=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.")
if config['MISP_maltego.local.misp_verify'] in ['True', 'true', 1, 'yes', 'Yes']:
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']:
else:
misp_verify = False
if config['MISP_maltego.local.misp_debug'] in ['True', 'true', 1, 'yes', 'Yes']:
misp_debug = True
else:
misp_debug = False
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(url=misp_url, key=misp_key, ssl=misp_verify, debug=misp_debug, tool='misp_maltego', timeout=(2, 60))
misp_connection = PyMISP(config['MISP_maltego.local.misp_url'], config['MISP_maltego.local.misp_key'], misp_verify, 'json', misp_debug)
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:
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)
raise MaltegoException("ERROR: Cannot connect to MISP server. Please verify your MISP_Maltego.conf settings")
return misp_connection
def entity_obj_to_entity(entity_obj, v, t, **kwargs):
@ -219,7 +227,6 @@ def attribute_to_entity(a, link_label=None, event_tags=[], only_self=False):
if a['type'] in ('url', 'uri'):
yield(URL(url=a['value'], short_title=a['value'], link_label=link_label, notes=notes, bookmark=Bookmark.Green))
return
# FIXME implement attachment screenshot type
# 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']):
@ -250,7 +257,60 @@ def attribute_to_entity(a, link_label=None, event_tags=[], only_self=False):
# not supported in our maltego mapping are not handled
# LATER relationships from attributes - not yet supported by MISP yet, but there are references in the datamodel
# 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):
# 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
misp = get_misp_connection()
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 = '{}: {}'.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 = '{}: {}'.format(o['name'], '|'.join(parts))
except Exception:
human_readable = o['name']
pass
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'),
link_label=link_label,
link_direction=link_direction,
bookmark=Bookmark.Green
)
def object_to_attributes(o, e):
@ -266,13 +326,37 @@ 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:
return o
def get_attribute_in_object(o, attribute_type=False, attribute_value=False, drop=False, substring=False):
def get_attribute_in_object(o, attribute_type=False, attribute_value=False, drop=False):
'''Gets the first attribute of a specific type within an object'''
found_attribute = {'value': ''}
for i, a in enumerate(o['Attribute']):
@ -285,87 +369,22 @@ def get_attribute_in_object(o, attribute_type=False, attribute_value=False, drop
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
if '|' in a['type'] or a['type'] == 'malware-sample':
if attribute_value in a['value'].split('|'):
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
# substring matching
if substring:
keyword = attribute_value.strip('%')
if attribute_value.startswith('%') and attribute_value.endswith('%'):
if attribute_value in a['value']:
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
if '|' in a['type'] or a['type'] == 'malware-sample':
val1, val2 = a['value'].split('|')
if attribute_value in val1 or attribute_value in val2:
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
elif attribute_value.startswith('%'):
if a['value'].endswith(keyword):
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
if '|' in a['type'] or a['type'] == 'malware-sample':
val1, val2 = a['value'].split('|')
if val1.endswith(keyword) or val2.endswith(keyword):
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
elif attribute_value.endswith('%'):
if a['value'].startswith(keyword):
return a
if '|' in a['type'] or a['type'] == 'malware-sample':
val1, val2 = a['value'].split('|')
if val1.startswith(keyword) or val2.startswith(keyword):
found_attribute = a.copy()
if drop: # drop the attribute from the object
o['Attribute'].pop(i)
break
return found_attribute
def get_attribute_in_event(e, attribute_value, substring=False):
def get_attribute_in_event(e, attribute_value):
for a in e['Event']["Attribute"]:
if a['value'] == attribute_value:
return a
if '|' in a['type'] or a['type'] == 'malware-sample':
if attribute_value in a['value'].split('|'):
return a
if substring:
keyword = attribute_value.strip('%')
if attribute_value.startswith('%') and attribute_value.endswith('%'):
if attribute_value in a['value']:
return a
if '|' in a['type'] or a['type'] == 'malware-sample':
val1, val2 = a['value'].split('|')
if attribute_value in val1 or attribute_value in val2:
return a
elif attribute_value.startswith('%'):
if a['value'].endswith(keyword):
return a
if '|' in a['type'] or a['type'] == 'malware-sample':
val1, val2 = a['value'].split('|')
if val1.endswith(keyword) or val2.endswith(keyword):
return a
elif attribute_value.endswith('%'):
if a['value'].startswith(keyword):
return a
if '|' in a['type'] or a['type'] == 'malware-sample':
val1, val2 = a['value'].split('|')
if val1.startswith(keyword) or val2.startswith(keyword):
return a
return None
@ -388,7 +407,7 @@ def tag_matches_note_prefix(tag):
return False
def event_to_entity(e, link_style=LinkStyle.Normal, link_label=None, link_direction=LinkDirection.InputToOutput):
def event_to_entity(e, link_style=LinkStyle.Normal, link_direction=LinkDirection.InputToOutput):
tags = []
if 'Tag' in e['Event']:
for t in e['Event']['Tag']:
@ -399,10 +418,7 @@ def event_to_entity(e, link_style=LinkStyle.Normal, link_label=None, link_direct
uuid=e['Event']['uuid'],
info=e['Event']['info'],
link_style=link_style,
link_label=link_label,
link_direction=link_direction,
count_attributes=len(e['Event'].get('Attribute') or ""),
count_objects=len(e['Event'].get('Object') or ""),
notes=notes,
bookmark=Bookmark.Green)
@ -416,13 +432,14 @@ def galaxycluster_to_entity(c, link_label=None, link_direction=LinkDirection.Inp
else:
synonyms = ''
galaxy_cluster = get_galaxy_cluster(uuid=c['uuid'])
galaxy_cluster = get_galaxy_cluster(c['uuid'])
# 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:
# it's not in our mapping, just ignore and leave the default icon
icon_url = None
# it's not in our mapping, just ignore and leave the default Galaxy icon
pass
# create the right sub-galaxy: ThreatActor, Software, AttackTechnique, ... or MISPGalaxy
try:
@ -445,9 +462,9 @@ def galaxycluster_to_entity(c, link_label=None, link_direction=LinkDirection.Inp
# LATER this uses the galaxies from github as the MISP web UI does not fully support the Galaxies in the webui.
# See https://github.com/MISP/MISP/issues/3801
galaxy_archive_url = 'https://github.com/MISP/misp-galaxy/archive/main.zip'
galaxy_archive_url = 'https://github.com/MISP/misp-galaxy/archive/master.zip'
local_path_uuid_mapping = os.path.join(local_path_root, 'MISP_maltego_galaxy_mapping.json')
local_path_clusters = os.path.join(local_path_root, 'misp-galaxy-main', 'clusters')
local_path_clusters = os.path.join(local_path_root, 'misp-galaxy-master', 'clusters')
galaxy_cluster_uuids = None
@ -467,12 +484,6 @@ def galaxy_update_local_copy(force=False):
force = True
if force:
# create a lock to prevent two processes doing the same, and writing to the file at the same time
lockfile = local_path_uuid_mapping + '.lock'
from pathlib import Path
while os.path.exists(lockfile):
time.sleep(0.3)
Path(local_path_uuid_mapping + '.lock').touch()
# download the latest zip of the public galaxy
try:
resp = requests.get(galaxy_archive_url)
@ -480,8 +491,6 @@ def galaxy_update_local_copy(force=False):
zf.extractall(local_path_root)
zf.close()
except Exception:
# remove the lock
os.remove(lockfile)
raise(MaltegoException("ERROR: Could not download Galaxy data from htts://github.com/MISP/MISP-galaxy/. Please check internet connectivity."))
# generate the uuid mapping and save it to a file
@ -516,9 +525,7 @@ def galaxy_update_local_copy(force=False):
pass
with open(local_path_uuid_mapping, 'w') as f:
json.dump(cluster_uuids, f)
# remove the lock
os.remove(lockfile)
json.dump(cluster_uuids, f, sort_keys=True, indent=4)
def galaxy_load_cluster_mapping():
@ -528,7 +535,7 @@ def galaxy_load_cluster_mapping():
return cluster_uuids
def get_galaxy_cluster(uuid=None, tag=None, request_entity=None):
def get_galaxy_cluster(uuid=None, tag=None):
global galaxy_cluster_uuids
if not galaxy_cluster_uuids:
galaxy_cluster_uuids = galaxy_load_cluster_mapping()
@ -539,13 +546,6 @@ def get_galaxy_cluster(uuid=None, tag=None, request_entity=None):
for item in galaxy_cluster_uuids.values():
if item['tag_name'] == tag:
return item
if request_entity:
if request_entity.uuid:
return get_galaxy_cluster(uuid=request_entity.uuid)
elif request_entity.tag_name:
return get_galaxy_cluster(tag=request_entity.tag_name)
elif request_entity.name:
return get_galaxy_cluster(tag=request_entity.name)
def search_galaxy_cluster(keyword):
@ -553,36 +553,8 @@ def search_galaxy_cluster(keyword):
global galaxy_cluster_uuids
if not galaxy_cluster_uuids:
galaxy_cluster_uuids = galaxy_load_cluster_mapping()
# % only at start
if keyword.startswith('%') and not keyword.endswith('%'):
keyword = keyword.strip('%')
for item in galaxy_cluster_uuids.values():
if item['value'].lower().endswith(keyword):
yield item
else:
if 'meta' in item and 'synonyms' in item['meta']:
for synonym in item['meta']['synonyms']:
if synonym.lower().endswith(keyword):
yield item
# % only at end
elif keyword.endswith('%') and not keyword.startswith('%'):
keyword = keyword.strip('%')
for item in galaxy_cluster_uuids.values():
if item['value'].lower().startswith(keyword):
yield item
else:
if 'meta' in item and 'synonyms' in item['meta']:
for synonym in item['meta']['synonyms']:
if synonym.lower().startswith(keyword):
yield item
# search substring assuming % at start and end
else:
keyword = keyword.strip('%')
for item in galaxy_cluster_uuids.values():
if keyword in item['value'].lower():
if keyword in item['tag_name'].lower():
yield item
else:
if 'meta' in item and 'synonyms' in item['meta']:

View File

@ -1,7 +1,8 @@
from canari.maltego.entities import Hashtag
from canari.maltego.transform import Transform
# from canari.framework import EnableDebugWindow
from MISP_maltego.transforms.common.entities import MISPEvent, MISPObject
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
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
from canari.maltego.message import LinkStyle
@ -15,7 +16,7 @@ __maintainer__ = 'Christophe Vandeplas'
__email__ = 'christophe@vandeplas.com'
__status__ = 'Development'
# TODO have a more human readable version of the MISP event value in the graph. change entity + event_to_entity + do_transform
# FIXME have a more human readable version of the MISP event value in the graph. change entity + event_to_entity + do_transform
class EventToTransform(Transform):
@ -26,7 +27,7 @@ class EventToTransform(Transform):
self.request = None
self.response = None
self.config = None
self.conn = None
self.misp = None
self.event_json = None
self.event_tags = None
@ -36,9 +37,9 @@ class EventToTransform(Transform):
self.config = config
self.response += check_update(config)
maltego_misp_event = request.entity
self.conn = MISPConnection(config, request.parameters)
self.misp = get_misp_connection(config)
event_id = maltego_misp_event.id
search_result = self.conn.misp.search(controller='events', eventid=event_id, with_attachments=False)
search_result = self.misp.search(controller='events', eventid=event_id, with_attachments=False)
if search_result:
self.event_json = search_result.pop()
else:
@ -76,18 +77,17 @@ class EventToTransform(Transform):
def gen_response_objects(self):
for o in self.event_json['Event']['Object']:
self.response += self.conn.object_to_entity(o)
self.response += object_to_entity(o)
def gen_response_relations(self):
for e in self.event_json['Event']['RelatedEvent']:
self.response += event_to_entity(e, link_style=LinkStyle.DashDot)
# @EnableDebugWindow
class EventToAll(EventToTransform):
input_type = MISPEvent
display_name = 'To All'
description = 'Expands an Event to Attributes, Objects, Tags, Galaxies'
remote = True
def do_transform(self, request, response, config):
if super().do_transform(request, response, config):
@ -99,24 +99,22 @@ class EventToAll(EventToTransform):
return self.response
# @EnableDebugWindow
class EventToAttributes(EventToTransform):
input_type = MISPEvent
display_name = 'To Attributes/Objects'
remote = True
description = 'Expands an Event to Attributes'
def do_transform(self, request, response, config):
if super().do_transform(request, response, config):
self.gen_response_attributes()
self.gen_response_objects()
return self.response
# @EnableDebugWindow
class EventToTags(EventToTransform):
input_type = MISPEvent
display_name = 'To Tags'
description = 'Expands an Event to Tags and Galaxies'
remote = True
def do_transform(self, request, response, config):
if super().do_transform(request, response, config):
@ -126,10 +124,10 @@ class EventToTags(EventToTransform):
return self.response
# @EnableDebugWindow
class EventToGalaxies(EventToTransform):
input_type = MISPEvent
display_name = 'To Galaxies / ATT&CK'
remote = True
description = 'Expands an Event to Galaxies'
def do_transform(self, request, response, config):
if super().do_transform(request, response, config):
@ -138,10 +136,10 @@ class EventToGalaxies(EventToTransform):
return self.response
# @EnableDebugWindow
class EventToObjects(EventToTransform):
input_type = MISPEvent
display_name = 'To Objects'
remote = True
description = 'Expands an Event to Objects'
def do_transform(self, request, response, config):
if super().do_transform(request, response, config):
@ -150,10 +148,10 @@ class EventToObjects(EventToTransform):
return self.response
# @EnableDebugWindow
class EventToRelations(EventToTransform):
input_type = MISPEvent
display_name = 'To Related Events'
remote = True
description = 'Expands an Event to related Events'
def do_transform(self, request, response, config):
if super().do_transform(request, response, config):
@ -162,41 +160,43 @@ class EventToRelations(EventToTransform):
return self.response
# @EnableDebugWindow
class ObjectToAttributes(Transform):
""""Expands an object to its attributes"""
input_type = MISPObject
display_name = 'To Attributes'
remote = True
description = 'Expands an Object to Attributes'
def do_transform(self, request, response, config):
response += check_update(config)
maltego_object = request.entity
conn = MISPConnection(config, request.parameters)
event_json = conn.misp.get_event(maltego_object.event_id)
misp = get_misp_connection(config)
event_json = misp.get_event(maltego_object.event_id)
for o in event_json['Event']['Object']:
if o['uuid'] == maltego_object.uuid:
for entity in object_to_attributes(o, event_json):
if entity:
response += entity
for entity in conn.object_to_relations(o, event_json):
for entity in object_to_relations(o, event_json):
if entity:
response += entity
return response
# @EnableDebugWindow
class ObjectToRelations(Transform):
"""Expands an object to the relations of the object"""
input_type = MISPObject
display_name = 'To Related Objects'
remote = True
description = 'Expands an Object to Relations'
def do_transform(self, request, response, config):
response += check_update(config)
maltego_object = request.entity
conn = MISPConnection(config, request.parameters)
event_json = conn.misp.get_event(maltego_object.event_id)
misp = get_misp_connection(config)
event_json = misp.get_event(maltego_object.event_id)
for o in event_json['Event']['Object']:
if o['uuid'] == maltego_object.uuid:
for entity in conn.object_to_relations(o, event_json):
for entity in object_to_relations(o, event_json):
if entity:
response += entity

View File

@ -1,6 +1,7 @@
from canari.maltego.transform import Transform
# from canari.framework import EnableDebugWindow
from MISP_maltego.transforms.common.entities import MISPEvent, MISPGalaxy, ThreatActor, Software, AttackTechnique
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
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
@ -15,26 +16,52 @@ __email__ = 'christophe@vandeplas.com'
__status__ = 'Development'
# @EnableDebugWindow
class GalaxyToEvents(Transform):
"""Expands a Galaxy to multiple MISP Events."""
# The transform input entity type.
input_type = MISPGalaxy
def do_transform(self, request, response, config):
response += check_update(config)
maltego_misp_galaxy = request.entity
misp = get_misp_connection(config)
if maltego_misp_galaxy.tag_name:
tag_name = maltego_misp_galaxy.tag_name
else:
tag_name = maltego_misp_galaxy.value
events_json = misp.search(controller='events', tags=tag_name, with_attachments=False)
for e in events_json:
response += MISPEvent(e['Event']['id'], uuid=e['Event']['uuid'], info=e['Event']['info'], link_direction=LinkDirection.OutputToInput)
return response
# @EnableDebugWindow
class GalaxyToTransform(Transform):
input_type = None
def do_transform(self, request, response, config, type_filter=MISPGalaxy):
response += check_update(config)
maltego_misp_galaxy = request.entity
current_cluster = get_galaxy_cluster(request_entity=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:
current_cluster = get_galaxy_cluster(tag=maltego_misp_galaxy.tag_name)
elif maltego_misp_galaxy.name:
current_cluster = get_galaxy_cluster(tag=maltego_misp_galaxy.name)
# legacy - replaced by Search in MISP
if not current_cluster and request.entity.name != '-':
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(request.entity.name)
potential_clusters = search_galaxy_cluster(maltego_misp_galaxy.name)
# TODO check if duplicates are possible
if potential_clusters:
for potential_cluster in potential_clusters:
new_entity = galaxycluster_to_entity(potential_cluster, link_label='Search result')
if isinstance(new_entity, type_filter):
response += new_entity
response += galaxycluster_to_entity(potential_cluster, link_label='Search result')
return response
# end of legacy
if not current_cluster:
response += UIMessage("Galaxy Cluster UUID not in local mapping. Please update local cache; non-public UUID are not supported yet.", type=UIMessageType.Inform)
@ -42,7 +69,7 @@ class GalaxyToTransform(Transform):
c = current_cluster
# update existing object
galaxy_cluster = get_galaxy_cluster(uuid=c['uuid'])
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:
@ -67,7 +94,7 @@ class GalaxyToTransform(Transform):
# find related objects
if 'related' in current_cluster:
for related in current_cluster['related']:
related_cluster = get_galaxy_cluster(uuid=related['dest-uuid'])
related_cluster = get_galaxy_cluster(related['dest-uuid'])
if related_cluster:
new_entity = galaxycluster_to_entity(related_cluster, link_label=related['type'])
if isinstance(new_entity, type_filter):
@ -86,36 +113,32 @@ class GalaxyToTransform(Transform):
class GalaxyToRelations(GalaxyToTransform):
"""Expands a Galaxy to related Galaxies and Clusters"""
input_type = MISPGalaxy
display_name = 'To Related Galaxies'
remote = True
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
display_name = 'To Malware/Software/Tools'
remote = True
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
display_name = 'To Threat Actors'
remote = True
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
display_name = 'To Attack Techniques'
remote = True
def do_transform(self, request, response, config, type_filter=AttackTechnique):
return super().do_transform(request, response, config, type_filter)