parent
c54258d547
commit
29ce499f35
|
@ -0,0 +1,262 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
######
|
||||||
|
# TODO: DETECT DUPLICATE DATA
|
||||||
|
#####
|
||||||
|
|
||||||
|
import os
|
||||||
|
import pymisp
|
||||||
|
import tempfile
|
||||||
|
import logging
|
||||||
|
import datetime
|
||||||
|
import time
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
from pymisp.abstract import MISPEncode
|
||||||
|
from misp_stix_converter.converters import convert
|
||||||
|
from misp_stix_converter.converters import buildMISPAttribute
|
||||||
|
from pyaml import yaml
|
||||||
|
from io import StringIO
|
||||||
|
|
||||||
|
log = logging.getLogger("__main__")
|
||||||
|
|
||||||
|
from opentaxii.signals import (
|
||||||
|
CONTENT_BLOCK_CREATED, INBOX_MESSAGE_CREATED
|
||||||
|
)
|
||||||
|
|
||||||
|
## CONFIG
|
||||||
|
if "OPENTAXII_CONFIG" in os.environ:
|
||||||
|
log.info("Using config from {}".format(os.environ["OPENTAXII_CONFIG"]))
|
||||||
|
CONFIG = yaml.load(open(os.environ["OPENTAXII_CONFIG"], "r"))
|
||||||
|
else:
|
||||||
|
log.info("Trying to use env variables...")
|
||||||
|
if "MISP_URL" in os.environ:
|
||||||
|
misp_url = os.environ["MISP_URL"]
|
||||||
|
else:
|
||||||
|
log.info("Unkown misp URL. Set OPENTAXII_CONFIG or MISP_URL.")
|
||||||
|
misp_url = "UNKNOWN"
|
||||||
|
if "MISP_API" in os.environ:
|
||||||
|
misp_api = os.environ["MISP_API"]
|
||||||
|
else:
|
||||||
|
log.info("Unknown misp API key. Set OPENTAXII_CONFIG or MISP_API.")
|
||||||
|
misp_api = "UNKNOWN"
|
||||||
|
|
||||||
|
CONFIG = {
|
||||||
|
"misp" : {
|
||||||
|
"url" : misp_url,
|
||||||
|
"api" : misp_api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MISP = pymisp.PyMISP(
|
||||||
|
CONFIG["misp"]["url"],
|
||||||
|
CONFIG["misp"]["api"],
|
||||||
|
ssl = CONFIG["misp"].get("verifySSL", False)
|
||||||
|
)
|
||||||
|
|
||||||
|
def mytimestamp():
|
||||||
|
return datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d_%H:%M:%S')
|
||||||
|
|
||||||
|
def sanitizer(s):
|
||||||
|
return s.strip(' \t\n\r')
|
||||||
|
|
||||||
|
|
||||||
|
def detect_tlp(package):
|
||||||
|
|
||||||
|
handling = {}
|
||||||
|
|
||||||
|
if hasattr(package, "stix_header"):
|
||||||
|
if hasattr(package.stix_header, "handling"):
|
||||||
|
|
||||||
|
for markingitem in package.stix_header.handling:
|
||||||
|
|
||||||
|
if hasattr(markingitem, "controlled_structure"):
|
||||||
|
|
||||||
|
handling["marking"] = markingitem.controlled_structure
|
||||||
|
|
||||||
|
for itemcolor in markingitem.marking_structures:
|
||||||
|
if hasattr(itemcolor, "color"):
|
||||||
|
handling["color"] = itemcolor.color
|
||||||
|
else:
|
||||||
|
log.info("NO Handling found in this package")
|
||||||
|
else:
|
||||||
|
log.info("No Header found in this package")
|
||||||
|
|
||||||
|
return handling
|
||||||
|
|
||||||
|
|
||||||
|
def detect_source(package):
|
||||||
|
|
||||||
|
source = False
|
||||||
|
|
||||||
|
if hasattr(package, "stix_header"):
|
||||||
|
if hasattr(package.stix_header, "information_source"):
|
||||||
|
if hasattr(package.stix_header.information_source.identity, "name"):
|
||||||
|
source = package.stix_header.information_source.identity.name
|
||||||
|
|
||||||
|
return source
|
||||||
|
|
||||||
|
|
||||||
|
def detect_title(package):
|
||||||
|
'''
|
||||||
|
Return a list
|
||||||
|
- event tile + attachment filename
|
||||||
|
- 1 for detectable event (and possible event update), 0 for undetectable event
|
||||||
|
'''
|
||||||
|
timestamp = mytimestamp()
|
||||||
|
|
||||||
|
if hasattr(package, "stix_header"):
|
||||||
|
if hasattr(package.stix_header, "title"):
|
||||||
|
return package.stix_header.title, 1
|
||||||
|
|
||||||
|
elif hasattr(package.stix_header, "description"):
|
||||||
|
return package.stix_header.description, 1
|
||||||
|
else:
|
||||||
|
return "STIX_FILE_NO_TITLE_" + timestamp, 0
|
||||||
|
else:
|
||||||
|
return "STIX_FILE_NO_TITLE_" + timestamp, 0
|
||||||
|
|
||||||
|
def searchEvent(search):
|
||||||
|
result = MISP.search("events", values=str(sanitizer(search)))
|
||||||
|
|
||||||
|
if result["response"] == []:
|
||||||
|
# New event
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
# Maybe an update
|
||||||
|
# Check if a stix file named with `title`.xml exists
|
||||||
|
event = result["response"][0]["Event"]["Attribute"][0]["event_id"]
|
||||||
|
filename = search + ".xml"
|
||||||
|
attachment = MISP.search("events", values=str(filename), type_attribute="attachment", eventid=event)
|
||||||
|
|
||||||
|
if attachment["response"] == []:
|
||||||
|
# New event
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
# Event to update
|
||||||
|
return event
|
||||||
|
|
||||||
|
def post_stix(manager, content_block, collection_ids, service_id):
|
||||||
|
'''
|
||||||
|
Callback function for when our taxii server gets new data
|
||||||
|
Will convert it to a MISPEvent and push to the server
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Load the package
|
||||||
|
log.info("Posting STIX...")
|
||||||
|
block = content_block.content
|
||||||
|
if isinstance(block, bytes):
|
||||||
|
block = block.decode()
|
||||||
|
|
||||||
|
package = convert.load_stix(StringIO(block))
|
||||||
|
# Building event obj
|
||||||
|
distribution=3
|
||||||
|
threat_level_id=2
|
||||||
|
analysis=0
|
||||||
|
|
||||||
|
misp_event = buildMISPAttribute.buildEvent(package, distribution=distribution, threat_level_id=threat_level_id, analysis=analysis)
|
||||||
|
log.info("STIX loaded succesfully. Let's go!")
|
||||||
|
|
||||||
|
evaluatePackage = detect_title(package)
|
||||||
|
tlp = detect_tlp(package)
|
||||||
|
source = detect_source(package)
|
||||||
|
|
||||||
|
title = evaluatePackage[0]
|
||||||
|
detectable = evaluatePackage[1]
|
||||||
|
|
||||||
|
if detectable == 1:
|
||||||
|
|
||||||
|
search = searchEvent(title)
|
||||||
|
|
||||||
|
if search == 0 :
|
||||||
|
# New Event!
|
||||||
|
b64Pkg = base64.b64encode(package.to_xml()).decode("utf-8")
|
||||||
|
|
||||||
|
if misp_event.attributes:
|
||||||
|
filename = title + ".xml"
|
||||||
|
misp_event.add_attribute(type="attachment", value=filename, data=b64Pkg)
|
||||||
|
|
||||||
|
if tlp:
|
||||||
|
misp_event.add_tag("tlp:"+tlp['color'])
|
||||||
|
misp_event.add_tag("Marking_Controlled_Structure:"+tlp['marking'])
|
||||||
|
|
||||||
|
if source:
|
||||||
|
misp_event.add_tag("source:"+source)
|
||||||
|
|
||||||
|
response = MISP.add_event(json.dumps(misp_event, cls=MISPEncode))
|
||||||
|
|
||||||
|
if response.get('errors'):
|
||||||
|
raise Exception("PACKAGE: {}\nERROR: {}".format(json.dumps(misp_event, cls=MISPEncode),response.get('errors')))
|
||||||
|
|
||||||
|
else:
|
||||||
|
MISP.fast_publish(response["Event"]["id"])
|
||||||
|
else:
|
||||||
|
log.info("No attributes detected")
|
||||||
|
else:
|
||||||
|
myeventid = search
|
||||||
|
|
||||||
|
# Just the library default!
|
||||||
|
# Edit if you need
|
||||||
|
distribution=3
|
||||||
|
threat_level_id=2
|
||||||
|
analysis=0
|
||||||
|
buildattribute = buildMISPAttribute.buildEvent(package, distribution=distribution, threat_level_id=threat_level_id, analysis=analysis)
|
||||||
|
|
||||||
|
items = [x for x in buildattribute.attributes]
|
||||||
|
|
||||||
|
for attrib in items:
|
||||||
|
|
||||||
|
searchatt = MISP.search("attributes", values=str(sanitizer(attrib.value)), type_attribute=str(attrib.type), eventid=myeventid)
|
||||||
|
|
||||||
|
if searchatt["response"] != []:
|
||||||
|
log.info("%s is a duplicate, we'll ignore it.", attrib.value)
|
||||||
|
buildattribute.attributes.pop([x.value for x in buildattribute.attributes].index(attrib.value))
|
||||||
|
else:
|
||||||
|
log.info("%s is unique, we'll keep it", attrib.value)
|
||||||
|
|
||||||
|
if (len(buildattribute.attributes) > 0 ):
|
||||||
|
b64Pkg = base64.b64encode(package.to_xml()).decode("utf-8")
|
||||||
|
updatetimestamp = mytimestamp()
|
||||||
|
filename = title + "_" + updatetimestamp + ".xml"
|
||||||
|
misp_event.add_attribute(type="attachment", value=filename, data=b64Pkg)
|
||||||
|
|
||||||
|
if tlp:
|
||||||
|
misp_event.add_tag("tlp:"+tlp['color'])
|
||||||
|
misp_event.add_tag("Marking_Controlled_Structure:"+tlp['marking'])
|
||||||
|
|
||||||
|
if source:
|
||||||
|
misp_event.add_tag("source:"+source)
|
||||||
|
|
||||||
|
MISP.update_event(myeventid,json.dumps(misp_event, cls=MISPEncode))
|
||||||
|
MISP.fast_publish(myeventid)
|
||||||
|
log.info("Updating event: " +myeventid)
|
||||||
|
else:
|
||||||
|
log.info("Nothing to update for event: "+ myeventid)
|
||||||
|
else:
|
||||||
|
log.info("Event undetectable. Will be used old style import!")
|
||||||
|
values = [x.value for x in misp_event.attributes]
|
||||||
|
log.info("Extracted %s", values)
|
||||||
|
|
||||||
|
for attrib in values:
|
||||||
|
log.info("Checking for existence of %s", attrib)
|
||||||
|
search = MISP.search("attributes", values=str(sanitizer(attrib)))
|
||||||
|
|
||||||
|
if search["response"] != []:
|
||||||
|
# This means we have it!
|
||||||
|
log.info("%s is a duplicate, we'll ignore it.", attrib)
|
||||||
|
misp_event.attributes.pop([x.value for x in misp_event.attributes].index(attrib))
|
||||||
|
else:
|
||||||
|
log.info("%s is unique, we'll keep it", attrib)
|
||||||
|
|
||||||
|
# Push the event to MISP
|
||||||
|
# TODO: There's probably a proper method to do this rather than json_full
|
||||||
|
# But I don't wanna read docs
|
||||||
|
if (len(misp_event.attributes) > 0):
|
||||||
|
log.info("Uploading event to MISP with attributes %s", [x.value for x in misp_event.attributes])
|
||||||
|
MISP.add_event(misp_event)
|
||||||
|
else:
|
||||||
|
log.info("No attributes, not bothering.")
|
||||||
|
|
||||||
|
# Make TAXII call our push function whenever it gets new data
|
||||||
|
CONTENT_BLOCK_CREATED.connect(post_stix)
|
||||||
|
|
Loading…
Reference in New Issue