mirror of https://github.com/MISP/misp-modules
				
				
				
			
		
			
				
	
	
		
			152 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
'''
 | 
						|
Submit sample to VMRay.
 | 
						|
 | 
						|
Requires "vmray_rest_api"
 | 
						|
 | 
						|
The expansion module vmray_submit and import module vmray_import are a two step
 | 
						|
process to import data from VMRay.
 | 
						|
You can automate this by setting the PyMISP example script 'vmray_automation'
 | 
						|
as a cron job
 | 
						|
 | 
						|
'''
 | 
						|
 | 
						|
import json
 | 
						|
import base64
 | 
						|
from distutils.util import strtobool
 | 
						|
 | 
						|
import io
 | 
						|
import zipfile
 | 
						|
 | 
						|
from ._vmray.vmray_rest_api import VMRayRESTAPI
 | 
						|
 | 
						|
misperrors = {'error': 'Error'}
 | 
						|
mispattributes = {'input': ['attachment', 'malware-sample'], 'output': ['text', 'sha1', 'sha256', 'md5', 'link']}
 | 
						|
moduleinfo = {'version': '0.3', 'author': 'Koen Van Impe',
 | 
						|
              'description': 'Submit a sample to VMRay',
 | 
						|
              'module-type': ['expansion']}
 | 
						|
moduleconfig = ['apikey', 'url', 'shareable', 'do_not_reanalyze', 'do_not_include_vmrayjobids']
 | 
						|
 | 
						|
 | 
						|
include_vmrayjobids = False
 | 
						|
 | 
						|
 | 
						|
def handler(q=False):
 | 
						|
    global include_vmrayjobids
 | 
						|
 | 
						|
    if q is False:
 | 
						|
        return False
 | 
						|
    request = json.loads(q)
 | 
						|
 | 
						|
    try:
 | 
						|
        data = request.get("data")
 | 
						|
        if 'malware-sample' in request:
 | 
						|
            # malicious samples are encrypted with zip (password infected) and then base64 encoded
 | 
						|
            sample_filename = request.get("malware-sample").split("|", 1)[0]
 | 
						|
            data = base64.b64decode(data)
 | 
						|
            fl = io.BytesIO(data)
 | 
						|
            zf = zipfile.ZipFile(fl)
 | 
						|
            sample_hashname = zf.namelist()[0]
 | 
						|
            data = zf.read(sample_hashname, b"infected")
 | 
						|
            zf.close()
 | 
						|
        elif 'attachment' in request:
 | 
						|
            # All attachments get base64 encoded
 | 
						|
            sample_filename = request.get("attachment")
 | 
						|
            data = base64.b64decode(data)
 | 
						|
 | 
						|
        else:
 | 
						|
            misperrors['error'] = "No malware sample or attachment supplied"
 | 
						|
            return misperrors
 | 
						|
    except Exception:
 | 
						|
        misperrors['error'] = "Unable to process submited sample data"
 | 
						|
        return misperrors
 | 
						|
 | 
						|
    if (request["config"].get("apikey") is None) or (request["config"].get("url") is None):
 | 
						|
        misperrors["error"] = "Missing API key or server URL (hint: try cloud.vmray.com)"
 | 
						|
        return misperrors
 | 
						|
 | 
						|
    api = VMRayRESTAPI(request["config"].get("url"), request["config"].get("apikey"), False)
 | 
						|
 | 
						|
    shareable = request["config"].get("shareable")
 | 
						|
    do_not_reanalyze = request["config"].get("do_not_reanalyze")
 | 
						|
    do_not_include_vmrayjobids = request["config"].get("do_not_include_vmrayjobids")
 | 
						|
 | 
						|
    try:
 | 
						|
        shareable = bool(strtobool(shareable))                                 # Do we want the sample to be shared?
 | 
						|
        reanalyze = not bool(strtobool(do_not_reanalyze))                      # Always reanalyze the sample?
 | 
						|
        include_vmrayjobids = not bool(strtobool(do_not_include_vmrayjobids))  # Include the references to VMRay job IDs
 | 
						|
    except ValueError:
 | 
						|
        misperrors["error"] = "Error while processing settings. Please double-check your values."
 | 
						|
        return misperrors
 | 
						|
 | 
						|
    if data and sample_filename:
 | 
						|
        args = {}
 | 
						|
        args["shareable"] = shareable
 | 
						|
        args["sample_file"] = {'data': io.BytesIO(data), 'filename': sample_filename}
 | 
						|
        args["reanalyze"] = reanalyze
 | 
						|
 | 
						|
        try:
 | 
						|
            vmraydata = vmraySubmit(api, args)
 | 
						|
            if vmraydata["errors"] and "Submission not stored" not in vmraydata["errors"][0]["error_msg"]:
 | 
						|
                misperrors['error'] = "VMRay: %s" % vmraydata["errors"][0]["error_msg"]
 | 
						|
                return misperrors
 | 
						|
            else:
 | 
						|
                return vmrayProcess(vmraydata)
 | 
						|
        except Exception:
 | 
						|
            misperrors['error'] = "Problem when calling API."
 | 
						|
            return misperrors
 | 
						|
    else:
 | 
						|
        misperrors['error'] = "No sample data or filename."
 | 
						|
        return misperrors
 | 
						|
 | 
						|
 | 
						|
