Modules for expansion services, enrichment, import and export in MISP and other tools.
MISP modules are autonomous modules that can be used to extend MISP for new services such as expansion, import and export.
The modules are written in Python 3 following a simple API interface. The objective is to ease the extensions of MISP functionalities without modifying core components. The API is available via a simple REST API which is independent from MISP installation or configuration.
For more information: Extending MISP with Python modules slides from MISP training.
Be sure to run the latest version of pip
. To install the latest version of pip, pip install --upgrade pip
will do the job.
sudo apt-get install python3-dev python3-pip libpq5 libjpeg-dev tesseract-ocr libpoppler-cpp-dev imagemagick virtualenv libopencv-dev zbar-tools libzbar0 libzbar-dev libfuzzy-dev build-essential -y
sudo -u www-data virtualenv -p python3 /var/www/MISP/venv
cd /usr/local/src/
sudo chown -R www-data: .
sudo -u www-data git clone https://github.com/MISP/misp-modules.git
cd misp-modules
sudo -u www-data /var/www/MISP/venv/bin/pip install -I -r REQUIREMENTS
sudo -u www-data /var/www/MISP/venv/bin/pip install .
# Start misp-modules as a service
sudo cp etc/systemd/system/misp-modules.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now misp-modules
sudo service misp-modules start #or
/var/www/MISP/venv/bin/misp-modules -l 127.0.0.1 & #to start the modules
As of this writing, the official RHEL repositories only contain Ruby 2.0.0 and Ruby 2.1 or higher is required. As such, this guide installs Ruby 2.2 from the SCL repository.
sudo yum install rh-python36 rh-ruby22
sudo yum install openjpeg-devel
sudo yum install rubygem-rouge rubygem-asciidoctor zbar-devel opencv-devel gcc-c++ pkgconfig poppler-cpp-devel python-devel redhat-rpm-config
cd /var/www/MISP
git clone https://github.com/MISP/misp-modules.git
cd misp-modules
sudo -u apache /usr/bin/scl enable rh-python36 "virtualenv -p python3 /var/www/MISP/venv"
sudo -u apache /var/www/MISP/venv/bin/pip install -U -I -r REQUIREMENTS
sudo -u apache /var/www/MISP/venv/bin/pip install -U .
Create the service file /etc/systemd/system/misp-modules.service :
echo "[Unit]
Description=MISP's modules
After=misp-workers.service
[Service]
Type=simple
User=apache
Group=apache
ExecStart=/usr/bin/scl enable rh-python36 rh-ruby22 '/var/www/MISP/venv/bin/misp-modules -l 127.0.0.1'
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/misp-modules.service
The After=misp-workers.service
must be changed or removed if you have not created a misp-workers service.
Then, enable the misp-modules service and start it:
systemctl daemon-reload
systemctl enable --now misp-modules
Create your module in misp_modules/modules/expansion/, misp_modules/modules/export_mod/, or misp_modules/modules/import_mod/. The module should have at minimum three functions:
Don't forget to return an error key and value if an error is raised to propagate it to the MISP user-interface.
Your module's script name should also be added in the __all__
list of <module type folder>/__init__.py
in order for it to be loaded.
...
# Checking for required value
if not request.get('ip-src'):
# Return an error message
return {'error': "A source IP is required"}
...
The function that returns a dict of the supported attributes (input and output) by your expansion module.
mispattributes = {'input': ['link', 'url'],
'output': ['attachment', 'malware-sample']}
def introspection():
return mispattributes
The function that returns a dict with the version and the associated meta-data including potential configurations required of the module.
If your module requires additional configuration (to be exposed via the MISP user-interface), you can define those in the moduleconfig value returned by the version function.
# config fields that your code expects from the site admin
moduleconfig = ["apikey", "event_limit"]
def version():
moduleinfo['config'] = moduleconfig
return moduleinfo
When you do this a config array is added to the meta-data output containing all the potential configuration values:
"meta": {
"description": "PassiveTotal expansion service to expand values with multiple Passive DNS sources",
"config": [
"username",
"password"
],
"module-type": [
"expansion",
"hover"
],
...
If you want to use the configuration values set in the web interface they are stored in the key config
in the JSON object passed to the handler.
def handler(q=False):
# Check if we were given a configuration
config = q.get("config", {})
# Find out if there is a username field
username = config.get("username", None)
The function which accepts a JSON document to expand the values and return a dictionary of the expanded values.
def handler(q=False):
"Fully functional rot-13 encoder"
if q is False:
return False
request = json.loads(q)
src = request.get('ip-src')
if src is None:
# Return an error message
return {'error': "A source IP is required"}
else:
return {'results':
codecs.encode(src, "rot-13")}
For an export module, the request["data"]
object corresponds to a list of events (dictionaries) to handle.
Iterating over events attributes is performed using their Attribute
key.
...
for event in request["data"]:
for attribute in event["Attribute"]:
# do stuff w/ attribute['type'], attribute['value'], ...
...
### Returning Binary Data
If you want to return a file or other data you need to add a data attribute.
~~~python
{"results": {"values": "filename.txt",
"types": "attachment",
"data" : base64.b64encode(<ByteIO>) # base64 encode your data first
"comment": "This is an attachment"}}
If the binary file is malware you can use 'malware-sample' as the type. If you do this the malware sample will be automatically zipped and password protected ('infected') after being uploaded.
{"results": {"values": "filename.txt",
"types": "malware-sample",
"data" : base64.b64encode(<ByteIO>) # base64 encode your data first
"comment": "This is an attachment"}}
To learn more about how data attributes are processed you can read the processing code here.
A MISP module can be of four types:
module-type is an array where the list of supported types can be added.
MISP uses the modules function to discover the available MISP modules and their supported MISP attributes:
% curl -s http://127.0.0.1:6666/modules | jq .
[
{
"name": "passivetotal",
"type": "expansion",
"mispattributes": {
"input": [
"hostname",
"domain",
"ip-src",
"ip-dst"
],
"output": [
"ip-src",
"ip-dst",
"hostname",
"domain"
]
},
"meta": {
"description": "PassiveTotal expansion service to expand values with multiple Passive DNS sources",
"config": [
"username",
"password"
],
"author": "Alexandre Dulaunoy",
"version": "0.1"
}
},
{
"name": "sourcecache",
"type": "expansion",
"mispattributes": {
"input": [
"link"
],
"output": [
"link"
]
},
"meta": {
"description": "Module to cache web pages of analysis reports, OSINT sources. The module returns a link of the cached page.",
"author": "Alexandre Dulaunoy",
"version": "0.1"
}
},
{
"name": "dns",
"type": "expansion",
"mispattributes": {
"input": [
"hostname",
"domain"
],
"output": [
"ip-src",
"ip-dst"
]
},
"meta": {
"description": "Simple DNS expansion service to resolve IP address from MISP attributes",
"author": "Alexandre Dulaunoy",
"version": "0.1"
}
}
]
The MISP module service returns the available modules in a JSON array containing each module name along with their supported input attributes.
Based on this information, a query can be built in a JSON format and saved as body.json:
{
"hostname": "www.foo.be",
"module": "dns"
}
Then you can POST this JSON format query towards the MISP object server:
curl -s http://127.0.0.1:6666/query -H "Content-Type: application/json" --data @body.json -X POST
The module should output the following JSON:
{
"results": [
{
"types": [
"ip-src",
"ip-dst"
],
"values": [
"188.65.217.78"
]
}
]
}
It is also possible to restrict the category options of the resolved attributes by passing a list of categories along (optional):
{
"results": [
{
"types": [
"ip-src",
"ip-dst"
],
"values": [
"188.65.217.78"
],
"categories": [
"Network activity",
"Payload delivery"
]
}
]
}
For both the type and the category lists, the first item in the list will be the default setting on the interface.
For a module to be activated in the MISP web interface it must be enabled in the "Plugin Settings.
Go to "Administration > Server Settings" in the top menu
Priority Setting Value Description Error Message
Recommended Plugin.Import_ocr_enabled false Enable or disable the ocr module. Value not set.
Priority Setting Value Description Error Message
Recommended Plugin.Import_ocr_enabled true Enable or disable the ocr module. Value not set.
In this same menu set any other plugin settings that are required for testing.
First, you need to grab all necessary packages for example like this :
Use pip wheel to create an archive
mkdir misp-modules-offline
pip3 wheel -r REQUIREMENTS shodan --wheel-dir=./misp-modules-offline
tar -cjvf misp-module-bundeled.tar.bz2 ./misp-modules-offline/*
On offline machine :
mkdir misp-modules-bundle
tar xvf misp-module-bundeled.tar.bz2 -C misp-modules-bundle
cd misp-modules-bundle
ls -1|while read line; do sudo pip3 install --force-reinstall --ignore-installed --upgrade --no-index --no-deps ${line};done
Next you can follow standard install procedure.
Fork the project, add your module, test it and make a pull-request. Modules can be also private as you can add a module in your own MISP installation.
Download a pre-built virtual image from the MISP training materials.
cd /usr/local/src/misp-modules
Set the git repo to your fork and checkout your development branch. If you SSH'ed in as the misp user you will have to use sudo.
sudo git remote set-url origin https://github.com/YourRepo/misp-modules.git
sudo git pull
sudo git checkout MyModBranch
Remove the contents of the build directory and re-install misp-modules.
sudo rm -fr build/*
sudo -u www-data /var/www/MISP/venv/bin/pip install --upgrade .
SSH in with a different terminal and run misp-modules
with debugging enabled.
# In case misp-modules is not a service do:
# sudo killall misp-modules
sudo systemctl disable --now misp-modules
sudo -u www-data /var/www/MISP/venv/bin/misp-modules -d
In your original terminal you can now run your tests manually and see any errors that arrive
cd tests/
curl -s http://127.0.0.1:6666/query -H "Content-Type: application/json" --data @MY_TEST_FILE.json -X POST
cd ../
In order to provide documentation about some modules that require specific input / output / configuration, the doc directory contains detailed information about the general purpose, requirements, features, input and ouput of each of these modules: