From 323ca67a6c5689319e4b7da195ae148877f2bf2d Mon Sep 17 00:00:00 2001 From: Daniel Pascual Date: Thu, 3 Feb 2022 13:25:29 +0100 Subject: [PATCH] MISP exportmodule to create a VT Collection form an event --- misp_modules/modules/export_mod/__init__.py | 3 +- .../export_mod/virustotal_collections.py | 134 ++++++++++++++++++ 2 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 misp_modules/modules/export_mod/virustotal_collections.py diff --git a/misp_modules/modules/export_mod/__init__.py b/misp_modules/modules/export_mod/__init__.py index 5b69d02..ea90d19 100644 --- a/misp_modules/modules/export_mod/__init__.py +++ b/misp_modules/modules/export_mod/__init__.py @@ -1,2 +1,3 @@ __all__ = ['cef_export', 'mass_eql_export', 'liteexport', 'goamlexport', 'threat_connect_export', 'pdfexport', - 'threatStream_misp_export', 'osqueryexport', 'nexthinkexport', 'vt_graph', 'defender_endpoint_export'] + 'threatStream_misp_export', 'osqueryexport', 'nexthinkexport', 'vt_graph', 'defender_endpoint_export', + 'virustotal_collections'] diff --git a/misp_modules/modules/export_mod/virustotal_collections.py b/misp_modules/modules/export_mod/virustotal_collections.py new file mode 100644 index 0000000..fa2929c --- /dev/null +++ b/misp_modules/modules/export_mod/virustotal_collections.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 + +# Copyright 2022 Google Inc. All Rights Reserved. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Creates a VT Collection with indicators present in a given event.""" + +import base64 +import json +import requests + +misperrors = { + 'error': 'Error' +} + +mispattributes = { + 'input': [ + 'hostname', + 'domain', + 'ip-src', + 'ip-dst', + 'md5', + 'sha1', + 'sha256', + 'url' + ], + 'format': 'misp_standard', + 'responseType': 'application/txt', + 'outputFileExtension': 'txt', +} + +moduleinfo = { + 'version': '1.0', + 'author': 'VirusTotal', + 'description': 'Creates a VT Collection from an event iocs.', + 'module-type': ['export'] +} + +moduleconfig = [ + 'vt_api_key', + 'proxy_host', + 'proxy_port', + 'proxy_username', + 'proxy_password' +] + + +class VTError(Exception): + "Exception class to map vt api response errors." + pass + + +def create_collection(api_key, event_data): + headers = { + 'x-apikey': api_key, + 'content-type': 'application/json', + 'x-tool': 'MISPModuleVirusTotalCollectionExport', + } + + response = requests.post('https://www.virustotal.com/api/v3/integrations/misp/collections', + headers=headers, + json=event_data) + + uuid = event_data['Event']['uuid'] + response_data = response.json() + + if response.status_code == 200: + link = response_data['data']['links']['self'] + return f'{uuid}: {link}' + + error = response_data['error']['message'] + if response.status_code == 400: + return f'{uuid}: {error}' + else: + misperrors['error'] = error + raise VTError(error) + + +def normalize_misp_data(data): + normalized_data = {'Event': data.pop('Event', {})} + for attr_key in data: + if isinstance(data[attr_key], list) or isinstance(data[attr_key], dict): + if attr_key == 'EventTag': + normalized_data['Event']['Tag'] = [tag['Tag'] for tag in data[attr_key]] + else: + normalized_data['Event'][attr_key] = data[attr_key] + + return normalized_data + + +def handler(q=False): + request = json.loads(q) + + if not request.get('config') or not request['config'].get('vt_api_key'): + misperrors['error'] = 'A VirusTotal api key is required for this module.' + return misperrors + + config = request['config'] + data = request['data'] + responses = [] + + try: + for event_data in data: + normalized_event = normalize_misp_data(event_data) + responses.append(create_collection(config.get('vt_api_key'), + normalized_event)) + + output = '\n'.join(responses) + return { + "response": [], + "data": str(base64.b64encode(bytes(output, 'utf-8')), 'utf-8'), + } + except VTError: + return misperrors + + + +def introspection(): + return mispattributes + + +def version(): + moduleinfo['config'] = moduleconfig + return moduleinfo