misp-modules/misp_modules/modules/expansion/cuckoo_submit.py

162 lines
4.7 KiB
Python
Raw Permalink Normal View History

import base64
import io
2019-04-18 00:23:38 +02:00
import json
import logging
2019-04-18 00:23:38 +02:00
import requests
import sys
import urllib.parse
import zipfile
2019-04-18 00:23:38 +02:00
from requests.exceptions import RequestException
log = logging.getLogger("cuckoo_submit")
log.setLevel(logging.DEBUG)
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG)
fmt = logging.Formatter(
"%(asctime)s - %(name)s - %(levelname)s - %(message)s"
)
sh.setFormatter(fmt)
log.addHandler(sh)
moduleinfo = {
2024-08-12 11:23:10 +02:00
'version': '0.1',
'author': 'Evert Kors',
'description': 'Submit files and URLs to Cuckoo Sandbox',
'module-type': ['expansion', 'hover'],
'name': 'Cuckoo Submit',
'logo': 'cuckoo.png',
'requirements': ['Access to a Cuckoo Sandbox API and an API key if the API requires it. (api_url and api_key)'],
'features': 'The module takes a malware-sample, attachment, url or domain and submits it to Cuckoo Sandbox.\n The returned task id can be used to retrieve results when the analysis completed.',
'references': ['https://cuckoosandbox.org/', 'https://cuckoo.sh/docs/'],
'input': 'A malware-sample or attachment for files. A url or domain for URLs.',
'output': "A text field containing 'Cuckoo task id: <id>'",
}
misperrors = {"error": "Error"}
moduleconfig = ["api_url", "api_key"]
2019-04-18 00:23:38 +02:00
mispattributes = {
"input": ["attachment", "malware-sample", "url", "domain"],
"output": ["text"]
2019-04-18 00:23:38 +02:00
}
class APIKeyError(RequestException):
"""Raised if the Cuckoo API returns a 401. This means no or an invalid
bearer token was supplied."""
pass
class CuckooAPI(object):
def __init__(self, api_url, api_key=""):
self.api_key = api_key
if not api_url.startswith("http"):
api_url = "https://{}".format(api_url)
self.api_url = api_url
def _post_api(self, endpoint, files=None, data={}):
data.update({
"owner": "MISP"
})
try:
response = requests.post(
urllib.parse.urljoin(self.api_url, endpoint),
files=files, data=data,
headers={"Authorization": "Bearer {}".format(self.api_key)}
)
except RequestException as e:
log.error("Failed to submit sample to Cuckoo Sandbox. %s", e)
return None
if response.status_code == 401:
raise APIKeyError("Invalid or no Cuckoo Sandbox API key provided")
if response.status_code != 200:
log.error("Invalid Cuckoo API response")
return None
return response.json()
def create_task(self, filename, fp):
response = self._post_api(
"/tasks/create/file", files={"file": (filename, fp)}
)
if not response:
return False
return response["task_id"]
def create_url(self, url):
response = self._post_api(
"/tasks/create/url", data={"url": url}
)
if not response:
return False
return response["task_id"]
2019-04-18 00:23:38 +02:00
def handler(q=False):
if q is False:
return False
2019-04-18 00:23:38 +02:00
request = json.loads(q)
# See if the API URL was provided. The API key is optional, as it can
# be disabled in the Cuckoo API settings.
api_url = request["config"].get("api_url")
api_key = request["config"].get("api_key", "")
if not api_url:
misperrors["error"] = "No Cuckoo API URL provided"
2019-04-18 00:23:38 +02:00
return misperrors
url = request.get("url") or request.get("domain")
data = request.get("data")
filename = None
if data:
data = base64.b64decode(data)
2019-04-18 00:23:38 +02:00
if "malware-sample" in request:
filename = request.get("malware-sample").split("|", 1)[0]
with zipfile.ZipFile(io.BytesIO(data)) as zipf:
data = zipf.read(zipf.namelist()[0], pwd=b"infected")
2019-04-18 00:23:38 +02:00
elif "attachment" in request:
filename = request.get("attachment")
2019-04-18 00:23:38 +02:00
cuckoo_api = CuckooAPI(api_url=api_url, api_key=api_key)
task_id = None
2019-04-18 00:23:38 +02:00
try:
if url:
log.debug("Submitting URL to Cuckoo Sandbox %s", api_url)
task_id = cuckoo_api.create_url(url)
elif data and filename:
log.debug("Submitting file to Cuckoo Sandbox %s", api_url)
task_id = cuckoo_api.create_task(
filename=filename, fp=io.BytesIO(data)
)
except APIKeyError as e:
misperrors["error"] = "Failed to submit to Cuckoo: {}".format(e)
return misperrors
if not task_id:
misperrors["error"] = "File or URL submission failed"
2019-04-18 00:23:38 +02:00
return misperrors
return {
"results": [
{"types": "text", "values": "Cuckoo task id: {}".format(task_id)}
]
}
2019-04-18 00:23:38 +02:00
def introspection():
return mispattributes
def version():
moduleinfo["config"] = moduleconfig
2019-04-18 00:23:38 +02:00
return moduleinfo