From b41ac8773d7be3b9d4e25c8a37f8d9e8ce02a5db Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Sun, 2 Apr 2023 10:06:16 -0500 Subject: [PATCH 1/6] Splunk Identifier Activity Analysis --- .../Splunk_Identifier_Activity_Analysis.json | 1057 +++++++++++++++++ .../Splunk_Identifier_Activity_Analysis.png | Bin 0 -> 85004 bytes .../Splunk_Identifier_Activity_Analysis.py | 766 ++++++++++++ .../Splunk_Identifier_Activity_Analysis.yml | 24 + 4 files changed, 1847 insertions(+) create mode 100644 playbooks/Splunk_Identifier_Activity_Analysis.json create mode 100644 playbooks/Splunk_Identifier_Activity_Analysis.png create mode 100644 playbooks/Splunk_Identifier_Activity_Analysis.py create mode 100644 playbooks/Splunk_Identifier_Activity_Analysis.yml diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.json b/playbooks/Splunk_Identifier_Activity_Analysis.json new file mode 100644 index 0000000000..6db337db50 --- /dev/null +++ b/playbooks/Splunk_Identifier_Activity_Analysis.json @@ -0,0 +1,1057 @@ +{ + "blockly": false, + "blockly_xml": "", + "category": "Identifier Activity Analysis", + "coa": { + "data": { + "description": "Accepts a file_hash, domain name, URL, or IP Address, and asks Splunk for a list of devices that have interacted with each. It then produces a normalized output and summary table.", + "edges": [ + { + "id": "port_0_to_port_2", + "sourceNode": "0", + "sourcePort": "0_out", + "targetNode": "2", + "targetPort": "2_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_2_to_port_3", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "3", + "targetPort": "3_in" + }, + { + "conditions": [ + { + "index": 1 + } + ], + "id": "port_2_to_port_4", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "4", + "targetPort": "4_in" + }, + { + "conditions": [ + { + "index": 2 + } + ], + "id": "port_2_to_port_5", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "5", + "targetPort": "5_in" + }, + { + "conditions": [ + { + "index": 3 + } + ], + "id": "port_2_to_port_6", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "6", + "targetPort": "6_in" + }, + { + "id": "port_3_to_port_8", + "sourceNode": "3", + "sourcePort": "3_out", + "targetNode": "8", + "targetPort": "8_in" + }, + { + "id": "port_4_to_port_9", + "sourceNode": "4", + "sourcePort": "4_out", + "targetNode": "9", + "targetPort": "9_in" + }, + { + "id": "port_5_to_port_10", + "sourceNode": "5", + "sourcePort": "5_out", + "targetNode": "10", + "targetPort": "10_in" + }, + { + "id": "port_6_to_port_11", + "sourceNode": "6", + "sourcePort": "6_out", + "targetNode": "11", + "targetPort": "11_in" + }, + { + "id": "port_8_to_port_12", + "sourceNode": "8", + "sourcePort": "8_out", + "targetNode": "12", + "targetPort": "12_in" + }, + { + "id": "port_9_to_port_13", + "sourceNode": "9", + "sourcePort": "9_out", + "targetNode": "13", + "targetPort": "13_in" + }, + { + "id": "port_10_to_port_14", + "sourceNode": "10", + "sourcePort": "10_out", + "targetNode": "14", + "targetPort": "14_in" + }, + { + "id": "port_11_to_port_15", + "sourceNode": "11", + "sourcePort": "11_out", + "targetNode": "15", + "targetPort": "15_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_12_to_port_16", + "sourceNode": "12", + "sourcePort": "12_out", + "targetNode": "16", + "targetPort": "16_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_13_to_port_17", + "sourceNode": "13", + "sourcePort": "13_out", + "targetNode": "17", + "targetPort": "17_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_14_to_port_18", + "sourceNode": "14", + "sourcePort": "14_out", + "targetNode": "18", + "targetPort": "18_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_15_to_port_19", + "sourceNode": "15", + "sourcePort": "15_out", + "targetNode": "19", + "targetPort": "19_in" + }, + { + "id": "port_16_to_port_20", + "sourceNode": "16", + "sourcePort": "16_out", + "targetNode": "20", + "targetPort": "20_in" + }, + { + "id": "port_17_to_port_21", + "sourceNode": "17", + "sourcePort": "17_out", + "targetNode": "21", + "targetPort": "21_in" + }, + { + "id": "port_18_to_port_22", + "sourceNode": "18", + "sourcePort": "18_out", + "targetNode": "22", + "targetPort": "22_in" + }, + { + "id": "port_19_to_port_23", + "sourceNode": "19", + "sourcePort": "19_out", + "targetNode": "23", + "targetPort": "23_in" + }, + { + "id": "port_20_to_port_1", + "sourceNode": "20", + "sourcePort": "20_out", + "targetNode": "1", + "targetPort": "1_in" + }, + { + "id": "port_21_to_port_1", + "sourceNode": "21", + "sourcePort": "21_out", + "targetNode": "1", + "targetPort": "1_in" + }, + { + "id": "port_22_to_port_1", + "sourceNode": "22", + "sourcePort": "22_out", + "targetNode": "1", + "targetPort": "1_in" + }, + { + "id": "port_23_to_port_1", + "sourceNode": "23", + "sourcePort": "23_out", + "targetNode": "1", + "targetPort": "1_in" + } + ], + "hash": "f27290aa40261ee9ee5b1dbeb45c0f71c072d880", + "nodes": { + "0": { + "data": { + "advanced": { + "join": [] + }, + "functionName": "on_start", + "id": "0", + "type": "start" + }, + "errors": {}, + "id": "0", + "type": "start", + "warnings": {}, + "x": 530, + "y": -1.2789769243681803e-13 + }, + "1": { + "data": { + "advanced": { + "join": [] + }, + "functionName": "on_finish", + "id": "1", + "type": "end" + }, + "errors": {}, + "id": "1", + "type": "end", + "warnings": {}, + "x": 540, + "y": 1100 + }, + "10": { + "data": { + "action": "run query", + "actionType": "investigate", + "advanced": { + "customName": "run domain query", + "customNameId": 0, + "join": [] + }, + "connector": "Splunk", + "connectorConfigs": [ + "splunk" + ], + "connectorId": "91883aa8-9c81-470b-97a1-5d8f7995f560", + "connectorVersion": "v1", + "functionId": 3, + "functionName": "run_domain_query", + "id": "10", + "parameters": { + "command": "tstats", + "query": "build_domain_query:formatted_data", + "search_mode": "smart" + }, + "requiredParameters": [ + { + "data_type": "string", + "field": "query" + }, + { + "data_type": "string", + "default": "search", + "field": "command" + }, + { + "data_type": "string", + "default": "smart", + "field": "search_mode" + } + ], + "type": "action" + }, + "errors": {}, + "id": "10", + "type": "action", + "warnings": {}, + "x": 680, + "y": 480 + }, + "11": { + "data": { + "action": "run query", + "actionType": "investigate", + "advanced": { + "customName": "run IP query", + "customNameId": 0, + "join": [] + }, + "connector": "Splunk", + "connectorConfigs": [ + "splunk" + ], + "connectorId": "91883aa8-9c81-470b-97a1-5d8f7995f560", + "connectorVersion": "v1", + "functionId": 4, + "functionName": "run_ip_query", + "id": "11", + "parameters": { + "command": "tstats", + "display": "src, src_asset_id, src_dns, src_ip", + "query": "build_ip_query:formatted_data", + "search_mode": "smart" + }, + "requiredParameters": [ + { + "data_type": "string", + "field": "query" + }, + { + "data_type": "string", + "default": "search", + "field": "command" + }, + { + "data_type": "string", + "default": "smart", + "field": "search_mode" + } + ], + "type": "action" + }, + "errors": {}, + "id": "11", + "type": "action", + "warnings": {}, + "x": 1020, + "y": 480 + }, + "12": { + "data": { + "advanced": { + "customName": "filter URL query", + "customNameId": 0, + "join": [] + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": ">", + "param": "run_url_query:action_result.summary.total_events", + "value": "0" + } + ], + "conditionIndex": 0, + "customName": "Results", + "logic": "and" + } + ], + "functionId": 2, + "functionName": "filter_url_query", + "id": "12", + "type": "filter" + }, + "errors": {}, + "id": "12", + "type": "filter", + "warnings": {}, + "x": 60, + "y": 624 + }, + "13": { + "data": { + "advanced": { + "customName": "filter file query", + "customNameId": 0, + "join": [] + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": ">", + "param": "run_file_query:action_result.summary.total_events", + "value": "0" + } + ], + "conditionIndex": 0, + "customName": "Results", + "logic": "and" + } + ], + "functionId": 3, + "functionName": "filter_file_query", + "id": "13", + "type": "filter" + }, + "errors": {}, + "id": "13", + "type": "filter", + "warnings": {}, + "x": 400, + "y": 624 + }, + "14": { + "data": { + "advanced": { + "customName": "filter domain query", + "customNameId": 0, + "join": [] + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": ">", + "param": "run_domain_query:action_result.summary.total_events", + "value": "0" + } + ], + "conditionIndex": 0, + "customName": "Results", + "logic": "and" + } + ], + "functionId": 4, + "functionName": "filter_domain_query", + "id": "14", + "type": "filter" + }, + "errors": {}, + "id": "14", + "type": "filter", + "warnings": {}, + "x": 740, + "y": 624 + }, + "15": { + "data": { + "advanced": { + "customName": "filter IP query", + "customNameId": 0, + "join": [] + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": ">", + "param": "run_ip_query:action_result.summary.{summaryVar}", + "value": "0" + } + ], + "conditionIndex": 0, + "customName": "Results", + "logic": "and" + } + ], + "functionId": 5, + "functionName": "filter_ip_query", + "id": "15", + "type": "filter" + }, + "errors": {}, + "id": "15", + "type": "filter", + "warnings": {}, + "x": 1080, + "y": 620 + }, + "16": { + "data": { + "advanced": { + "customName": "format URL report", + "customNameId": 0, + "join": [] + }, + "functionId": 5, + "functionName": "format_url_report", + "id": "16", + "parameters": [ + "filtered-data:filter_1:condition_1:playbook_input:url", + "run_url_query:action_result.data.*.src_dns", + "run_url_query:action_result.data.*.src_ip", + "run_url_query:action_result.data.*.src_asset_id" + ], + "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| URL | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", + "type": "format" + }, + "errors": {}, + "id": "16", + "type": "format", + "warnings": {}, + "x": 0, + "y": 800 + }, + "17": { + "data": { + "advanced": { + "customName": "format file report", + "customNameId": 0, + "join": [] + }, + "functionId": 6, + "functionName": "format_file_report", + "id": "17", + "parameters": [ + "filtered-data:filter_1:condition_2:playbook_input:file", + "run_file_query:action_result.data.*.dest_dns", + "run_file_query:action_result.data.*.dest_ip", + "run_file_query:action_result.data.*.dest_asset_id" + ], + "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| File | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", + "type": "format" + }, + "errors": {}, + "id": "17", + "type": "format", + "warnings": {}, + "x": 340, + "y": 800 + }, + "18": { + "data": { + "advanced": { + "customName": "format domain report", + "customNameId": 0, + "join": [] + }, + "functionId": 7, + "functionName": "format_domain_report", + "id": "18", + "parameters": [ + "filtered-data:filter_1:condition_3:playbook_input:domain", + "run_domain_query:action_result.data.*.src_dns", + "run_domain_query:action_result.data.*.src_ip", + "run_domain_query:action_result.data.*.src_asset_id" + ], + "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| Domain | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", + "type": "format" + }, + "errors": {}, + "id": "18", + "type": "format", + "warnings": {}, + "x": 680, + "y": 800 + }, + "19": { + "data": { + "advanced": { + "customName": "format ip report", + "customNameId": 0, + "join": [] + }, + "functionId": 8, + "functionName": "format_ip_report", + "id": "19", + "parameters": [ + "filtered-data:filter_1:condition_4:playbook_input:ip", + "run_ip_query:action_result.data.*.src_dns", + "run_ip_query:action_result.data.*.src_ip", + "run_ip_query:action_result.data.*.src_asset_id" + ], + "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| IP | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", + "type": "format" + }, + "errors": {}, + "id": "19", + "type": "format", + "warnings": {}, + "x": 1020, + "y": 800 + }, + "2": { + "data": { + "advanced": { + "join": [] + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": "!=", + "param": "playbook_input:url", + "value": "None" + } + ], + "conditionIndex": 0, + "customName": "url", + "logic": "and" + }, + { + "comparisons": [ + { + "conditionIndex": 1, + "op": "!=", + "param": "playbook_input:file", + "value": "None" + } + ], + "conditionIndex": 1, + "customName": "file", + "logic": "and" + }, + { + "comparisons": [ + { + "conditionIndex": 2, + "op": "!=", + "param": "playbook_input:domain", + "value": "None" + } + ], + "conditionIndex": 2, + "customName": "domain", + "logic": "and" + }, + { + "comparisons": [ + { + "conditionIndex": 3, + "op": "!=", + "param": "playbook_input:ip", + "value": "None" + } + ], + "conditionIndex": 3, + "customName": "ip", + "logic": "and" + } + ], + "functionId": 1, + "functionName": "filter_1", + "id": "2", + "type": "filter" + }, + "errors": {}, + "id": "2", + "type": "filter", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, + "x": 570, + "y": 148 + }, + "20": { + "data": { + "advanced": { + "customName": "build URL output", + "customNameId": 0, + "join": [] + }, + "functionId": 1, + "functionName": "build_url_output", + "id": "20", + "inputParameters": [ + "filtered-data:filter_1:condition_1:playbook_input:url", + "run_url_query:action_result.data.*.src_dns", + "run_url_query:action_result.data.*.src_ip", + "run_url_query:action_result.data.*.src_asset_id" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "20", + "type": "code", + "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", + "warnings": {}, + "x": 0, + "y": 960 + }, + "21": { + "data": { + "advanced": { + "customName": "build file output", + "customNameId": 0, + "join": [] + }, + "functionId": 2, + "functionName": "build_file_output", + "id": "21", + "inputParameters": [ + "filtered-data:filter_1:condition_2:playbook_input:file", + "run_file_query:action_result.data.*.dest_dns", + "run_file_query:action_result.data.*.dest_ip", + "run_file_query:action_result.data.*.dest_asset_id" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "21", + "type": "code", + "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", + "warnings": {}, + "x": 340, + "y": 960 + }, + "22": { + "data": { + "advanced": { + "customName": "build domain output", + "customNameId": 0, + "join": [] + }, + "functionId": 3, + "functionName": "build_domain_output", + "id": "22", + "inputParameters": [ + "filtered-data:filter_1:condition_3:playbook_input:domain", + "run_domain_query:action_result.data.*.src_dns", + "run_domain_query:action_result.data.*.src_ip", + "run_domain_query:action_result.data.*.src_asset_id" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "22", + "type": "code", + "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", + "warnings": {}, + "x": 680, + "y": 960 + }, + "23": { + "data": { + "advanced": { + "customName": "build ip output", + "customNameId": 0, + "join": [] + }, + "functionId": 4, + "functionName": "build_ip_output", + "id": "23", + "inputParameters": [ + "filtered-data:filter_1:condition_4:playbook_input:ip", + "run_ip_query:action_result.data.*.src_dns", + "run_ip_query:action_result.data.*.src_ip", + "run_ip_query:action_result.data.*.src_asset_id" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "23", + "type": "code", + "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", + "warnings": {}, + "x": 1020, + "y": 960 + }, + "3": { + "data": { + "advanced": { + "customName": "build URL query", + "customNameId": 0, + "join": [] + }, + "functionId": 1, + "functionName": "build_url_query", + "id": "3", + "parameters": [ + "filtered-data:filter_1:condition_1:playbook_input:url" + ], + "template": "| tstats count from datamodel=Web.Web where Web.url={0} by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "type": "format" + }, + "errors": {}, + "id": "3", + "type": "format", + "warnings": {}, + "x": 0, + "y": 320 + }, + "4": { + "data": { + "advanced": { + "customName": "build file query", + "customNameId": 0, + "join": [] + }, + "functionId": 2, + "functionName": "build_file_query", + "id": "4", + "parameters": [ + "filtered-data:filter_1:condition_2:playbook_input:file" + ], + "template": "| tstats count from Endpoint.Filesystem where Filesystem.file_hash={0} by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip", + "type": "format" + }, + "errors": {}, + "id": "4", + "type": "format", + "warnings": {}, + "x": 340, + "y": 328 + }, + "5": { + "data": { + "advanced": { + "customName": "build domain query", + "customNameId": 0, + "join": [] + }, + "functionId": 3, + "functionName": "build_domain_query", + "id": "5", + "parameters": [ + "filtered-data:filter_1:condition_3:playbook_input:domain" + ], + "template": "| tstats count from datamodel=Network_Resolution where DNS.query IN (\"*.{0}\", \"{0}\") by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "type": "format" + }, + "errors": {}, + "id": "5", + "type": "format", + "warnings": {}, + "x": 680, + "y": 320 + }, + "6": { + "data": { + "advanced": { + "customName": "build IP query", + "customNameId": 0, + "join": [] + }, + "functionId": 4, + "functionName": "build_ip_query", + "id": "6", + "parameters": [ + "filtered-data:filter_1:condition_4:playbook_input:ip" + ], + "template": "| tstats count from datamodel=Network_Traffic where All_Traffic.direction=\"outbound\" All_Traffic.dest_ip={0} by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "type": "format" + }, + "errors": {}, + "id": "6", + "type": "format", + "warnings": {}, + "x": 1020, + "y": 328 + }, + "8": { + "data": { + "action": "run query", + "actionType": "investigate", + "advanced": { + "customName": "run URL query", + "customNameId": 0, + "join": [] + }, + "connector": "Splunk", + "connectorConfigs": [ + "splunk" + ], + "connectorId": "91883aa8-9c81-470b-97a1-5d8f7995f560", + "connectorVersion": "v1", + "functionId": 1, + "functionName": "run_url_query", + "id": "8", + "parameters": { + "command": "tstats", + "query": "build_url_query:formatted_data", + "search_mode": "smart" + }, + "requiredParameters": [ + { + "data_type": "string", + "field": "query" + }, + { + "data_type": "string", + "default": "search", + "field": "command" + }, + { + "data_type": "string", + "default": "smart", + "field": "search_mode" + } + ], + "type": "action" + }, + "errors": {}, + "id": "8", + "type": "action", + "warnings": {}, + "x": 0, + "y": 476 + }, + "9": { + "data": { + "action": "run query", + "actionType": "investigate", + "advanced": { + "customName": "run file query", + "customNameId": 0, + "join": [] + }, + "connector": "Splunk", + "connectorConfigs": [ + "splunk" + ], + "connectorId": "91883aa8-9c81-470b-97a1-5d8f7995f560", + "connectorVersion": "v1", + "functionId": 2, + "functionName": "run_file_query", + "id": "9", + "parameters": { + "command": "tstats", + "query": "build_file_query:formatted_data", + "search_mode": "smart" + }, + "requiredParameters": [ + { + "data_type": "string", + "field": "query" + }, + { + "data_type": "string", + "default": "search", + "field": "command" + }, + { + "data_type": "string", + "default": "smart", + "field": "search_mode" + } + ], + "type": "action" + }, + "errors": {}, + "id": "9", + "type": "action", + "warnings": {}, + "x": 340, + "y": 480 + } + }, + "notes": "Inputs: file_hash, domain, url, ip_address\nInteractions: Splunk\nActions: run_query\nOutputs: observable, markdown report" + }, + "input_spec": [ + { + "contains": [ + "url" + ], + "description": "", + "name": "url" + }, + { + "contains": [ + "hash", + "sha1", + "sha256", + "sha512" + ], + "description": "", + "name": "file" + }, + { + "contains": [ + "domain" + ], + "description": "", + "name": "domain" + }, + { + "contains": [ + "ip" + ], + "description": "", + "name": "ip" + } + ], + "output_spec": [ + { + "contains": [], + "datapaths": [ + "build_url_output:custom_function:observable_array", + "build_domain_output:custom_function:observable_array", + "build_ip_output:custom_function:observable_array", + "build_file_output:custom_function:observable_array" + ], + "deduplicate": false, + "description": "", + "metadata": {}, + "name": "observable" + }, + { + "contains": [], + "datapaths": [ + "format_url_report:formatted_data", + "format_file_report:formatted_data", + "format_domain_report:formatted_data", + "format_ip_report:formatted_data" + ], + "deduplicate": false, + "description": "", + "metadata": {}, + "name": "markdown_report" + } + ], + "playbook_type": "data", + "python_version": "3", + "schema": "5.0.9", + "version": "6.0.0.114895" + }, + "create_time": "2023-04-01T21:05:04.325360+00:00", + "draft_mode": false, + "labels": [ + "*" + ], + "tags": [ + "domain", + "url", + "ip", + "file_hash", + "splunk", + "identifier_activity" + ] +} \ No newline at end of file diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.png b/playbooks/Splunk_Identifier_Activity_Analysis.png new file mode 100644 index 0000000000000000000000000000000000000000..79f7ee5c26c49b5ecc5497d6b495b8bf32b26d5c GIT binary patch literal 85004 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 z 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 literal 0 HcmV?d00001 diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.py b/playbooks/Splunk_Identifier_Activity_Analysis.py new file mode 100644 index 0000000000..6b7a9432f6 --- /dev/null +++ b/playbooks/Splunk_Identifier_Activity_Analysis.py @@ -0,0 +1,766 @@ +""" +Accepts a file_hash, domain name, URL, or IP Address, and asks Splunk for a list of devices that have interacted with each. It then produces a normalized output and summary table. +""" + + +import phantom.rules as phantom +import json +from datetime import datetime, timedelta + + +@phantom.playbook_block() +def on_start(container): + phantom.debug('on_start() called') + + # call 'filter_1' block + filter_1(container=container) + + return + +@phantom.playbook_block() +def filter_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("filter_1() called") + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["playbook_input:url", "!=", None] + ], + name="filter_1:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + build_url_query(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + + # collect filtered artifact ids and results for 'if' condition 2 + matched_artifacts_2, matched_results_2 = phantom.condition( + container=container, + conditions=[ + ["playbook_input:file", "!=", None] + ], + name="filter_1:condition_2") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_2 or matched_results_2: + build_file_query(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_2, filtered_results=matched_results_2) + + # collect filtered artifact ids and results for 'if' condition 3 + matched_artifacts_3, matched_results_3 = phantom.condition( + container=container, + conditions=[ + ["playbook_input:domain", "!=", None] + ], + name="filter_1:condition_3") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_3 or matched_results_3: + build_domain_query(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_3, filtered_results=matched_results_3) + + # collect filtered artifact ids and results for 'if' condition 4 + matched_artifacts_4, matched_results_4 = phantom.condition( + container=container, + conditions=[ + ["playbook_input:ip", "!=", None] + ], + name="filter_1:condition_4") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_4 or matched_results_4: + build_ip_query(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_4, filtered_results=matched_results_4) + + return + + +@phantom.playbook_block() +def build_url_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_url_query() called") + + template = """| tstats count from datamodel=Web.Web where Web.url={0} by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_1:playbook_input:url" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="build_url_query") + + run_url_query(container=container) + + return + + +@phantom.playbook_block() +def build_file_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_file_query() called") + + template = """| tstats count from Endpoint.Filesystem where Filesystem.file_hash={0} by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_2:playbook_input:file" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="build_file_query") + + run_file_query(container=container) + + return + + +@phantom.playbook_block() +def build_domain_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_domain_query() called") + + template = """| tstats count from datamodel=Network_Resolution where DNS.query IN (\"*.{0}\", \"{0}\") by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_3:playbook_input:domain" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="build_domain_query") + + run_domain_query(container=container) + + return + + +@phantom.playbook_block() +def build_ip_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_ip_query() called") + + template = """| tstats count from datamodel=Network_Traffic where All_Traffic.direction=\"outbound\" All_Traffic.dest_ip={0} by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_4:playbook_input:ip" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="build_ip_query") + + run_ip_query(container=container) + + return + + +@phantom.playbook_block() +def run_url_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("run_url_query() called") + + # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + + build_url_query = phantom.get_format_data(name="build_url_query") + + parameters = [] + + if build_url_query is not None: + parameters.append({ + "query": build_url_query, + "command": "tstats", + "search_mode": "smart", + }) + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.act("run query", parameters=parameters, name="run_url_query", assets=["splunk"], callback=filter_url_query) + + return + + +@phantom.playbook_block() +def run_file_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("run_file_query() called") + + # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + + build_file_query = phantom.get_format_data(name="build_file_query") + + parameters = [] + + if build_file_query is not None: + parameters.append({ + "query": build_file_query, + "command": "tstats", + "search_mode": "smart", + }) + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.act("run query", parameters=parameters, name="run_file_query", assets=["splunk"], callback=filter_file_query) + + return + + +@phantom.playbook_block() +def run_domain_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("run_domain_query() called") + + # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + + build_domain_query = phantom.get_format_data(name="build_domain_query") + + parameters = [] + + if build_domain_query is not None: + parameters.append({ + "query": build_domain_query, + "command": "tstats", + "search_mode": "smart", + }) + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.act("run query", parameters=parameters, name="run_domain_query", assets=["splunk"], callback=filter_domain_query) + + return + + +@phantom.playbook_block() +def run_ip_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("run_ip_query() called") + + # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + + build_ip_query = phantom.get_format_data(name="build_ip_query") + + parameters = [] + + if build_ip_query is not None: + parameters.append({ + "query": build_ip_query, + "command": "tstats", + "search_mode": "smart", + "display": "src, src_asset_id, src_dns, src_ip", + }) + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.act("run query", parameters=parameters, name="run_ip_query", assets=["splunk"], callback=filter_ip_query) + + return + + +@phantom.playbook_block() +def filter_url_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("filter_url_query() called") + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["run_url_query:action_result.summary.total_events", ">", 0] + ], + name="filter_url_query:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + format_url_report(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + + return + + +@phantom.playbook_block() +def filter_file_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("filter_file_query() called") + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["run_file_query:action_result.summary.total_events", ">", 0] + ], + name="filter_file_query:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + format_file_report(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + + return + + +@phantom.playbook_block() +def filter_domain_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("filter_domain_query() called") + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["run_domain_query:action_result.summary.total_events", ">", 0] + ], + name="filter_domain_query:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + format_domain_report(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + + return + + +@phantom.playbook_block() +def filter_ip_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("filter_ip_query() called") + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["run_ip_query:action_result.summary.{summaryVar}", ">", 0] + ], + name="filter_ip_query:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + format_ip_report(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + + return + + +@phantom.playbook_block() +def format_url_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("format_url_report() called") + + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| URL | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_1:playbook_input:url", + "run_url_query:action_result.data.*.src_dns", + "run_url_query:action_result.data.*.src_ip", + "run_url_query:action_result.data.*.src_asset_id" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="format_url_report") + + build_url_output(container=container) + + return + + +@phantom.playbook_block() +def format_file_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("format_file_report() called") + + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| File | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_2:playbook_input:file", + "run_file_query:action_result.data.*.dest_dns", + "run_file_query:action_result.data.*.dest_ip", + "run_file_query:action_result.data.*.dest_asset_id" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="format_file_report") + + build_file_output(container=container) + + return + + +@phantom.playbook_block() +def format_domain_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("format_domain_report() called") + + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| Domain | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_3:playbook_input:domain", + "run_domain_query:action_result.data.*.src_dns", + "run_domain_query:action_result.data.*.src_ip", + "run_domain_query:action_result.data.*.src_asset_id" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="format_domain_report") + + build_domain_output(container=container) + + return + + +@phantom.playbook_block() +def format_ip_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("format_ip_report() called") + + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| IP | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:filter_1:condition_4:playbook_input:ip", + "run_ip_query:action_result.data.*.src_dns", + "run_ip_query:action_result.data.*.src_ip", + "run_ip_query:action_result.data.*.src_asset_id" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="format_ip_report") + + build_ip_output(container=container) + + return + + +@phantom.playbook_block() +def build_url_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_url_output() called") + + filtered_input_0_url = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:playbook_input:url"]) + run_url_query_result_data = phantom.collect2(container=container, datapath=["run_url_query:action_result.data.*.src_dns","run_url_query:action_result.data.*.src_ip","run_url_query:action_result.data.*.src_asset_id"], action_results=results) + + filtered_input_0_url_values = [item[0] for item in filtered_input_0_url] + run_url_query_result_item_0 = [item[0] for item in run_url_query_result_data] + run_url_query_result_item_1 = [item[1] for item in run_url_query_result_data] + run_url_query_result_item_2 = [item[2] for item in run_url_query_result_data] + + build_url_output__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Init variables + convenience naming + build_url_output__observable_array = [] + device_list = [] + indicator = filtered_input_0_url_values + + # Build device list + for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2): + device = { + "name": dns, + "id": asset_id, + "ip_address": ip, + "operating_system": "Unknown" + } + device_list.append(device) + + # Build observable object + observable_array = { + "indicator": indicator, + "type": "url", + "total_count": len(device_list), + "source": "Splunk", + "identifier_activity": device_list + } + + # Send output + build_url_output__observable_array.append(observable_array) + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="build_url_output:observable_array", value=json.dumps(build_url_output__observable_array)) + + return + + +@phantom.playbook_block() +def build_file_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_file_output() called") + + filtered_input_0_file = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_2:playbook_input:file"]) + run_file_query_result_data = phantom.collect2(container=container, datapath=["run_file_query:action_result.data.*.dest_dns","run_file_query:action_result.data.*.dest_ip","run_file_query:action_result.data.*.dest_asset_id"], action_results=results) + + filtered_input_0_file_values = [item[0] for item in filtered_input_0_file] + run_file_query_result_item_0 = [item[0] for item in run_file_query_result_data] + run_file_query_result_item_1 = [item[1] for item in run_file_query_result_data] + run_file_query_result_item_2 = [item[2] for item in run_file_query_result_data] + + build_file_output__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + + + # Init variables + convenience naming + build_file_output__observable_array = [] + device_list = [] + indicator = filtered_input_0_file_values + + # Build device list + for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2): + device = { + "name": dns, + "id": asset_id, + "ip_address": ip, + "operating_system": "Unknown" + } + device_list.append(device) + + # Build observable object + observable_array = { + "indicator": indicator, + "type": "file_hash", + "total_count": len(device_list), + "source": "Splunk", + "identifier_activity": device_list + } + + # Send output + build_file_output__observable_array.append(observable_array) + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="build_file_output:observable_array", value=json.dumps(build_file_output__observable_array)) + + return + + +@phantom.playbook_block() +def build_domain_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_domain_output() called") + + filtered_input_0_domain = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_3:playbook_input:domain"]) + run_domain_query_result_data = phantom.collect2(container=container, datapath=["run_domain_query:action_result.data.*.src_dns","run_domain_query:action_result.data.*.src_ip","run_domain_query:action_result.data.*.src_asset_id"], action_results=results) + + filtered_input_0_domain_values = [item[0] for item in filtered_input_0_domain] + run_domain_query_result_item_0 = [item[0] for item in run_domain_query_result_data] + run_domain_query_result_item_1 = [item[1] for item in run_domain_query_result_data] + run_domain_query_result_item_2 = [item[2] for item in run_domain_query_result_data] + + build_domain_output__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Init variables + convenience naming + build_domain_output__observable_array = [] + device_list = [] + indicator = filtered_input_0_domain_values + + # Build device list + for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2): + device = { + "name": dns, + "id": asset_id, + "ip_address": ip, + "operating_system": "Unknown" + } + device_list.append(device) + + # Build observable object + observable_array = { + "indicator": indicator, + "type": "domain", + "total_count": len(device_list), + "source": "Splunk", + "identifier_activity": device_list + } + + # Send output + build_domain_output__observable_array.append(observable_array) + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="build_domain_output:observable_array", value=json.dumps(build_domain_output__observable_array)) + + return + + +@phantom.playbook_block() +def build_ip_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_ip_output() called") + + filtered_input_0_ip = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_4:playbook_input:ip"]) + run_ip_query_result_data = phantom.collect2(container=container, datapath=["run_ip_query:action_result.data.*.src_dns","run_ip_query:action_result.data.*.src_ip","run_ip_query:action_result.data.*.src_asset_id"], action_results=results) + + filtered_input_0_ip_values = [item[0] for item in filtered_input_0_ip] + run_ip_query_result_item_0 = [item[0] for item in run_ip_query_result_data] + run_ip_query_result_item_1 = [item[1] for item in run_ip_query_result_data] + run_ip_query_result_item_2 = [item[2] for item in run_ip_query_result_data] + + build_ip_output__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Init variables + convenience naming + build_ip_output__observable_array = [] + device_list = [] + indicator = filtered_input_0_ip_values + + # Build device list + for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2): + device = { + "name": dns, + "id": asset_id, + "ip_address": ip, + "operating_system": "Unknown" + } + device_list.append(device) + + # Build observable object + observable_array = { + "indicator": indicator, + "type": "ip_address", + "total_count": len(device_list), + "source": "Splunk", + "identifier_activity": device_list + } + + # Send Output + build_ip_output__observable_array.append(observable_array) + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="build_ip_output:observable_array", value=json.dumps(build_ip_output__observable_array)) + + return + + +@phantom.playbook_block() +def on_finish(container, summary): + phantom.debug("on_finish() called") + + format_url_report = phantom.get_format_data(name="format_url_report") + format_file_report = phantom.get_format_data(name="format_file_report") + format_domain_report = phantom.get_format_data(name="format_domain_report") + format_ip_report = phantom.get_format_data(name="format_ip_report") + build_url_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_url_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + build_domain_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_domain_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + build_ip_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_ip_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + build_file_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_file_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + + observable_combined_value = phantom.concatenate(build_url_output__observable_array, build_domain_output__observable_array, build_ip_output__observable_array, build_file_output__observable_array) + markdown_report_combined_value = phantom.concatenate(format_url_report, format_file_report, format_domain_report, format_ip_report) + + output = { + "observable": observable_combined_value, + "markdown_report": markdown_report_combined_value, + } + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_playbook_output_data(output=output) + + return \ No newline at end of file diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.yml b/playbooks/Splunk_Identifier_Activity_Analysis.yml new file mode 100644 index 0000000000..5a10f2f521 --- /dev/null +++ b/playbooks/Splunk_Identifier_Activity_Analysis.yml @@ -0,0 +1,24 @@ +name: Splunk Identifier Activity Analysis +id: 5299d9dc-e9c4-46fa-da42-92ace0ff816d +version: 1 +date: '2023-03-31' +author: Lou Stella, Splunk +type: Investigation +description: "Accepts a file_hash, domain, IP address, or URL, and asks Splunk for a list of devices that have interacted with each. It then produces a normalized output and summary table." +playbook: Splunk_Identifier_Activity_Analysis +how_to_implement: This input playbook requires the Splunk connector to be configured. It is designed to work in conjunction with the Dynamic Identifier Activity Analysis playbook or other playbooks in the same style. +references: [] +app_list: + - Splunk +tags: + platform_tags: + - identifier_activity + - domain + - file_hash + - url + - ip_address + playbook_type: Input + vpe_type: Modern + playbook_fields: [] + product: + - Splunk SOAR From 5febdaad3a31155da8ea5d5c0d46490854ed109f Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Sun, 2 Apr 2023 11:12:58 -0500 Subject: [PATCH 2/6] Updated base query formatting --- .../Splunk_Identifier_Activity_Analysis.json | 18 +++++++++--------- .../Splunk_Identifier_Activity_Analysis.py | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.json b/playbooks/Splunk_Identifier_Activity_Analysis.json index 6db337db50..dc6cb77b24 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.json +++ b/playbooks/Splunk_Identifier_Activity_Analysis.json @@ -222,7 +222,7 @@ "targetPort": "1_in" } ], - "hash": "f27290aa40261ee9ee5b1dbeb45c0f71c072d880", + "hash": "c50aaf29ded7bb67271a3061db213de5fb0fa2e7", "nodes": { "0": { "data": { @@ -238,7 +238,7 @@ "type": "start", "warnings": {}, "x": 530, - "y": -1.2789769243681803e-13 + "y": -2.5579538487363607e-13 }, "1": { "data": { @@ -468,7 +468,7 @@ { "conditionIndex": 0, "op": ">", - "param": "run_ip_query:action_result.summary.{summaryVar}", + "param": "run_ip_query:action_result.summary.total_events", "value": "0" } ], @@ -797,7 +797,7 @@ "parameters": [ "filtered-data:filter_1:condition_1:playbook_input:url" ], - "template": "| tstats count from datamodel=Web.Web where Web.url={0} by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "template": "count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", "type": "format" }, "errors": {}, @@ -820,7 +820,7 @@ "parameters": [ "filtered-data:filter_1:condition_2:playbook_input:file" ], - "template": "| tstats count from Endpoint.Filesystem where Filesystem.file_hash={0} by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip", + "template": "count from Endpoint.Filesystem where Filesystem.file_hash=\"{0}\" by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip", "type": "format" }, "errors": {}, @@ -828,7 +828,7 @@ "type": "format", "warnings": {}, "x": 340, - "y": 328 + "y": 320 }, "5": { "data": { @@ -843,7 +843,7 @@ "parameters": [ "filtered-data:filter_1:condition_3:playbook_input:domain" ], - "template": "| tstats count from datamodel=Network_Resolution where DNS.query IN (\"*.{0}\", \"{0}\") by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "template": "count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", "type": "format" }, "errors": {}, @@ -866,7 +866,7 @@ "parameters": [ "filtered-data:filter_1:condition_4:playbook_input:ip" ], - "template": "| tstats count from datamodel=Network_Traffic where All_Traffic.direction=\"outbound\" All_Traffic.dest_ip={0} by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "template": "count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", "type": "format" }, "errors": {}, @@ -1041,7 +1041,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-01T21:05:04.325360+00:00", + "create_time": "2023-04-02T16:11:35.642611+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.py b/playbooks/Splunk_Identifier_Activity_Analysis.py index 6b7a9432f6..97caec8453 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.py +++ b/playbooks/Splunk_Identifier_Activity_Analysis.py @@ -76,7 +76,7 @@ def filter_1(action=None, success=None, container=None, results=None, handle=Non def build_url_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_url_query() called") - template = """| tstats count from datamodel=Web.Web where Web.url={0} by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + template = """count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" # parameter list for template variable replacement parameters = [ @@ -104,7 +104,7 @@ def build_url_query(action=None, success=None, container=None, results=None, han def build_file_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_file_query() called") - template = """| tstats count from Endpoint.Filesystem where Filesystem.file_hash={0} by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip""" + template = """count from Endpoint.Filesystem where Filesystem.file_hash=\"{0}\" by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip""" # parameter list for template variable replacement parameters = [ @@ -132,7 +132,7 @@ def build_file_query(action=None, success=None, container=None, results=None, ha def build_domain_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_domain_query() called") - template = """| tstats count from datamodel=Network_Resolution where DNS.query IN (\"*.{0}\", \"{0}\") by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + template = """count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" # parameter list for template variable replacement parameters = [ @@ -160,7 +160,7 @@ def build_domain_query(action=None, success=None, container=None, results=None, def build_ip_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_ip_query() called") - template = """| tstats count from datamodel=Network_Traffic where All_Traffic.direction=\"outbound\" All_Traffic.dest_ip={0} by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + template = """count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" # parameter list for template variable replacement parameters = [ @@ -294,8 +294,8 @@ def run_ip_query(action=None, success=None, container=None, results=None, handle parameters.append({ "query": build_ip_query, "command": "tstats", - "search_mode": "smart", "display": "src, src_asset_id, src_dns, src_ip", + "search_mode": "smart", }) ################################################################################ @@ -378,7 +378,7 @@ def filter_ip_query(action=None, success=None, container=None, results=None, han matched_artifacts_1, matched_results_1 = phantom.condition( container=container, conditions=[ - ["run_ip_query:action_result.summary.{summaryVar}", ">", 0] + ["run_ip_query:action_result.summary.total_events", ">", 0] ], name="filter_ip_query:condition_1") From 770e0373700d7329f007621734da4185f365d854 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Wed, 5 Apr 2023 12:35:36 -0500 Subject: [PATCH 3/6] Updated with new query for file hashes --- .../Splunk_Identifier_Activity_Analysis.json | 101 ++++++++++++------ .../Splunk_Identifier_Activity_Analysis.py | 74 ++++++++++++- 2 files changed, 138 insertions(+), 37 deletions(-) diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.json b/playbooks/Splunk_Identifier_Activity_Analysis.json index dc6cb77b24..ce84d84dcf 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.json +++ b/playbooks/Splunk_Identifier_Activity_Analysis.json @@ -222,7 +222,7 @@ "targetPort": "1_in" } ], - "hash": "c50aaf29ded7bb67271a3061db213de5fb0fa2e7", + "hash": "fce7af7ffe7b23a7bcac38ab930673217f6594ea", "nodes": { "0": { "data": { @@ -238,7 +238,7 @@ "type": "start", "warnings": {}, "x": 530, - "y": -2.5579538487363607e-13 + "y": -3.197442310920451e-13 }, "1": { "data": { @@ -263,7 +263,9 @@ "advanced": { "customName": "run domain query", "customNameId": 0, - "join": [] + "description": "May need to change search command dependent on your format block", + "join": [], + "note": "Splunk query execution" }, "connector": "Splunk", "connectorConfigs": [ @@ -311,7 +313,9 @@ "advanced": { "customName": "run IP query", "customNameId": 0, - "join": [] + "description": "May need to change search command dependent on your format block", + "join": [], + "note": "Splunk query execution" }, "connector": "Splunk", "connectorConfigs": [ @@ -358,7 +362,8 @@ "advanced": { "customName": "filter URL query", "customNameId": 0, - "join": [] + "join": [], + "note": "only proceed when there are results" }, "conditions": [ { @@ -392,7 +397,8 @@ "advanced": { "customName": "filter file query", "customNameId": 0, - "join": [] + "join": [], + "note": "only proceed when there are results" }, "conditions": [ { @@ -426,7 +432,8 @@ "advanced": { "customName": "filter domain query", "customNameId": 0, - "join": [] + "join": [], + "note": "only proceed when there are results" }, "conditions": [ { @@ -453,14 +460,15 @@ "type": "filter", "warnings": {}, "x": 740, - "y": 624 + "y": 620 }, "15": { "data": { "advanced": { "customName": "filter IP query", "customNameId": 0, - "join": [] + "join": [], + "note": "only proceed when there are results" }, "conditions": [ { @@ -494,7 +502,9 @@ "advanced": { "customName": "format URL report", "customNameId": 0, - "join": [] + "description": "Markdown report used in calling playbook", + "join": [], + "note": "Markdown report output" }, "functionId": 5, "functionName": "format_url_report", @@ -520,7 +530,9 @@ "advanced": { "customName": "format file report", "customNameId": 0, - "join": [] + "description": "Markdown report used in calling playbook", + "join": [], + "note": "Markdown report output" }, "functionId": 6, "functionName": "format_file_report", @@ -546,7 +558,9 @@ "advanced": { "customName": "format domain report", "customNameId": 0, - "join": [] + "description": "Markdown report used in calling playbook", + "join": [], + "note": "Markdown report output" }, "functionId": 7, "functionName": "format_domain_report", @@ -572,7 +586,9 @@ "advanced": { "customName": "format ip report", "customNameId": 0, - "join": [] + "description": "Markdown report used in calling playbook", + "join": [], + "note": "Markdown report output" }, "functionId": 8, "functionName": "format_ip_report", @@ -596,7 +612,8 @@ "2": { "data": { "advanced": { - "join": [] + "join": [], + "note": "Routing paths by indicator type" }, "conditions": [ { @@ -673,7 +690,9 @@ "advanced": { "customName": "build URL output", "customNameId": 0, - "join": [] + "description": "Observable object expected by calling playbook", + "join": [], + "note": "Observable output" }, "functionId": 1, "functionName": "build_url_output", @@ -692,7 +711,7 @@ "errors": {}, "id": "20", "type": "code", - "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", + "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 0, "y": 960 @@ -702,7 +721,9 @@ "advanced": { "customName": "build file output", "customNameId": 0, - "join": [] + "description": "Observable object expected by calling playbook", + "join": [], + "note": "Observable output" }, "functionId": 2, "functionName": "build_file_output", @@ -721,7 +742,7 @@ "errors": {}, "id": "21", "type": "code", - "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", + "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 340, "y": 960 @@ -731,7 +752,9 @@ "advanced": { "customName": "build domain output", "customNameId": 0, - "join": [] + "description": "Observable object expected by calling playbook", + "join": [], + "note": "Observable output" }, "functionId": 3, "functionName": "build_domain_output", @@ -750,7 +773,7 @@ "errors": {}, "id": "22", "type": "code", - "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", + "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 680, "y": 960 @@ -760,7 +783,9 @@ "advanced": { "customName": "build ip output", "customNameId": 0, - "join": [] + "description": "Observable object expected by calling playbook", + "join": [], + "note": "Observable output" }, "functionId": 4, "functionName": "build_ip_output", @@ -779,7 +804,7 @@ "errors": {}, "id": "23", "type": "code", - "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"indicator\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", + "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", "warnings": {}, "x": 1020, "y": 960 @@ -789,7 +814,9 @@ "advanced": { "customName": "build URL query", "customNameId": 0, - "join": [] + "description": "Query may need editing to reflect your splunk environment", + "join": [], + "note": "Splunk query template" }, "functionId": 1, "functionName": "build_url_query", @@ -812,7 +839,9 @@ "advanced": { "customName": "build file query", "customNameId": 0, - "join": [] + "description": "Query may need editing to reflect your splunk environment", + "join": [], + "note": "Splunk query template" }, "functionId": 2, "functionName": "build_file_query", @@ -820,7 +849,7 @@ "parameters": [ "filtered-data:filter_1:condition_2:playbook_input:file" ], - "template": "count from Endpoint.Filesystem where Filesystem.file_hash=\"{0}\" by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip", + "template": "count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip", "type": "format" }, "errors": {}, @@ -835,7 +864,9 @@ "advanced": { "customName": "build domain query", "customNameId": 0, - "join": [] + "description": "Query may need editing to reflect your splunk environment", + "join": [], + "note": "Splunk query template" }, "functionId": 3, "functionName": "build_domain_query", @@ -858,7 +889,9 @@ "advanced": { "customName": "build IP query", "customNameId": 0, - "join": [] + "description": "Query may need editing to reflect your splunk environment", + "join": [], + "note": "Splunk query template" }, "functionId": 4, "functionName": "build_ip_query", @@ -874,7 +907,7 @@ "type": "format", "warnings": {}, "x": 1020, - "y": 328 + "y": 320 }, "8": { "data": { @@ -883,7 +916,9 @@ "advanced": { "customName": "run URL query", "customNameId": 0, - "join": [] + "description": "May need to change search command dependent on your format block", + "join": [], + "note": "Splunk query execution" }, "connector": "Splunk", "connectorConfigs": [ @@ -922,7 +957,7 @@ "type": "action", "warnings": {}, "x": 0, - "y": 476 + "y": 480 }, "9": { "data": { @@ -931,7 +966,9 @@ "advanced": { "customName": "run file query", "customNameId": 0, - "join": [] + "description": "May need to change search command dependent on your format block", + "join": [], + "note": "Splunk query execution" }, "connector": "Splunk", "connectorConfigs": [ @@ -1041,7 +1078,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-02T16:11:35.642611+00:00", + "create_time": "2023-04-05T17:29:00.883561+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.py b/playbooks/Splunk_Identifier_Activity_Analysis.py index 97caec8453..d376a0ea19 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.py +++ b/playbooks/Splunk_Identifier_Activity_Analysis.py @@ -76,6 +76,10 @@ def filter_1(action=None, success=None, container=None, results=None, handle=Non def build_url_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_url_query() called") + ################################################################################ + # Query may need editing to reflect your splunk environment + ################################################################################ + template = """count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" # parameter list for template variable replacement @@ -104,7 +108,11 @@ def build_url_query(action=None, success=None, container=None, results=None, han def build_file_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_file_query() called") - template = """count from Endpoint.Filesystem where Filesystem.file_hash=\"{0}\" by Filesystem.dest | `drop_dm_object_name(\"Filesystem\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip""" + ################################################################################ + # Query may need editing to reflect your splunk environment + ################################################################################ + + template = """count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip""" # parameter list for template variable replacement parameters = [ @@ -132,6 +140,10 @@ def build_file_query(action=None, success=None, container=None, results=None, ha def build_domain_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_domain_query() called") + ################################################################################ + # Query may need editing to reflect your splunk environment + ################################################################################ + template = """count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" # parameter list for template variable replacement @@ -160,6 +172,10 @@ def build_domain_query(action=None, success=None, container=None, results=None, def build_ip_query(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_ip_query() called") + ################################################################################ + # Query may need editing to reflect your splunk environment + ################################################################################ + template = """count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" # parameter list for template variable replacement @@ -190,6 +206,10 @@ def run_url_query(action=None, success=None, container=None, results=None, handl # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + ################################################################################ + # May need to change search command dependent on your format block + ################################################################################ + build_url_query = phantom.get_format_data(name="build_url_query") parameters = [] @@ -222,6 +242,10 @@ def run_file_query(action=None, success=None, container=None, results=None, hand # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + ################################################################################ + # May need to change search command dependent on your format block + ################################################################################ + build_file_query = phantom.get_format_data(name="build_file_query") parameters = [] @@ -254,6 +278,10 @@ def run_domain_query(action=None, success=None, container=None, results=None, ha # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + ################################################################################ + # May need to change search command dependent on your format block + ################################################################################ + build_domain_query = phantom.get_format_data(name="build_domain_query") parameters = [] @@ -286,6 +314,10 @@ def run_ip_query(action=None, success=None, container=None, results=None, handle # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) + ################################################################################ + # May need to change search command dependent on your format block + ################################################################################ + build_ip_query = phantom.get_format_data(name="build_ip_query") parameters = [] @@ -393,6 +425,10 @@ def filter_ip_query(action=None, success=None, container=None, results=None, han def format_url_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("format_url_report() called") + ################################################################################ + # Markdown report used in calling playbook + ################################################################################ + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| URL | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" # parameter list for template variable replacement @@ -424,6 +460,10 @@ def format_url_report(action=None, success=None, container=None, results=None, h def format_file_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("format_file_report() called") + ################################################################################ + # Markdown report used in calling playbook + ################################################################################ + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| File | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" # parameter list for template variable replacement @@ -455,6 +495,10 @@ def format_file_report(action=None, success=None, container=None, results=None, def format_domain_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("format_domain_report() called") + ################################################################################ + # Markdown report used in calling playbook + ################################################################################ + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| Domain | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" # parameter list for template variable replacement @@ -486,6 +530,10 @@ def format_domain_report(action=None, success=None, container=None, results=None def format_ip_report(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("format_ip_report() called") + ################################################################################ + # Markdown report used in calling playbook + ################################################################################ + template = """SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| IP | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%""" # parameter list for template variable replacement @@ -517,6 +565,10 @@ def format_ip_report(action=None, success=None, container=None, results=None, ha def build_url_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_url_output() called") + ################################################################################ + # Observable object expected by calling playbook + ################################################################################ + filtered_input_0_url = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:playbook_input:url"]) run_url_query_result_data = phantom.collect2(container=container, datapath=["run_url_query:action_result.data.*.src_dns","run_url_query:action_result.data.*.src_ip","run_url_query:action_result.data.*.src_asset_id"], action_results=results) @@ -548,7 +600,7 @@ def build_url_output(action=None, success=None, container=None, results=None, ha # Build observable object observable_array = { - "indicator": indicator, + "value": indicator, "type": "url", "total_count": len(device_list), "source": "Splunk", @@ -571,6 +623,10 @@ def build_url_output(action=None, success=None, container=None, results=None, ha def build_file_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_file_output() called") + ################################################################################ + # Observable object expected by calling playbook + ################################################################################ + filtered_input_0_file = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_2:playbook_input:file"]) run_file_query_result_data = phantom.collect2(container=container, datapath=["run_file_query:action_result.data.*.dest_dns","run_file_query:action_result.data.*.dest_ip","run_file_query:action_result.data.*.dest_asset_id"], action_results=results) @@ -603,7 +659,7 @@ def build_file_output(action=None, success=None, container=None, results=None, h # Build observable object observable_array = { - "indicator": indicator, + "value": indicator, "type": "file_hash", "total_count": len(device_list), "source": "Splunk", @@ -626,6 +682,10 @@ def build_file_output(action=None, success=None, container=None, results=None, h def build_domain_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_domain_output() called") + ################################################################################ + # Observable object expected by calling playbook + ################################################################################ + filtered_input_0_domain = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_3:playbook_input:domain"]) run_domain_query_result_data = phantom.collect2(container=container, datapath=["run_domain_query:action_result.data.*.src_dns","run_domain_query:action_result.data.*.src_ip","run_domain_query:action_result.data.*.src_asset_id"], action_results=results) @@ -657,7 +717,7 @@ def build_domain_output(action=None, success=None, container=None, results=None, # Build observable object observable_array = { - "indicator": indicator, + "value": indicator, "type": "domain", "total_count": len(device_list), "source": "Splunk", @@ -680,6 +740,10 @@ def build_domain_output(action=None, success=None, container=None, results=None, def build_ip_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): phantom.debug("build_ip_output() called") + ################################################################################ + # Observable object expected by calling playbook + ################################################################################ + filtered_input_0_ip = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_4:playbook_input:ip"]) run_ip_query_result_data = phantom.collect2(container=container, datapath=["run_ip_query:action_result.data.*.src_dns","run_ip_query:action_result.data.*.src_ip","run_ip_query:action_result.data.*.src_asset_id"], action_results=results) @@ -711,7 +775,7 @@ def build_ip_output(action=None, success=None, container=None, results=None, han # Build observable object observable_array = { - "indicator": indicator, + "value": indicator, "type": "ip_address", "total_count": len(device_list), "source": "Splunk", From c14530df92544ead08976eb4a4838e7bedeeefea Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Fri, 14 Apr 2023 09:44:24 -0500 Subject: [PATCH 4/6] Cleaning up outputs, filling null fields --- .../Splunk_Identifier_Activity_Analysis.json | 22 +++++----- .../Splunk_Identifier_Activity_Analysis.py | 40 +++++++++++++++++-- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.json b/playbooks/Splunk_Identifier_Activity_Analysis.json index ce84d84dcf..976946eeae 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.json +++ b/playbooks/Splunk_Identifier_Activity_Analysis.json @@ -222,7 +222,7 @@ "targetPort": "1_in" } ], - "hash": "fce7af7ffe7b23a7bcac38ab930673217f6594ea", + "hash": "c14475bb50b8d38260b3f22f4bcb9f6b7a476e63", "nodes": { "0": { "data": { @@ -238,7 +238,7 @@ "type": "start", "warnings": {}, "x": 530, - "y": -3.197442310920451e-13 + "y": -5.115907697472721e-13 }, "1": { "data": { @@ -711,7 +711,7 @@ "errors": {}, "id": "20", "type": "code", - "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", + "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 0, "y": 960 @@ -742,7 +742,7 @@ "errors": {}, "id": "21", "type": "code", - "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", + "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 340, "y": 960 @@ -773,7 +773,7 @@ "errors": {}, "id": "22", "type": "code", - "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", + "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 680, "y": 960 @@ -804,7 +804,7 @@ "errors": {}, "id": "23", "type": "code", - "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", + "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", "warnings": {}, "x": 1020, "y": 960 @@ -824,7 +824,7 @@ "parameters": [ "filtered-data:filter_1:condition_1:playbook_input:url" ], - "template": "count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "template": "count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"", "type": "format" }, "errors": {}, @@ -849,7 +849,7 @@ "parameters": [ "filtered-data:filter_1:condition_2:playbook_input:file" ], - "template": "count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip", + "template": "count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip | fillnull value=\"Unknown\"", "type": "format" }, "errors": {}, @@ -874,7 +874,7 @@ "parameters": [ "filtered-data:filter_1:condition_3:playbook_input:domain" ], - "template": "count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "template": "count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"", "type": "format" }, "errors": {}, @@ -899,7 +899,7 @@ "parameters": [ "filtered-data:filter_1:condition_4:playbook_input:ip" ], - "template": "count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip", + "template": "count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"", "type": "format" }, "errors": {}, @@ -1078,7 +1078,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-05T17:29:00.883561+00:00", + "create_time": "2023-04-14T14:41:40.655194+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.py b/playbooks/Splunk_Identifier_Activity_Analysis.py index d376a0ea19..79072dde48 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.py +++ b/playbooks/Splunk_Identifier_Activity_Analysis.py @@ -80,7 +80,7 @@ def build_url_query(action=None, success=None, container=None, results=None, han # Query may need editing to reflect your splunk environment ################################################################################ - template = """count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + template = """count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"""" # parameter list for template variable replacement parameters = [ @@ -112,7 +112,7 @@ def build_file_query(action=None, success=None, container=None, results=None, ha # Query may need editing to reflect your splunk environment ################################################################################ - template = """count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip""" + template = """count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip | fillnull value=\"Unknown\"""" # parameter list for template variable replacement parameters = [ @@ -144,7 +144,7 @@ def build_domain_query(action=None, success=None, container=None, results=None, # Query may need editing to reflect your splunk environment ################################################################################ - template = """count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\") | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + template = """count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"""" # parameter list for template variable replacement parameters = [ @@ -176,7 +176,7 @@ def build_ip_query(action=None, success=None, container=None, results=None, hand # Query may need editing to reflect your splunk environment ################################################################################ - template = """count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip""" + template = """count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"""" # parameter list for template variable replacement parameters = [ @@ -596,6 +596,14 @@ def build_url_output(action=None, success=None, container=None, results=None, ha "ip_address": ip, "operating_system": "Unknown" } + + # Drop devices from list if we don't know anything about them + if device.get("name") == "Unknown": + if device.get("id") == "Unknown": + if device.get("ip_address") == "Unknown": + continue + + # Add devices to list device_list.append(device) # Build observable object @@ -655,6 +663,14 @@ def build_file_output(action=None, success=None, container=None, results=None, h "ip_address": ip, "operating_system": "Unknown" } + + # Drop devices from list if we don't know anything about them + if device.get("name") == "Unknown": + if device.get("id") == "Unknown": + if device.get("ip_address") == "Unknown": + continue + + # Add devices to list device_list.append(device) # Build observable object @@ -713,6 +729,14 @@ def build_domain_output(action=None, success=None, container=None, results=None, "ip_address": ip, "operating_system": "Unknown" } + + # Drop devices from list if we don't know anything about them + if device.get("name") == "Unknown": + if device.get("id") == "Unknown": + if device.get("ip_address") == "Unknown": + continue + + # Add devices to list device_list.append(device) # Build observable object @@ -771,6 +795,14 @@ def build_ip_output(action=None, success=None, container=None, results=None, han "ip_address": ip, "operating_system": "Unknown" } + + # Drop devices from list if we don't know anything about them + if device.get("name") == "Unknown": + if device.get("id") == "Unknown": + if device.get("ip_address") == "Unknown": + continue + + # Add devices to list device_list.append(device) # Build observable object From 7ebe42725aa88efd18007a21b32578e3a5c89b5e Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Thu, 4 May 2023 14:35:44 -0500 Subject: [PATCH 5/6] Resolved Kelby's findings --- .../Splunk_Identifier_Activity_Analysis.json | 114 +++++++++--------- .../Splunk_Identifier_Activity_Analysis.py | 104 ++++++++-------- 2 files changed, 109 insertions(+), 109 deletions(-) diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.json b/playbooks/Splunk_Identifier_Activity_Analysis.json index 976946eeae..7382bc06bc 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.json +++ b/playbooks/Splunk_Identifier_Activity_Analysis.json @@ -222,7 +222,7 @@ "targetPort": "1_in" } ], - "hash": "c14475bb50b8d38260b3f22f4bcb9f6b7a476e63", + "hash": "204e4629fa3da0ab5d189b9b1a9a6709b6b7afb1", "nodes": { "0": { "data": { @@ -237,8 +237,8 @@ "id": "0", "type": "start", "warnings": {}, - "x": 530, - "y": -5.115907697472721e-13 + "x": 520, + "y": -6.394884621840902e-14 }, "1": { "data": { @@ -390,7 +390,7 @@ "type": "filter", "warnings": {}, "x": 60, - "y": 624 + "y": 620 }, "13": { "data": { @@ -510,10 +510,10 @@ "functionName": "format_url_report", "id": "16", "parameters": [ - "filtered-data:filter_1:condition_1:playbook_input:url", - "run_url_query:action_result.data.*.src_dns", - "run_url_query:action_result.data.*.src_ip", - "run_url_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_1:playbook_input:url", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_dns", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_ip", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_asset_id" ], "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| URL | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", "type": "format" @@ -538,10 +538,10 @@ "functionName": "format_file_report", "id": "17", "parameters": [ - "filtered-data:filter_1:condition_2:playbook_input:file", - "run_file_query:action_result.data.*.dest_dns", - "run_file_query:action_result.data.*.dest_ip", - "run_file_query:action_result.data.*.dest_asset_id" + "filtered-data:input_filter:condition_2:playbook_input:file", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_dns", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_ip", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_asset_id" ], "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| File | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", "type": "format" @@ -566,10 +566,10 @@ "functionName": "format_domain_report", "id": "18", "parameters": [ - "filtered-data:filter_1:condition_3:playbook_input:domain", - "run_domain_query:action_result.data.*.src_dns", - "run_domain_query:action_result.data.*.src_ip", - "run_domain_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_3:playbook_input:domain", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_dns", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_ip", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_asset_id" ], "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| Domain | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", "type": "format" @@ -594,10 +594,10 @@ "functionName": "format_ip_report", "id": "19", "parameters": [ - "filtered-data:filter_1:condition_4:playbook_input:ip", - "run_ip_query:action_result.data.*.src_dns", - "run_ip_query:action_result.data.*.src_ip", - "run_ip_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_4:playbook_input:ip", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_dns", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_ip", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_asset_id" ], "template": "SOAR searched for occurrences of `{0}` within your environment using Splunk. The table below shows a summary of the information gathered.\n\n| IP | Computer | IP Address | Asset ID | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | Splunk |\n%%", "type": "format" @@ -612,6 +612,8 @@ "2": { "data": { "advanced": { + "customName": "input filter", + "customNameId": 0, "join": [], "note": "Routing paths by indicator type" }, @@ -670,7 +672,7 @@ } ], "functionId": 1, - "functionName": "filter_1", + "functionName": "input_filter", "id": "2", "type": "filter" }, @@ -698,10 +700,10 @@ "functionName": "build_url_output", "id": "20", "inputParameters": [ - "filtered-data:filter_1:condition_1:playbook_input:url", - "run_url_query:action_result.data.*.src_dns", - "run_url_query:action_result.data.*.src_ip", - "run_url_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_1:playbook_input:url", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_dns", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_ip", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_asset_id" ], "outputVariables": [ "observable_array" @@ -711,7 +713,7 @@ "errors": {}, "id": "20", "type": "code", - "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", + "userCode": "\n # Init variables + convenience naming\n build_url_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_url_values\n \n # Build device list\n for dns, ip, asset_id in zip(filtered_result_0_data___src_dns, filtered_result_0_data___src_ip, filtered_result_0_data___src_asset_id):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"url\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_url_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 0, "y": 960 @@ -729,10 +731,10 @@ "functionName": "build_file_output", "id": "21", "inputParameters": [ - "filtered-data:filter_1:condition_2:playbook_input:file", - "run_file_query:action_result.data.*.dest_dns", - "run_file_query:action_result.data.*.dest_ip", - "run_file_query:action_result.data.*.dest_asset_id" + "filtered-data:input_filter:condition_2:playbook_input:file", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_dns", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_ip", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_asset_id" ], "outputVariables": [ "observable_array" @@ -742,7 +744,7 @@ "errors": {}, "id": "21", "type": "code", - "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", + "userCode": "\n \n # Init variables + convenience naming\n build_file_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_file_values\n \n # Build device list\n for dns, ip, asset_id in zip(filtered_result_0_datadata___dest_dns, filtered_result_0_datadata___dest_ip, filtered_result_0_datadata___dest_asset_id):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"file_hash\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_file_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 340, "y": 960 @@ -760,10 +762,10 @@ "functionName": "build_domain_output", "id": "22", "inputParameters": [ - "filtered-data:filter_1:condition_3:playbook_input:domain", - "run_domain_query:action_result.data.*.src_dns", - "run_domain_query:action_result.data.*.src_ip", - "run_domain_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_3:playbook_input:domain", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_dns", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_ip", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_asset_id" ], "outputVariables": [ "observable_array" @@ -773,7 +775,7 @@ "errors": {}, "id": "22", "type": "code", - "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", + "userCode": "\n # Init variables + convenience naming\n build_domain_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_domain_values\n \n # Build device list\n for dns, ip, asset_id in zip(filtered_result_0_data___src_dns, filtered_result_0_data___src_ip, filtered_result_0_data___src_asset_id):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"domain\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send output\n build_domain_output__observable_array.append(observable_array)\n\n", "warnings": {}, "x": 680, "y": 960 @@ -791,10 +793,10 @@ "functionName": "build_ip_output", "id": "23", "inputParameters": [ - "filtered-data:filter_1:condition_4:playbook_input:ip", - "run_ip_query:action_result.data.*.src_dns", - "run_ip_query:action_result.data.*.src_ip", - "run_ip_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_4:playbook_input:ip", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_dns", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_ip", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_asset_id" ], "outputVariables": [ "observable_array" @@ -804,7 +806,7 @@ "errors": {}, "id": "23", "type": "code", - "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", + "userCode": " \n # Init variables + convenience naming\n build_ip_output__observable_array = []\n device_list = []\n indicator = filtered_input_0_ip_values\n \n # Build device list\n for dns, ip, asset_id in zip(filtered_result_0_data___src_dns, filtered_result_0_data___src_ip, filtered_result_0_data___src_asset_id):\n device = {\n \"name\": dns,\n \"id\": asset_id, \n \"ip_address\": ip,\n \"operating_system\": \"Unknown\" \n }\n \n # Drop devices from list if we don't know anything about them\n if device.get(\"name\") == \"Unknown\":\n if device.get(\"id\") == \"Unknown\":\n if device.get(\"ip_address\") == \"Unknown\":\n continue\n \n # Add devices to list \n device_list.append(device)\n \n # Build observable object \n observable_array = {\n \"value\": indicator,\n \"type\": \"ip_address\",\n \"total_count\": len(device_list),\n \"source\": \"Splunk\",\n \"identifier_activity\": device_list\n }\n \n # Send Output\n build_ip_output__observable_array.append(observable_array)\n \n", "warnings": {}, "x": 1020, "y": 960 @@ -822,7 +824,7 @@ "functionName": "build_url_query", "id": "3", "parameters": [ - "filtered-data:filter_1:condition_1:playbook_input:url" + "filtered-data:input_filter:condition_1:playbook_input:url" ], "template": "count from datamodel=Web.Web where Web.url=\"{0}\" by Web.src | `drop_dm_object_name(\"Web\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"", "type": "format" @@ -847,7 +849,7 @@ "functionName": "build_file_query", "id": "4", "parameters": [ - "filtered-data:filter_1:condition_2:playbook_input:file" + "filtered-data:input_filter:condition_2:playbook_input:file" ], "template": "count from datamodel=Endpoint.Processes where (Processes.process_hash=\"{0}\" OR Processes.process_hash=\"*={0}*\") by Processes.dest | `drop_dm_object_name(\"Processes\")` | `get_asset(dest)` | fields dest, dest_asset_id, dest_dns, dest_ip | fillnull value=\"Unknown\"", "type": "format" @@ -872,7 +874,7 @@ "functionName": "build_domain_query", "id": "5", "parameters": [ - "filtered-data:filter_1:condition_3:playbook_input:domain" + "filtered-data:input_filter:condition_3:playbook_input:domain" ], "template": "count from datamodel=Network_Resolution where DNS.query=\"{0}\" by DNS.src | `drop_dm_object_name(\"DNS\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"", "type": "format" @@ -897,7 +899,7 @@ "functionName": "build_ip_query", "id": "6", "parameters": [ - "filtered-data:filter_1:condition_4:playbook_input:ip" + "filtered-data:input_filter:condition_4:playbook_input:ip" ], "template": "count from datamodel=Network_Traffic where All_Traffic.dest_ip=\"{0}\" by All_Traffic.src | `drop_dm_object_name(\"All_Traffic\")` | `get_asset(src)` | fields src, src_asset_id, src_dns, src_ip | fillnull value=\"Unknown\"", "type": "format" @@ -1017,31 +1019,28 @@ "contains": [ "url" ], - "description": "", + "description": "A URL to look for within your events in Splunk", "name": "url" }, { "contains": [ - "hash", - "sha1", - "sha256", - "sha512" + "hash" ], - "description": "", + "description": "A file hash to look for within your events in Splunk", "name": "file" }, { "contains": [ "domain" ], - "description": "", + "description": "A domain name to look for within your events in Splunk", "name": "domain" }, { "contains": [ "ip" ], - "description": "", + "description": "An IP Address to look for within your events in Splunk", "name": "ip" } ], @@ -1055,7 +1054,7 @@ "build_file_output:custom_function:observable_array" ], "deduplicate": false, - "description": "", + "description": "An array of Observable objects ", "metadata": {}, "name": "observable" }, @@ -1068,7 +1067,7 @@ "format_ip_report:formatted_data" ], "deduplicate": false, - "description": "", + "description": "An array of markdown reports", "metadata": {}, "name": "markdown_report" } @@ -1078,7 +1077,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-14T14:41:40.655194+00:00", + "create_time": "2023-05-04T19:33:28.515583+00:00", "draft_mode": false, "labels": [ "*" @@ -1089,6 +1088,7 @@ "ip", "file_hash", "splunk", - "identifier_activity" + "identifier_activity", + "D3-IAA" ] } \ No newline at end of file diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.py b/playbooks/Splunk_Identifier_Activity_Analysis.py index 79072dde48..900da6edda 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.py +++ b/playbooks/Splunk_Identifier_Activity_Analysis.py @@ -12,14 +12,14 @@ from datetime import datetime, timedelta def on_start(container): phantom.debug('on_start() called') - # call 'filter_1' block - filter_1(container=container) + # call 'input_filter' block + input_filter(container=container) return @phantom.playbook_block() -def filter_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("filter_1() called") +def input_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("input_filter() called") # collect filtered artifact ids and results for 'if' condition 1 matched_artifacts_1, matched_results_1 = phantom.condition( @@ -27,7 +27,7 @@ def filter_1(action=None, success=None, container=None, results=None, handle=Non conditions=[ ["playbook_input:url", "!=", None] ], - name="filter_1:condition_1") + name="input_filter:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: @@ -39,7 +39,7 @@ def filter_1(action=None, success=None, container=None, results=None, handle=Non conditions=[ ["playbook_input:file", "!=", None] ], - name="filter_1:condition_2") + name="input_filter:condition_2") # call connected blocks if filtered artifacts or results if matched_artifacts_2 or matched_results_2: @@ -51,7 +51,7 @@ def filter_1(action=None, success=None, container=None, results=None, handle=Non conditions=[ ["playbook_input:domain", "!=", None] ], - name="filter_1:condition_3") + name="input_filter:condition_3") # call connected blocks if filtered artifacts or results if matched_artifacts_3 or matched_results_3: @@ -63,7 +63,7 @@ def filter_1(action=None, success=None, container=None, results=None, handle=Non conditions=[ ["playbook_input:ip", "!=", None] ], - name="filter_1:condition_4") + name="input_filter:condition_4") # call connected blocks if filtered artifacts or results if matched_artifacts_4 or matched_results_4: @@ -84,7 +84,7 @@ def build_url_query(action=None, success=None, container=None, results=None, han # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_1:playbook_input:url" + "filtered-data:input_filter:condition_1:playbook_input:url" ] ################################################################################ @@ -116,7 +116,7 @@ def build_file_query(action=None, success=None, container=None, results=None, ha # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_2:playbook_input:file" + "filtered-data:input_filter:condition_2:playbook_input:file" ] ################################################################################ @@ -148,7 +148,7 @@ def build_domain_query(action=None, success=None, container=None, results=None, # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_3:playbook_input:domain" + "filtered-data:input_filter:condition_3:playbook_input:domain" ] ################################################################################ @@ -180,7 +180,7 @@ def build_ip_query(action=None, success=None, container=None, results=None, hand # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_4:playbook_input:ip" + "filtered-data:input_filter:condition_4:playbook_input:ip" ] ################################################################################ @@ -433,10 +433,10 @@ def format_url_report(action=None, success=None, container=None, results=None, h # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_1:playbook_input:url", - "run_url_query:action_result.data.*.src_dns", - "run_url_query:action_result.data.*.src_ip", - "run_url_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_1:playbook_input:url", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_dns", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_ip", + "filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_asset_id" ] ################################################################################ @@ -468,10 +468,10 @@ def format_file_report(action=None, success=None, container=None, results=None, # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_2:playbook_input:file", - "run_file_query:action_result.data.*.dest_dns", - "run_file_query:action_result.data.*.dest_ip", - "run_file_query:action_result.data.*.dest_asset_id" + "filtered-data:input_filter:condition_2:playbook_input:file", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_dns", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_ip", + "filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_asset_id" ] ################################################################################ @@ -503,10 +503,10 @@ def format_domain_report(action=None, success=None, container=None, results=None # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_3:playbook_input:domain", - "run_domain_query:action_result.data.*.src_dns", - "run_domain_query:action_result.data.*.src_ip", - "run_domain_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_3:playbook_input:domain", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_dns", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_ip", + "filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_asset_id" ] ################################################################################ @@ -538,10 +538,10 @@ def format_ip_report(action=None, success=None, container=None, results=None, ha # parameter list for template variable replacement parameters = [ - "filtered-data:filter_1:condition_4:playbook_input:ip", - "run_ip_query:action_result.data.*.src_dns", - "run_ip_query:action_result.data.*.src_ip", - "run_ip_query:action_result.data.*.src_asset_id" + "filtered-data:input_filter:condition_4:playbook_input:ip", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_dns", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_ip", + "filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_asset_id" ] ################################################################################ @@ -569,13 +569,13 @@ def build_url_output(action=None, success=None, container=None, results=None, ha # Observable object expected by calling playbook ################################################################################ - filtered_input_0_url = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_1:playbook_input:url"]) - run_url_query_result_data = phantom.collect2(container=container, datapath=["run_url_query:action_result.data.*.src_dns","run_url_query:action_result.data.*.src_ip","run_url_query:action_result.data.*.src_asset_id"], action_results=results) + filtered_input_0_url = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_1:playbook_input:url"]) + filtered_result_0_data_filter_url_query = phantom.collect2(container=container, datapath=["filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_dns","filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_ip","filtered-data:filter_url_query:condition_1:run_url_query:action_result.data.*.src_asset_id"]) filtered_input_0_url_values = [item[0] for item in filtered_input_0_url] - run_url_query_result_item_0 = [item[0] for item in run_url_query_result_data] - run_url_query_result_item_1 = [item[1] for item in run_url_query_result_data] - run_url_query_result_item_2 = [item[2] for item in run_url_query_result_data] + filtered_result_0_data___src_dns = [item[0] for item in filtered_result_0_data_filter_url_query] + filtered_result_0_data___src_ip = [item[1] for item in filtered_result_0_data_filter_url_query] + filtered_result_0_data___src_asset_id = [item[2] for item in filtered_result_0_data_filter_url_query] build_url_output__observable_array = None @@ -589,7 +589,7 @@ def build_url_output(action=None, success=None, container=None, results=None, ha indicator = filtered_input_0_url_values # Build device list - for dns, ip, asset_id in zip(run_url_query_result_item_0, run_url_query_result_item_1, run_url_query_result_item_2): + for dns, ip, asset_id in zip(filtered_result_0_data___src_dns, filtered_result_0_data___src_ip, filtered_result_0_data___src_asset_id): device = { "name": dns, "id": asset_id, @@ -635,13 +635,13 @@ def build_file_output(action=None, success=None, container=None, results=None, h # Observable object expected by calling playbook ################################################################################ - filtered_input_0_file = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_2:playbook_input:file"]) - run_file_query_result_data = phantom.collect2(container=container, datapath=["run_file_query:action_result.data.*.dest_dns","run_file_query:action_result.data.*.dest_ip","run_file_query:action_result.data.*.dest_asset_id"], action_results=results) + filtered_input_0_file = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_2:playbook_input:file"]) + filtered_result_0_data_filter_file_query = phantom.collect2(container=container, datapath=["filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_dns","filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_ip","filtered-data:filter_file_query:condition_1:run_file_query:action_result.datadata.*.dest_asset_id"]) filtered_input_0_file_values = [item[0] for item in filtered_input_0_file] - run_file_query_result_item_0 = [item[0] for item in run_file_query_result_data] - run_file_query_result_item_1 = [item[1] for item in run_file_query_result_data] - run_file_query_result_item_2 = [item[2] for item in run_file_query_result_data] + filtered_result_0_datadata___dest_dns = [item[0] for item in filtered_result_0_data_filter_file_query] + filtered_result_0_datadata___dest_ip = [item[1] for item in filtered_result_0_data_filter_file_query] + filtered_result_0_datadata___dest_asset_id = [item[2] for item in filtered_result_0_data_filter_file_query] build_file_output__observable_array = None @@ -656,7 +656,7 @@ def build_file_output(action=None, success=None, container=None, results=None, h indicator = filtered_input_0_file_values # Build device list - for dns, ip, asset_id in zip(run_file_query_result_item_0, run_file_query_result_item_1, run_file_query_result_item_2): + for dns, ip, asset_id in zip(filtered_result_0_datadata___dest_dns, filtered_result_0_datadata___dest_ip, filtered_result_0_datadata___dest_asset_id): device = { "name": dns, "id": asset_id, @@ -702,13 +702,13 @@ def build_domain_output(action=None, success=None, container=None, results=None, # Observable object expected by calling playbook ################################################################################ - filtered_input_0_domain = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_3:playbook_input:domain"]) - run_domain_query_result_data = phantom.collect2(container=container, datapath=["run_domain_query:action_result.data.*.src_dns","run_domain_query:action_result.data.*.src_ip","run_domain_query:action_result.data.*.src_asset_id"], action_results=results) + filtered_input_0_domain = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_3:playbook_input:domain"]) + filtered_result_0_data_filter_domain_query = phantom.collect2(container=container, datapath=["filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_dns","filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_ip","filtered-data:filter_domain_query:condition_1:run_domain_query:action_result.data.*.src_asset_id"]) filtered_input_0_domain_values = [item[0] for item in filtered_input_0_domain] - run_domain_query_result_item_0 = [item[0] for item in run_domain_query_result_data] - run_domain_query_result_item_1 = [item[1] for item in run_domain_query_result_data] - run_domain_query_result_item_2 = [item[2] for item in run_domain_query_result_data] + filtered_result_0_data___src_dns = [item[0] for item in filtered_result_0_data_filter_domain_query] + filtered_result_0_data___src_ip = [item[1] for item in filtered_result_0_data_filter_domain_query] + filtered_result_0_data___src_asset_id = [item[2] for item in filtered_result_0_data_filter_domain_query] build_domain_output__observable_array = None @@ -722,7 +722,7 @@ def build_domain_output(action=None, success=None, container=None, results=None, indicator = filtered_input_0_domain_values # Build device list - for dns, ip, asset_id in zip(run_domain_query_result_item_0, run_domain_query_result_item_1, run_domain_query_result_item_2): + for dns, ip, asset_id in zip(filtered_result_0_data___src_dns, filtered_result_0_data___src_ip, filtered_result_0_data___src_asset_id): device = { "name": dns, "id": asset_id, @@ -768,13 +768,13 @@ def build_ip_output(action=None, success=None, container=None, results=None, han # Observable object expected by calling playbook ################################################################################ - filtered_input_0_ip = phantom.collect2(container=container, datapath=["filtered-data:filter_1:condition_4:playbook_input:ip"]) - run_ip_query_result_data = phantom.collect2(container=container, datapath=["run_ip_query:action_result.data.*.src_dns","run_ip_query:action_result.data.*.src_ip","run_ip_query:action_result.data.*.src_asset_id"], action_results=results) + filtered_input_0_ip = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_4:playbook_input:ip"]) + filtered_result_0_data_filter_ip_query = phantom.collect2(container=container, datapath=["filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_dns","filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_ip","filtered-data:filter_ip_query:condition_1:run_ip_query:action_result.data.*.src_asset_id"]) filtered_input_0_ip_values = [item[0] for item in filtered_input_0_ip] - run_ip_query_result_item_0 = [item[0] for item in run_ip_query_result_data] - run_ip_query_result_item_1 = [item[1] for item in run_ip_query_result_data] - run_ip_query_result_item_2 = [item[2] for item in run_ip_query_result_data] + filtered_result_0_data___src_dns = [item[0] for item in filtered_result_0_data_filter_ip_query] + filtered_result_0_data___src_ip = [item[1] for item in filtered_result_0_data_filter_ip_query] + filtered_result_0_data___src_asset_id = [item[2] for item in filtered_result_0_data_filter_ip_query] build_ip_output__observable_array = None @@ -788,7 +788,7 @@ def build_ip_output(action=None, success=None, container=None, results=None, han indicator = filtered_input_0_ip_values # Build device list - for dns, ip, asset_id in zip(run_ip_query_result_item_0, run_ip_query_result_item_1, run_ip_query_result_item_2): + for dns, ip, asset_id in zip(filtered_result_0_data___src_dns, filtered_result_0_data___src_ip, filtered_result_0_data___src_asset_id): device = { "name": dns, "id": asset_id, From 7416bfbe8ca52371bd06f5223de3b20c5bc75097 Mon Sep 17 00:00:00 2001 From: Lou Stella Date: Mon, 8 May 2023 11:11:42 -0500 Subject: [PATCH 6/6] changed yml ID --- playbooks/Splunk_Identifier_Activity_Analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/Splunk_Identifier_Activity_Analysis.yml b/playbooks/Splunk_Identifier_Activity_Analysis.yml index 5a10f2f521..605fac3797 100644 --- a/playbooks/Splunk_Identifier_Activity_Analysis.yml +++ b/playbooks/Splunk_Identifier_Activity_Analysis.yml @@ -1,5 +1,5 @@ name: Splunk Identifier Activity Analysis -id: 5299d9dc-e9c4-46fa-da42-92ace0ff816d +id: 5299b9dc-e8c4-46ba-d942-92ace0ff816d version: 1 date: '2023-03-31' author: Lou Stella, Splunk