def _add_vault_hashes_to_dictionary(self, cef_artifact, vault_id): success, message, vault_info = phantom_rules.vault_info( vault_id=vault_id) if not vault_info: return phantom.APP_ERROR, "Vault ID not found" # The return value is a list, each item represents an item in the vault # matching the vault id, the info that we are looking for (the hashes) # will be the same for every entry, so just access the first one try: metadata = vault_info[0].get('metadata') except Exception: return phantom.APP_ERROR, PROC_EMAIL_FAILED_VAULT_CONT_DATA try: cef_artifact['fileHashSha256'] = metadata['sha256'] except Exception: pass try: cef_artifact['fileHashMd5'] = metadata['md5'] except Exception: pass try: cef_artifact['fileHashSha1'] = metadata['sha1'] except Exception: pass return phantom.APP_SUCCESS, PROC_EMAIL_MAPPED_HASH_VAL
def playbook_local_soc_fork_customer_request_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): phantom.debug('playbook_local_soc_fork_customer_request_1() called') # ----- start of added code ----- import csv # get container id container_id = container.get('id', None) # use the container id to get information about any files in the vault vault_info = phantom.vault_info(container_id=container_id) # filter info returned to find the path where the file is stored in the vault file_path = vault_info[2][0]["path"] phantom.debug('vault file path: {}'.format(file_path)) # read the .csv file, file and add artifacts with the label "customer_request" to container raw_data = {} reader = None try: with open(file_path, 'r') as f: reader = csv.DictReader(f) for cef_data in reader: cef_data_keys = cef_data.keys() if 'action' in cef_data_keys and ( 'sourceAddress' in cef_data_keys or 'destinationAddress' in cef_data_keys): phantom.debug('adding artifact: {}'.format(cef_data)) success, message, artifact_id = phantom.add_artifact( container=container, raw_data=raw_data, cef_data=cef_data, label='customer_request', name='Parsed CSV Artifact', severity='high', identifier=None, artifact_type='network') if not success: phantom.error( "Adding Artifact failed: {}".format(message)) except Exception as e: phantom.error("Exception Occurred: {}".format(e.args[1])) return # ----- end of added code ----- # call playbook "local/soc_fork_customer_request", returns the playbook_run_id playbook_run_id = phantom.playbook("local/soc_fork_customer_request", container) return
def _add_attachment(self, param): # Create action result action_result = self.add_action_result(ActionResult(param)) # Create RT object if phantom.is_fail(self._create_rt_session(action_result)): return action_result.get_status() # Get params ticket_id = param[RT_JSON_ID] vault_id = param[RT_JSON_VAULT] comment = param.get('comment') # Set default comment if not comment: comment = 'File uploaded from Phantom' # Check for vault file _, _, file_info = vault_info(vault_id=vault_id, container_id=self.get_container_id()) if not file_info: return action_result.set_status( phantom.APP_ERROR, "Vault ID is invalid. Vault file not found") file_info = file_info[0] if not file_info['name']: file_info['name'] = vault_id # Create payload for request content = { 'content': 'Action: comment\nText: {0}\nAttachment: {1}'.format( comment, file_info['name']) } upfile = {'attachment_1': open(file_info['path'], 'rb')} ret_val, resp_text = self._make_rest_call( "ticket/{0}/comment".format(ticket_id), action_result, data=content, files=upfile, method='post') if phantom.is_fail(ret_val): return ret_val return action_result.set_status(phantom.APP_SUCCESS)
def _query_file(self, param): vault_id = param['vault_id'] if param.get('force_analysis') is False: task_id = self._check_existing(vault_id) if task_id: return self._get_detonation_results(task_id) filename = param.get('file_name') if not filename: filename = vault_id try: _, _, file_info = phantom_rules.vault_info(vault_id=vault_id) if not file_info: self.threatgrid_action_result.set_status( phantom.APP_ERROR, THREATGRID_FILE_NOT_FOUND_ERROR.format(vault_id)) return file_path = list(file_info)[0].get('path') payload = open(file_path, 'rb') except: self.threatgrid_action_result.set_status( phantom.APP_ERROR, THREATGRID_FILE_NOT_FOUND_ERROR.format(vault_id)) return files = { 'sample': (filename, payload), } data = { 'api_key': self.threatgrid_api_key, 'filename': filename, 'tags': [], 'os': '', 'osver': '', 'source': '', 'vm': param.get('vm', ''), 'playbook': param.get('playbook', 'default') } if param.get('private'): data['private'] = True self._queue_analysis(data, files, filename)
def _handle_create_observable(self, param): self.save_progress("In action handler for: {0}".format(self.get_action_identifier())) action_result = self.add_action_result(ActionResult(dict(param))) data = dict() case_id = param['id'] data_type = param['data_type'] message = param['description'] tlp = param.get('tlp', 'Amber') try: int_tlp = THEHIVE_TLP_DICT[tlp] except KeyError: return action_result.set_status(phantom.APP_ERROR, THEHIVE_ERR_INVALID_TLP) tags = param.get('tags', '') try: tags = [x.strip() for x in tags.split(',')] tags = list(filter(None, tags)) if not tags: return action_result.set_status(phantom.APP_ERROR, "Tags format invalid. Please supply one or more tags separated by a comma") except: return action_result.set_status(phantom.APP_ERROR, "Tags format invalid. Please supply one or more tags separated by a comma") ioc = param.get('ioc', False) data = param.get('data', '') # if a file is to be supplied, the vault_id parameter will be used to grab a file from the Vault if data_type == 'file': vault_id = param.get('vault_id') if not vault_id: return action_result.set_status(phantom.APP_ERROR, "Parameter Vault ID is mandatory if 'data_type' is file") try: _, _, vault_file_info = ph_rules.vault_info(container_id=self.get_container_id(), vault_id=vault_id) vault_file_info = list(vault_file_info) except Exception as e: error_msg = self._get_error_message_from_exception(e) self.debug_print(error_msg) return action_result.set_status( phantom.APP_ERROR, "Unable to find specified Vault file. Please check Vault ID and try again. {}".format(error_msg) ) if len(vault_file_info) != 1: return action_result.set_status(phantom.APP_ERROR, "Unable to find specified Vault file. Please check Vault ID and try again.") vault_file_info = vault_file_info[0] file_path = vault_file_info.get('path') file_name = vault_file_info.get('name') # file name is being ignored by Hive. It uses the file_path. file_data = {'attachment': (file_name, open(file_path, 'rb'), magic.Magic(mime=True).from_file(file_path))} endpoint = "api/case/{0}/artifact".format(case_id) authToken = "Bearer {}".format(self._api_key) headers = {'Authorization': authToken} mesg = { "dataType": data_type, "message": message, "tlp": int_tlp, "tags": tags, "ioc": ioc } if data_type == 'file': data = {"_json": json.dumps(mesg)} ret_val, response = self._make_rest_call(endpoint, action_result, params=None, headers=headers, data=data, method="post", files=file_data) else: mesg['data'] = data headers['Content-Type'] = 'application/json' ret_val, response = self._make_rest_call(endpoint, action_result, params=None, headers=headers, data=mesg, method="post") if phantom.is_fail(ret_val): return action_result.get_status() # need to flatten hashes if file was uploaded if response.get('attachment'): hashes = response.get('attachment').get('hashes', []) response['attachment']['sha256'] = hashes[0] response['attachment']['sha1'] = hashes[1] response['attachment']['md5'] = hashes[2] del response['attachment']['hashes'] action_result.add_data(response) return action_result.set_status(phantom.APP_SUCCESS, "Successfully created observable")
def _upload_vault_file(self, action_result, vault_id): """Uploads a file from vault to Redmine and returns a dictionary suitable for attaching the uploaded file to an existing ticket""" try: self.debug_print("Rules.vault_info start") success, message, vault_info = Rules.vault_info(vault_id=vault_id) vault_info = list(vault_info) self.debug_print( "Rules.vault_info results: success: {}, message: {}, info: {}".format( success, message, vault_info ) ) if not vault_info: return action_result.set_status(phantom.APP_ERROR, REDMINE_ERR_GETTING_VAULT_INFO) except requests.exceptions.HTTPError: return action_result.set_status(phantom.APP_ERROR, REDMINE_ERR_INVALID_VAULT_ID.format(vault_id=vault_id)) except Exception as e: error_msg = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, REDMINE_ERR_OPENING_FILE.format(error=error_msg) ) try: file_info = vault_info[0] except Exception as e: error_msg = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, REDMINE_ERR_GETTING_FILE_INFO.format(error=error_msg), ) try: file_path = file_info["path"] except Exception: return action_result.set_status(phantom.APP_ERROR, REDMINE_ERR_GETTING_FILE_PATH) with open(file_path, "rb") as f: file_contents = f.read() ret_val, response = self._make_rest_call( "/uploads.json", action_result, params=None, method="post", headers={"Content-Type": "application/octet-stream"}, data=file_contents, ) if phantom.is_fail(ret_val): return action_result.set_status( phantom.APP_ERROR, REDMINE_ERR_UPLOAD_ATTACHMENT ) try: upload = { "token": response["upload"]["token"], "filename": file_info["name"], "content_type": file_info["mime_type"], } except Exception as e: self.debug_print(self._get_error_message_from_exception(e)) return action_result.set_status( phantom.APP_ERROR, REDMINE_ERR_PROCESSING_UPLOAD_DICT ) return upload
def mark_evidence(container=None, input_object=None, content_type=None, **kwargs): """ Mark an object as Evidence in a container Args: container (CEF type: phantom container id): Container ID or Container Object input_object (CEF type: *): The object to mark as evidence. This could be a vault_id, artifact_id, note_id, container_id, or action_run_id. If the previous playbook block is an action then "keyword_argument:results" can be used for the action_run_id with the content_type "action_run_id". Vault_id can be an ID or a vault hash. content_type (CEF type: *): The content type of the object to add as evidence which must be one of the following: vault_id artifact_id container_id note_id action_run_id Returns a JSON-serializable object that implements the configured data paths: *.id (CEF type: *): ID of the evidence item """ ############################ Custom Code Goes Below This Line ################################# import json import phantom.rules as phantom outputs = [] container_id = None data = [] valid_types = [ 'vault_id', 'artifact_id', 'container_id', 'note_id', 'action_run_id' ] # Ensure valid content_type: if content_type.lower() not in valid_types: raise TypeError( f"The content_type '{content_type}' is not a valid content_type") # Ensure valid container input if isinstance(container, dict) and container.get('id'): container_id = container['id'] elif isinstance(container, int) or (isinstance(container, str) and container.isdigit()): container_id = container else: raise TypeError( "The input 'container' is neither a container dictionary nor an int, so it cannot be used" ) # If content added is type 'action_run_id', # then iterate through an input object that is a results object, # and append the action_run_id's to data if isinstance(input_object, list) and content_type.lower() == 'action_run_id': for action_result in input_object: if action_result.get('action_run_id'): data.append({ "container_id": container_id, "object_id": action_result['action_run_id'], "content_type": 'actionrun', }) # If data is still an empty list after for loop, # it indicates that the input_object was not a valid results object if not data: raise TypeError( "The input for 'input_object' is not a valid integer or supported object." ) # If 'input_object' is already an action_run_id, no need to translate it. elif (isinstance(input_object, int) or (isinstance(input_object, str) and input_object.isdigit()) ) and content_type.lower() == 'action_run_id': data = [{ "container_id": container_id, "object_id": input_object, "content_type": 'actionrun', }] # If vault_id was entered, check to see if user already entered a vault integer # else if user entered a hash vault_id, attempt to translate to a vault integer elif input_object and content_type.lower() == 'vault_id': if isinstance(input_object, int) or (isinstance(input_object, str) and input_object.isdigit()): content_type = "containerattachment" else: success, message, info = phantom.vault_info(vault_id=input_object) if success == False: raise RuntimeError(f"Invalid vault_id: {message}") else: input_object = info[0]['id'] content_type = "containerattachment" data = [{ "container_id": container_id, "object_id": input_object, "content_type": content_type, }] # If 'container_id' was entered, the content_type needs to be set to 'container'. # Phantom does not allow a literal input of 'container' so thus 'container_id is used. elif (isinstance(input_object, int) or (isinstance(input_object, str) and input_object.isdigit())) and content_type.lower() == 'container_id': data = [{ "container_id": container_id, "object_id": input_object, "content_type": 'container', }] # If 'artifact_id' was entered, the content_type needs to be set to 'artifact' elif (isinstance(input_object, int) or (isinstance(input_object, str) and input_object.isdigit())) and content_type.lower() == 'artifact_id': data = [{ "container_id": container_id, "object_id": input_object, "content_type": 'artifact', }] # If 'note_id' was entered, the content_type needs to be set to 'note' elif (isinstance(input_object, int) or (isinstance(input_object, str) and input_object.isdigit())) and content_type.lower() == 'note_id': data = [{ "container_id": container_id, "object_id": input_object, "content_type": 'note', }] else: raise TypeError( f"The input_object is not a valid integer or supported object. Type '{type(input_object)}'" ) # Build url for evidence endpoint url = phantom.build_phantom_rest_url('evidence') # Post data to evidence endpoint for item in data: response = phantom.requests.post(uri=url, json=item, verify=False).json() # If successful add evidence id to outputs # elif evidence already exists print to debug # else error out if response.get('success'): outputs.append({'id': response['id']}) elif response.get('failed') and response.get( 'message') == 'Already added to Evidence.': phantom.debug( f"{content_type} \'{container_id}\' {response['message']}") else: raise RuntimeError(f"Unable to add evidence: {response}") # Return a JSON-serializable object assert json.dumps( outputs ) # Will raise an exception if the :outputs: object is not JSON-serializable return outputs
def _provide_attachment_details(self, attachment_list, action_result): """ Helper function that is used to get attachment from the vault, and provide attachment details which can be used to add attachment to an incident. :param attachment_list: list of vault IDs :param action_result: object of ActionResult class :return: status (success/failure) and (add_attachment_params_dict dictionary having attachment related information and attachment_data dictionary containing attachment) / None """ file_obj = [] filename = [] attachment_data = dict() add_attachment_params_dict = dict() attachment_list = [ value.strip() for value in attachment_list.split(',') if value.strip() ] if not attachment_list: self.debug_print( consts.BMCREMEDY_ERR_INVALID_FIELDS.format(field='vault_id')) return action_result.set_status( phantom.APP_ERROR, consts.BMCREMEDY_ERR_INVALID_FIELDS.format( field='vault_id')), None, None # At most, three attachments should be provided if len(attachment_list) > 3: self.debug_print(consts.BMCREMEDY_ATTACHMENT_LIMIT_EXCEED) return action_result.set_status( phantom.APP_ERROR, consts.BMCREMEDY_ATTACHMENT_LIMIT_EXCEED), None, None try: # Searching for file with vault id in current container _, _, files_array = (ph_rules.vault_info( container_id=self.get_container_id())) files_array = list(files_array) for vault_id in attachment_list: file_found = False for file_data in files_array: if file_data[consts.BMCREMEDY_JSON_VAULT_ID] == vault_id: # Getting filename to use filename.append(file_data['name']) # Reading binary data of file with open(file_data.get('path'), 'rb') as f: file_obj.append(f.read()) file_found = True break if not file_found: self.debug_print("{}: {}".format( consts.BMCREMEDY_UNKNOWN_VAULT_ID, vault_id)) return action_result.set_status( phantom.APP_ERROR, "{}: {}".format(consts.BMCREMEDY_UNKNOWN_VAULT_ID, vault_id)), None, None except Exception as e: err_msg = self._get_error_message_from_exception(e) return action_result.set_status(phantom.APP_ERROR, err_msg), None, None for index, value in enumerate(file_obj): add_attachment_params_dict['z2AF Work Log0{}'.format( index + 1)] = filename[index] attachment_data['attach-z2AF Work Log0{}'.format(index + 1)] = ( filename[index], value) return phantom.APP_SUCCESS, add_attachment_params_dict, attachment_data
def _handle_create_object(self, param): self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) action_result = self.add_action_result(ActionResult(dict(param))) if not self._create_storage_client(action_result): self.save_progress("Could not create API client") return action_result.get_status() # Required values can be accessed directly path = param.get('path') bucket = param['bucket'] vault_id = param['vault_id'] try: self.debug_print('Rules.vault_info start') success, message, vault_info = Rules.vault_info(vault_id=vault_id) self.debug_print( 'Rules.vault_info results: success: {}, message: {}, info: {}'. format(success, message, vault_info)) except requests.exceptions.HTTPError: error_message = "Invalid Vault ID: %s" % (vault_id) return action_result.set_status(phantom.APP_ERROR, error_message) except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error opening file. {}".format(err)) try: vault_info = list(vault_info) file_info = vault_info[0] except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error occurred while getting 'File Info'. {}".format(err)) file_path = file_info['path'] mime = magic.Magic(mime=True) file_mime_type = mime.from_file(file_path) file = MediaFileUpload(file_path, mimetype=file_mime_type) if not file_path: return action_result.set_status( phantom.APP_ERROR, "Could not find given vault ID in vault") if path: target_path = os.path.join(path, file_info["name"]) else: target_path = file_info["name"] request = self._client.objects().insert(bucket=bucket, name=target_path, media_body=file) ret_val, response = self._send_request(request, action_result) if phantom.is_fail(ret_val): return ret_val action_result.add_data(response) return action_result.set_status(phantom.APP_SUCCESS, "Successfully created object")
def _handle_send_htmlemail(self, param): # noqa: C901 self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) action_result = self.add_action_result(ActionResult(param)) config = self.get_config() # Derive 'from' email address sender_address = config.get('sender_address', config.get(phantom.APP_JSON_USERNAME)) email_from = param.get(SMTP_JSON_FROM, sender_address) email_to = self._handle_py_ver_compat_for_input_str(param['to']) email_cc = self._handle_py_ver_compat_for_input_str(param.get('cc')) email_bcc = self._handle_py_ver_compat_for_input_str(param.get('bcc')) # Filter method returns a Filter object on Python v3 and a List on Python v2 # So, to maintain the uniformity the Filter object has been explicitly type casted to List email_to = [x.strip() for x in email_to.split(",")] email_to = list(filter(None, email_to)) if email_cc: email_cc = [x.strip() for x in email_cc.split(",")] email_cc = list(filter(None, email_cc)) if email_bcc: email_bcc = [x.strip() for x in email_bcc.split(",")] email_bcc = list(filter(None, email_bcc)) email_subject = param.get('subject') email_headers = param.get('headers') email_html = param['html_body'] email_text = param.get('text_body') attachment_json = param.get('attachment_json') encoding = config.get(SMTP_ENCODING, False) smtputf8 = config.get(SMTP_ALLOW_SMTPUTF8, False) if encoding: message_encoding = 'utf-8' else: message_encoding = 'ascii' # Validation for the 'from' email address if not email_from: return action_result.set_status( phantom.APP_ERROR, "Error: failed to get email sender") if not len(email_to): return action_result.set_status( phantom.APP_ERROR, "Error: failed to get email recipents") if email_headers: try: email_headers = json.loads(email_headers) except: return action_result.set_status( phantom.APP_ERROR, "Error: custom email headers field is not valid json") if not isinstance(email_headers, dict): return action_result.set_status( phantom.APP_ERROR, "Error: custom email headers field is not a dictionary") else: email_headers = {} if attachment_json: try: attachment_json = json.loads(attachment_json) except: return action_result.set_status( phantom.APP_ERROR, "Error: attachment json field is not valid json") if not isinstance(attachment_json, list): return action_result.set_status( phantom.APP_ERROR, "Error: attachment json field is not a list") has_dictionary = False for x in attachment_json: if isinstance(x, dict) and x.get('vault_id'): has_dictionary = True break if not has_dictionary: return action_result.set_status( phantom.APP_ERROR, "Error: attachment json field does not contain any dictionaries with the \"vault_id\" key" ) for attachment in attachment_json: for key, value in list(attachment.items()): attachment.pop(key) attachment[self._handle_py_ver_compat_for_input_str( key)] = self._handle_py_ver_compat_for_input_str(value) else: attachment_json = [] for i in range(1, 6): attachment_json += [{ 'vault_id': self._handle_py_ver_compat_for_input_str( param.get('attachment{}'.format(i))), 'content_id': self._handle_py_ver_compat_for_input_str( param.get('content_id{}'.format(i))) }] attachment_json = list( filter(lambda x: isinstance(x, dict) and x.get('vault_id'), attachment_json)) root = MIMEMultipart('related') root['from'] = email_from root['to'] = ",".join(email_to) if email_cc: root['cc'] = ", ".join(email_cc) email_to.extend(email_cc) if email_bcc: email_to.extend(email_bcc) if email_subject: root['subject'] = self._handle_py_ver_compat_for_input_str( email_subject) for k, v in list(email_headers.items()): k = self._handle_py_ver_compat_for_input_str(k) root[k] = self._handle_py_ver_compat_for_input_str(v) if not email_text: email_text = self.html_to_text(email_html) msg = MIMEMultipart('alternative') try: if message_encoding == 'utf-8': msg.attach( MIMEText( self._handle_py_ver_compat_for_input_str(email_text), 'plain', 'utf-8')) msg.attach( MIMEText( self._handle_py_ver_compat_for_input_str(email_html), 'html', 'utf-8')) else: msg.attach(MIMEText(email_text, 'plain', 'ascii')) msg.attach(MIMEText(email_html, 'html', 'ascii')) except Exception as e: return action_result.set_status( phantom.APP_ERROR, "{0} Error message: {1}".format( SMTP_UNICODE_ERROR_MSG, self._get_error_message_from_exception(e))) root.attach(msg) for x in attachment_json: vault_id = x['vault_id'] content_id = x.get('content_id') try: _, _, data = ph_rules.vault_info( container_id=self.get_container_id(), vault_id=vault_id) if not data: _, _, data = ph_rules.vault_info(vault_id=vault_id) data = list(data) except: return action_result.set_status( phantom.APP_ERROR, "Error: failed to find vault ID: {}".format(vault_id)) if data and len(data) > 0 and isinstance( data[0], dict) and data[0].get('path'): path = data[0].get('path') attachment_path = data[0].get('name') filename = os.path.basename(attachment_path) filename = self._handle_py_ver_compat_for_input_str(filename) ctype, encoding = mimetypes.guess_type(attachment_path) if ctype is None: ctype = 'application/octet-stream' maintype, subtype = ctype.split('/', 1) try: if maintype == 'text': with open(path, "r") as fp: attachment = MIMEText(fp.read(), _subtype=subtype) elif maintype == 'message': with open(path, "r") as fp: base_msg = message_from_file(fp) attachment = MIMEMessage(base_msg, _subtype=subtype) elif maintype == 'image': # Python 2to3 change with open(path, "rb") as fp: attachment = MIMEImage(fp.read(), _subtype=subtype) else: with open(path, "rb") as rfp: attachment = MIMEBase(maintype, subtype) attachment.set_payload(rfp.read()) encoders.encode_base64(attachment) except: return action_result.set_status( phantom.APP_ERROR, "Error: failed to read the file for the vault ID: {}". format(vault_id)) attachment.add_header('Content-Disposition', 'attachment', filename=filename) if content_id: attachment.add_header( 'Content-ID', "<{}>".format(content_id.strip().lstrip( '<').rstrip('>').strip())) root.attach(attachment) else: return action_result.set_status( phantom.APP_ERROR, "Error: failed to find vault id: {}".format(vault_id)) try: # Provided mail_options=["SMTPUTF8"], to allow Unicode characters for py3 in to_list parameter # This will ensure that the to_list gets encoded with 'utf-8' and not the default encoding which is 'ascii' mail_options = list() if smtputf8: mail_options.append("SMTPUTF8") self._smtp_conn.sendmail(email_from, email_to, root.as_string(), mail_options=mail_options) except UnicodeEncodeError: return action_result.set_status( phantom.APP_ERROR, "{} {}".format(SMTP_ERR_SMTP_SEND_EMAIL, SMTP_ERR_SMTPUTF8_CONFIG)) except Exception as e: return action_result.set_status( phantom.APP_ERROR, "{} {}".format(SMTP_ERR_SMTP_SEND_EMAIL, self._get_error_message_from_exception(e))) return action_result.set_status(phantom.APP_SUCCESS, SMTP_SUCC_SMTP_EMAIL_SENT)
def _add_attachments(self, outer, attachments, action_result, message_encoding): if (not attachments): return phantom.APP_SUCCESS for attachment_vault_id in attachments: if self.get_container_id() == '0': if '.pdf' not in attachment_vault_id: return action_result.set_status(phantom.APP_ERROR, SMTP_ERR_SMTP_SEND_EMAIL) if hasattr(Vault, "get_phantom_home"): report_dir_pre_4_0 = '{0}/www/reports'.format( self.get_phantom_home()) report_dir_post_4_0 = '{0}/vault/reports'.format( self.get_phantom_home()) else: report_dir_pre_4_0 = '/opt/phantom/www/reports' report_dir_post_4_0 = '/opt/phantom/vault/reports' filename = '' for report_dir in (report_dir_post_4_0, report_dir_pre_4_0): test_filename = os.path.join(report_dir, attachment_vault_id) test_filename = os.path.abspath(test_filename) if os.path.isfile(test_filename): filename = test_filename break is_valid_path = filename.startswith( report_dir_pre_4_0) or filename.startswith( report_dir_post_4_0) if not filename or not is_valid_path: return action_result.set_status(phantom.APP_ERROR, SMTP_ERR_SMTP_SEND_EMAIL) with open(filename, 'rb') as fp: msg = MIMEBase('application', 'pdf') msg.set_payload(fp.read()) filename = os.path.basename(filename) # handling ugly file names that are of the format "report_type__id-X__ts-<timestamp>.pdf", where 'X' is any number if '__' in filename: pieces = filename.split('__') if len(pieces) == 3: filename = '{}_{}'.format( pieces[0], pieces[2]) # get rid of __id_x__ # Encode the payload using Base64 encoders.encode_base64(msg) else: try: _, _, vault_meta_info = ph_rules.vault_info( container_id=self.get_container_id(), vault_id=attachment_vault_id) if not vault_meta_info: _, _, vault_meta_info = ph_rules.vault_info( vault_id=attachment_vault_id) if not vault_meta_info: self.invalid_vault_ids.append(attachment_vault_id) continue vault_meta_info = list(vault_meta_info) except: self.invalid_vault_ids.append(attachment_vault_id) continue # Check if we have any results if (len(vault_meta_info) == 0): continue # pick up the first one, they all point to the same file vault_meta_info = vault_meta_info[0] attachment_path = vault_meta_info['name'] file_path = vault_meta_info['path'] # Guess the content type based on the file's extension. Encoding # will be ignored, although we should check for simple things like # gzip'd or compressed files. filename = os.path.basename(attachment_path) ctype, encoding = mimetypes.guess_type(attachment_path) if ctype is None or encoding is not None: # No guess could be made, or the file is encoded (compressed), so # use a generic bag-of-bits type. ctype = 'application/octet-stream' maintype, subtype = ctype.split('/', 1) try: if maintype == 'text': fp = open(file_path) # Note: we should handle calculating the charset msg = MIMEText(fp.read(), _subtype=subtype) fp.close() elif maintype == 'message': fp = open(file_path) base_msg = message_from_file(fp) msg = MIMEMessage(base_msg, _subtype=subtype) fp.close() elif maintype == 'image': fp = open(file_path, 'rb') msg = MIMEImage(fp.read(), _subtype=subtype) fp.close() else: fp = open(file_path, 'rb') msg = MIMEBase(maintype, subtype) msg.set_payload(fp.read()) fp.close() # Encode the payload using Base64 encoders.encode_base64(msg) except Exception as e: return action_result.set_status( phantom.APP_ERROR, self._get_error_message_from_exception(e)) # if using utf-8 encode the filename of the attachment as well if message_encoding == 'utf-8': filename = self._handle_py_ver_compat_for_input_str(filename) # Set the filename parameter msg.add_header('Content-Disposition', 'attachment', filename=filename) outer.attach(msg) return phantom.APP_SUCCESS
def _handle_detonate_file(self, param): action_result = self.add_action_result(ActionResult(dict(param))) ret_val = self._check_version(action_result) if phantom.is_fail(ret_val): return action_result.get_status() vault_id = param['vault_id'] file_name = param.get('file_name') dozip = param.get("zip_and_encrypt", False) zip_password = param.get("zip_password") or "infected" try: self.debug_print('Rules.vault_info start') success, message, vault_info = Rules.vault_info(vault_id=vault_id) self.debug_print( 'Rules.vault_info results: success: {}, message: {}, info: {}'. format(success, message, vault_info)) except requests.exceptions.HTTPError: error_message = "Invalid Vault ID: %s" % (vault_id) return action_result.set_status(phantom.APP_ERROR, error_message) except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error opening file. {}".format(err)) if not vault_info: return action_result.set_status(phantom.APP_ERROR, "Invalid Vault ID") try: vault_info = list(vault_info) file_info = vault_info[0] except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error occurred while getting 'File Info'. {}".format(err)) try: file_path = file_info['path'] except KeyError: return action_result.set_status( phantom.APP_ERROR, "Error occurred while getting 'File Path'") if not file_name: try: file_name = file_info['name'] except KeyError: return action_result.set_status( phantom.APP_ERROR, "Error occurred while getting 'File Name'") try: payload = open(file_path, 'rb') except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error opening file. {}".format(err)) if dozip: try: import zip_and_encrypt as z zae = z.zip_and_encrypt("/tmp/phcuckoo_app_", zip_password) zae.add(file_path) payload = zae.archive_fp except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status(phantom.APP_ERROR, err) files = {'file': (file_name, payload)} ret_val, task_id = self._queue_analysis(action_result, "file", files=files) if phantom.is_fail(ret_val): return action_result.get_status() return self._poll_for_task(action_result, task_id, key=file_name)
def _upload_file(self, param): action_result = self.add_action_result( phantom.ActionResult(dict(param))) caption = param.get('caption', '') if caption: caption += ' -- ' caption += 'Uploaded from Phantom' kwargs = {} params = {'channels': param['destination'], 'initial_comment': caption} if 'filetype' in param: params['filetype'] = param.get('filetype') if 'filename' in param: params['filename'] = param.get('filename') if 'parent_message_ts' in param: # Support for replying in thread params['thread_ts'] = param.get('parent_message_ts') if 'file' in param: vault_id = param.get('file') # check the vault for a file with the supplied ID try: success, message, vault_meta_info = ph_rules.vault_info( vault_id=vault_id) vault_meta_info = list(vault_meta_info) if not success or not vault_meta_info: error_msg = " Error Details: {}".format( unquote(message)) if message else '' return action_result.set_status( phantom.APP_ERROR, "{}.{}".format( SLACK_ERR_UNABLE_TO_FETCH_FILE.format(key="info"), error_msg)) except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "{}. {}".format( SLACK_ERR_UNABLE_TO_FETCH_FILE.format(key="info"), err)) # phantom vault file path file_path = vault_meta_info[0].get('path') if not file_path: return action_result.set_status( phantom.APP_ERROR, SLACK_ERR_UNABLE_TO_FETCH_FILE.format(key="path")) # phantom vault file name file_name = vault_meta_info[0].get('name') if not file_name: return action_result.set_status( phantom.APP_ERROR, SLACK_ERR_UNABLE_TO_FETCH_FILE.format(key="name")) upfile = open(file_path, 'rb') params['filename'] = file_name kwargs['files'] = {'file': upfile} elif 'content' in param: params['content'] = self._handle_py_ver_compat_for_input_str( param.get('content')) else: return action_result.set_status( phantom.APP_ERROR, SLACK_ERR_FILE_OR_CONTENT_NOT_PROVIDED) ret_val, resp_json = self._make_slack_rest_call( action_result, SLACK_UPLOAD_FILE, params, **kwargs) if 'files' in kwargs: upfile.close() if not ret_val: message = action_result.get_message() if message: error_message = "{}: {}".format(SLACK_ERR_UPLOADING_FILE, message) else: error_message = SLACK_ERR_UPLOADING_FILE return action_result.set_status(phantom.APP_ERROR, error_message) file_json = resp_json.get('file', {}) thumbnail_dict = {} pop_list = [] for key, value in list(file_json.items()): if key.startswith('thumb'): pop_list.append(key) name_arr = key.split('_') thumb_name = "{0}_{1}".format(name_arr[0], name_arr[1]) if thumb_name not in thumbnail_dict: thumbnail_dict[thumb_name] = {} thumb_dict = thumbnail_dict[thumb_name] if len(name_arr) == 2: thumb_dict['img_url'] = value elif name_arr[2] == 'w': thumb_dict['width'] = value elif name_arr[2] == 'h': thumb_dict['height'] = value elif key == 'initial_comment': resp_json['caption'] = value pop_list.append(key) elif key in ['channels', 'ims', 'groups']: if 'destinations' not in resp_json: resp_json['destinations'] = [] resp_json['destinations'] += value pop_list.append(key) elif key == 'username': pop_list.append(key) elif key == 'user': resp_json['sender'] = value pop_list.append(key) for poppee in pop_list: file_json.pop(poppee) resp_json['thumbnails'] = thumbnail_dict action_result.add_data(resp_json) return action_result.set_status(phantom.APP_SUCCESS, SLACK_SUCC_FILE_UPLOAD)
def _handle_detonate_file(self, param: Dict[str, Any]): action_result = self.add_action_result(ActionResult(dict(param))) status, api = self._get_api(action_result) if api is None: return status vault_id = param["vault_id"] try: _, _, vault_info = phantom_rules.vault_info(vault_id=vault_id) except Exception: # pylint: disable=broad-except return action_result.set_status( phantom.APP_ERROR, f"Error while getting vault info for vault_id {vault_id}", ) if len(vault_info) > 1: return action_result.set_status( phantom.APP_ERROR, f"Found multiple files for vault_id {vault_id}") if len(vault_info) == 0: return action_result.set_status( phantom.APP_ERROR, f"No sample found for vault_id {vault_id}") vault_info = vault_info[0] file_path = vault_info.get("path") if not file_path: return action_result.set_status( phantom.APP_ERROR, f"Cannot find a path for vault id {vault_id}") self.save_progress("Submitting file %s" % vault_id) params = {"reanalyze": True} if param.get("comment"): params["comment"] = param.get("comment") if param.get("tags"): params["tags"] = param.get("tags") if param.get("type"): params["sample_type"] = param.get("type") if param.get("config"): params["user_config"] = param.get("config") if param.get("jobrules"): params["jobrule_entries"] = param.get("jobrules") if param.get("file_name"): params["sample_filename_b64enc"] = base64.b64encode( param.get("file_name").encode()).decode() elif vault_info.get("name"): params["sample_filename_b64enc"] = base64.b64encode( vault_info["name"].encode()).decode() try: res = api.submit_file(file_path, params=params) except Exception as exc: # pylint: disable=broad-except err = self._get_error_message_from_exception(exc) return action_result.set_status( phantom.APP_ERROR, f"{VMRAY_ERR_SUBMIT_FILE}. Details: {err}") try: if res["errors"]: errors = [ err.get("error_msg", "NO_ERROR_MSG_GIVEN") for err in res["errors"] ] return action_result.set_status(phantom.APP_ERROR, ";".join(errors)) submission_id = res["submissions"][0]["submission_id"] submission_url = res["submissions"][0]["submission_webif_url"] except Exception as exc: # pylint: disable=broad-except err = self._get_error_message_from_exception(exc) return action_result.set_status(phantom.APP_ERROR, VMRAY_ERR_SERVER_RES.format(err)) submission_finished = True iocs_only = param.get("ioc_only", True) status, report = self._get_report(action_result, submission_id, DEFAULT_TIMEOUT, iocs_only) if phantom.is_fail(status): if report: error_msg, _exc = report if error_msg == VMRAY_ERR_SUBMISSION_NOT_FINISHED: submission_finished = False else: return action_result.set_status(phantom.APP_ERROR, f"{error_msg}, {_exc}") report = None else: return action_result.get_status() try: if report: for analysis in report["analyses"]: action_result.add_data({"analysis": analysis}) if report["reputation_lookup"]: action_result.add_data( {"reputation_lookup": report["reputation_lookup"][0]}) action_result.update_summary({ "billing_type": report["billing_type"], "recursive_submission_ids": report["recursive_submission_ids"], "verdict": report["verdict"], }) except Exception as exc: # pylint: disable=broad-except err = self._get_error_message_from_exception(exc) return action_result.set_status(phantom.APP_ERROR, VMRAY_ERR_SERVER_RES.format(err)) action_result.update_summary({ "submission_id": submission_id, "url": submission_url, "submission_finished": submission_finished, }) return action_result.set_status(phantom.APP_SUCCESS)
def _handle_request_cert(self, param): self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) action_result = self.add_action_result(ActionResult(dict(param))) container_id = self.get_container_id() # required params csr = param["csr"] # vault_id or filename server_platform_id = consts.PLATFORM_MAP[param["server_platform"]] order_validity_years = int(param["order_validity"]) # optional params cn = param.get("common_name") signature_hash = param.get("signature_hash") success, msg, vault_info = ph_rules.vault_info( container_id=container_id) if not success: return action_result.set_status(phantom.APP_ERROR, msg) filename = "" for vault_item in vault_info: if csr == vault_item["vault_id"] or csr == vault_item["name"]: filename = vault_item["path"] break else: # 'else' on a for loop is called if it IS NOT broken (i.e. break is never called) # in this case that means we did not find a vault item in the list, so return an error return action_result.set_status( phantom.APP_ERROR, "Could not find the csr in the file vault") with open(filename, "r") as fp: csr_data = fp.read() # try to parse the csr try: csr_req = x509.load_pem_x509_csr(bytes(csr_data, "utf-8")) parsed_signature_hash = csr_req.signature_hash_algorithm.name # https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Name.get_attributes_for_oid parsed_cn = csr_req.subject.get_attributes_for_oid( x509.oid.NameOID.COMMON_NAME)[0].value self.save_progress( "Parsed csr values: siganture_hash={0} CN={1}".format( parsed_signature_hash, parsed_cn)) except Exception: return action_result.set_status( phantom.APP_ERROR, "Failed to parse the csr, make sure it is in PEM format") payload = { "certificate": { "common_name": cn or parsed_cn, "csr": csr_data, "signature_hash": signature_hash or parsed_signature_hash, "sever_platform": { "id": server_platform_id, }, }, "organization": { "id": int(self._org_id) }, "order_validity": { "years": order_validity_years } } # submit the request ret_val, response = self._make_rest_call("/order/certificate/ssl", action_result, method="post", json=payload) if phantom.is_fail(ret_val): # the call to the 3rd party device or service failed, action result should contain all the error details return action_result.get_status() # Add the response into the data section action_result.add_data(response) # Add a dictionary that is made up of the most important values from data into the summary action_result.update_summary({ "order_id": response["id"], "request_id": response["requests"][0]["id"], "request_status": response["requests"][0]["status"], "common_name": payload["certificate"]["common_name"], "signature_hash": payload["certificate"]["signature_hash"] }) return action_result.set_status(phantom.APP_SUCCESS)
def _get_attachment(self, param): # Create action result action_result = self.add_action_result(ActionResult(param)) # Create RT object if phantom.is_fail(self._create_rt_session(action_result)): return action_result.get_status() # Get parameters ticket_id = param[RT_JSON_ID] attachment_id = param[RT_JSON_ATTACHMENT] # Set up the result data data = action_result.add_data(dict()) data['id'] = ticket_id data['attachment_id'] = attachment_id # Request the attachment meta data ret_val, resp_text = self._make_rest_call( "ticket/{0}/attachments/{1}".format(ticket_id, attachment_id), action_result) if phantom.is_fail(ret_val): return ret_val if '# Invalid attachment id' in resp_text: return action_result.set_status( phantom.APP_ERROR, "Attachment with ID {0} not found on ticket {1}".format( attachment_id, ticket_id)) if re.findall('# Ticket .+ does not exist.', resp_text): return action_result.set_status( phantom.APP_ERROR, "Ticket {0} does not exist.".format(ticket_id)) # Look for the filename in the meta data filename_index = resp_text.find('filename') if filename_index == -1: file_name = "noname" self.debug_print("Filename not found in header, setting to noname") else: start_filename = resp_text.find('=', filename_index) + 2 end_filename = resp_text.find('\n', filename_index) - 1 file_name = resp_text[start_filename:end_filename] if not file_name: file_name = "noname" self.save_progress( "Filename not found in header, setting to noname") # Look for the file length in the meta data length_index = resp_text.find('Content-Length') if length_index == -1: return action_result.set_status( phantom.APP_ERROR, "Cannot find Content-Length of attachment") start_length = resp_text.find(':', length_index) + 2 end_length = resp_text.find('\n', length_index) length = int(resp_text[start_length:end_length]) if length == 0: return action_result.set_status( phantom.APP_SUCCESS, "File is empty, nothing to do here") # Request the attachment content ret_val, response = self._make_rest_call( "ticket/{0}/attachments/{1}/content".format( ticket_id, attachment_id), action_result) # Convert to bytes and strip away headers and trailers. content = response.content if phantom.is_fail(ret_val): return ret_val # find first newline skip = content.find(b'\n') if skip == -1: return action_result.set_status( phantom.APP_ERROR, "Cannot find first line of file content") # skip first and second newline skip += 2 # Let the actiond handle i/o exceptions and return the errors. ret_val = Vault.create_attachment(content[skip:length + skip], self.get_container_id(), file_name=file_name) if ret_val['succeeded'] is not True: return action_result.set_status( phantom.APP_ERROR, "Error saving file to vault: {0}".format( ret_val.get('error', 'Unknwon error'))) vault_id = ret_val['vault_id'] _, _, file_info = vault_info(vault_id=vault_id, container_id=self.get_container_id()) if len(file_info) == 0: return action_result.set_status( phantom.APP_ERROR, "File not found in vault after adding") file_info = file_info[0] data['vault_id'] = vault_id data['name'] = file_info['name'] data['size'] = file_info['size'] data['sha1'] = file_info['metadata']['sha1'] data['sha256'] = file_info['metadata']['sha256'] data['md5'] = file_info['metadata']['md5'] data['path'] = file_info['path'] return action_result.set_status(phantom.APP_SUCCESS)
def _handle_detonate_file(self, param): self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) action_result = self.add_action_result(ActionResult(dict(param))) vault_id = param['vault_id'] try: success, message, vault_meta_info = ph_rules.vault_info( vault_id=vault_id) vault_meta_info = list(vault_meta_info) if not success or not vault_meta_info: error_msg = " Error Details: {}".format( unquote(message)) if message else '' return action_result.set_status( phantom.APP_ERROR, "{}. {}".format( ANYRUN_ERR_UNABLE_TO_FETCH_FILE.format( key="vault meta info"), error_msg)) except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "{}. {}".format( ANYRUN_ERR_UNABLE_TO_FETCH_FILE.format( key="vault meta info"), err)) try: # phantom vault file path file_path = vault_meta_info[0].get('path') if not file_path: return action_result.set_status( phantom.APP_ERROR, ANYRUN_ERR_UNABLE_TO_FETCH_FILE.format(key="path")) except: return action_result.set_status( phantom.APP_ERROR, ANYRUN_ERR_UNABLE_TO_FETCH_FILE.format(key="path")) self.save_progress("Detonating file {}".format(file_path)) files = [('file', open(file_path, 'rb'))] # make rest call ret_val, response = self._make_rest_call(ANYRUN_DETONATE_FILE_ENDPOINT, action_result, method="post", files=files, headers=self._headers) if phantom.is_fail(ret_val): # the call to the 3rd party device or service failed, action result should contain all the error details return action_result.get_status() self.save_progress("Successfully detonated file") try: action_result.add_data(response['data']) except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error occurred while processing response from server. {}". format(err)) return action_result.set_status(phantom.APP_SUCCESS, "Successfully detonated file")
def zip_extract(container=None, vault_id=None, password=None, **kwargs): """ Extract all files recursively from a .zip archive. Add the extracted files to the vault and return the vault IDs of the extracted files. Provide a password if needed to decrypt. Args: container (CEF type: phantom container id): The container that extracted files will be added to. Should be a container ID or a container dictionary. vault_id: The vault ID of the zip archive to be unzipped. password: The password to use for decryption of the zip archive if necessary. Returns a JSON-serializable object that implements the configured data paths: zip_file_info.name: File name of the zip file in the vault zip_file_info.user: User who added the zip file to the vault output_files.*.file_name: The names of the files extracted from the zip archive. output_files.*.file_path: The file paths of the files extracted from the zip archive. output_files.*.vault_id: The vault IDs of the files extracted from the zip archive. """ ############################ Custom Code Goes Below This Line ################################# import json import phantom.rules as phantom import os from pathlib import Path import zipfile outputs = {'output_files': []} # Ensure valid container input if isinstance(container, dict) and container.get('id'): container_id = container['id'] elif isinstance(container, int): container_id = container else: raise TypeError( "The input 'container' is neither a container dictionary nor an int, so it cannot be used" ) # check the vault_id input success, message, info = phantom.vault_info(vault_id=vault_id, container_id=container_id) if not success: raise ValueError("Could not find file in vault") outputs['zip_file_info'] = info[0] if password and not isinstance(password, str): raise TypeError("password must be a string") # create a directory to store the extracted files before adding to the vault extract_path = Path("/opt/phantom/vault/tmp/") / vault_id extract_path.mkdir(parents=True, exist_ok=True) # extract the files with ZipFile with zipfile.ZipFile(info[0]["path"]) as f_zip: if password: f_zip.extractall(str(extract_path), pwd=password.encode()) else: f_zip.extractall(str(extract_path)) # add each extracted file to the vault and the output for p in extract_path.rglob("*"): if p.is_file(): success, message, vault_id = phantom.vault_add( container=container_id, file_location=str(p), file_name=p.name) if not success: raise RuntimeError( 'failed to add file to vault with path {}'.format(str(p))) outputs['output_files'].append({ 'file_path': str(p), 'file_name': p.name, 'vault_id': vault_id }) # Return a JSON-serializable object assert json.dumps( outputs ) # Will raise an exception if the :outputs: object is not JSON-serializable return outputs
def _handle_detonate_file(self, param): # Implement the handler here # use self.save_progress(...) to send progress messages back to the platform self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) # Add an action result object to self (BaseConnector) to represent the action for this param action_result = self.add_action_result(ActionResult(dict(param))) # Access action parameters passed in the 'param' dictionary vault_id = self._handle_py_ver_compat_for_input_str( param.get('vault_id')) # Get vault info from the vauld_id parameter try: success, msg, vault_info = phantom_rules.vault_info( vault_id=vault_id) except: return action_result.set_status( phantom.APP_ERROR, "Error occurred while fetching the vault information of the specified Vault ID" ) if not vault_info: try: error_msg = "Error occurred while fetching the vault information of the Vault ID: {}".format( vault_id) except: error_msg = "Error occurred while fetching the vault information of the specified Vault ID" return action_result.set_status(phantom.APP_ERROR, error_msg) # Loop through the Vault infomation for item in vault_info: vault_path = item.get('path') if vault_path is None: return action_result.set_status( phantom.APP_ERROR, "Could not find a path associated with the provided Vault ID" ) try: # Open the file vault_file = open(vault_path, 'rb') # Create the files data to send to the console files = {'file': (item['name'], vault_file)} except Exception as e: error_msg = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Unable to open vault file: {}".format(error_msg)) # Process parameters profile = param.get("profile") try: profile = [x.strip() for x in profile.split(',')] except Exception: return action_result.set_status( phantom.APP_ERROR, "Error occurred while processing the {}".format( PROFILE_ACTION_PARAM)) profile = list(filter(None, profile)) if not profile: return action_result.set_status( phantom.APP_ERROR, "Please provide a valid value for the {}".format( PROFILE_ACTION_PARAM)) # Get the other parameters and information priority = 0 if param['priority'].lower() == 'normal' else 1 analysis_type = 1 if param['analysis_type'].lower() == 'live' else 2 timeout = param.get('timeout') # Validate 'timeout' action parameter ret_val, timeout = self._validate_integer(action_result, timeout, TIMEOUT_ACTION_PARAM) if phantom.is_fail(ret_val): return action_result.get_status() force = "true" if param.get('force', True) else "false" # When analysis type = 2 (Sandbox), prefetch must equal 1 if analysis_type == 2: prefetch = 1 else: prefetch = 1 if param.get('prefetch', False) else 0 enable_vnc = "true" if param.get('enable_vnc', False) else "false" data = {} # Get the application code to use for the detonation application = self.get_application_code(param.get('application')) # Create the data based on the parameters options = { "priority": priority, "analysistype": analysis_type, "force": force, "prefetch": prefetch, "profiles": profile, "application": application, "timeout": timeout, "enable_vnc": enable_vnc } # Need to stringify the options parameter data = {'options': json.dumps(options)} endpoint = FIREEYEAX_DETONATE_FILE_ENDPOINT # make rest call ret_val, response = self._make_rest_call(endpoint, action_result, method="post", files=files, data=data) if phantom.is_fail(ret_val): return action_result.get_status() # Now post process the data, uncomment code as you deem fit try: resp_data = response[0] except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error occurred while fetching data from API response. {}". format(err)) try: resp_data['submission_details'] = json.loads( resp_data['submission_details']) except Exception as e: err = self._get_error_message_from_exception(e) return action_result.set_status( phantom.APP_ERROR, "Error occurred while processing API response. {}".format(err)) # Add the response into the data section if isinstance(resp_data, list): for alert in resp_data: action_result.add_data(alert) else: action_result.add_data(resp_data) return action_result.set_status(phantom.APP_SUCCESS)
def _handle_detonate_file(self, param): # Implement the handler here # use self.save_progress(...) to send progress messages back to the platform self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) # Add an action result object to self (BaseConnector) to represent the action for this param action_result = self.add_action_result(ActionResult(dict(param))) # Place vault id in in its own variable. vault_id_str = param['vault_id'] try: success, message, info = rules.vault_info(vault_id=vault_id_str) info = json.loads(json.dumps(info[0])) filepath = info['path'] except: return action_result.set_status( phantom.APP_ERROR, 'File not found in vault: {}'.format(vault_id_str)) # Issue request to Intezer Analyze endpoint = 'analyze' # Perform file check file_check = self.check_file(str(filepath), action_result) # Check file type is correct if not file_check: return str_vault_dict = str(filepath) file = {'file': open(str_vault_dict, 'rb')} self.save_progress( "File has been found location is: {}".format(str_vault_dict)) # Make connection to the Intezer Analyze endpoint ret_val, response = self._make_rest_call(endpoint, action_result, method="post", params=file) if (phantom.is_fail(ret_val)): # so just return from here # the call to the 3rd party device or service failed, action result should contain all the error details message = "Failed request to detonate file endpoint. Message: {}, Response: {}".format( response, ret_val) return action_result.set_status(phantom.APP_ERROR, status_message=message) if response['result_url']: self.save_progress( "File has been posted to Intezer Analyze successfully") # Get result URL result_url = response['result_url'][1:] # Make connection to the Intezer Report Endpoint ret_val, response = self._make_rest_call(result_url, action_result) # If the response is a failure if (phantom.is_fail(ret_val)): message = "Failed retrieving analysis id from output. Message: {}".format( response) return action_result.set_status(phantom.APP_ERROR, status_message=message) # Report is Queued or In Progress i = 0 # While the result is not succeeded or 50 seconds has not elapsed while i < 10 and response['status'] != 'succeeded': self.save_progress( "Sleeping 60 seconds while report is being fetched") # Sleep 60 seconds time.sleep(60) # Make connection to the Intezer Report Endpoint ret_val, response = self._make_rest_call( result_url, action_result) # Increment the timeout counter i += 1 # If we reach 10 minutes and the analysis has not returned if i == 10: return action_result.set_status( phantom.APP_ERROR, status_message= "Timed out whilst waiting for report to build") # If the response is a failure if (phantom.is_fail(ret_val)): message = "Failed retrieving report for Intezer Analyze. Message: {}".format( response) return action_result.set_status(phantom.APP_ERROR, status_message=message) # Create new python dictionary to store output data_output = response # Add the response into the data section action_result.add_data(data_output) # Add a dictionary that is made up of the most important values from data into the summary summary = action_result.update_summary({}) summary['sha256'] = data_output['result']['sha256'] summary['verdict'] = data_output['result']['verdict'] summary['analysis_url'] = data_output['result']['analysis_url'] # Return success, no need to set the message, only the status # BaseConnector will create a textual message based off of the summary dictionary return action_result.set_status(phantom.APP_SUCCESS)
def _handle_upload_file(self, param): # Implement the handler here # use self.save_progress(...) to send progress messages back to the platform self.save_progress("In action handler for: {0}".format( self.get_action_identifier())) # Add an action result object to self (BaseConnector) to represent the action for this param action_result = self.add_action_result(ActionResult(dict(param))) # Access action parameters passed in the 'param' dictionary # Required values can be accessed directly config = self.get_config() endpoint = config['endpoint'] username = config['username'] password = config['password'] vault_id = param['vault_id'] # Find vault path and info for given vault ID try: success, message, vault_info = phrules.vault_info( vault_id=vault_id) vault_info = list(vault_info)[0] except IndexError: return action_result.set_status(phantom.APP_ERROR, VAULT_ERR_FILE_NOT_FOUND), None except Exception: return action_result.set_status(phantom.APP_ERROR, VAULT_ERR_VAULT_ID_NOT_VALID), None if not vault_info: return action_result.set_status( phantom.APP_ERROR, "Error while fetching the vault information of the vault id: '{}'" .format(vault_id)) file_name = vault_info.get('path') if file_name is None: return action_result.set_status(phantom.APP_ERROR, VAULT_ERR_PATH_NOT_FOUND) # Optional values should use the .get() function node_name = param.get('node_name', 'wlWhiteListIPv4') file_type = param.get('file_type', 'IPv4') dry_run = param.get('dry_run', False) is_remove = param.get('is_remove', False) is_update = param.get('is_update', False) self.save_progress("[-] Path: {}".format(os.getcwd())) self.save_progress( "vault id: {} - target node_name: {} file_type: {} dry_run: {}" .format(vault_id, node_name, file_type, dry_run)) # Command Constructor dir_path = os.path.dirname(os.path.realpath(__file__)) script_abs_name = "{}/minemeld-sync.py".format( dir_path) # /opt/phantom/bin/phenv python2.7 .. dry_run_arg = "--dry-run" remove_arg = "--delete" update_arg = "--update" base_cmd = ("/opt/phantom/bin/phenv python2.7 {script_abs_name}" " -k -m {endpoint} -u {username} -p {password}" " -t {file_type} {node_name} {input_file}".format( script_abs_name=script_abs_name, endpoint=endpoint, username=username, password=password, file_type=file_type, node_name=node_name, input_file=file_name)) final_cmd = base_cmd if dry_run: final_cmd = "{} {}".format(final_cmd, dry_run_arg) if is_remove: final_cmd = "{} {}".format(final_cmd, remove_arg) if is_update: final_cmd = "{} {}".format(final_cmd, update_arg) out = subprocess.Popen(final_cmd.split(" "), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = out.communicate() self.save_progress("stdout: {} - stderr: {} ".format(stdout, stderr)) # Now post process the data, uncomment code as you deem fit # Add the response into the data section action_result.add_data(stdout) action_result.add_data(stderr) # Add a dictionary that is made up of the most important values from data into the summary # summary = action_result.update_summary({}) # summary['num_data'] = len(action_result['data']) # Return success, no need to set the message, only the status # BaseConnector will create a textual message based off of the summary dictionary # For now return Error with a message, in case of success we don't set the message, but use the summary # return action_result.set_status(phantom.APP_ERROR, "Action not yet implemented") return action_result.set_status(phantom.APP_SUCCESS)
def _handle_post_url(self, param): action_result = ActionResult(dict(param)) self.add_action_result(action_result) params = dict() params['url'] = param["url"] params['filename'] = param.get('filename') params['key'] = self._api_key # Check if we have a size sizes = {"tiny": "T", "small": "S", "normal": "N", "medium": "M", "large": "L", "full page": "F"} test = param.get("size") if not test: self.save_progress("Size was blank, using the default \"full page\" size") test = "full page" if not sizes.get(test.lower()): self.save_progress("Given size not found, using the default \"full page\" size") params['size'] = "F" else: params['size'] = sizes[test.lower()] # Check if we have a Secret Phrase if self._api_phrase is None: params['hash'] = "" else: params['hash'] = str(hashlib.md5((params['url'] + self._api_phrase).encode('utf-8')).hexdigest()) params['cacheLimit'] = '0' params['format'] = 'JPG' params['timeout'] = '200' ret_val, image = self._make_rest_call('', action_result, params, method='post', stream=True) if phantom.is_fail(ret_val): return action_result.get_status() permalink = None # only create a permalink if the hash is used if params['hash']: permalink = self._get_sspermalink('', params=params, method='post') if params['filename']: file_name = "{}.jpg".format(params['filename']) else: file_name = "{0}{1}".format(param["url"], "_screenshot.jpg") is_download = False if hasattr(Vault, "create_attachment"): vault_ret = Vault.create_attachment(image, self.get_container_id(), file_name=file_name) if vault_ret.get('succeeded'): action_result.set_status(phantom.APP_SUCCESS, "Downloaded screenshot") _, _, vault_meta_info = ph_rules.vault_info(container_id=self.get_container_id(), vault_id=vault_ret[phantom.APP_JSON_HASH]) if not vault_meta_info: self.debug_print("Error while fetching meta information for vault ID: {}".format(vault_ret[phantom.APP_JSON_HASH])) return action_result.set_status(phantom.APP_ERROR, "Could not find meta information of the downloaded screenshot's Vault") vault_path = list(vault_meta_info)[0]['path'] summary = { phantom.APP_JSON_VAULT_ID: vault_ret[phantom.APP_JSON_HASH], phantom.APP_JSON_NAME: file_name, 'vault_file_path': vault_path, phantom.APP_JSON_SIZE: vault_ret.get(phantom.APP_JSON_SIZE)} if permalink: summary['permalink'] = permalink action_result.update_summary(summary) is_download = True else: is_download = False if not is_download: if hasattr(Vault, 'get_vault_tmp_dir'): temp_dir = Vault.get_vault_tmp_dir() else: temp_dir = '/opt/phantom/vault/tmp' temp_dir = "{0}{1}".format(temp_dir, '/{}'.format(uuid.uuid4())) os.makedirs(temp_dir) file_path = os.path.join(temp_dir, 'tempimage.jpg') with open(file_path, 'wb') as f: f.write(image) success, message, vault_id = ph_rules.vault_add(container=self.get_container_id(), file_location=file_path, file_name=file_name) if success: action_result.set_status(phantom.APP_SUCCESS, "Downloaded screenshot") _, _, vault_meta_info = ph_rules.vault_info(container_id=self.get_container_id(), vault_id=vault_id) if not vault_meta_info: self.debug_print("Error while fetching meta information for vault ID: {}".format(vault_id)) return action_result.set_status(phantom.APP_ERROR, "Could not find meta information of the downloaded screenshot's Vault") vault_path = list(vault_meta_info)[0]['path'] summary = { phantom.APP_JSON_VAULT_ID: vault_id, phantom.APP_JSON_NAME: file_name, 'vault_file_path': vault_path} if permalink: summary['permalink'] = permalink action_result.update_summary(summary) else: return action_result.set_status(phantom.APP_ERROR, "Error occurred while saving file to vault: {}".format(message)) return action_result.get_status()
def _download_report_pdf(self, report_id, container_id, action_result, summary_data): file_name = 'deepsight_report_{}.pdf'.format(report_id) self.send_progress(DEEPSIGHT_MSG_DOWNLOADING_REPORT) pdf_accept_headers = {"Accept": "application/pdf"} return_val, resp = self._make_rest_call( DEEPSIGHT_ENDPOINT_MATI_REPORT_PDF.format(mati_id=report_id), action_result, accept_header=pdf_accept_headers) # Something went wrong with the request if phantom.is_fail(return_val): return action_result.get_status() # Generate pdf from response content if (resp.get(DEEPSIGHT_JSON_RESPONSE)): pdf_resp = resp.get(DEEPSIGHT_JSON_RESPONSE) temp_dir = tempfile.mkdtemp() file_path = os.path.join(temp_dir, file_name) with open(file_path, 'wb') as fw: fw.write(pdf_resp) # Check if the report pdf with same file name is already available in vault success, message, vault_list = ph_rules.vault_info( vault_id=None, file_name=None, container_id=container_id) # Iterate through each vault item in the container and compare name and size of file for vault in vault_list: if vault.get('name') == file_name and vault.get( 'size') == os.path.getsize(file_path): self.send_progress(DEEPSIGHT_REPORT_PDF_ALREADY_AVAILABLE) vault_details = {} vault_details[phantom.APP_JSON_SIZE] = vault.get('size') vault_details[ phantom.APP_JSON_TYPE] = DEEPSIGHT_REPORT_FILE_TYPE vault_details[phantom.APP_JSON_CONTAINS] = [ DEEPSIGHT_REPORT_FILE_TYPE ] vault_details[ phantom.APP_JSON_ACTION_NAME] = self.get_action_name() vault_details[ phantom.APP_JSON_APP_RUN_ID] = self.get_app_run_id() vault_details[phantom.APP_JSON_VAULT_ID] = vault.get( 'vault_id') vault_details[DEEPSIGHT_JSON_REPORT_FILE_NAME] = file_name json_data = action_result.get_data() json_data[0].update({'vault': vault_details}) summary_data.update({'vault_id': vault.get('vault_id')}) summary_data['pdf_availability'] = True return phantom.APP_SUCCESS ret_val = self._move_file_to_vault(container_id, os.path.getsize(file_path), DEEPSIGHT_REPORT_FILE_TYPE, file_path, action_result, summary_data) shutil.rmtree(temp_dir) # Something went wrong while moving file to vault if phantom.is_fail(ret_val): return action_result.get_status() summary_data['pdf_availability'] = True else: # If pdf not available, treated as app success self.send_progress(DEEPSIGHT_REPORT_PDF_UNAVAILABLE) summary_data['pdf_availability'] = False return phantom.APP_SUCCESS