mirror of https://github.com/MISP/misp-modules
				
				
				
			
		
			
				
	
	
		
			141 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Python
		
	
	
| import jbxapi
 | |
| import base64
 | |
| import io
 | |
| import json
 | |
| import logging
 | |
| import sys
 | |
| import zipfile
 | |
| import re
 | |
| 
 | |
| from urllib.parse import urljoin
 | |
| 
 | |
| 
 | |
| log = logging.getLogger(__name__)
 | |
| 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 = {
 | |
|     "version": "1.0",
 | |
|     "author": "Joe Security LLC",
 | |
|     "description": "Submit files and URLs to Joe Sandbox",
 | |
|     "module-type": ["expansion", "hover"]
 | |
| }
 | |
| moduleconfig = [
 | |
|     "apiurl",
 | |
|     "apikey",
 | |
|     "accept-tac",
 | |
|     "report-cache",
 | |
|     "systems",
 | |
| ]
 | |
| 
 | |
| mispattributes = {
 | |
|     "input": ["attachment", "malware-sample", "url", "domain"],
 | |
|     "output": ["link"],
 | |
| }
 | |
| 
 | |
| 
 | |
| def handler(q=False):
 | |
|     if q is False:
 | |
|         return False
 | |
| 
 | |
|     request = json.loads(q)
 | |
| 
 | |
|     apiurl = request["config"].get("apiurl") or "https://jbxcloud.joesecurity.org/api"
 | |
|     apikey = request["config"].get("apikey")
 | |
| 
 | |
|     # systems
 | |
|     systems = request["config"].get("systems") or ""
 | |
|     systems = [s.strip() for s in re.split(r"[\s,;]", systems) if s.strip()]
 | |
| 
 | |
|     try:
 | |
|         accept_tac = _parse_bool(request["config"].get("accept-tac"), "accept-tac")
 | |
|         report_cache = _parse_bool(request["config"].get("report-cache"), "report-cache")
 | |
|     except _ParseError as e:
 | |
|         return {"error": str(e)}
 | |
| 
 | |
|     params = {
 | |
|         "report-cache": report_cache,
 | |
|         "systems": systems,
 | |
|     }
 | |
| 
 | |
|     if not apikey:
 | |
|         return {"error": "No API key provided"}
 | |
| 
 | |
|     joe = jbxapi.JoeSandbox(apiurl=apiurl, apikey=apikey, user_agent="MISP joesandbox_submit", accept_tac=accept_tac)
 | |
| 
 | |
|     try:
 | |
|         is_url_submission = "url" in request or "domain" in request
 | |
| 
 | |
|         if is_url_submission:
 | |
|             url = request.get("url") or request.get("domain")
 | |
| 
 | |
|             log.info("Submitting URL: %s", url)
 | |
|             result = joe.submit_url(url, params=params)
 | |
|         else:
 | |
|             if "malware-sample" in request:
 | |
|                 filename = request.get("malware-sample").split("|", 1)[0]
 | |
|                 data = _decode_malware(request["data"], True)
 | |
|             elif "attachment" in request:
 | |
|                 filename = request["attachment"]
 | |
|                 data = _decode_malware(request["data"], False)
 | |
| 
 | |
|             data_fp = io.BytesIO(data)
 | |
|             log.info("Submitting sample: %s", filename)
 | |
|             result = joe.submit_sample((filename, data_fp), params=params)
 | |
| 
 | |
|         assert "submission_id" in result
 | |
|     except jbxapi.JoeException as e:
 | |
|         return {"error": str(e)}
 | |
| 
 | |
|     link_to_analysis = urljoin(apiurl, "../submissions/{}".format(result["submission_id"]))
 | |
| 
 | |
|     return {
 | |
|         "results": [{
 | |
|             "types": "link",
 | |
|             "categories": "External analysis",
 | |
|             "values": link_to_analysis,
 | |
|         }]
 | |
|     }
 | |
| 
 | |
| 
 | |
| def introspection():
 | |
|     return mispattributes
 | |
| 
 | |
| 
 | |
| def version():
 | |
|     moduleinfo["config"] = moduleconfig
 | |
|     return moduleinfo
 | |
| 
 | |
| 
 | |
| def _decode_malware(data, is_encrypted):
 | |
|     data = base64.b64decode(data)
 | |
| 
 | |
|     if is_encrypted:
 | |
|         with zipfile.ZipFile(io.BytesIO(data)) as zipf:
 | |
|             data = zipf.read(zipf.namelist()[0], pwd=b"infected")
 | |
| 
 | |
|     return data
 | |
| 
 | |
| 
 | |
| class _ParseError(Exception):
 | |
|     pass
 | |
| 
 | |
| 
 | |
| def _parse_bool(value, name="bool"):
 | |
|     if value is None or value == "":
 | |
|         return None
 | |
| 
 | |
|     if value == "true":
 | |
|         return True
 | |
| 
 | |
|     if value == "false":
 | |
|         return False
 | |
| 
 | |
|     raise _ParseError("Cannot parse {}. Must be 'true' or 'false'".format(name))
 |