def Make_List(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): phantom.debug('Make_List() called') results_data_1 = phantom.collect2(container=container, datapath=['run_query_1:action_result.data'], action_results=results) results_item_1_0 = [item[0] for item in results_data_1] ################################################################################ ## Custom Code Start ################################################################################ # Write your custom code here... list_name = "temp_peer_list_%s" % container['id'] # Store list name in container data data = phantom.get_container(container['id'])['data'] data.update({"peer_list":list_name}) phantom.update(container, {'data':data} ) phantom.remove_list(list_name) #phantom.debug(results_item_1_0) for row in results_item_1_0[0]: phantom.add_list(list_name, [row["peer"],row["count"], row["priority"]]) ################################################################################ ## Custom Code End ################################################################################ add_comment_1(container=container) return
def write_container_data(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): phantom.debug('write_container_data() called') input_parameter_0 = "my_data_2" input_parameter_1 = "More Stuff" ################################################################################ ## Custom Code Start ################################################################################ # Write your custom code here... #input_parameter_0 is the key #input_parameter_1 is the value data = phantom.get_container(container['id'])['data'] data.update({input_parameter_0: input_parameter_1}) phantom.update(container, {'data': data}) ################################################################################ ## Custom Code End ################################################################################ get_container_data(container=container) return
def pin_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): import random phantom.debug('pin_1() called') # collect data for 'pin_to_hud_6' call dest_ip_artifacts = filter( lambda x: x[0], phantom.collect2(container=container, datapath=['artifact:*.cef.destinationAddress'])) sorc_ip_artifacts = filter( lambda x: x[0], phantom.collect2(container=container, datapath=['artifact:*.cef.sourceAddress'])) styles = set(["white", "red", "purple"]) pin_name = pin_name_mangle("pin_1", container) pin_id = phantom.get_data(pin_name) if not pin_id: ret_val, message, pin_id = phantom.pin( container=container, message="Affected IPs", data=str(len(dest_ip_artifacts) + len(sorc_ip_artifacts)), pin_type="card_medium", pin_style="white") phantom.debug("new pin_1") else: style = random.sample(styles, 1)[0] phantom.debug(style) ret_val, message = phantom.update_pin( pin_id, message="Affected IPs", data=str(len(dest_ip_artifacts) + len(sorc_ip_artifacts)), pin_style=style) if ret_val: phantom.save_data(pin_id, pin_name) # set container properties for: update_data = {} phantom.update(container, update_data) return
def L5_CF_Get_Query_Results_py3_SOAR53(peer=None, priority=None, count=None, container=None, **kwargs): """ created with SOAR 5.3 Args: peer priority count container (CEF type: phantom container id) Returns a JSON-serializable object that implements the configured data paths: results_list_name """ ############################ Custom Code Goes Below This Line ################################# import json import phantom.rules as phantom outputs = {} # Write your custom code here... phantom.debug(container) phantom.debug(type(container)) list_name = "temp_peer_list_%s" % container # You need the container object in order to update it. update_container = phantom.get_container(container) # Get the data node of the container data = phantom.get_container(container)['data'] data.update({"peer_list": list_name}) phantom.update(update_container, {'data': data}) phantom.remove_list(list_name) for i in range(0, len(peer)): phantom.add_list(list_name, [peer[i], priority[i], count[i]]) # The actual list is in slot 3 of the tuple returned by phantom.get_list() results_list = phantom.get_list(list_name)[2] phantom.debug(results_list) outputs = {'results_list_name': list_name} # Return a JSON-serializable object assert json.dumps( outputs ) # Will raise an exception if the :outputs: object is not JSON-serializable return outputs
def pin_2(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): import random phantom.debug('pin_2() called') # collect data for 'pin_to_hud_6' call dest_username = filter( lambda x: x[0], phantom.collect2(container=container, datapath=['artifact:*.cef.destinationUserName'])) sorc_username = filter( lambda x: x[0], phantom.collect2(container=container, datapath=['artifact:*.cef.sourceUserName'])) styles = set(["white", "red", "purple"]) pin_name = pin_name_mangle("pin_2", container) pin_id = phantom.get_data(pin_name) if not pin_id: ret_val, message, pin_id = phantom.pin( container=container, message="Affected Users", data=str(len(dest_username) + len(sorc_username)), pin_type="card_medium", pin_style="purple") phantom.debug("new pin_2") else: # Delete and remake this one, for the sake of demonstration ret_val, message = phantom.delete_pin(pin_id) ret_val, message, pin_id = phantom.pin( container=container, message="Affected Users", data=str(len(dest_username) + len(sorc_username)), pin_type="card_medium", pin_style=random.sample(styles, 1)[0]) if ret_val: phantom.save_data(pin_id, pin_name) # set container properties for: update_data = {} phantom.update(container, update_data) return
def pin_3(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): import random phantom.debug('pin_3() called') # collect data for 'pin_to_hud_6' call dest_domain = filter( lambda x: x[0], phantom.collect2(container=container, datapath=['artifact:*.cef.destinationDnsDomain'])) pin_name = pin_name_mangle("pin_3", container) try: most_rcnt_domain = dest_domain[0][0] except: pass else: pin_id = phantom.get_data(pin_name) if not pin_id: ret_val, message, pin_id = phantom.pin( container=container, message="Most Recent Domain", data=most_rcnt_domain, pin_type="card_medium", pin_style="red") phantom.debug("new pin_3") else: ret_val, message = phantom.update_pin(pin_id, message="Most Recent Domain", data=most_rcnt_domain, pin_type="card_medium", pin_style="red") if ret_val: phantom.save_data(pin_id, pin_name) # set container properties for: update_data = {} phantom.update(container, update_data) return
def resolve_container(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None): phantom.debug('resolve_container() called') # Closing out the container phantom.set_severity(container, "low") my_add_tags(tags=["automation"]) # Updating required fields resolution_data = {"Resolution": "Trivial True Positive"} custom_fields = {"custom_fields": resolution_data} phantom.update(container, custom_fields) phantom.set_status(container, "closed") return
def on_start(container): phantom.debug('on_start() called') # get the _raw data from splunk data = phantom.collect2(container=container, datapath=['artifact:*.cef._raw']) data = data[0][0] phantom.debug(data) split_data = data.split(',') data_dict = {} custom_dict = {} # get a name for the container to update container_name = split_data[0] del (split_data[0]) counter = 1 # breaks out the data into items with k,v or additional items with multiple : for i in split_data: if len(i.split(":")) == 2: data_key = i.split(":")[0] data_value = i.split(":")[1] data_dict[data_key.replace(" ", "")] = data_value else: custom_dict["cs" + str(counter)] = i counter = counter + 1 phantom.debug(container_name) phantom.debug(data_dict) phantom.debug(custom_dict) raw = {} # add our artifacts that were easily parsed phantom.add_artifact(container=container, raw_data=raw, cef_data=data_dict, label='event', name='splunk data', severity='low', identifier=None, artifact_type='network') # everything else phantom.add_artifact(container=container, raw_data=raw, cef_data=custom_dict, label='event', name='splunk data', severity='low', identifier=None, artifact_type='network') # update the name of the container update_data = {"name": container_name} success, message = phantom.update(container, update_data) return
def custom_function_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug('custom_function_1() called') formatted_data_1 = phantom.get_format_data(name='format_2') ################################################################################ ## Custom Code Start ################################################################################ phantom.debug(phantom.update(container, json.loads(formatted_data_1))) ################################################################################ ## Custom Code End ################################################################################ return
def container_merge(target_container=None, container_list=None, workbook=None, close_containers=None, **kwargs): """ An alternative to the add-to-case API call. This function will copy all artifacts, automation, notes and comments over from every container within the container_list into the target_container. The target_container will be upgraded to a case. The notes will be copied over with references to the child containers from where they came. A note will be left in the child containers with a link to the target container. The child containers will be marked as evidence within the target container. Any notes left as a consequence of the merge process will be skipped in subsequent merges. Args: target_container (CEF type: phantom container id): The target container to copy the information over. Supports container dictionary or container id. container_list: A list of container IDs to copy into the target container. workbook: Name or ID of the workbook to add if the container does not have a workbook yet. If no workbook is provided, the system default workbook will be added. close_containers: True or False to close the child containers in the container_list after merge. Defaults to False. Returns a JSON-serializable object that implements the configured data paths: """ ############################ Custom Code Goes Below This Line ################################# import json import phantom.rules as phantom outputs = {} # Check if valid target_container input was provided if isinstance(target_container, int): container = phantom.get_container(target_container) elif isinstance(target_container, dict): container = target_container else: raise TypeError( f"target_container '{target_container}' is neither a int or a dictionary" ) container_url = phantom.build_phantom_rest_url('container', container['id']) # Check if container_list input is a list of IDs if isinstance(container_list, list) and (all( isinstance(x, int) for x in container_list) or all(x.isnumeric() for x in container_list)): pass else: raise TypeError( f"container_list '{container_list}' is not a list of integers") ## Prep parent container as case with workbook ## workbook_name = phantom.requests.get( container_url, verify=False).json().get('workflow_name') # If workbook already exists, proceed to promote to case if workbook_name: phantom.debug( "workbook already exists. adding [Parent] to container name and promoting to case" ) update_data = {'container_type': 'case'} if not '[Parent]' in container['name']: update_data['name'] = "[Parent] {}".format(container['name']) phantom.update(container, update_data) else: phantom.update(container, update_data) # If no workbook exists, add one else: phantom.debug( "no workbook in container. adding one by name or using the default" ) # If workbook ID was provided, add it if isinstance(workbook, int): workbook_id = workbook phantom.add_workbook(container=container['id'], workbook_id=workbook_id) # elif workbook name was provided, attempt to translate it to an id elif isinstance(workbook, str): workbook_url = phantom.build_phantom_rest_url( 'workbook_template') + '?_filter_name="{}"'.format(workbook) response = phantom.requests.get(workbook_url, verify=False).json() if response['count'] > 1: raise RuntimeError( 'Unable to add workbook - more than one ID matches workbook name' ) elif response['data'][0]['id']: workbook_id = response['data'][0]['id'] phantom.add_workbook(container=container['id'], workbook_id=workbook_id) else: # Adding default workbook phantom.promote(container=container['id']) # Check again to see if a workbook now exists workbook_name = phantom.requests.get( container_url, verify=False).json().get('workflow_name') # If workbook is now present, promote to case if workbook_name: update_data = {'container_type': 'case'} if not '[Parent]' in container['name']: update_data['name'] = "[Parent] {}".format(container['name']) phantom.update(container, update_data) else: phantom.update(container, update_data) else: raise RuntimeError( f"Error occurred during workbook add for workbook '{workbook_name}'" ) ## Check if current phase is set. If not, set the current phase to the first available phase to avoid artifact merge error ## if not container.get('current_phase_id'): phantom.debug( "no current phase, so setting first available phase to current") workbook_phase_url = phantom.build_phantom_rest_url( 'workbook_phase') + "?_filter_container={}".format(container['id']) request_json = phantom.requests.get(workbook_phase_url, verify=False).json() update_data = {'current_phase_id': request_json['data'][0]['id']} phantom.update(container, update_data) child_container_list = [] child_container_name_list = [] # Iterate through child containers for child_container_id in container_list: ### Begin child container processing ### phantom.debug( "Processing Child Container ID: {}".format(child_container_id)) child_container = phantom.get_container(child_container_id) child_container_list.append(child_container_id) child_container_name_list.append(child_container['name']) child_container_url = phantom.build_phantom_rest_url( 'container', child_container_id) ## Update container name with parent relationship if not "[Parent:" in child_container['name']: update_data = { 'name': "[Parent: {0}] {1}".format(container['id'], child_container['name']) } phantom.update(child_container, update_data) ## Gather and add notes ## for note in phantom.get_notes(container=child_container_id): # Avoid copying any notes related to the merge process. if note['success'] and not note['data']['title'] in ( '[Auto-Generated] Related Containers', '[Auto-Generated] Parent Container', '[Auto-Generated] Child Containers'): phantom.add_note(container=container['id'], note_type='general', note_format=note['data']['note_format'], title="[From Event {0}] {1}".format( note['data']['container'], note['data']['title']), content=note['data']['content']) ## Copy information and add to case data = { 'add_to_case': True, 'container_id': child_container_id, 'copy_artifacts': True, 'copy_automation': True, 'copy_files': True, 'copy_comments': True } phantom.requests.post(container_url, json=data, verify=False) ## Leave a note with a link to the parent container phantom.debug( "Adding parent relationship note to child container '{}'".format( child_container_id)) data_row = "{0} | [{1}]({2}/mission/{0}) |".format( container['id'], container['name'], phantom.get_base_url()) phantom.add_note( container=child_container_id, note_type="general", note_format="markdown", title="[Auto-Generated] Parent Container", content="| Container_ID | Container_Name |\n| --- | --- |\n| {}". format(data_row)) ## Mark child container as evidence in target_container data = { "container_id": container['id'], "object_id": child_container_id, "content_type": "container" } evidence_url = phantom.build_phantom_rest_url('evidence') response = phantom.requests.post(evidence_url, json=data, verify=False).json() ## Close child container if isinstance(close_containers, str) and close_containers.lower() == 'true': phantom.set_status(container=child_container_id, status="closed") ### End child container processing ### ## Format and add note for link back to child_containers in parent_container note_title = "[Auto-Generated] Child Containers" note_format = "markdown" format_list = [] # Build new note for child_container_id, child_container_name in zip( child_container_list, child_container_name_list): format_list.append("| {0} | [{1}]({2}/mission/{0}) |\n".format( child_container_id, child_container_name, phantom.get_base_url())) # Fetch any previous merge note params = { '_filter_container': '"{}"'.format(container['id']), '_filter_title': '"[Auto-Generated] Child Containers"' } note_url = phantom.build_phantom_rest_url('note') response_data = phantom.requests.get(note_url, verify=False).json() # If an old note was found, proceed to overwrite it if response_data['count'] > 0: note_item = response_data['data'][0] note_content = note_item['content'] # Append new information to existing note for c_note in format_list: note_content += c_note data = { "note_type": "general", "title": note_title, "content": note_content, "note_format": note_format } # Overwrite note response_data = phantom.requests.post(note_url + "/{}".format(note_item['id']), json=data, verify=False).json() # If no old note was found, add new with header else: template = "| Container ID | Container Name |\n| --- | --- |\n" for c_note in format_list: template += c_note success, message, process_container_merge__note_id = phantom.add_note( container=container, note_type="general", title=note_title, content=template, note_format=note_format) # Return a JSON-serializable object assert json.dumps( outputs ) # Will raise an exception if the :outputs: object is not JSON-serializable return outputs
def container_update(container_input=None, name=None, description=None, label=None, owner=None, sensitivity=None, severity=None, status=None, tags=None, input_json=None, **kwargs): """ Allows updating various attributes of a container in a single custom function. Any attributes of a container not listed can be updated via the input_json parameter. Args: container_input (CEF type: phantom container id): Supports a container id or container dictionary name: Optional parameter to change container name description: Optional parameter to change the container description label (CEF type: phantom container label): Optional parameter to change the container label owner: Optional parameter to change the container owner. Accepts a username or role name or keyword "current" to set the currently running playbook user as the owner. sensitivity: Optional parameter to change the container sensitivity. severity: Optional parameter to change the container severity. status: Optional parameter to change the container status. tags: Optional parameter to change the container tags. Must be in the format of a comma separated list. input_json: Optional parameter to modify any extra attributes of a container. Input_json will be merged with other inputs. In the event of a conflict, input_json will take precedence. Returns a JSON-serializable object that implements the configured data paths: """ ############################ Custom Code Goes Below This Line ################################# import json import phantom.rules as phantom outputs = {} update_dict = {} if isinstance(container_input, int): container = phantom.get_container(container_input) elif isinstance(container_input, dict): container = container_input else: raise TypeError("container_input is neither a int or a dictionary") if name: update_dict['name'] = name if description: update_dict['description'] = description if label: update_dict['label'] = label if owner: # If keyword 'current' entered then translate effective_user id to a username if owner.lower() == 'current': update_dict['owner_id'] = phantom.get_effective_user() else: # Attempt to translate name to owner_id url = phantom.build_phantom_rest_url( 'ph_user') + f'?_filter_username="******"' data = phantom.requests.get(url, verify=False).json().get('data') if data and len(data) == 1: update_dict['owner_id'] = data[0]['id'] elif data and len(data) > 1: phantom.error(f'Multiple matches for owner "{owner}"') else: # Attempt to translate name to role_id url = phantom.build_phantom_rest_url( 'role') + f'?_filter_name="{owner}"' data = phantom.requests.get(url, verify=False).json().get('data') if data and len(data) == 1: update_dict['role_id'] = data[0]['id'] elif data and len(data) > 1: phantom.error(f'Multiple matches for role "{owner}"') else: phantom.error(f'"{owner}" is not a valid username or role') if sensitivity: update_dict['sensitivity'] = sensitivity if severity: update_dict['severity'] = severity if status: update_dict['status'] = status if tags: tags = tags.replace(" ", "").split(",") update_dict['tags'] = tags if input_json: json_dict = json.loads(input_json) # Merge dictionaries together. The second argument, "**json_dict" will take precedence and overwrite any duplicate parameters. update_dict = {**update_dict, **json_dict} if update_dict: phantom.debug( 'Updating container {0} with the following information: "{1}"'. format(container['id'], update_dict)) phantom.update(container, update_dict) else: phantom.debug( "Valid container entered but no valid container changes provided.") # Return a JSON-serializable object assert json.dumps( outputs ) # Will raise an exception if the :outputs: object is not JSON-serializable return outputs