def introspection():
 | 
						|
    return mispattributes
 | 
						|
 | 
						|
 | 
						|
def version():
 | 
						|
    moduleinfo['config'] = moduleconfig
 | 
						|
    return moduleinfo
 | 
						|
 | 
						|
 | 
						|
def vmrayProcess(vmraydata):
 | 
						|
    ''' Process the JSON file returned by vmray'''
 | 
						|
    if vmraydata:
 | 
						|
        try:
 | 
						|
            sample = vmraydata["samples"][0]
 | 
						|
            jobs = vmraydata["jobs"]
 | 
						|
 | 
						|
            # Result received?
 | 
						|
            if sample:
 | 
						|
                r = {'results': []}
 | 
						|
                r['results'].append({'types': 'md5', 'values': sample['sample_md5hash']})
 | 
						|
                r['results'].append({'types': 'sha1', 'values': sample['sample_sha1hash']})
 | 
						|
                r['results'].append({'types': 'sha256', 'values': sample['sample_sha256hash']})
 | 
						|
                r['results'].append({'types': 'text', 'values': 'VMRay Sample ID: %s' % sample['sample_id'], 'tags': 'workflow:state="incomplete"'})
 | 
						|
                r['results'].append({'types': 'link', 'values': sample['sample_webif_url']})
 | 
						|
 | 
						|
                # Include data from different jobs
 | 
						|
                if include_vmrayjobids and len(jobs) > 0:
 | 
						|
                    for job in jobs:
 | 
						|
                        job_id = job["job_id"]
 | 
						|
                        job_vm_name = job["job_vm_name"]
 | 
						|
                        job_configuration_name = job["job_configuration_name"]
 | 
						|
                        r["results"].append({"types": "text", "values": "VMRay Job ID %s (%s - %s)" % (job_id, job_vm_name, job_configuration_name)})
 | 
						|
                return r
 | 
						|
            else:
 | 
						|
                misperrors['error'] = "No valid results returned."
 | 
						|
                return misperrors
 | 
						|
        except Exception:
 | 
						|
            misperrors['error'] = "No valid submission data returned."
 | 
						|
            return misperrors
 | 
						|
    else:
 | 
						|
        misperrors['error'] = "Unable to parse results."
 | 
						|
        return misperrors
 | 
						|
 | 
						|
 | 
						|
def vmraySubmit(api, args):
 | 
						|
    ''' Submit the sample to VMRay'''
 | 
						|
    vmraydata = api.call("POST", "/rest/sample/submit", args)
 | 
						|
    return vmraydata
 |