mirror of https://github.com/MISP/misp-modules
commit
7695dd1b62
|
@ -1 +1 @@
|
||||||
__all__ = ['testexport','cef_export','liteexport']
|
__all__ = ['testexport','cef_export','liteexport','threat_connect_export']
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
"""
|
||||||
|
Export module for converting MISP events into ThreatConnect Structured Import files. This export data is meant to be used with the "Structured Import" ability of ThreatConnect.
|
||||||
|
|
||||||
|
Source: http://kb.threatconnect.com/customer/en/portal/articles/1912599-using-structured-import/
|
||||||
|
Source: http://kb.threatconnect.com/customer/en/portal/articles/2092925-the-threatconnect-data-model/
|
||||||
|
"""
|
||||||
|
import base64
|
||||||
|
import csv
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
misperrors = {"error": "Error"}
|
||||||
|
|
||||||
|
moduleinfo = {
|
||||||
|
"version": "0.1",
|
||||||
|
"author": "CenturyLink CIRT",
|
||||||
|
"description": "Export a structured CSV file for uploading to ThreatConnect",
|
||||||
|
"module-type": ["export"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# config fields expected from the MISP administrator
|
||||||
|
# Default_Source: The source of the data. Typically this won't be changed from the default
|
||||||
|
moduleconfig = ["Default_Source"]
|
||||||
|
|
||||||
|
# Map of MISP fields => ThreatConnect fields
|
||||||
|
fieldmap = {
|
||||||
|
"domain": "Host",
|
||||||
|
"domain|ip": "Host|Address",
|
||||||
|
"hostname": "Host",
|
||||||
|
"ip-src": "Address",
|
||||||
|
"ip-dst": "Address",
|
||||||
|
"ip-src|port": "Address",
|
||||||
|
"ip-dst|port": "Address",
|
||||||
|
"whois-registrant-email": "EmailAddress",
|
||||||
|
"email-src": "EmailAddress",
|
||||||
|
"email-dst": "EmailAddress",
|
||||||
|
"url": "URL",
|
||||||
|
"md5": "File",
|
||||||
|
"filename|md5": "File"
|
||||||
|
}
|
||||||
|
|
||||||
|
# combine all the MISP fields from fieldmap into one big list
|
||||||
|
mispattributes = {
|
||||||
|
"input": list(fieldmap.keys())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def handler(q=False):
|
||||||
|
"""
|
||||||
|
Convert a MISP query into a CSV file matching the ThreatConnect Structured Import file format.
|
||||||
|
Input
|
||||||
|
q: Query dictionary
|
||||||
|
"""
|
||||||
|
if q is False or not q:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if we were given a configuration
|
||||||
|
request = json.loads(q)
|
||||||
|
config = request.get("config", {"Default_Source": ""})
|
||||||
|
logging.info("Setting config to: %s", config)
|
||||||
|
|
||||||
|
response = io.StringIO()
|
||||||
|
writer = csv.DictWriter(response, fieldnames=["Type", "Value", "Source", "Description"])
|
||||||
|
writer.writeheader()
|
||||||
|
|
||||||
|
# start parsing MISP data
|
||||||
|
for event in request["data"]:
|
||||||
|
for attribute in event["Attribute"]:
|
||||||
|
if attribute["type"] in mispattributes["input"]:
|
||||||
|
logging.debug("Adding %s to structured CSV export of ThreatConnectExport", attribute["value"])
|
||||||
|
if "|" in attribute["type"]:
|
||||||
|
# if the attribute type has multiple values, line it up with the corresponding ThreatConnect values in fieldmap
|
||||||
|
indicators = tuple(attribute["value"].split("|"))
|
||||||
|
tc_types = tuple(fieldmap[attribute["type"]].split("|"))
|
||||||
|
for i, indicator in enumerate(indicators):
|
||||||
|
writer.writerow({
|
||||||
|
"Type": tc_types[i],
|
||||||
|
"Value": indicator,
|
||||||
|
"Source": config["Default_Source"],
|
||||||
|
"Description": attribute["comment"]
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
writer.writerow({
|
||||||
|
"Type": fieldmap[attribute["type"]],
|
||||||
|
"Value": attribute["value"],
|
||||||
|
"Source": config["Default_Source"],
|
||||||
|
"Description": attribute["comment"]
|
||||||
|
})
|
||||||
|
|
||||||
|
return {"response": [], "data": str(base64.b64encode(bytes(response.getvalue(), 'utf-8')), 'utf-8')}
|
||||||
|
|
||||||
|
|
||||||
|
def introspection():
|
||||||
|
"""
|
||||||
|
Relay the supported attributes to MISP.
|
||||||
|
No Input
|
||||||
|
Output
|
||||||
|
Dictionary of supported MISP attributes
|
||||||
|
"""
|
||||||
|
modulesetup = {
|
||||||
|
"responseType": "application/txt",
|
||||||
|
"outputFileExtension": "csv",
|
||||||
|
"userConfig": {},
|
||||||
|
"inputSource": []
|
||||||
|
}
|
||||||
|
return modulesetup
|
||||||
|
|
||||||
|
|
||||||
|
def version():
|
||||||
|
"""
|
||||||
|
Relay module version and associated metadata to MISP.
|
||||||
|
No Input
|
||||||
|
Output
|
||||||
|
moduleinfo: metadata output containing all potential configuration values
|
||||||
|
"""
|
||||||
|
moduleinfo["config"] = moduleconfig
|
||||||
|
return moduleinfo
|
|
@ -0,0 +1,297 @@
|
||||||
|
{
|
||||||
|
"Event": {
|
||||||
|
"id": "625",
|
||||||
|
"orgc_id": "2",
|
||||||
|
"org_id": "1",
|
||||||
|
"date": "2017-05-24",
|
||||||
|
"threat_level_id": "3",
|
||||||
|
"info": "M2M - Fwd: IMG_3428.pdf",
|
||||||
|
"published": false,
|
||||||
|
"uuid": "59259036-fcd0-4749-8a6c-4d88950d210f",
|
||||||
|
"attribute_count": "2",
|
||||||
|
"analysis": "1",
|
||||||
|
"timestamp": "1500496265",
|
||||||
|
"distribution": "3",
|
||||||
|
"proposal_email_lock": false,
|
||||||
|
"user_id": "1",
|
||||||
|
"locked": false,
|
||||||
|
"publish_timestamp": "0",
|
||||||
|
"sharing_group_id": "0",
|
||||||
|
"disable_correlation": false
|
||||||
|
},
|
||||||
|
"User": {
|
||||||
|
"email": "admin@misp.training",
|
||||||
|
"id": "1"
|
||||||
|
},
|
||||||
|
"ThreatLevel": {
|
||||||
|
"name": "Low",
|
||||||
|
"id": "3"
|
||||||
|
},
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "2",
|
||||||
|
"name": "CIRCL",
|
||||||
|
"uuid": "55f6ea5e-2c60-40e5-964f-47a8950d210f"
|
||||||
|
},
|
||||||
|
"Attribute": [{
|
||||||
|
"id": "157835",
|
||||||
|
"type": "attachment",
|
||||||
|
"category": "Artifacts dropped",
|
||||||
|
"to_ids": false,
|
||||||
|
"uuid": "59259037-1014-4669-96b1-46af950d210f",
|
||||||
|
"event_id": "625",
|
||||||
|
"distribution": "5",
|
||||||
|
"timestamp": "1495633975",
|
||||||
|
"comment": "IMG_3428.pdf",
|
||||||
|
"sharing_group_id": "0",
|
||||||
|
"deleted": false,
|
||||||
|
"disable_correlation": false,
|
||||||
|
"value": "tmpzuni0skf",
|
||||||
|
"AttributeTag": [],
|
||||||
|
"ShadowAttribute": []
|
||||||
|
}, {
|
||||||
|
"id": "164191",
|
||||||
|
"type": "domain|ip",
|
||||||
|
"category": "Network activity",
|
||||||
|
"to_ids": false,
|
||||||
|
"uuid": "59430251-e6a4-4900-b78b-060dc0a83832",
|
||||||
|
"event_id": "625",
|
||||||
|
"distribution": "5",
|
||||||
|
"timestamp": "1497563729",
|
||||||
|
"comment": "Test data",
|
||||||
|
"sharing_group_id": "0",
|
||||||
|
"deleted": false,
|
||||||
|
"disable_correlation": false,
|
||||||
|
"value": "google.com|127.0.0.1",
|
||||||
|
"AttributeTag": [],
|
||||||
|
"ShadowAttribute": []
|
||||||
|
}],
|
||||||
|
"ShadowAttribute": [],
|
||||||
|
"EventTag": [{
|
||||||
|
"id": "1482",
|
||||||
|
"event_id": "625",
|
||||||
|
"tag_id": "2",
|
||||||
|
"Tag": {
|
||||||
|
"id": "2",
|
||||||
|
"name": "tlp:white",
|
||||||
|
"colour": "#ffffff",
|
||||||
|
"exportable": true,
|
||||||
|
"org_id": "0",
|
||||||
|
"hide_tag": false
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"Galaxy": [],
|
||||||
|
"RelatedEvent": [{
|
||||||
|
"Event": {
|
||||||
|
"id": "226",
|
||||||
|
"date": "2015-11-05",
|
||||||
|
"threat_level_id": "4",
|
||||||
|
"info": "OSINT Expansion on Systematic cyber attacks against Israeli and Palestinian targets going on for a year by Norman",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "563b3ea6-b26c-401f-a68b-4d84950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1487757679",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"Event": {
|
||||||
|
"id": "207",
|
||||||
|
"date": "2015-04-03",
|
||||||
|
"threat_level_id": "4",
|
||||||
|
"info": "OSINT The Dyre Wolf report from IBM",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "551e8745-ace0-461c-b9eb-ce36950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1428070986",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"Event": {
|
||||||
|
"id": "209",
|
||||||
|
"date": "2015-01-26",
|
||||||
|
"threat_level_id": "2",
|
||||||
|
"info": "OSINT I Know You Want Me - Unplugging PlugX from Takahiro Haruyama & Hiroshi Suzuki Black Hat Asia 2014 presentation",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "54c60f43-b084-453a-a162-4e08950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1422356942",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"Event": {
|
||||||
|
"id": "214",
|
||||||
|
"date": "2014-12-18",
|
||||||
|
"threat_level_id": "4",
|
||||||
|
"info": "Expansion on two IPs listed in OSINT IOCs from various campaigns listed in Detecting Bleeding Edge Malware presentation at hack.lu 2014",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "54932a3e-7284-4753-b95c-4e08950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1442489489",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"Event": {
|
||||||
|
"id": "208",
|
||||||
|
"date": "2014-11-20",
|
||||||
|
"threat_level_id": "4",
|
||||||
|
"info": "Import of CitizenLab public DB of malware indicators",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "546e08ce-3134-4892-997b-73ff950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1487758220",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"Event": {
|
||||||
|
"id": "373",
|
||||||
|
"date": "2014-11-18",
|
||||||
|
"threat_level_id": "4",
|
||||||
|
"info": "OSINT Expansion on Additional indicators relating to Sofacy (APT28) phishing blog post by PWC",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "546bc3e8-d498-4e0c-b169-f2ea950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1487758281",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"Event": {
|
||||||
|
"id": "230",
|
||||||
|
"date": "2014-10-02",
|
||||||
|
"threat_level_id": "3",
|
||||||
|
"info": "OSINT ShellShock scanning IPs from OpenDNS",
|
||||||
|
"published": true,
|
||||||
|
"uuid": "542e4c9c-cadc-4f8f-bb11-6d13950d210b",
|
||||||
|
"analysis": "2",
|
||||||
|
"timestamp": "1442489604",
|
||||||
|
"distribution": "3",
|
||||||
|
"org_id": "1",
|
||||||
|
"orgc_id": "3",
|
||||||
|
"Org": {
|
||||||
|
"id": "1",
|
||||||
|
"name": "MISP",
|
||||||
|
"uuid": "56ef3277-1ad4-42f6-b90b-04e5c0a83832"
|
||||||
|
},
|
||||||
|
"Orgc": {
|
||||||
|
"id": "3",
|
||||||
|
"name": "CthulhuSPRL.be",
|
||||||
|
"uuid": "55f6ea5f-fd34-43b8-ac1d-40cb950d210f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"RelatedAttribute": {
|
||||||
|
"164191": [{
|
||||||
|
"id": "207",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "OSINT The Dyre Wolf report from IBM",
|
||||||
|
"value": "google.com"
|
||||||
|
}, {
|
||||||
|
"id": "208",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "Import of CitizenLab public DB of malware indicators",
|
||||||
|
"value": "127.0.0.1"
|
||||||
|
}, {
|
||||||
|
"id": "209",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "OSINT I Know You Want Me - Unplugging PlugX from Takahiro Haruyama & Hiroshi Suzuki Black Hat Asia 2014 presentation",
|
||||||
|
"value": "127.0.0.1"
|
||||||
|
}, {
|
||||||
|
"id": "214",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "Expansion on two IPs listed in OSINT IOCs from various campaigns listed in Detecting Bleeding Edge Malware presentation at hack.lu 2014",
|
||||||
|
"value": "127.0.0.1"
|
||||||
|
}, {
|
||||||
|
"id": "226",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "OSINT Expansion on Systematic cyber attacks against Israeli and Palestinian targets going on for a year by Norman",
|
||||||
|
"value": "127.0.0.1"
|
||||||
|
}, {
|
||||||
|
"id": "230",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "OSINT ShellShock scanning IPs from OpenDNS",
|
||||||
|
"value": "127.0.0.1"
|
||||||
|
}, {
|
||||||
|
"id": "373",
|
||||||
|
"org_id": "1",
|
||||||
|
"info": "OSINT Expansion on Additional indicators relating to Sofacy (APT28) phishing blog post by PWC",
|
||||||
|
"value": "127.0.0.1"
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"RelatedShadowAttribute": [],
|
||||||
|
"Sighting": []
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""Test module for the ThreatConnect Export module"""
|
||||||
|
import base64
|
||||||
|
import csv
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
class TestModules(unittest.TestCase):
|
||||||
|
"""Unittest module for threat_connect_export.py"""
|
||||||
|
def setUp(self):
|
||||||
|
self.headers = {'Content-Type': 'application/json'}
|
||||||
|
self.url = "http://127.0.0.1:6666/"
|
||||||
|
self.module = "threat_connect_export"
|
||||||
|
input_event_path = "%s/test_files/misp_event.json" % os.path.dirname(os.path.realpath(__file__))
|
||||||
|
with open(input_event_path, "r") as ifile:
|
||||||
|
self.event = json.load(ifile)
|
||||||
|
|
||||||
|
def test_01_introspection(self):
|
||||||
|
"""Taken from test.py"""
|
||||||
|
try:
|
||||||
|
response = requests.get(self.url + "modules")
|
||||||
|
modules = [module["name"] for module in response.json()]
|
||||||
|
assert self.module in modules
|
||||||
|
finally:
|
||||||
|
response.connection.close()
|
||||||
|
|
||||||
|
def test_02_export(self):
|
||||||
|
"""Test an event export"""
|
||||||
|
test_source = "Test Export"
|
||||||
|
query = {
|
||||||
|
"module": self.module,
|
||||||
|
"data": [self.event],
|
||||||
|
"config": {
|
||||||
|
"Default_Source": test_source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(self.url + "query", headers=self.headers, data=json.dumps(query))
|
||||||
|
data = base64.b64decode(response.json()["data"]).decode("utf-8")
|
||||||
|
csvfile = io.StringIO(data)
|
||||||
|
reader = csv.DictReader(csvfile)
|
||||||
|
|
||||||
|
values = [field["Value"] for field in reader]
|
||||||
|
assert "google.com" in values
|
||||||
|
assert "127.0.0.1" in values
|
||||||
|
|
||||||
|
# resetting file pointer to read through again and extract sources
|
||||||
|
csvfile.seek(0)
|
||||||
|
# use a set comprehension to deduplicate sources
|
||||||
|
sources = {field["Source"] for field in reader}
|
||||||
|
assert test_source in sources
|
||||||
|
finally:
|
||||||
|
response.connection.close()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue