MISP-Taxii-Server/misp_taxii_hooks/hooks.py

177 lines
6.6 KiB
Python
Raw Normal View History

2016-11-18 10:28:00 +01:00
#!/usr/bin/env python3
######
# TODO: DETECT DUPLICATE DATA
#####
2016-11-23 13:16:30 +01:00
import os
2016-11-18 10:28:00 +01:00
import pymisp
import tempfile
import logging
2016-11-23 13:16:30 +01:00
from pyaml import yaml
2020-05-07 02:00:04 +02:00
from yaml import Loader
from io import StringIO
from requests.exceptions import ConnectionError
2020-06-16 04:00:47 +02:00
from pymisp.exceptions import PyMISPError
2016-11-18 10:28:00 +01:00
logging_level = logging.INFO
log = logging.getLogger("__main__")
2020-06-16 03:12:24 +02:00
handler = logging.StreamHandler()
log.setLevel(logging_level)
handler.setLevel(logging_level)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
2020-06-16 03:12:24 +02:00
log.addHandler(handler)
2016-11-18 10:28:00 +01:00
from opentaxii.signals import (
CONTENT_BLOCK_CREATED, INBOX_MESSAGE_CREATED
)
def env_config_helper(env_name):
if env_name in os.environ:
if env_name == "MISP_COLLECTIONS":
name = os.environ[env_name]
return name.split(',')
return os.environ[env_name]
else:
log.error("Missing env setting {0}. Set OPENTAXII_CONFIG or {0}.".format(env_name))
return "UNKNOWN"
def yaml_config_helper(config_name, CONFIG):
if config_name in CONFIG["misp"]:
if not CONFIG["misp"][config_name] and CONFIG["misp"][config_name] != False:
CONFIG["misp"][config_name] = "UNKNOWN"
else:
CONFIG["misp"][config_name] = "UNKNOWN"
return CONFIG
2016-11-18 10:28:00 +01:00
## CONFIG
2016-11-23 16:31:56 +01:00
if "OPENTAXII_CONFIG" in os.environ:
log.info("Using config from {}".format(os.environ["OPENTAXII_CONFIG"]))
2020-05-07 02:00:04 +02:00
CONFIG = yaml.load(open(os.environ["OPENTAXII_CONFIG"], "r"), Loader=Loader)
# validate dedup and collections and publish
CONFIG = yaml_config_helper("dedup", CONFIG)
CONFIG = yaml_config_helper("collections", CONFIG)
CONFIG = yaml_config_helper("publish", CONFIG)
2016-11-23 13:16:30 +01:00
else:
log.debug("Trying to use env variables...")
misp_url = env_config_helper("MISP_URL")
misp_api = env_config_helper("MISP_API")
misp_dedup = env_config_helper("MISP_DEDUP")
misp_collections = env_config_helper("MISP_COLLECTIONS")
misp_publish = env_config_helper("MISP_PUBLISH")
2016-11-18 10:28:00 +01:00
2016-11-23 13:16:30 +01:00
CONFIG = {
2016-11-23 16:31:56 +01:00
"misp" : {
"url" : misp_url,
"api" : misp_api,
"dedup" : misp_dedup,
"collections": misp_collections
2016-11-23 16:31:56 +01:00
}
2016-11-23 13:16:30 +01:00
}
2020-06-16 04:24:18 +02:00
MISP = ''
2020-06-16 04:00:47 +02:00
try:
MISP = pymisp.PyMISP(
2016-11-23 16:31:56 +01:00
CONFIG["misp"]["url"],
CONFIG["misp"]["api"],
2017-05-22 17:45:06 +02:00
ssl = CONFIG["misp"].get("verifySSL", True)
)
2020-06-16 04:00:47 +02:00
except PyMISPError:
log.error("Cannot connect to MISP; please ensure that MISP is up and running at {}. Skipping MISP upload.".format(CONFIG['misp']['url']))
def post_stix(manager, content_block, collection_ids, service_id):
'''
Callback function for when our taxii server gets new data
Will convert it to a MISPEvent and push to the server
'''
# make sure collections, if specified are supposed to be sent to
if CONFIG["misp"]["collections"] != "UNKNOWN" or CONFIG["misp"]["collections"] == False:
2020-05-15 01:18:53 +02:00
log.info("Using collections")
should_send_to_misp = False
collection_names = [collection.name for collection in manager.get_collections(service_id) if collection.id in collection_ids]
for collection in CONFIG["misp"]["collections"]:
if collection in collection_names or collection in collection_ids:
2020-05-15 01:18:53 +02:00
log.info("Collection specified matches push collection: {}".format(collection))
should_send_to_misp = True
2020-05-15 01:18:53 +02:00
break
if should_send_to_misp == False:
log.info('''No collections match misp.collections; aborting MISP extraction.
Collection ids whitelisted: {}
Collection ids sent to: {}
Collection names sent to: {}'''.format(
CONFIG["misp"]["collections"],
collection_ids,
collection_names
))
return None
# Load the package
log.info("Posting STIX...")
block = content_block.content
if isinstance(block, bytes):
block = block.decode()
package = pymisp.tools.stix.load_stix(StringIO(block))
log.info("STIX loaded succesfully.")
2017-06-14 14:52:29 +02:00
values = [x.value for x in package.attributes]
log.info("Extracted %s", values)
# if deduping is enabled, start deduping
2020-05-13 22:49:34 +02:00
if (
CONFIG["misp"]["dedup"] or
2020-05-13 22:49:34 +02:00
CONFIG["misp"]["dedup"] == "True" or
CONFIG["misp"]["dedup"] == "UNKNOWN"
):
for attrib in values:
log.info("Checking for existence of %s", attrib)
2020-06-16 04:24:18 +02:00
search = ''
if MISP:
search = MISP.search("attributes", values=str(attrib))
if 'response' in search:
if search["response"]["Attribute"] != []:
# This means we have it!
log.info("%s is a duplicate, we'll ignore it.", attrib)
package.attributes.pop([x.value for x in package.attributes].index(attrib))
else:
log.info("%s is unique, we'll keep it", attrib)
elif 'Attribute' in search:
if search["Attribute"] != []:
# This means we have it!
log.info("%s is a duplicate, we'll ignore it.", attrib)
package.attributes.pop([x.value for x in package.attributes].index(attrib))
else:
log.info("%s is unique, we'll keep it", attrib)
else:
log.error("Something went wrong with search, and it doesn't have an 'attribute' or a 'response' key: {}".format(search.keys()))
else:
log.info("Skipping deduplication")
# Push the event to MISP
# TODO: There's probably a proper method to do this rather than json_full
# But I don't wanna read docs
if (len(package.attributes) > 0):
log.info("Uploading event to MISP with attributes %s", [x.value for x in package.attributes])
2020-06-16 04:24:18 +02:00
event = ''
try:
2020-06-16 04:24:18 +02:00
if MISP:
event = MISP.add_event(package)
except ConnectionError, NameError:
2020-06-16 04:00:47 +02:00
log.error("Cannot push to MISP; please ensure that MISP is up and running at {}. Skipping MISP upload.".format(CONFIG['misp']['url']))
if (
CONFIG["misp"]["publish"] == True or
CONFIG["misp"]["publish"] == "True"
):
log.info("Publishing event to MISP with ID {}".format(event.get('uuid')))
2020-06-16 04:24:18 +02:00
if MISP:
MISP.publish(event)
2020-05-15 01:43:01 +02:00
else:
log.info("Skipping MISP event publishing")
else:
log.info("No attributes, not bothering.")
2016-11-18 10:28:00 +01:00
# Make TAXII call our push function whenever it gets new data
2016-11-18 10:28:00 +01:00
CONTENT_BLOCK_CREATED.connect(post_stix)