├── otx-misp ├── timestamp └── otx_to_misp.py ├── README.md └── .gitignore /otx-misp/timestamp: -------------------------------------------------------------------------------- 1 | 2015-11-12T09:41:41.677035 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #CTI scripts 2 | 3 | Code snippets for interacting with and transforming cyber threat 4 | intelligence from a range of sources and platforms. 5 | 6 | ## otx_to_misp 7 | 8 | Sample code to process cyber threat indicators distributed via AlienVault's 9 | open threat exchange (OTX) platform and store these pulses as events and attributes 10 | in the malware information sharing platform (MISP). 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | -------------------------------------------------------------------------------- /otx-misp/otx_to_misp.py: -------------------------------------------------------------------------------- 1 | 2 | from OTXv2 import OTXv2 3 | from pandas.io.json import json_normalize 4 | from datetime import datetime, timedelta 5 | import requests 6 | from pymisp import PyMISP 7 | import time 8 | 9 | # service parameters 10 | otx_key = 'XXXXXXXXXXXX' 11 | misp_url = 'http://misp.tld' 12 | misp_key = 'NNNNNNNNNNNN' 13 | 14 | def saveTimestamp(timestamp=None): 15 | mtimestamp = timestamp 16 | if not timestamp: 17 | mtimestamp = datetime.now().isoformat() 18 | 19 | fname = "timestamp" 20 | f = open(fname, "w") 21 | f.write(mtimestamp) 22 | f.close() 23 | 24 | def readTimestamp(): 25 | fname = "timestamp" 26 | f = open(fname, "r") 27 | mtimestamp = f.read() 28 | f.close() 29 | return mtimestamp 30 | 31 | def pulse_to_misp(misp, pulse): 32 | """ 33 | Given a MISP context and a pulse containing indicators, use this information to 34 | - create a new MISP event using this context and metadata from the pulse object 35 | - populate attributes of this event that are derived from the indicators contained within the pulse 36 | """ 37 | # create the event 38 | pulse_name = pulse['author_name'] + ' | ' + pulse['name'] 39 | pulse_date = pulse['modified'] 40 | dt = datetime.strptime(pulse_date, '%Y-%m-%dT%H:%M:%S.%f') 41 | event = misp.new_event(0,4,2,pulse_name, date=dt.strftime('%Y-%m-%d'), published=True) 42 | 43 | time.sleep(0.2) 44 | 45 | # populate the attributes 46 | for ind in pulse['indicators']: 47 | ind_type = ind['type'] 48 | ind_val = ind['indicator'] 49 | """ 50 | u'FileHash-SHA256', 51 | u'domain', 52 | u'URL', 53 | u'hostname', 54 | u'URI', 55 | u'email', 56 | u'FileHash-SHA1', 57 | u'Mutex', 58 | u'IPv4', 59 | u'FileHash-MD5'] 60 | """ 61 | if ind_type == 'FileHash-SHA256': 62 | misp.add_hashes(event, sha256=ind_val) 63 | 64 | elif ind_type == 'FileHash-SHA1': 65 | misp.add_hashes(event, sha1=ind_val) 66 | 67 | elif ind_type == 'FileHash-MD5': 68 | misp.add_hashes(event, md5=ind_val) 69 | 70 | elif ind_type == 'URI' or ind_type == 'URL': 71 | misp.add_url(event, ind_val) 72 | 73 | elif ind_type == 'domain': 74 | misp.add_domain(event, ind_val) 75 | 76 | elif ind_type == 'hostname': 77 | misp.add_hostname(event, ind_val) 78 | 79 | elif ind_type == 'IPv4' or ind_type == 'IPv6': 80 | misp.add_ipdst(event, ind_val) 81 | 82 | elif ind_type == 'email': 83 | misp.add_email_src(event, ind_val) 84 | 85 | elif ind_type == 'Mutex': 86 | misp.add_mutex(event, ind_val) 87 | 88 | else: 89 | print("Unsupported indicator type: %s" % ind_type) 90 | 91 | time.sleep(0.2) 92 | 93 | if __name__ == "__main__": 94 | 95 | otx = OTXv2(otx_key) 96 | 97 | # The getall() method downloads all the OTX pulses and their assocciated indicators of compromise (IOCs) from your account. 98 | # This includes all of the following: 99 | # - OTX pulses to which you subscribed through the web UI 100 | # - Pulses created by OTX users to whom you subscribe 101 | # - OTX pulses you created. 102 | # If this is the first time you are using your account, the download includes all pulses created by AlienVault. 103 | # All users are subscribed to these by default. 104 | 105 | mtime = readTimestamp() 106 | pulses = otx.getsince(mtime) 107 | print("Retrived %d pulses" % len(pulses)) 108 | 109 | # Create a connection to a MISP server where arguments are: 110 | # - MISP URL 111 | # - Key for user who can create and update events 112 | 113 | misp = PyMISP(misp_url, misp_key, False, 'json') 114 | 115 | # Summary of information retrieved from OTX 116 | for p in pulses: 117 | print(p['modified']) 118 | print(p['name']) 119 | print(p['author_name']) 120 | print(len(p['indicators'])) 121 | print('='*12) 122 | 123 | # Add retrieved indicators (pulses) to MISP 124 | for pulse in pulses: 125 | pulse_to_misp(misp,pulse) 126 | 127 | saveTimestamp() 128 | --------------------------------------------------------------------------------