mirror of https://github.com/MISP/misp-modules
				
				
				
			
		
			
				
	
	
		
			142 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			142 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Python
		
	
	
| #!/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'],
 | |
|     'name': 'VirusTotal Collections Export',
 | |
|     'logo': 'virustotal.png',
 | |
|     'requirements': ['An access to the VirusTotal API (apikey).'],
 | |
|     'features': 'This export module which takes advantage of a new endpoint in VT APIv3 to create VT Collections from IOCs contained in a MISP event. With this module users will be able to create a collection just using the Download as... button.',
 | |
|     'references': ['https://www.virustotal.com/', 'https://blog.virustotal.com/2021/11/introducing-virustotal-collections.html'],
 | |
|     'input': 'A domain, hash (md5, sha1, sha256 or sha512), hostname, url or IP address attribute.',
 | |
|     'output': 'A VirusTotal collection in VT.',
 | |
| }
 | |
| 
 | |
| 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:
 | |
|     col_id = response_data['data']['id']
 | |
|     return f'{uuid}: https://www.virustotal.com/gui/collection/{col_id}/iocs'
 | |
| 
 | |
|   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
 |