import demistomock as demisto from CommonServerPython import * from CommonServerUserPython import * """ IMPORTS """ import json import time import traceback from datetime import datetime import pytz import requests import urllib3 from oauthlib.oauth2 import BackendApplicationClient from requests_oauthlib import OAuth2Session # Disable insecure warnings urllib3.disable_warnings() """GLOBAL/PARAMS""" URL = "https://cloud.threatdown.com" # Maps the region selected in the integration params to its ThreatDown Nebula base URL. REGION_URLS = { "US": "https://cloud.threatdown.com", "EU": "https://cloud.euc1.threatdown.com", } # Get OAuth2 token def nebula_url(path): return f"{URL}{path}" def get_nebula_client(client_id, client_secret, account_id, use_ssl): client_scope = ["read", "write", "execute"] headers = {"x-mwb-clientid": client_id, "x-mwb-accountid": account_id} client = BackendApplicationClient(client_id, scope=client_scope) nebula = OAuth2Session(client=client, scope=client_scope) nebula.headers.update(headers) token = nebula.fetch_token( token_url=nebula_url("/oauth2/token"), client_secret=client_secret, scope=client_scope, verify=use_ssl ) return "Bearer " + token.get("access_token") # Test connectivity to the Nebula cloud def test_connectivity(account_id, client_id, auth_token, USE_SSL): url = URL + "/api/v2/endpoints" headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: return True else: raise Exception(response.text) # Get all the endpoints from the Malwarebytes Cloud def get_all_endpoints(account_id, client_id, auth_token, USE_SSL): url = URL + "/api/v2/endpoints" headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: machine_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") machines = machine_json["machines"] cursor = machine_json["next_cursor"] while cursor != "": cursor_temp, machines_temp = get_all_endpoints_paginated(account_id, client_id, auth_token, cursor, USE_SSL) machines.extend(machines_temp) cursor = cursor_temp return machines else: raise Exception(response.text) def get_all_endpoints_paginated(account_id, client_id, auth_token, cursor, USE_SSL): url = URL + "/api/v2/endpoints?next_cursor=" + cursor headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: machine_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") machines = machine_json["machines"] cursor = machine_json["next_cursor"] return cursor, machines else: return False # Get the scan status to acquire the scan info def get_scan_status(account_id, client_id, auth_token, job_id, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs/" + str(job_id) response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: scan_status_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") scan_status = scan_status_json["state"] return scan_status else: raise Exception(response.text) # Get the scanid for the scan jobs initiated def get_scan_id(account_id, client_id, auth_token, job_id, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs/" + str(job_id) response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: scan_id_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") scan_id = scan_id_json["scan_id"] return scan_id else: raise Exception(response.text) # Get the machine_id associated with scan_id def get_scan_id_machine(account_id, client_id, auth_token, job_id, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs/" + str(job_id) response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: scan_id_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") machine_id = scan_id_json["machine_id"] return machine_id else: raise Exception(response.text) # Get detections for the scan jobs initiated def get_scan_detections(account_id, client_id, auth_token, ids, scan_id, USE_SSL): url = URL headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = url + "/api/v2/endpoints/" + str(ids) + "/scans/" + str(scan_id) + "/detections" response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: detection_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") detections = detection_json["detections"] cursor = detection_json["next_cursor"] while cursor != "": cursor_temp, detections_temp = get_scan_detections_paginated( account_id, client_id, auth_token, ids, scan_id, cursor, USE_SSL ) detections.extend(detections_temp) cursor = cursor_temp return detections else: raise Exception(response.text) def get_scan_detections_paginated(account_id, client_id, auth_token, ids, scan_id, cursor, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/endpoints/" + str(ids) + "/scans/" + str(scan_id) + "/detections?next_cursor=" + cursor response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: detection_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") detections = detection_json["detections"] cursor = detection_json["next_cursor"] return cursor, detections else: raise Exception(response.text) # Get Suspicious Activities found on all the endpoints def get_suspicious_activities(account_id, client_id, auth_token, USE_SSL): url = URL + "/api/v2/sa" headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: sa_detections_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") sa_detection = sa_detections_json["sa"] cursor = sa_detections_json["next_cursor"] while cursor != "": cursor_temp, sa_detections_temp = get_suspicious_activities_paginated( account_id, client_id, auth_token, cursor, USE_SSL ) sa_detection.extend(sa_detections_temp) cursor = cursor_temp return sa_detection else: raise Exception(response.text) def get_suspicious_activities_paginated(account_id, client_id, auth_token, cursor, USE_SSL): url = URL + "/api/v2/sa?next_cursor=" + cursor headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: try: sa_detections_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") sa_detection = sa_detections_json["sa"] cursor = sa_detections_json["next_cursor"] return cursor, sa_detection else: return False, False # Get RTP Detections found on all the endpoints def get_rtp_detections(account_id, client_id, auth_token, USE_SSL): url = URL + "/api/v2/detections/search" headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } body = {"is_rtp_stream_event": True, "sort_field": "reported_at", "sort_order": "asc"} response = requests.post(url, data=json.dumps(body), headers=headers, verify=USE_SSL) if response.status_code == 200: try: rtp_detections_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") detections = rtp_detections_json["detections"] cursor = rtp_detections_json["next_cursor"] while cursor != "": cursor_temp, detections_temp = get_rtp_detections_paginated(account_id, client_id, auth_token, cursor, USE_SSL) detections.extend(detections_temp) cursor = cursor_temp return detections else: raise Exception(response.text) def get_rtp_detections_paginated(account_id, client_id, auth_token, cursor, USE_SSL): url = URL + "/api/v2/detections/search" headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } body = {"is_rtp_stream_event": True, "sort_field": "reported_at", "sort_order": "asc", "next_cursor": cursor} response = requests.post(url, data=json.dumps(body), headers=headers, verify=USE_SSL) if response.status_code == 200: try: rtp_detections_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") detections = rtp_detections_json["detections"] cursor = rtp_detections_json["next_cursor"] return cursor, detections else: raise Exception(response.text) # Resolve machine_id from IP Address def get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/endpoints" data = {"nics.ips": ip} response = requests.post(url, data=json.dumps(data), headers=headers, verify=USE_SSL) if response.status_code == 200: try: machine_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") if len(machine_json["machines"]) != 0: machine_id = machine_json["machines"][0] machine_id = machine_id["machine"]["id"] return machine_id else: return False else: raise Exception(response.text) # Resolve machine_id from Hostname def get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/endpoints" data = {"fully_qualified_host_name.keyword": hostname} response = requests.post(url, data=json.dumps(data), headers=headers, verify=USE_SSL) if response.status_code == 200: try: machine_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") if len(machine_json["machines"]) != 0: machine_id = machine_json["machines"][0] machine_id = machine_id["machine"]["id"] return machine_id else: return False else: raise Exception(response.text) # Initiate POST Request for scan and report def post_scan_report(account_id, client_id, auth_token, ids, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs" scan = { "command": "command.threat.scan", "data": {"scan_settings": {"type": "ThreatScan", "remove": False}}, "machine_ids": [ids], } response = requests.post(url, data=json.dumps(scan), headers=headers, verify=USE_SSL) if response.status_code == 201: try: scan_result_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") job_id = scan_result_json["jobs"][0] job_id = job_id["job_id"] return job_id else: raise Exception(response.text) # Initiate POST Request for scan and quarantine def post_scan_remediate(account_id, client_id, auth_token, ids, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs" scan = { "command": "command.threat.scan", "data": {"scan_settings": {"type": "ThreatScan", "remove": True}}, "machine_ids": [ids], } response = requests.post(url, data=json.dumps(scan), headers=headers, verify=USE_SSL) if response.status_code == 201: try: scan_result_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") job_id = scan_result_json["jobs"][0] job_id = job_id["job_id"] return job_id else: raise Exception(response.text) # Initiate POST Request for Isolate actions def post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs/endpoints/isolate" if isolated == "isolate": isolate = {"data": {"desktop": True, "network": True, "process": True}, "machine_ids": [ids]} elif isolated == "desktop_isolate": isolate = {"data": {"desktop": True, "network": False, "process": False}, "machine_ids": [ids]} elif isolated == "network_isolate": isolate = {"data": {"desktop": False, "network": True, "process": False}, "machine_ids": [ids]} elif isolated == "process_isolate": isolate = {"data": {"desktop": False, "network": False, "process": True}, "machine_ids": [ids]} else: isolate = {} demisto.debug(f"{isolated=} didn't match any condition. {isolate=}") response = requests.post(url, data=json.dumps(isolate), headers=headers, verify=USE_SSL) if response.status_code == 201: try: isolate_result_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") job_id = isolate_result_json["jobs"][0] job_id = job_id["job_id"] return job_id else: raise Exception(response.text) # Initiate POST Request for deisolation def post_deisolate(account_id, client_id, auth_token, ids, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/jobs/endpoints/unlock" deisolate = {"machine_ids": [ids]} response = requests.post(url, data=json.dumps(deisolate), headers=headers, verify=USE_SSL) if response.status_code == 201: try: deisolate_result_json = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") job_id = deisolate_result_json["jobs"][0] job_id = job_id["job_id"] return job_id else: raise Exception(response.text) # Get endpoint info def get_endpoint_info(account_id, client_id, auth_token, ids, USE_SSL): headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = URL + "/api/v2/endpoints/" + ids + "/assets" response = requests.request("GET", url, headers=headers, verify=USE_SSL) if response.status_code == 200: # noqa: RET503 try: assets = response.json() except Exception: raise Exception(f"Failed to parse response to json. response: {response.text}") return assets # Get Latest High Severity Suspicious Activities to create incidents def fetch_suspicious_activities(suspicious_activities, suspicious_activity_severity, last_fetch_time, last_custom): if last_custom is not None and last_custom != suspicious_activity_severity: demisto.setLastRun({"time": None, "custom": None}) return False, False asc_suspicious_activities = [] if len(suspicious_activities) != 0: for i in reversed(suspicious_activities): asc_suspicious_activities.append(i) else: return False, False SA_severity = [] for i in suspicious_activity_severity: SA_severity.append(severity_text_to_number(i)) Filtered_SA = [] if len(asc_suspicious_activities) != 0: if last_fetch_time is None and last_custom is None: incident = { "name": "Malwarebytes Suspicious Activity Incident Detected on Host: " + str(asc_suspicious_activities[-1]["pc_hostname"]), "occurred": str(asc_suspicious_activities[-1]["timestamp"]), "severity": 3, "rawJSON": json.dumps(asc_suspicious_activities[-1]), } lastPointer = {"time": asc_suspicious_activities[-1]["timestamp"], "custom": suspicious_activity_severity} return lastPointer, incident else: last_count = 0 for i in asc_suspicious_activities: if i["level"] in SA_severity and i["status"] == "detected" and i["timestamp"] > last_fetch_time: Filtered_SA.append(i) if len(Filtered_SA) != 0: incident = { "name": "Malwarebytes Suspicious Activity Incident Detected on Host: " + str(Filtered_SA[last_count]["pc_hostname"]), "occurred": str(Filtered_SA[last_count]["timestamp"]), "severity": 3, "rawJSON": json.dumps(Filtered_SA[last_count]), } lastPointer = {"time": Filtered_SA[last_count]["timestamp"], "custom": suspicious_activity_severity} return lastPointer, incident else: return False, False else: return False, False # Get Real Time Protection Detections to Create Incidents def fetch_rtp_detections(category, rtp_detections, last_fetch_time, last_custom): if last_custom is not None and last_custom != category: demisto.setLastRun({"time": None, "custom": None}) return False, False asc_rtp_events_by_category = [] if len(rtp_detections) != 0: if last_fetch_time is None and last_custom is None: incident = { "name": "Malwarebytes RTP has protected against threat " + str(rtp_detections[-1]["threat_name"]) + " on Host: " + str(rtp_detections[-1]["machine_name"]), "occurred": str(rtp_detections[-1]["reported_at"]), "severity": 3, "rawJSON": json.dumps(rtp_detections[-1]), } lastPointer = {"time": rtp_detections[-1]["reported_at"], "custom": category} return lastPointer, incident else: last_count = 0 for i in rtp_detections: if i["category"] in category and i["reported_at"] > last_fetch_time: asc_rtp_events_by_category.append(i) if len(asc_rtp_events_by_category) != 0: incident = { "name": "Malwarebytes RTP has protected against threat " + str(asc_rtp_events_by_category[last_count]["threat_name"]) + " on Host: " + str(asc_rtp_events_by_category[last_count]["machine_name"]), "occurred": str(asc_rtp_events_by_category[last_count]["reported_at"]), "severity": 3, "rawJSON": json.dumps(asc_rtp_events_by_category[last_count]), } lastPointer = {"time": asc_rtp_events_by_category[last_count]["reported_at"], "custom": category} return lastPointer, incident else: return False, False else: return False, False # Convert Text Category to Malwarebytes Category def category_to_code(rtp_malware_category): if rtp_malware_category == "Malware": # noqa: RET503 return "MALWARE" elif rtp_malware_category == "PUP": return "PUP" elif rtp_malware_category == "PUM": return "PUM" elif rtp_malware_category == "Exploit": return "AE" elif rtp_malware_category == "Ransomware": return "ARW" elif rtp_malware_category == "Website": return "MWAC" # Remove empty fields from Scan Detections def remove_empty_detection_fields(detections): for i in detections: del ( i["affected_application"], i["detection_id_from_endpoint"], i["group_id"], i["id"], i["ip_address"], i["md5"], i["port"], i["process_name"], i["scan_id"], i["scanned_at"], i["scanned_at_local"], i["url"], ) # Convert Severity Text to Severity Number def severity_text_to_number(suspicious_activity_severity): if suspicious_activity_severity == "High": # noqa: RET503 return 3 elif suspicious_activity_severity == "Medium": return 2 elif suspicious_activity_severity == "Low": return 1 # Function to execute Scan and Remediate action def scan_and_remediate(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating scan_and_remediate action if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_scan_remediate(account_id, client_id, auth_token, ids, USE_SSL) return_outputs( "Scan and Remediate action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to check status and " "malwarebytes-get-scan-detections command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_scan_remediate(account_id, client_id, auth_token, ids, USE_SSL) return_outputs( "Scan and Remediate action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to check status and " "malwarebytes-get-scan-detections command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") # Function to execute Scan and Report action def scan_and_report(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating scan_and_report action if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_scan_report(account_id, client_id, auth_token, ids, USE_SSL) return_outputs( "Scan and Report action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to check status and " "malwarebytes-get-scan-detections command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_scan_report(account_id, client_id, auth_token, ids, USE_SSL) return_outputs( "Scan and Report action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to check status and " "malwarebytes-get-scan-detections command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") # Functions to execute Isolate Command def isolate_endpoint(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating Isolate Endpoint action isolated = "isolate" if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Isolation action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Isolation action Failed for the Endpoint: " + str(ip), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Isolation action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Isolation action Failed for the Endpoint: " + str(hostname), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") def isolate_process(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating Isolate Process action isolated = "process_isolate" if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Process Isolation action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Process Isolation action Failed for the Endpoint: " + str(ip), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Process Isolation action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Process Isolation action Failed for the Endpoint: " + str(hostname), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") def isolate_network(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating Isolate Network action isolated = "network_isolate" if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Network Isolation action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Network Isolation action Failed for the Endpoint: " + str(ip), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Network Isolation action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Network Isolation action Failed for the Endpoint: " + str(hostname), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") def isolate_desktop(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating Isolate Desktop action isolated = "desktop_isolate" if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Desktop Isolation action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Desktop Isolation action Failed for the Endpoint: " + str(ip), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_isolate(account_id, client_id, auth_token, ids, isolated, USE_SSL) if job_id: return_outputs( "Desktop Isolation action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Desktop Isolation action Failed for the Endpoint: " + str(hostname), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") # Function to execute Deisolate Command def deisolate_endpoint(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating scan_and_report action if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: job_id = post_deisolate(account_id, client_id, auth_token, ids, USE_SSL) if job_id: return_outputs( "Deisolation action has been successfully started on the Endpoint: " + str(ip) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Deisolation action Failed for the Endpoint: " + str(ip), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: job_id = post_deisolate(account_id, client_id, auth_token, ids, USE_SSL) if job_id: return_outputs( "Deisolation action has been successfully started on the Endpoint: " + str(hostname) + " with the job_id: " + str(job_id) + ". Use job_id in malwarebytes-get-job-status command to view results", outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Machine_ID": ids, "Job_ID": job_id}}, ) else: return_error(message="Deisolation action Failed for the Endpoint: " + str(hostname), error="") else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Enter IP or Hostname", error="") # Function to execute List all endpoints Command def list_all_endpoints(account_id, client_id, auth_token, endpoint, USE_SSL): machines = get_all_endpoints(account_id, client_id, auth_token, USE_SSL) found_machines = [] if len(machines) != 0: if endpoint == "all": found_machines = machines elif endpoint == "online": for i in machines: if i["online"] is True: found_machines.append(i) elif endpoint == "offline": for i in machines: if i["online"] is False: found_machines.append(i) return_outputs( readable_output=tableToMarkdown( "Found " + endpoint + " " + str(len(found_machines)) + " Endpoints from Malwarebytes Cloud: ", found_machines ), outputs={"Malwarebytes.Endpoint(val.total_count == obj.total_count)": {"total_count": len(found_machines)}}, raw_response=found_machines, ) else: demisto.results("No Endpoint Found!") # Function to execute List endpoint info Command def list_endpoint_info(account_id, client_id, auth_token, ip, hostname, USE_SSL): # This is the call made initiating get endpoint info action if ip: ids = get_machine_id_ip(account_id, client_id, auth_token, ip, USE_SSL) if ids: assets = get_endpoint_info(account_id, client_id, auth_token, ids, USE_SSL) ip_address = None mac_address = None try: ip_address = assets.get("nics")[0].get("ips")[0] except Exception: pass try: mac_address = assets.get("nics")[0].get("mac_address") except Exception: pass return_outputs( readable_output=tableToMarkdown(f"Endpoint Information for the IP: {ip}", assets), outputs={ "Malwarebytes.Endpoint(val.Hostname == obj.Hostname)": { "Hostname": assets.get("host_name"), "IPAddress": assets.get("nics"), "Domain": assets.get("domain_name"), "MACAddress": mac_address, "OS": assets.get("os_info", {}).get("os_platform"), "OSVersion": assets.get("os_info", {}).get("os_version"), "Model": assets.get("computer_info", {}).get("model"), "Memory": assets.get("memory", {}), "Assets": assets, }, "Endpoint(val.Hostname == obj.Hostname)": { "Hostname": assets.get("host_name"), "IPAddress": ip_address, "Domain": assets.get("domain_name"), "MACAddress": mac_address, "OS": assets.get("os_info", {}).get("os_platform"), "OSVersion": assets.get("os_info", {}).get("os_version"), "Model": assets.get("computer_info", {}).get("model"), "Memory": assets.get("memory", {}), }, }, raw_response=assets, ) else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") elif hostname: ids = get_machine_id_hostname(account_id, client_id, auth_token, hostname, USE_SSL) if ids: assets = get_endpoint_info(account_id, client_id, auth_token, ids, USE_SSL) ip_address = None mac_address = None try: ip_address = assets.get("nics")[0].get("ips")[0] except Exception: pass try: mac_address = assets.get("nics")[0].get("mac_address") except Exception: pass return_outputs( readable_output=tableToMarkdown(f"Endpoint Information for the Hostname: {hostname}", assets), outputs={ "Malwarebytes.Endpoint(val.Hostname == obj.Hostname)": { "Hostname": assets.get("host_name"), "IPAddress": assets.get("nics"), "Domain": assets.get("domain_name"), "MACAddress": mac_address, "OS": assets.get("os_info", {}).get("os_platform"), "OSVersion": assets.get("os_info", {}).get("os_version"), "Model": assets.get("computer_info", {}).get("model"), "Memory": assets.get("memory", {}), "Assets": assets, }, "Endpoint(val.Hostname == obj.Hostname)": { "Hostname": assets.get("host_name"), "IPAddress": ip_address, "Domain": assets.get("domain_name"), "MACAddress": mac_address, "OS": assets.get("os_info", {}).get("os_platform"), "OSVersion": assets.get("os_info", {}).get("os_version"), "Model": assets.get("computer_info", {}).get("model"), "Memory": assets.get("memory", {}), }, }, raw_response=assets, ) else: demisto.results("Endpoint is not found in the Malwarebytes Cloud") else: return_error(message="Please Input an IP or Hostname", error="") # Function to execute Get Scan Detections Command def scan_detections(account_id, client_id, auth_token, job_id, USE_SSL): if job_id: status = get_scan_status(account_id, client_id, auth_token, job_id, USE_SSL) if status == "COMPLETED": scan_id = get_scan_id(account_id, client_id, auth_token, job_id, USE_SSL) machine_id = get_scan_id_machine(account_id, client_id, auth_token, job_id, USE_SSL) detections = get_scan_detections(account_id, client_id, auth_token, machine_id, scan_id, USE_SSL) if len(detections) != 0: remove_empty_detection_fields(detections) return_outputs( readable_output=tableToMarkdown(f"Scan Detections Report for the Job_Id: {job_id}", detections), outputs={ "Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": { "Job_ID": job_id, "Status": status, "Detections": detections, } }, raw_response=detections, ) else: demisto.results("Scan has been Completed and No Threats have been found !") else: demisto.results("Scan Status for the job_id " + str(job_id) + " is " + str(status)) else: return_error(message="Please Enter Job_Id", error="") # Function to execute Get Scan Status Command def scan_status(account_id, client_id, auth_token, job_id, USE_SSL): if job_id: status = get_scan_status(account_id, client_id, auth_token, job_id, USE_SSL) return_outputs( "Scan Status for the job_id " + str(job_id) + " is " + str(status), outputs={"Malwarebytes.Scan(val.Job_ID == obj.Job_ID)": {"Job_ID": job_id, "Status": status}}, raw_response=[], ) else: return_error(message="Please Enter Job_Id", error="") # Function to execute Fetch Incidents Command def fetch_incidents(account_id, client_id, auth_token, event_list, USE_SSL): incident = None lastPointer = None if event_list == "Suspicious Activity (EPR)": suspicious_activities = get_suspicious_activities(account_id, client_id, auth_token, USE_SSL) # And retrieve it for use later lastRun = demisto.getLastRun() last_fetch_time = lastRun.get("time") last_custom = lastRun.get("custom") suspicious_activity_severity = demisto.params().get("suspicious_activity_severity") if suspicious_activity_severity: lastPointer, incident = fetch_suspicious_activities( suspicious_activities, suspicious_activity_severity, last_fetch_time, last_custom ) else: demisto.incidents([]) elif event_list == "RTP Detections (EP)": rtp_detections = get_rtp_detections(account_id, client_id, auth_token, USE_SSL) # And retrieve it for use later lastRun = demisto.getLastRun() last_fetch_time = lastRun.get("time") last_custom = lastRun.get("custom") rtp_malware_category = demisto.params().get("rtp_threat_category") category = [] if rtp_malware_category: for i in rtp_malware_category: category.append(category_to_code(i)) lastPointer, incident = fetch_rtp_detections(category, rtp_detections, last_fetch_time, last_custom) else: demisto.incidents([]) if incident and lastPointer: demisto.incidents([incident]) # You can store the last run time... demisto.setLastRun(lastPointer) else: demisto.incidents([]) def get_token(client_id, client_secret, account_id, use_ssl): integration_context = demisto.getIntegrationContext() token = integration_context.get("access_token") valid_until = integration_context.get("valid_until") time_now = int(time.time()) if token and valid_until and time_now < valid_until: # Token is still valid - did not expire yet return token token = get_nebula_client(client_id, client_secret, account_id, use_ssl) integration_context = { "access_token": token, "valid_until": time_now + 3595, # Assuming the expiration time is 1 hour } demisto.setIntegrationContext(integration_context) return token # Function to execute Open SA action def open_sa_incident(account_id, client_id, auth_token, machine_id, detection_id, USE_SSL): url = URL headers = {"X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Authorization": auth_token} url = url + "/api/v2/endpoints/" + str(machine_id) + "/sa/" + str(detection_id) + "/open" response = requests.put(url, data=None, headers=headers, verify=USE_SSL) if response.status_code == 201: return_outputs( "Open SA Incident action is initiated Successfully for the detection id: " + str(detection_id), outputs={"Malwarebytes.SA(val.Machine_ID == obj.Machine_ID)": {"Machine_ID": machine_id}}, raw_response=[], ) else: raise Exception(response.text) # Function to execute Remediate SA action def remediate_sa_incident(account_id, client_id, auth_token, machine_id, detection_id, USE_SSL): url = URL headers = { "X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Content-Type": "application/json", "Authorization": auth_token, } url = url + "/api/v2/endpoints/" + str(machine_id) + "/sa/" + str(detection_id) + "/remediate" response = requests.post(url, data=json.dumps({}), headers=headers, verify=USE_SSL) if response.status_code == 201: return_outputs( "Remediate SA Incident action is initiated Successfully for the detection id: " + str(detection_id), outputs={"Malwarebytes.SA(val.Machine_ID == obj.Machine_ID)": {"Machine_ID": machine_id}}, raw_response=[], ) elif response.status_code == 404: return_error( message="HTTP Error: " + str(response.status_code) + " as threat with detection id: " + str(detection_id) + " is already remediated", error="", ) else: raise Exception(response.text) # Function to execute Close Remediate SA action def close_sa_incident(account_id, client_id, auth_token, machine_id, detection_id, USE_SSL): url = URL headers = {"X-MWB-AccountID": account_id, "X-MWB-ClientID": client_id, "Authorization": auth_token} url = url + "/api/v2/endpoints/" + str(machine_id) + "/sa/" + str(detection_id) + "/close" response = requests.put(url, data=None, headers=headers, verify=USE_SSL) if response.status_code == 201: return_outputs( "Close SA Incident action is initiated Successfully for the detection id: " + str(detection_id), outputs={"Malwarebytes.SA(val.Machine_ID == obj.Machine_ID)": {"Machine_ID": machine_id}}, raw_response=[], ) else: raise Exception(response.text) def get_sa_activities_command(account_id, client_id, auth_token, hostname, path, USE_SSL): suspicious_activities = get_suspicious_activities(account_id, client_id, auth_token, USE_SSL) if hostname: Filtered_SA = [] for i in suspicious_activities: if i["pc_hostname"] == hostname: Filtered_SA.append(i) if len(Filtered_SA) != 0: return_outputs( readable_output=tableToMarkdown(f"Suspicious Activites found for the host: {hostname}", Filtered_SA), outputs={ "Malwarebytes.Endpoint(val.Suspicious_Activities == obj.Suspicious_Activities)": { "Suspicious_Activities": Filtered_SA } }, raw_response=Filtered_SA, ) else: demisto.results("No Suspicious Activites found for the host: " + str(hostname)) elif path: Filtered_SA = [] for i in suspicious_activities: if i["path"] == path: Filtered_SA.append(i) if len(Filtered_SA) != 0: return_outputs( readable_output=tableToMarkdown(f"File Path: {path} is found on the hosts", Filtered_SA), outputs={ "Malwarebytes.Endpoint(val.Suspicious_Activities == obj.Suspicious_Activities)": { "Suspicious_Activities": Filtered_SA } }, raw_response=Filtered_SA, ) else: demisto.results("File Path: " + str(path) + " is not found on any hosts.") else: return_error(message="Please enter hostname or path", error="") def send_usage_data(auth_token, account_id, client_id, msg_type, USE_SSL): url = "https://api-msp-telemetry.threatdown.com/data" headers = {"Content-Type": "application/json"} tz = pytz.timezone("Europe/Moscow") dt = datetime.now() loc_dt = tz.localize(dt).replace(microsecond=0) v = loc_dt.isoformat() temp = v.split("+") timestamp = temp[0] + "Z" body = { "timestamp": timestamp, "integration_code": "TA-CX", "integration_name": "Cortex XSOAR", "integration_app": "ThreatDown", "integration_app_version": "1.1.7", "nebula_account_id": account_id, "ov_account_id": "", "mbbr_license_key": "", "api_client_id": client_id, "custom_fields": [], "msg_type": msg_type, "token": auth_token, } try: response = requests.request("POST", url, data=json.dumps(body), headers=headers, verify=USE_SSL) if response.status_code == 200: data = json.loads(response.text) return data.get("statusCode") == 201 else: return False except Exception as e: demisto.results("Cannot send usage data, details" + str(e)) def main(): """CONSTANTS""" global URL region = demisto.params().get("region", "US") URL = REGION_URLS.get(region, REGION_URLS["US"]) account_id = demisto.params().get("accountid") client_id = demisto.params().get("clientid") client_secret = demisto.params().get("clientsecret") msg_type = "INTEGRATION INUSE" use_ssl = not demisto.params().get("insecure", False) auth_token = get_token(client_id, client_secret, account_id, use_ssl) try: # Remove proxy if not set to true in params handle_proxy() # Function to send usage data of customers to Malwarebytes send_usage_data(auth_token, account_id, client_id, msg_type, use_ssl) # The command demisto.command() holds the command sent from the user. LOG(f"command is {demisto.command()}") if demisto.command() == "test-module": # This is the call made when pressing the integration test button. status = test_connectivity(account_id, client_id, auth_token, use_ssl) if status is True: demisto.results("ok") elif demisto.command() == "malwarebytes-scan-and-remediate": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") scan_and_remediate(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-scan-and-report": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") scan_and_report(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-isolate-endpoint": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") isolate_endpoint(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-isolate-process": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") isolate_process(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-isolate-network": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") isolate_network(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-isolate-desktop": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") isolate_desktop(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-deisolate-endpoint": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") deisolate_endpoint(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-list-endpoints": endpoint = demisto.args().get("endpoints") list_all_endpoints(account_id, client_id, auth_token, endpoint, use_ssl) elif demisto.command() == "malwarebytes-list-endpoint-info": ip = demisto.args().get("ip") hostname = demisto.args().get("hostname") list_endpoint_info(account_id, client_id, auth_token, ip, hostname, use_ssl) elif demisto.command() == "malwarebytes-get-scan-detections": job_id = demisto.args().get("job_id") scan_detections(account_id, client_id, auth_token, job_id, use_ssl) elif demisto.command() == "malwarebytes-get-job-status": job_id = demisto.args().get("job_id") scan_status(account_id, client_id, auth_token, job_id, use_ssl) elif demisto.command() == "malwarebytes-open-sa-incident": machine_id = demisto.args().get("machine_id") detection_id = demisto.args().get("detection_id") open_sa_incident(account_id, client_id, auth_token, machine_id, detection_id, use_ssl) elif demisto.command() == "malwarebytes-remediate-sa-incident": machine_id = demisto.args().get("machine_id") detection_id = demisto.args().get("detection_id") remediate_sa_incident(account_id, client_id, auth_token, machine_id, detection_id, use_ssl) elif demisto.command() == "malwarebytes-close-sa-incident": machine_id = demisto.args().get("machine_id") detection_id = demisto.args().get("detection_id") close_sa_incident(account_id, client_id, auth_token, machine_id, detection_id, use_ssl) elif demisto.command() == "malwarebytes-get-sa-activities": hostname = demisto.args().get("hostname") path = demisto.args().get("path") get_sa_activities_command(account_id, client_id, auth_token, hostname, path, use_ssl) elif demisto.command() == "fetch-incidents": event_list = demisto.params().get("Fetch_Event_List") fetch_incidents(account_id, client_id, auth_token, event_list, use_ssl) except Exception as e: # Log exceptions return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}{traceback.format_exc()!s}") if __name__ in ["__main__", "builtin", "builtins"]: main()