From 473cf04336b9e9f1df3e9f5657882ced68750028 Mon Sep 17 00:00:00 2001 From: Jared Jennings Date: Wed, 16 Sep 2020 21:24:35 +0000 Subject: [PATCH] add initial mimecast responder --- .../Mimecast/Mimecast_Block_Domain.json | 56 ++++++++++++ responders/Mimecast/Mimecast_Block_URL.json | 56 ++++++++++++ responders/Mimecast/mimecast_responder.py | 88 +++++++++++++++++++ responders/Mimecast/requirements.txt | 6 ++ 4 files changed, 206 insertions(+) create mode 100644 responders/Mimecast/Mimecast_Block_Domain.json create mode 100644 responders/Mimecast/Mimecast_Block_URL.json create mode 100755 responders/Mimecast/mimecast_responder.py create mode 100644 responders/Mimecast/requirements.txt diff --git a/responders/Mimecast/Mimecast_Block_Domain.json b/responders/Mimecast/Mimecast_Block_Domain.json new file mode 100644 index 000000000..b0aacf790 --- /dev/null +++ b/responders/Mimecast/Mimecast_Block_Domain.json @@ -0,0 +1,56 @@ +{ + "name": "Mimecast Block URL Domain", + "version": "0.3", + "author": "Jared Jennings ", + "license": "AGPL-V3", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "description": "Block access to all URLs under a DNS name from emails through Mimecast URL Protect", + "dataTypeList": ["thehive:case_artifact"], + "command": "Mimecast/mimecast_responder.py", + "baseConfig": "Mimecast API", + "config": { + "service": "block_domain", + "max_tlp": 2, + "check_tlp": true, + "max_pap": 2, + "check_pap": true + }, + "configurationItems": [ + { + "name": "base_url", + "description": "Mimecast API base URL", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "https://us-api.mimecast.com" + }, + { + "name": "app_id", + "description": "Application ID, a GUID", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "app_key", + "description": "App key, a GUID", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "access_key", + "description": "Access key, some Base64 text", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "secret_key", + "description": "Secret key, some Base64 text", + "type": "string", + "multi": false, + "required": true + } + ] +} diff --git a/responders/Mimecast/Mimecast_Block_URL.json b/responders/Mimecast/Mimecast_Block_URL.json new file mode 100644 index 000000000..1c216b4e3 --- /dev/null +++ b/responders/Mimecast/Mimecast_Block_URL.json @@ -0,0 +1,56 @@ +{ + "name": "Mimecast Block Explicit URL", + "version": "0.3", + "author": "Jared Jennings ", + "license": "AGPL-V3", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "description": "Block access to a URL from emails through Mimecast URL Protect", + "dataTypeList": ["thehive:case_artifact"], + "command": "Mimecast/mimecast.py", + "baseConfig": "Mimecast API", + "config": { + "service": "block_url", + "max_tlp": 2, + "check_tlp": true, + "max_pap": 2, + "check_pap": true + }, + "configurationItems": [ + { + "name": "base_url", + "description": "Mimecast API base URL", + "type": "string", + "multi": false, + "required": true, + "defaultValue": "https://us-api.mimecast.com" + }, + { + "name": "app_id", + "description": "Application ID, a GUID", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "app_key", + "description": "App key, a GUID", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "access_key", + "description": "Access key, some Base64 text", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "secret_key", + "description": "Secret key, some Base64 text", + "type": "string", + "multi": false, + "required": true + } + ] +} diff --git a/responders/Mimecast/mimecast_responder.py b/responders/Mimecast/mimecast_responder.py new file mode 100755 index 000000000..b9d066583 --- /dev/null +++ b/responders/Mimecast/mimecast_responder.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +import traceback +import datetime +from cortexutils.responder import Responder +from mimecast_api import MimecastAPI + +class MimecastResponder(Responder): + def __init__(self): + super().__init__() + self.service = self.get_param('config.service', None, + 'Service parameter is missing') + mimecast_config = { + 'base_url': self.get_param('config.base_url', None, + 'Mimecast API base URL is missing'), + 'app_id': self.get_param('config.app_id', None, + 'App ID is missing'), + 'app_key': self.get_param('config.app_key', None, + 'App key is missing'), + 'access_key': self.get_param('config.access_key', None, + 'Access key is missing'), + 'secret_key': self.get_param('config.secret_key', None, + 'Secret key is missing'), + } + self.mimecast_api = MimecastAPI(**mimecast_config) + + def operations(self, raw): + out = [] + if 'blocked' in raw: + out.append(self.build_operation('AddTagToArtifact', + tag=f"MC:Blocked={raw['blocked']}")) + return out + + def run(self): + super().run() + # I tried supporting 'domain' typed observables too, but the + # Mimecast API seemed to want a whole URL, not just a DNS + # name, even for domain blocks. + if self.get_param('data.dataType') == 'url': + url = self.get_param('data.data', None, 'No url found') + else: + self.error('Only url dataType is supported for this operation') + if self.service == 'block_url': + thing = 'URL' + only_match_domain_blocks = False + block_type = 'explicit' + elif self.service == 'block_domain': + thing = 'Domain' + only_match_domain_blocks = True + block_type = 'domain' + else: + self.error(f'Unknown service {self.service}') + try: + managed = self.mimecast_api.is_url_managed( + url, only_match_domain_blocks=only_match_domain_blocks) + if managed: + if managed['action'] == 'block': + self.report({"message": f"{thing} is already blocked", + "matchType": managed['matchType'], + "action": managed['action'], + "comment": managed['comment'], + "blocked": managed['matchType']}) + else: + # this request conflicts with previous direction given + # to Mimecast, so make an error + # + # unfortunately i don't think you can fail to run + # and also render a report visible in the hive. so + # this failure will always result in an empty + # report and you'll only see this message if you + # go into cortex + self.error(thing + " is already managed: matched by {matchType}, action {action}, comment {comment!r}. Nothing done. Visit Mimecast's website manually.".format(managed)) + else: + case_number = self.get_param('data.case.caseId', None) + if case_number: + comment = f"Hive #{case_number}" + else: + now = datetime.datetime.utcnow().strftime('%Y%m%dT%H%M%S+0000') + comment = f"Cyber threat response #{now}" + now_managed = self.mimecast_api.block_url(url, block_type, comment) + self.report({"message": f"{thing} block added with comment {comment!r}", + "blocked": now_managed['matchType']}) + except Exception as e: + # expected errors would have been handled with self.error + self.error('Unexpected exception:\n'+traceback.format_exc()) + + +if __name__ == '__main__': + MimecastResponder().run() diff --git a/responders/Mimecast/requirements.txt b/responders/Mimecast/requirements.txt new file mode 100644 index 000000000..ff6b8fee6 --- /dev/null +++ b/responders/Mimecast/requirements.txt @@ -0,0 +1,6 @@ +certifi==2020.4.5.1 +chardet==3.0.4 +idna==2.9 +requests==2.23.0 +urllib3==1.25.9 +mimecast-api==0.3