mirror of https://github.com/MISP/misp-modules
Modules for expansion services, import and export in MISP
http://misp.github.io/misp-modules
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
5.5 KiB
162 lines
5.5 KiB
#!/usr/bin/env python3 |
|
|
|
''' |
|
Submit sample to VMRay. |
|
|
|
Submit a sample to VMRay |
|
|
|
TODO: |
|
# Deal with archive submissions |
|
|
|
''' |
|
|
|
import json |
|
import base64 |
|
|
|
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.2', '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: |
|
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") |
|
|
|
# Do we want the sample to be shared? |
|
if shareable == "True": |
|
shareable = True |
|
else: |
|
shareable = False |
|
|
|
# Always reanalyze the sample? |
|
if do_not_reanalyze == "True": |
|
do_not_reanalyze = True |
|
else: |
|
do_not_reanalyze = False |
|
reanalyze = not do_not_reanalyze |
|
|
|
# Include the references to VMRay job IDs |
|
if do_not_include_vmrayjobids == "True": |
|
do_not_include_vmrayjobids = True |
|
else: |
|
do_not_include_vmrayjobids = False |
|
include_vmrayjobids = not do_not_include_vmrayjobids |
|
|
|
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"]: |
|
misperrors['error'] = "VMRay: %s" % vmraydata["errors"][0]["error_msg"] |
|
return misperrors |
|
else: |
|
return vmrayProcess(vmraydata) |
|
except: |
|
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: |
|
submissions = vmraydata["submissions"][0] |
|
jobs = vmraydata["jobs"] |
|
|
|
# Result received? |
|
if submissions and jobs: |
|
r = {'results': []} |
|
r["results"].append({"types": "md5", "values": submissions["submission_sample_md5"]}) |
|
r["results"].append({"types": "sha1", "values": submissions["submission_sample_sha1"]}) |
|
r["results"].append({"types": "sha256", "values": submissions["submission_sample_sha256"]}) |
|
r["results"].append({"types": "text", "values": "VMRay Sample ID: %s" % submissions["submission_sample_id"]}) |
|
r["results"].append({"types": "text", "values": "VMRay Submission ID: %s" % submissions["submission_id"]}) |
|
r["results"].append({"types": "text", "values": "VMRay Submission Sample IP: %s" % submissions["submission_ip_ip"]}) |
|
r["results"].append({"types": "link", "values": submissions["submission_webif_url"]}) |
|
|
|
# Include data from different jobs |
|
if include_vmrayjobids: |
|
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: |
|
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
|
|
|