From 5abe5b161847d81bd6b897bf7f2ece0ba191cef9 Mon Sep 17 00:00:00 2001 From: tccontre Date: Thu, 13 Apr 2023 16:45:28 +0200 Subject: [PATCH 1/3] splunk_attack_analyzer_playbook --- playbooks/SAA_Dynamic_Analysis.png | Bin 79000 -> 0 bytes ...unk_Attack_Analyzer_Dynamic_Analysis.json} | 293 +++++------ ...plunk_Attack_Analyzer_Dynamic_Analysis.png | Bin 0 -> 69469 bytes ...plunk_Attack_Analyzer_Dynamic_Analysis.py} | 469 ++++++++---------- ...lunk_Attack_Analyzer_Dynamic_Analysis.yml} | 6 +- 5 files changed, 338 insertions(+), 430 deletions(-) delete mode 100644 playbooks/SAA_Dynamic_Analysis.png rename playbooks/{SAA_Dynamic_Analysis.json => Splunk_Attack_Analyzer_Dynamic_Analysis.json} (58%) create mode 100644 playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.png rename playbooks/{SAA_Dynamic_Analysis.py => Splunk_Attack_Analyzer_Dynamic_Analysis.py} (57%) rename playbooks/{SAA_Dynamic_Analysis.yml => Splunk_Attack_Analyzer_Dynamic_Analysis.yml} (83%) diff --git a/playbooks/SAA_Dynamic_Analysis.png b/playbooks/SAA_Dynamic_Analysis.png deleted file mode 100644 index 94f07cf5207f77c8c6994428eb50fd04ebbbc6cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 79000 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
  • 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 0 and confidence_ < 10:\n score_id = 0\n elif confidence_ >= 10 and confidence_ < 20:\n score_id = 10\n elif confidence_ >= 20 and confidence_ < 30:\n score_id = 20\n elif confidence_ >= 30 and confidence_ < 40:\n score_id = 30\n elif confidence_ >= 40 and confidence_ < 50:\n score_id = 40\n elif confidence_ >= 50 and confidence_ < 60:\n score_id = 50\n elif confidence_ >= 60 and confidence_ < 70:\n score_id = 60\n elif confidence_ >= 70 and confidence_ < 80:\n score_id = 70\n elif confidence_ >= 80 and confidence_ < 90:\n score_id = 80\n elif confidence_ >= 90 and confidence_ < 100:\n score_id = 90\n elif confidence_ >= 100:\n score_id = 100\n \n score = score_table[str(score_id)]\n \n else:\n score = list(set(score_list))[0]\n \n # Attach final object\n normalized_job_forensic_report_output_1__file_score_object.append({'score': score, 'confidence': confidence_, 'categories': categories})\n normalized_job_forensic_report_output_1__scores.append(score)\n normalized_job_forensic_report_output_1__categories.append(categories)\n normalized_job_forensic_report_output_1__confidence.append(confidence_)\n #phantom.debug(\"normalized_job_forensic_report_output_1__file_score_object: {}\".format(normalized_job_forensic_report_output_1__file_score_object))\n #phantom.debug(\"normalized_job_forensic_report_output_1__scores: {}\".format(normalized_job_forensic_report_output_1__scores))\n #phantom.debug(\"normalized_job_forensic_report_output_1__categories: {}\".format(normalized_job_forensic_report_output_1__categories))\n", - "warnings": {}, + "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n #phantom.debug(\"vault_id: {}\".format(ssa_get_job_forensics_output_result_item_0))\n #phantom.debug(\"DisplayScore: {}\".format(ssa_get_job_forensics_output_result_item_1))\n #phantom.debug(\"Category: {}\".format(ssa_get_job_forensics_output_result_item_2))\n #phantom.debug(\"verdict: {}\".format(ssa_get_job_forensics_output_result_item_3))\n #phantom.debug(\"action_data: {}\".format(ssa_get_job_forensics_output_result_item_4))\n #phantom.debug(get_file_forensics_output_result_item_3)\n \n normalized_file_forensic_output__file_score_object = []\n normalized_file_forensic_output__scores = []\n normalized_file_forensic_output__categories = []\n normalized_file_forensic_output__score_id = []\n \n ## normalized NoneType value to avoid enumeration failure\n file_detonation_param_list = [(i or \"\") for i in playbook_input_vault_id_values] \n file_detonation_threat_score_list = [(i or 0) for i in get_file_forensics_output_result_item_0] \n file_detonation_category_list = [(i or \"\") for i in get_file_forensics_output_result_item_1] \n file_detonation_verdict_list = [(i or \"\") for i in get_file_forensics_output_result_item_2] \n \n ## get the set() or unique input url parameter.\n \n index_file_dict = {}\n set_file_inputs = set(file_detonation_param_list)\n \n for file_input in set_file_inputs:\n vaultid_list = []\n score_list = []\n display_score_list = []\n category_list = []\n \n ## getting the index of each detonation phase of the url group the result for each url detonation\n file_input_index = [indx for indx, vaultid_val in enumerate(file_detonation_param_list) if vaultid_val == file_input]\n index_file_dict[file_input] = file_input_index\n \n for idx,(_vaultid, _score, _display_score, _category) in enumerate(zip(file_detonation_param_list, file_detonation_verdict_list, file_detonation_threat_score_list, file_detonation_category_list)):\n if _vaultid == file_input and idx in index_file_dict[file_input]:\n vaultid_list.append(_vaultid)\n score_list.append(_score)\n display_score_list.append(_display_score)\n category_list.append(_category)\n \n ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation\n #phantom.debug(\"score_list: {} len: {}\".format(score_list, len(score_list)))\n #phantom.debug(\"category_list: {} len: {}\".format(category_list, len(category_list)))\n confidence_ = list(set(display_score_list))[0]\n categories = list(set(category_list))\n #score_ = list(set(score_list))[0]\n \n score = \"\"\n if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == \"\"):\n if confidence_ >= 0 and confidence_ < 10:\n score_id = 0\n elif confidence_ >= 10 and confidence_ < 20:\n score_id = 1\n elif confidence_ >= 20 and confidence_ < 30:\n score_id = 2\n elif confidence_ >= 30 and confidence_ < 40:\n score_id = 3\n elif confidence_ >= 40 and confidence_ < 50:\n score_id = 4\n elif confidence_ >= 50 and confidence_ < 60:\n score_id = 5\n elif confidence_ >= 60 and confidence_ < 70:\n score_id = 6\n elif confidence_ >= 70 and confidence_ < 80:\n score_id = 7\n elif confidence_ >= 80 and confidence_ < 90:\n score_id = 8\n elif confidence_ >= 90 and confidence_ < 100:\n score_id = 9\n elif confidence_ >= 100:\n score_id = 10\n \n else:\n score_id = round(confidence_/ 10)\n \n score = score_table[str(score_id)]\n #phantom.debug(\"score: {} score_id {}\".format(score, score_id))\n # Attach final object\n normalized_file_forensic_output__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories})\n normalized_file_forensic_output__scores.append(score)\n normalized_file_forensic_output__categories.append(\", \".join(categories))\n normalized_file_forensic_output__score_id.append(score_id)\n #phantom.debug(\"normalized_job_forensic_report_output_1__file_score_object: {}\".format(normalized_job_forensic_report_output_1__file_score_object))\n #phantom.debug(\"normalized_job_forensic_report_output_1__scores: {}\".format(normalized_job_forensic_report_output_1__scores))\n #phantom.debug(\"normalized_job_forensic_report_output_1__categories: {}\".format(normalized_job_forensic_report_output_1__categories))\n", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, "x": 340, "y": 1260 }, @@ -420,19 +394,23 @@ "id": "18", "parameters": [ "playbook_input:vault_id", - "normalized_job_forensic_report_output_1:custom_function:scores", - "normalized_job_forensic_report_output_1:custom_function:confidence", - "normalized_job_forensic_report_output_1:custom_function:categories", - "get_jobid_of_file_detonation_output:custom_function:jobid" + "normalized_file_forensic_output:custom_function:scores", + "normalized_file_forensic_output:custom_function:score_id", + "normalized_file_forensic_output:custom_function:categories", + "file_detonation:action_result.data.*.JobID" ], - "template": "SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| File hash | Score | Confidence |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n", + "template": "SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| vault_id | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n", "type": "format" }, "errors": {}, "id": "18", "type": "format", "userCode": "\n # Write your custom code here...\n #phantom.debug(phantom.format(container=container, template=template, parameters=parameters, name=\"format_report_file\"))\n", - "warnings": {}, + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, "x": 340, "y": 1440 }, @@ -450,8 +428,8 @@ "id": "19", "inputParameters": [ "playbook_input:vault_id", - "get_jobid_of_file_detonation_output:custom_function:jobid", - "normalized_job_forensic_report_output_1:custom_function:file_score_object" + "file_detonation:action_result.data.*.JobID", + "normalized_file_forensic_output:custom_function:file_score_object" ], "outputVariables": [ "observable_array" @@ -461,8 +439,12 @@ "errors": {}, "id": "19", "type": "code", - "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n for jobs_id in get_jobid_of_file_detonation_output__jobid:\n for _vault_id, external_id, file_object in zip(playbook_input_vault_id_values, jobs_id, normalized_job_forensic_report_output_1__file_score_object):\n #phantom.debug(\"vault: {} id: {}\".format(_vault_id, external_id))\n observable_object = {\n\n \"value\": _vault_id,\n \"type\": \"hash\",\n \"reputation\": {\n \"score\": file_object['score'],\n \"confidence\": file_object['confidence'],\n\n },\n \"enrichment\": {\n \"provider\": \"Splunk Attack Analyzer\",\n \"type\": \"file\",\n\n },\n \"categories\": file_object['categories'],\n \"source\": \"Splunk Attack Analyzer (SAA)\",\n \"source_link\":f\"https://app.twinwave.io/job/{external_id}\"\n }\n build_file_output__observable_array.append(observable_object)\n #phantom.debug(\"build_file_output__observable_array: {}\".format(build_file_output__observable_array))\n", - "warnings": {}, + "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n\n for _vault_id, external_id, file_object in zip(playbook_input_vault_id_values, file_detonation_result_item_0, normalized_file_forensic_output__file_score_object):\n #phantom.debug(\"vault: {} id: {}\".format(_vault_id, external_id))\n observable_object = {\n\n \"value\": _vault_id,\n \"type\": \"hash\",\n \"reputation\": {\n \"score\": file_object['score'],\n \"score_id\": file_object['score_id'],\n \"confidence\": file_object['confidence'],\n\n },\n \"enrichment\": {\n \"provider\": \"Splunk Attack Analyzer\",\n \"type\": \"file\",\n\n },\n \"categories\": file_object['categories'],\n \"source\": \"Splunk Attack Analyzer (SAA)\",\n \"source_link\":f\"https://app.twinwave.io/job/{external_id}\"\n }\n build_file_output__observable_array.append(observable_object)\n #phantom.debug(\"build_file_output__observable_array: {}\".format(build_file_output__observable_array))\n", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, "x": 340, "y": 1620 }, @@ -511,7 +493,11 @@ "errors": {}, "id": "2", "type": "filter", - "warnings": {}, + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, "x": 230, "y": 148 }, @@ -530,7 +516,7 @@ { "conditionIndex": 0, "op": "==", - "param": "saa_url_detonation:action_result.status", + "param": "url_detonation:action_result.status", "value": "success" } ], @@ -562,7 +548,7 @@ { "conditionIndex": 0, "op": "==", - "param": "saa_get_file_job_forensics_output:action_result.status", + "param": "get_file_forensics_output:action_result.status", "value": "success" } ], @@ -588,20 +574,21 @@ "action": "detonate url", "actionType": "generic", "advanced": { - "customName": "saa url detonation", + "customName": "url detonation", "customNameId": 0, + "delayTime": 0, "description": "Queries SAA for information about the provided URL(s)", "join": [], "note": "Queries SAA for information about the provided URL(s)" }, "connector": "Splunk Attack Analyzer", "connectorConfigs": [ - "splunk attack analyzer" + "saa" ], "connectorId": "de681fee-c552-45bf-9212-827b1c7529f8", "connectorVersion": "v1", "functionId": 1, - "functionName": "saa_url_detonation", + "functionName": "url_detonation", "id": "3", "parameters": { "url": "playbook_input:url" @@ -618,9 +605,13 @@ "id": "3", "type": "action", "userCode": "\n # Write your custom code here...\n\n", - "warnings": {}, + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, "x": 0, - "y": 328 + "y": 320 }, "4": { "data": { @@ -637,7 +628,7 @@ { "conditionIndex": 1, "op": "==", - "param": "saa_file_detonation:action_result.status", + "param": "file_detonation:action_result.status", "value": "success" } ], @@ -658,58 +649,29 @@ "x": 400, "y": 500 }, - "5": { - "data": { - "advanced": { - "customName": "get jobid of url detonation output", - "customNameId": 0, - "description": "This block uses custom code for fetching JobID for URL(s) or file(s) detonation.", - "join": [], - "note": "This block uses custom code for fetching JobID for URL(s) or file(s) detonation.", - "scope": "default" - }, - "functionId": 1, - "functionName": "get_jobid_of_url_detonation_output", - "id": "5", - "inputParameters": [ - "saa_url_detonation:action_result.data.*.JobID" - ], - "outputVariables": [ - "jobid" - ], - "type": "code" - }, - "errors": {}, - "id": "5", - "type": "code", - "userCode": "\n # Write your custom code here...\n get_jobid_of_url_detonation_output__jobid = []\n\n get_jobid_of_url_detonation_output__jobid.append(saa_url_detonation_result_item_0)\n #phantom.debug(\"get_jobid_of_url_detonation_output__jobid: {}\".format(get_jobid_of_url_detonation_output__jobid))\n", - "warnings": {}, - "x": 0, - "y": 686 - }, "6": { "data": { "action": "get job forensics", "actionType": "investigate", "advanced": { - "customName": "ssa get job forensics output", + "customName": "get url forensics output", "customNameId": 0, + "delayTime": 2, "description": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated.", "join": [], "note": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated." }, "connector": "Splunk Attack Analyzer", "connectorConfigs": [ - "splunk attack analyzer" + "saa" ], "connectorId": "de681fee-c552-45bf-9212-827b1c7529f8", "connectorVersion": "v1", "functionId": 1, - "functionName": "ssa_get_job_forensics_output", + "functionName": "get_url_forensics_output", "id": "6", "parameters": { - "job_id": "get_jobid_of_url_detonation_output:custom_function:jobid", - "timeout": "5" + "job_id": "url_detonation:action_result.data.*.JobID" }, "requiredParameters": [ { @@ -722,7 +684,7 @@ "errors": {}, "id": "6", "type": "action", - "userCode": "\n # Write your custom code here...\n parameters = []\n for job_ids in get_jobid_of_url_detonation_output__jobid:\n for job in job_ids:\n if job is not None:\n parameters.append({\n \"job_id\": job,\n \"timeout\": 5,\n })\n #phantom.debug(parameters)\n", + "userCode": "\n # Write your custom code here...\n #parameters = []\n #for job_ids in url_jobid_detonation_output__jobid:\n # for job in job_ids:\n # if job is not None:\n # parameters.append({\n # \"job_id\": job,\n # \"timeout\": 5,\n # })\n #phantom.debug(parameters)\n", "warnings": {}, "x": 0, "y": 880 @@ -742,7 +704,7 @@ { "conditionIndex": 0, "op": "==", - "param": "ssa_get_job_forensics_output:action_result.status", + "param": "get_url_forensics_output:action_result.status", "value": "success" } ], @@ -766,37 +728,36 @@ "8": { "data": { "advanced": { - "customName": "normalized job forensic report output", + "customName": "normalized url forensic output", "customNameId": 0, "description": "This block uses custom code for normalizing score. Adjust the logic as desired in the documented sections.", "join": [], "note": "This block uses custom code for normalizing score. Adjust the logic as desired in the documented sections." }, "functionId": 2, - "functionName": "normalized_job_forensic_report_output", + "functionName": "normalized_url_forensic_output", "id": "8", "inputParameters": [ - "ssa_get_job_forensics_output:action_result.data.*.URLs.*.URL", - "ssa_get_job_forensics_output:action_result.data.*.DisplayScore", - "ssa_get_job_forensics_output:action_result.data.*.Detections.*.Description", - "ssa_get_job_forensics_output:action_result.data.*.Verdict", - "ssa_get_job_forensics_output:action_result.data" + "get_url_forensics_output:action_result.data.*.URLs.*.URL", + "get_url_forensics_output:action_result.data.*.DisplayScore", + "get_url_forensics_output:action_result.data.*.Detections.*.Description", + "get_url_forensics_output:action_result.data.*.Verdict" ], "outputVariables": [ "url_score_object", "scores", "categories", - "confidence" + "score_id" ], "type": "code" }, "errors": {}, "id": "8", "type": "code", - "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"10\":\"Very_Safe\",\n \"20\":\"Safe\",\n \"30\":\"Probably_Safe\",\n \"40\":\"Leans_Safe\",\n \"50\":\"May_not_be_Safe\",\n \"60\":\"Exercise_Caution\",\n \"70\":\"Suspicious_or_Risky\",\n \"80\":\"Possibly_Malicious\",\n \"90\":\"Probably_Malicious\",\n \"100\":\"Malicious\"\n }\n #phantom.debug(\"url: {}\".format(ssa_get_job_forensics_output_result_item_0))\n #phantom.debug(\"DisplayScore: {}\".format(ssa_get_job_forensics_output_result_item_1))\n #phantom.debug(\"Category: {}\".format(ssa_get_job_forensics_output_result_item_2))\n #phantom.debug(\"verdict: {}\".format(ssa_get_job_forensics_output_result_item_3))\n #phantom.debug(\"action_data: {}\".format(ssa_get_job_forensics_output_result_item_4))\n\n \n normalized_job_forensic_report_output__url_score_object = []\n normalized_job_forensic_report_output__scores = []\n normalized_job_forensic_report_output__categories = []\n normalized_job_forensic_report_output__confidence = []\n \n ## normalized NoneType value to avoid enumeration failure\n url_detonation_param_list = [(i or \"\") for i in ssa_get_job_forensics_output_result_item_0] \n url_detonation_threat_score_list = [(i or 0) for i in ssa_get_job_forensics_output_result_item_1] \n url_detonation_category_list = [(i or \"\") for i in ssa_get_job_forensics_output_result_item_2] \n url_detonation_verdict_list = [(i or \"\") for i in ssa_get_job_forensics_output_result_item_3] \n \n ## get the set() or unique input url parameter.\n \n index_url_dict = {}\n set_url_inputs = set(url_detonation_param_list)\n \n for url_input in set_url_inputs:\n url_list = []\n score_list = []\n display_score_list = []\n category_list = []\n \n ## getting the index of each detonation phase of the url group the result for each url detonation\n url_input_index = [indx for indx, url_val in enumerate(url_detonation_param_list) if url_val == url_input]\n index_url_dict[url_input] = url_input_index\n\n for idx,(_url, _score, _display_score, _category) in enumerate(zip(url_detonation_param_list, url_detonation_verdict_list, url_detonation_threat_score_list, url_detonation_category_list)):\n if _url == url_input and idx in index_url_dict[url_input]:\n url_list.append(_url)\n score_list.append(_score)\n display_score_list.append(_display_score)\n category_list.append(_category)\n \n ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation\n #phantom.debug(\"score_list: {} len: {}\".format(score_list, len(score_list)))\n #phantom.debug(\"category_list: {} len: {}\".format(category_list, len(category_list)))\n confidence_ = list(set(display_score_list))[0]\n categories = list(set(category_list))\n\n if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == \"\"):\n if confidence_ >= 0 and confidence_ < 10:\n score_id = 0\n elif confidence_ >= 10 and confidence_ < 20:\n score_id = 10\n elif confidence_ >= 20 and confidence_ < 30:\n score_id = 20\n elif confidence_ >= 30 and confidence_ < 40:\n score_id = 30\n elif confidence_ >= 40 and confidence_ < 50:\n score_id = 40\n elif confidence_ >= 50 and confidence_ < 60:\n score_id = 50\n elif confidence_ >= 60 and confidence_ < 70:\n score_id = 60\n elif confidence_ >= 70 and confidence_ < 80:\n score_id = 70\n elif confidence_ >= 80 and confidence_ < 90:\n score_id = 80\n elif confidence_ >= 90 and confidence_ < 100:\n score_id = 90\n elif confidence_ >= 100:\n score_id = 100\n \n score = score_table[str(score_id)]\n \n else:\n score = list(set(score_list))[0]\n \n # Attach final object\n normalized_job_forensic_report_output__url_score_object.append({'score': score, 'confidence': confidence_, 'categories': categories})\n normalized_job_forensic_report_output__scores.append(score)\n normalized_job_forensic_report_output__categories.append(categories)\n normalized_job_forensic_report_output__confidence.append(confidence_)\n #phantom.debug(\"normalized_job_forensic_report_output__url_score_object: {}\".format(normalized_job_forensic_report_output__url_score_object))\n #phantom.debug(\"normalized_job_forensic_report_output__categories: {}\".format(normalized_job_forensic_report_output__categories))\n #phantom.debug(\"normalized_job_forensic_report_output__confidence: {}\".format(normalized_job_forensic_report_output__confidence))\n\n", + "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n #phantom.debug(\"url: {}\".format(ssa_get_job_forensics_output_result_item_0))\n #phantom.debug(\"DisplayScore: {}\".format(ssa_get_job_forensics_output_result_item_1))\n #phantom.debug(\"Category: {}\".format(ssa_get_job_forensics_output_result_item_2))\n #phantom.debug(\"verdict: {}\".format(ssa_get_job_forensics_output_result_item_3))\n #phantom.debug(\"action_data: {}\".format(ssa_get_job_forensics_output_result_item_4))\n #phantom.debug(get_url_forensics_output_result_item_4)\n \n normalized_url_forensic_output__url_score_object = []\n normalized_url_forensic_output__scores = []\n normalized_url_forensic_output__categories = []\n normalized_url_forensic_output__score_id = []\n \n ## normalized NoneType value to avoid enumeration failure\n url_detonation_param_list = [(i or \"\") for i in get_url_forensics_output_result_item_0] \n url_detonation_threat_score_list = [(i or 0) for i in get_url_forensics_output_result_item_1] \n url_detonation_category_list = [(i or \"\") for i in get_url_forensics_output_result_item_2] \n url_detonation_verdict_list = [(i or \"\") for i in get_url_forensics_output_result_item_3] \n \n ## get the set() or unique input url parameter.\n \n index_url_dict = {}\n set_url_inputs = set(url_detonation_param_list)\n \n for url_input in set_url_inputs:\n url_list = []\n score_list = []\n display_score_list = []\n category_list = []\n \n ## getting the index of each detonation phase of the url group the result for each url detonation\n url_input_index = [indx for indx, url_val in enumerate(url_detonation_param_list) if url_val == url_input]\n index_url_dict[url_input] = url_input_index\n\n for idx,(_url, _score, _display_score, _category) in enumerate(zip(url_detonation_param_list, url_detonation_verdict_list, url_detonation_threat_score_list, url_detonation_category_list)):\n if _url == url_input and idx in index_url_dict[url_input]:\n url_list.append(_url)\n score_list.append(_score)\n display_score_list.append(_display_score)\n category_list.append(_category)\n \n ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation\n #phantom.debug(\"score_list: {} len: {}\".format(score_list, len(score_list)))\n #phantom.debug(\"category_list: {} len: {}\".format(category_list, len(category_list)))\n confidence_ = list(set(display_score_list))[0]\n categories = list(set(category_list))\n \n score = \"\"\n if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == \"\"):\n if confidence_ >= 0 and confidence_ < 10:\n score_id = 0\n elif confidence_ >= 10 and confidence_ < 20:\n score_id = 1\n elif confidence_ >= 20 and confidence_ < 30:\n score_id = 2\n elif confidence_ >= 30 and confidence_ < 40:\n score_id = 3\n elif confidence_ >= 40 and confidence_ < 50:\n score_id = 4\n elif confidence_ >= 50 and confidence_ < 60:\n score_id = 5\n elif confidence_ >= 60 and confidence_ < 70:\n score_id = 6\n elif confidence_ >= 70 and confidence_ < 80:\n score_id = 7\n elif confidence_ >= 80 and confidence_ < 90:\n score_id = 8\n elif confidence_ >= 90 and confidence_ < 100:\n score_id = 9\n elif confidence_ >= 100:\n score_id = 10\n #score = score_table[str(score_id)]\n else:\n score_id = round(confidence_/ 10)\n \n score = score_table[str(score_id)]\n \n # Attach final object\n normalized_url_forensic_output__url_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories})\n normalized_url_forensic_output__scores.append(score)\n normalized_url_forensic_output__categories.append(\", \".join(categories))\n normalized_url_forensic_output__score_id.append(score_id)\n #phantom.debug(\"normalized_job_forensic_report_output__url_score_object: {}\".format(normalized_url_forensic_output__url_score_object))\n #phantom.debug(\"normalized_job_forensic_report_output__categories: {}\".format(normalized_job_forensic_report_output__categories))\n #phantom.debug(\"normalized_job_forensic_report_output__confidence: {}\".format(normalized_job_forensic_report_output__confidence))\n\n", "warnings": {}, "x": 0, - "y": 1254 + "y": 1260 } }, "notes": "Inputs: url, vault_id\nInteractions: Splunk Attack Analyzer\nActions: url detonation, , file detonation\nOutputs: report, observables" @@ -843,10 +804,10 @@ ], "playbook_type": "data", "python_version": "3", - "schema": "5.0.8", - "version": "5.5.0.108488" + "schema": "5.0.9", + "version": "6.0.0.114895" }, - "create_time": "2023-04-05T16:45:42.058432+00:00", + "create_time": "2023-04-13T14:39:45.850050+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.png b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.png new file mode 100644 index 0000000000000000000000000000000000000000..d09d75c573056a0706c9280a1ee5a037688d426a GIT binary patch literal 69469 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�MKoKgy*zCs62lV!$waoldUw37Vj^^fJuTw@~zDF>jqub6SFjv%V_Cu3p8 zF~s?ns===O$i(j0KLk-PXdcX;kI*P8D_2!H=DphK?>=s>K|t$&W|nRbET5uFPNMD+=B$VJUL_5M!_L-HII6hHDl6C&66w6Aw>y%>6=7B?_*qR{&qa6@ zeksfvR2g#cPMC2kB{X=Le)QeJMr--@6zdHjF>z(b%-Fy5dtrPl?jelmdVbWWdvIVJ z44o(Hoxf}J3lNa4kqV#+x*=NuQ9rDoa`gO-xmX;>;KS!`&`QZiL!nUIq5_p`BkGul zxb^OD*^=f@PV3>1u9FhX7Vh7Ad0lPx+5UQOe{`KNbWNXiS5o0EF&SAadC-2-2CE)HC$Zty`2{DP;doyk24 zBhj4nB;5lr89bjhS_)HtTPey<&*J_3FhRD4850=Ro`b=nQaH698xuot_V!}$Z7$_{ zax}#s9lAzWLpSmjg);Ey@?P=T#sr4M$9{pY4%5xJMJz*)WF(>qx(WB*p8e{JtRhl*gDKLckm=8ymdPUu0W=xnt$tPZ(DlJlOXo-P}YC((;L=&v=ra1NzmnvyGx1 zDnx2nlnOS`aP?0xNNhO<2+NA)2a+UTk9Pv3B~wy9(3Y+y9I04DBAQo^kiyOAhrPgm ziLT=6y;gr=p?O>?;ia>DK>T!(DuI2H6VyI34KfXGy9NLD;uXl((_bQiyTlaqy+*-hX2;t0-nH-z?|cRszu>!JeHL z2Ydi5RYdanyTXpMVi)vzUy#D8V$7BkF)Ck|WF^FEfB6BCn~kOpg9VJcFnqgNqn=B^ z2#B!C78S<|;q{NQ(&HZc{TgyBx`Q|$*%+}$0#Y2NLfXy8E(dZp(H7rx=k7e!onRId ze;y-RV4=>Py$e%$Tw39c@ovHQ@9Wg1gN{mu#?Hm1EG{0zNDyu*Oq@EYDic&A)q2r=p&T2s0w` zhd#E_V@yY)#AtL6_>L3M%uhzX5c+4RtLyEVi3*4%b^yX%oE@g{3XI`rtrr7-`~oF? zAI>ec5Y`iNf64z`1QJ(3Gv7yfkmJUhR`>~hRYa7rl<`^YlYj_{pyxRL!YbfJ$o|4r z6rX>xH)|=C$dumQ9*1jTgJxrg&YT!fb#{XNx;P;LcRnEcM(XF3ln(EvNShCNEe&!p zrC~g+WzH{O!jhX~e_)$UYJkRhKV2(P1eu7C$Qer4AFx=?JVeL-j(o6WZEcwif&A2J z_yM(w!!{0Qzp)L7_Z0-aYbwBYiLYwo$p^^Z;>UAps(WlfO)AR9#_oHcd*F$82MGHx z2@M7r08clGvd*{N4*Atv53E@4!f0#|$pSLotsPoVD_DVh$tccmNut_=aAa0)UOK&a z!t(nO!YMp>9}g7@U6HvRReTlRCiIwEpW<5IjGP&@04%ZcGMi?q0Jc-=tQEmJc4Z|c ztM>^R8UAX8j7k9X+4&o%Qpy50Vt8Oj+hP+;{Pr57hRPswwRNIp-Uf0+feof}?k~Zy zmzFrKFgobdJb$e|2z`~R)*|?Z%L6@OEp;+5RTZLmI<#97qEHaZK7&Klw8I7J$e?Fu z*Dqo+0H%z&cw2gOo=%Tl9InwaGmqqt^(X3x%JbiaeBHeH@D$M2+Z(`bhR487Roy>4+R;(Tp;9yu@n4vNZll3jvvx23MybOJgy41_ zW4Om<1qCNjaoX7?- zz3nJNAmy#Wp*(A2US3O@D+!$6$sGVT6MT=coItioN!qJ{<6X}E6a@30uXs07Jl;KF ztm$pC#S0Pc?zbJH6sWXvV20{HHFtGBx6&29+3&I3F8F%l1nuw7oh+XInZRhw?zDvk z+Eb0aTgTmAlb6Zn$@F1bv|4w_YqWcQe|YDe=FNFTz_$ z0P)}GEy5wIQr|Rzzu1N_5*)I6KvDFMTW-e%2Gezy)D)bQVCaE&4nWHOS8p4|uHXI* zGC25TuC?XM4&hEfQA}d9VvE(Ly{AIrp{Ifgj3h2DMYNEv5T4f$4Gm4qO%Y677O8N) zOOtj{dpbEasSu`-GDHtXIsWU?VgFF+M3uqgc<(!6)w`-OzFAl+cG-Ivw%_2=+miEA z4IC0)f8Iy}@$JRq(3lt*2D>cAr2e!%h^N)R>&>6O0!m}W5utk8KZljW;d_hzcq%9WIJQF zm=&X%IvhQhgUF9XjqY#G8!xvMT&yb7R;#-{#JNw6sD5P6Yr;xXvY(p9yxiMF>Pn`{ zc=vfq@aQ>*75%ZIL$ftGd7{3d1nuJ!5_ zdThf4w@*_c3YRx9@6{Gc3m-k{R9*K>tzE*FSkWch!s7gxx=pN1SBgNAd?InW+2l|= zdhTFUtS$SFA8|2j^P{sLbi~5v>BV?OEyXBnC%7~c7O(oJClnedXRBF zKzZJQ#rk?roBsNO!D2>#ZT6N-{t45r<3VUz2}DN*54jyHa+Fjz9ZnO1?;RX+<{`t%=spk#^4g6oxH;0;K z%wyt&&um>_L}`CccEWg1a>sX8&-~C_y+8k$lZDlNgvKTh4AJWhA3Q~q$g(jr>7}c{ zD;+2FQlE=Q^zO&lQ2d$Tpk>$asN69ApfggT)3T(a`sUFJ?D@-G3v%i2P9_X_{NkPh zo=x24D52UbI`{k;E^XT%wT@>7NSqHeng;_kz5CVX-mEcR`gNow2iWVi(V>ygnA^DB zG0GBM@{O%X)&_oEYBEo~aOUwZ5p10nwaB-U8X6kyFR8y&bhkd`zSAGgS`cr$yGF2h zf!SJ^?qGJl%bZiivkaXw5U)er@jTqFv6-{d5>F_qid8GKJ4&@@i))6)0nG=la(sRt z^u4Qmdk-Zp1KQibLpwA2Wf2^`d*NH=O9l>`(r;deJ`u#`KLT0exE$VCd+du5crAV& z3mGJ$n;xZaRJug7tnSmUxPM7-Jl0ZkxPBhels4A*J9O$#_9|B*v`wRLRzUQA;EwuS zLdch^)O!`jy#ygK^ZjmyyN=2NJ@NL|ChYmJ?`Nl+ZqrO*?M*K+4olAWYeYUHlVhlL z%vw^$q(gWPk$KH6LFmaTMlGiwCkXk*D$AFcF+9 zlFV*pSe~qxPM>>2MihDuwO+ZC^5%U6^4;AIk z@i}Ldk9}tdY2&<4s7f1c8wY!Im=`G4X`rg-BT;hkwkHcX&4;(^h?e%uy-K5tjI zW#&G`8~M6ro`;3%O6AHCQ95!9Ro7=f9SaMqq-r91uNg;5*Yxhag-Sj2kxzMSVF`#m{P~PCgFo@|-HSgHvl#<|S5h06SeeQb*0kTp zz^NFooS7>#+BtUnN2qU_Uc0+V&F5WgUGG87VYjRK0E*s4>HMG^1Kyp?ejKOUVCcRTf46c!Hju@AQ*6zB>-M^j}ry+FDnQyguF;_H_+EMeJ zd$d%&w0ia1HBaoIfz@u;32#BlAg*m$Ap9!h9#4yT$^A+F&681%26@k2=?{PSqe31= zezC@pug--H&Z+_udT~8Usqca8j?2uQiqnoFYfPp`c&Ree0HcA;Xk|59kQYmosG}DV zv$l888o}`qrw|VfAu^wPzGd%G=rR>{+rY_z&4K-%zr%yiR{jSBf$QE+KV*Uii-L6; z7*zvKHu!su_HHoGfi2s%-|LuWYhUYbjYU$zQ2*q!cn&vrI=wB&4|0|ti7BT+@2)E> zCQYN$YsqmPIIpB3QCzMs6^&@-nCe`^@@d<|Cpc_xqjS9wR_2?u8X4<(3yV_yxyL(t z@NCZAe6y6LYHoV<;qB3H42CjqG6hIdUcQm>rcCJeGef(I^p7hZ61~k#Lb)Ys`q~jUIQojQp6- zxFApRG-$RC9N2*IeK|VE)qA|>q0g?nyYIa3@dr}FGBPcsA<+Rudtv_pF;b7 zFmC>~<^AEtCaGas9R$0?MpQatC|c%#R$H^k#Ky5H*Hpyz)tmM~ZsX2%Ml01)AeUqOnso80Sf=tUL(rDIrj3?Qkhvmn_7 zEHHRd#R5;N#6rLPCzq1~xm;;Vuls*;xvwCX>r%*~_;+#LbCAmwZ`d#WCzpfEhFRhY z{wcWIOX0a(k$W1~zg(^hp36Oo<{(pO0IQ_sF&<|LcVuOv7266IZA_l2W!aonT z%9MPBYxfI1to6}28HSR&nw-tav0or$x6_kR2t3r-rbV>zQw4+=M6I? z|3o|j;h09F6VEKqMEN}#CQT8|!JK}6qsx#~Q6UHE;chzVHJJ4OfOZL301)w5g-Hfu z5EuGt^O}zxvKw@?0&Wet^}L$gvez+j%jE{&kqklitEU5r%(=*;w_#whtyqDl+T6kf zS2q%{+o7(9B}Y2&uKz5pTaW}w;NxhI*($v)pkvAg&Z#W!VvkBc)OOHdQh>FY2IST- zB5#8SF{_QMOe8KjMAY)FGZWDcb0N<{+9jl58yy*-Hf)OkXM6!&8yvfpC=9qc=itf&sCa-pr= zM2lDLVQP6o#Qt;Y2Qmf`8DOD_3NWdEs)9rf$ml;(Q$rGM;bbQa0!qBoubA;{)6YYz z-V1o*)EOJ+DSLZUPQtESMwQ?03aCL;H8cDK7}!#kl$1*tgwauHWxnM$eBRlyT!-@e zcb%9%wqfx6lZ4oK6eq}DC8W!NG$t@M`KrQ67DM2TrNGeTcZzR*Df<$}51un)PBl1Q zd$#(;kUaz#o_=oxdc$D)>PLEVT^}3zTUf*4$v~q=%K5PzmC+|GGOXABp`I>Tzr5MZ z%a3stjGe1XR|_EykCN$l@JmO@qRE>&kS;uItZPjb&@;tl-V)9*NRw+t3R2xEFe_OB+(O!-*F`;+H**g=b*e;dL_tPm-#a-5Wth0wUPsd0LqlM4v0MbTxD76I31 zT@<*N$lE~S)c1@P-q^GuQnZLb^-H(?E-rzMspyXufnKD=Z2|DMAbW!Vle3@k(KqS) zBC$q=G6X?Mfoj2~635dbqP( zo_S$Z;6GKODP3DztF)%YBYQg!9WVz1{0jR%g$w5GUfPA zjR<%gkgS>L;;+2V8aB710x)xc=0QZy_c;8krG-TnA1sBw?vNCq<80ukrhliQk-}fU z!{dl{+L;KIs5MtlO*t z8KmxAIB8wv*G^yXdIM+^Y)GUu(woo(I=;c?g%6l5z)@Pkz~H(>mLG?r?gJqK!72Zv z%S#8)DgI1Tr#{T}0Pomnp=h%B)C-PnE5IRHoyE7(Rpi~tH6O*iYl^0Kos9>ax^3IA zg}&bb*^&9lmf}Qqugyu58!Q<>ZNVfCZ!m~?-aayW3NiWE>sGZZEipq)k$ z-uCH1i0a!)iBb@foou6zD!HHdz&7DfD`EARvsFn+b~{LA5X z!r&U1dUQoSs1xr!$dKJ!Zqv0*MqX36KR;36b%!cl9V8O_&lnI8(@hV--u{xLd_PO<{Mis}L%ZJZ+Rs`- z^@W}z)pK0MedmB)kD{jBQgelH$kf45TUDW=pOvTFl&oY7&v!)dJ{(4~XxZjESURVe zeHZWi?m>jb2O8n}+gbgvHVzLlxE3dviFkov6Yp?-KylbR4>+mV--)Jozt@~nJKoAUU(J} zm#I)o7CyLpOV_JIM)aCb<*y_KtX)36VEPRIN>%7lI?YnDLx{<4+-_tOn=yI4XJV6k z_)}Fjb~#fCs7~{qB-nl96jUa{Oa$-KZkjIh2)(*m*CMpHv+r9su}NjRDtk#7cQ^f2 zeg*ZCcuP)k81_^{iU?Rfa?7@C%6Y7vPH(k8ytgWr$zgl<$sosA)cXv)+WXZs*$1*$*$k#sAdbdzK;`&;u#CDi9$*a0C{?c5f z3HhbF>tU7Qx>I%lM?j7i(MU`ew#do;yt>aP$AQv1&kzl`3i;`@d)K2T$99D4alz|7 z#tr5XU#7V9^!-$`=f@p`+i@{5qL173-ttSU@Qc2B*&}8{R1JzlZ2|Az zXuDWtZW6Ywei<+at^yzL{y2M_T9rFHJ3CBqMLd~0JN@!v{*O((M_7LN#^h4AM}Ba1 z`B%2}*q>>+XFOjIyK2}CKNQU0Uz5lp^=RDMj z$?LnW=f3mm{3MUl2@B278~4n9H`%4#^83W3`|}qK1Ir~2eIqbYz2QuTx=W!l!@-%u zqa^0Ad??T>A6|~R@SaYzDHB__g*&- z^TCLC?=B2;{Y}5X{PIVV-h=OWlg?m+3YY9b2L`f1A$QKPywa#A%2@D{nI`ncH1v3n zaTi~Jx^dv{axIYk8c6kn_9ceTGTm+zQ)pY$H`|xTt8f2I=m924KwiZg^i7muRFLd2z0Zl z*kAO%!|R(mTKj}g$d1AmL^L7?v79bDfijejD$5IXDxy9G5-NGz^O(MwyFonNoLb9y z?QE-ZOraxM5HxBnnYtaJLn;A9b)b0duS~rpS#ZtWy)6~!w-ek|->*Dt-h0jboYJlI zPD71kmT>!<$^iQq&1QLPcbM-_?bsV9PJLma*_Z~nc6eu^X=^JxB2K#E5XvIpaqv^}OduycHUJR%)(FaE_f;`Zt^ zetY5h?N)Z=_Nwm(+O}(fZRY)mzg5>@!}`O%{ke(bx976Clo8y6 zn=kJu5?*W4t9P;RG5fC9fDRe1S0|g>JH=&DJ^k}UpA5`gbhWr9mWR1h>mMgC_dBeh zqye>98dJ;3$Jy9FVC72rEIpA3pOY5c8cewj(iq(&(Zs#?_v1pSn-M%uD#g)hg6p@> zitOEX3eBCqYVhm=Vemwjj4#LanroRH8o!8G^QU_g+2))tKkOSkLD2M0Xbdh0COoX2 z)|K_ef|0w6S{;1(-5$$r$1tW>ztzzuE#a}>R(9;}+b5s0kn1(fW4b*&-o$h;8b9`?CA2<*qd7!lJ-o2-Awb&OB!ZT-l)EUbtmd3xMR-ve)4K7+^ps<-!yHdZ7j3=9lINVxsY#KSN-gX zSoJY0iP##{p1Ju=`a#RN4G*`)T953>y1E`=_0BvaW9bRBxZ23|LY7Nc9y9E`4V6q^ zyd49S9GC7^o2`9;;Q2R0VywL2`z6o8R>?o=ne$^Y=;%$(ZgHsT-UUFI>v*BCvBAMd z5^1#F;4X%C!<3-?Rc*cDaT2jAt$#YTYdJx_&~F5xn>$v86<5j|LDnQgHk6Fn0AiI2 za`=20yYQRzQf7uTs#SYweWd$wv zV1n#`xmMSmMW-HoUyPzBz*ZmiZSVtmREMqa!3yt;b4R#-jq+sKmR6=HU%@srX-s8~ zeu&)saPC<&QaIy$K}Zy=Hu@+8IeaoyZbGyi`^w{ z_?LTS40Mc22pWoy%XAUcdgV~Ipx3;`$1~cj>24G5kbYwh{VU&fWWa-CBWFl%a^?{C z45iq7q;;O$rZ03Nm)O<7io!2}@iw$IiBAIZ+l^iTa&Fs}%)F8Aqiz#PA|N#!f4hD& zVyxRL@#jSj|Im`WkdOQ_^Nsi%pZx0)|IN6KqYuoai4Wyn`%jO&_5ZAhutwak@#MNc zqLW+mj8%OT8-IM{9n@w|%4t1k)?XdNy$90Rf0gmsaxv;ruvK9=w+`v;)~Yv1@~zcC@$vn zw7QZKnL##PskQEhkIDr*4{v(?+ZgsI3S-UPlM(KdpWCIgP3CQuKwt##4jzE3jIVw!0vi)$LWkIOr@lmU=AUTW-b)PA z-GYW91cOHFkGKXqfKLRCv56`4JOge`GU8I3lo6ZGfMR2x`p9>E=%`6x*m>A#H0#|M zIf>)RsMCC@^6x>CWv|=t4v}bvbg+o z;AjrzXLHry_h6#e>M7AUE5MrwfeMw|T+W<9)BPi^jX~8oXHYr^$5DneB*`N#QE$9M zRb7`HZ*t{!{UaKLAUZPrh}d<3Ub{|9u=q%e*M|?aemME)#EnhQ?R^<36oy^@x&L@t zYsldGv&QpMt=DxfaI1)8<)a!eqM1TnTI#yZ{MLD!w(%-{Aq-}nMwv27sO4u8_us#W zPbz^oB`^z-OHrPanquzGIav@GDE(mQw!W^uPSEaPqj)`K_CLG;U=pNLu|47Q+O2vz zl^-A3m(N?Sa^NlxLXR;^Hfk6I-ybuI8rPKe&Xsb_mt_@ zj%yzOR!EccB_^CaDFD9kVAQ!}cmim>n@PhbbA#6W{CJUO4h*h=C)~4F_N8o-$Rzmj zX1Jy@y_^8IIz~&HZO}|R9(iGkC5>42;Zt~~r29&={hw%LV)Rlxe_H=XVvJ|* z2G^CQ8Cwz)R?G3y6_p~4l1>voxIDE7^ z;HT>gDI6OHLUhW%GiY|U3&hq%^eCWbC>)aH4g-@`ee%fs->kg=CdNhIl*pd{g)9B4 z;rdl+y(rwjGb${CsOXSqjh;dO7SUe;4Pc(qWevHiCi|LECUvMs{(cTfHM{Cf8T+U& z0Eh5xNVvKZ6Ue)yIp5oLP?_0_&~jOxHQ)kTkMySa4i6X3T85ns^i*gS}g? zpUOsY*;fZVE8O(5Ddsyp4wgfW)p9}kB^~ z2N3xL?K9*PDEqI-$W|6$&n_&y{#;m4P(!8S{?4;5P;FR^99V^8DMa|0bXIt;s~FN_ zr4`cQI#XC$SmwL|?u?TDUyDS!l@3ALz_G3UPT7jl{2k#SBD8qxuzq!%E13nX6WPZkB-uwiCM1H z#ooFe$i}5xZau-B^ppbW4h|0Hr#l=q#?trzUkt3OsycaS7j|+;lQ)_sFO+Cc$XSBT zIDR(ryn}}cx)bv7e_u5M#BQLj4mo$msiT^yF}8GiE@D+*YPFr9v??Edbo_W*`PZ9E z+rb&du7L0h;=-xg0aW)FwzE@=ys~wb9hW;rnE#P{uXdwD%8P*(q1h>i7%#I2NhO*W zztMF*+ye(kS*r-P$GTA4WIm3JDNz*hI|}T3WJGY<^;UP@>ojvKZizkG{~L++To&uS zc$bmHI}lxb2d{1LFcU6SHfy^6Yb4iQmcRVg*y^JSHpY1^smV!In{NMOWQ1urtG(3X z>&E{d1lIp&+AJv21^TSEbVb@HH-G*5WM{6H&zfObDm)w`!Ss8~V1lRzm9PmjnUBj4 zC?-*P&RYYZvryVATXg#?L*DIXSJSrWWoHq71x5ZZnN=U>K@>TD$&)vTR-aTK2nQL3 zvxGg)VXOV#7>A@(+@QZ)Dz7(i{W+UNFg8dw9FZID-d+~G%XZvS; z67n3_DAW4CtA$_r0P%qQf$0v3&YH}>%_j?Pcm2P7Te!Km{{NM^H$?>nuyEg)mTrdY zSWL?B#x<-9iO0JhMAH)fpT?$wwH>tr-&V~U;WwM~l+n{%D{p~vv_N2E1XKt=E7 zA{E!pDkD{ZAnGSP2+&T$h=nPH!{G^BYWQ3Km$GXK#$(DV3h_VZFc1SL&vgXK{Ci?D zeK=7#1w-jysad!k7gVCPlcLc64&DQ#65w~ORV{-4ciC#!UNB_B%)ic1R zhPiJFMF8{j23;A$S}GqI?1yi&tl%r??O~5*Bdu|*@k9q*7!YlKP@$(f2ljL5M~@3?Yr|t=V`;(Go{~avdi&)Ix>lP( zxDQ*-IwRs9-X96}I`aNKmf?lsa%H%t zH%D3n_=-9^b6mCB2dovls^MqD{am8Rz!|*5@O)Uso7i1aLrY5(BOHUozFAL8Y4uq! zG2HVS;AQICnQ#+^UI|_w(b7|k^eok#0JLXhQFh>Bi{ck>7wIIax_32lsVS*O1ov60 z7GKb!|NVKV2>e-RQA4Unz91aql(nVuI|59K@jW>(Ee?a%e7sl3UphM*(rb>8v=n@+ zAT%~L-rcTsu2oTNjhB+*+O>z5ha}<3B!!W64NpVcXTehqkCK9`ojA-^u%LKsr0Y)5@7lHv(ZF4BgYvY> zxb3Gb*P=KFNA1U>Aatz;i_h{Ta1{Uu46K%Ut~wsP>UQqd;op)$?L$y{rp}_q`j*fVx(Za&Q%vMP;|1j)#h5CM5q98>nO=#Rg zxBTlF$Cn60XEeHZ4@>C^tEs$;N#PXr zB1hDPhi*+4@4*qbh+pTKIq7SZdDl-6vTPjiMVB5vJBzYvv;3>stPkioa$B^xl`yA( z)bU?wIXILQ&%S!h;MyJ`GC(}e&*WNjn+~!Y{)Mg9+sO!wHheSk2mMpyZFCu!@D3n+ z82x)P@87HO?Df{!t6h>RI%+etRDcMlbkU{_@i*M%3m@E;(pz}OCnq$kRROJQFgarI zKs5iw8K$b7_IjCS+l-nZe4h&;22t>y#+u|IB{0oSks94sl#{IjRMtaAYG3u7&oN_4 zbIlEbjn2E`oxzwm3>7LrV{!)y95?w1*ehw3tF`WRT>;^uE1v7Aa z%mS(#1v$B=7&zuyOLFPFqfv_}mch|&IPvi{AjOL0-t!Zt0Ox$2B_1TRPn}0`Fs)GP zC9qQ_+gWPitF5V#ti)x$6LAut8HHD-&)q#;e<7)tX~{~EdcWcT(m7yL3~Dp+8Ey^) z#LmBe#igb)B=9rju0;r>NOi-;$1}3M&+r*c=&l}4PEL+ac#?iw>@s>O@uj)He?OwN zwpNu_hg&(m47qQW8ob0@=6sM%xcghkZg&y=`GRAn+%+hYM?XEyC_EoG&(Dydl!*8S zIwlXCB4cQ^T=b9pzwejXKQi=`z}6BDJ21KC>l0buLBm*!3g z$BNK!+P1MlT~tu;EiUfN_6soL$rUr|g?@L1QB)?Fr3?5EWRberE{)=ETRE^Bezl$*0{JB-W75ZCi!(Q}RP8eC`ai4ESsEpRo%(RFS2 zyHjBjM>u|vE5GSQP3+iqciUY3QRpcW6K^Af_q{gbA@e6K_z|&9IhqriyHA7h@=XCF z8(RRxwjAAK^5tD~SIJ&$C*SxU9i@5sO87!?ZUHo74))cNk0)Lz7(qW7y5kvlW{0$|lqC%k%PS z?L2v4+Yb45@99CC&TmZTeLnEC&A7zshwXQr&`_1LFR@oYacJ;>M-?rJdzKX0bIAS5 z9>&QE;qEQ8aMspB$2*{}-jbfY?VT5PC!J-9b#F=Jsg6sWv2^CobHkP&rOJU_+F9R6Prx*QN(mLt!;-%Qzhv2qNAOcmnC&-A*_H=g^jzfzh!!{pmjNMYYb zf?_Mj>pE<&ukQLexL?_}@T5vfNg1UGG&&Mb#y*0l_12UY?b(45>9w)LHI-U6i>wmxR*9@wGFf>v$m0z!d;7ws)$ z{!BPwRyCo8 zmBY8HjMVlsa$Z0FTkVEW>M{n=H0xF9lk2$N%Cesy%jX1agc6mtobmG1!T4<3$lG{s z2j0hSmE&5m0mC?ukpUN_&-`BZP>#Mz( zMihM5*w4Yk>3sC)h|N{y6eT+nMTbr6esG*2@>o5q*qD8 zuXJBzuYSrR`{uWT3~j~OEg5XzRTf)raLk7-oiFL$trFTS7nKz$-*1oS7dP1w>mVO~ z#?y@}E^8GROk!DVtSDfUW|-0(po-wzLVd-E^qRDWB z_PMV3qrBui7EHF5dt&%hfI=-N&3Bq%+*41B21P?CG*oVM`E-*NMavgr7`_y_-+_f% zynnyI+xx-3+ZfqHQ#0A?yeYe6xmi6lgYRsidi(r*u}MAYNa(|{gv7kiS)1=`bsgu= zm9P=PYZ*EI?H3GD64hc54v*Z)$Z;N7(;tySoZVVUc?(y`zr}ghW*JC$CCQA-`L!* zh>53G&K}j@YD&(Z2eO5yTle_5V%IZ~yY+Uxcj&)Z*82Vvcfj%9O~hn4g^FvDTjc6| z*A&;1`(h>FHz@DMjgN3k13P!S?aAWA+90f0& zPTg?ONSIS&SZIE|;5ZVUdAFyM%zenzaIyM$Jyw3BKa9Ecm2Jyu#G2il=6(ww19JfKzDd?n^YL&K>&LD; z&|gA)05s~nIqZdcCwwm$jZ1KeQrAnT_ZTqm!0=_Q%QtMe`LlKZyah^Rt`kC}Uz___ zx0%novH5U=9W0GVKxdJlmZ|SYf2WQwUy`=5OW3^g`jlBwDOj5RwZDdTNuQ$Lh7Z#)cD7X?T5ynZBcxV9W0*p-Fw*`XD3Ni}%*3`&1sC(R>vRTJG35nrzuM<{kZo55 zZs)ZL9p>2QId1w`r8!#NW^sn%G2XP@dv*_sIxn2PRt%So}j>)ae~w#(h{c2`$d zRXAe7XUxIvv5MRVI7G{__cXyg{RWK1FoC>l(KrWJN45?d&^C8ZPdYFxNR}X#J*mKZj5qf?-@2bvaOphpt&kge=4gIgL~-7Jas{>c>hX_&$4QIVHwfktnXH8ecegZgj1Fh&&$VrITCE^| zWYRF2d9$uth$^>9!8MXO-CjPEbeCkSau3EFKVNNM>~6AchS0T14bfTivOnuH`}R*W zJ?$5gL4%A@RX+*`R3XK0ceHs9^*{+k1AiR8*2k zXqvQVx-ce2+1)+V7DjzloFCCl7u$W1E};AgSFxZ)>dceE?bLJZE@CJ*d{6-{_azQ8 zPyaiS{6Uuky74r*bVI`9J=1XK!M|rSL!)os1=MfG3S#=uthi!#QeAG3m!f8!(kd%q z#b@DTupcRIQ3aXOV~up#TM8>j@GjQa@aBN5*swj#v7A^b1`G!hBU;d8?L-J-0t>~m zm@&(WkNMd<5e$7w>Qtm&5s9UM&&rgr@ttkDIZwT+G8cPe!eO<#4Dti#+R z$8(zr^7=h=G>(ns;!KnCQ}q}`WMQ*)t~L>-S*vRL9vL$>^~QUKkc`dZJUA`!uKU@< zs`N8X<W-^#U_>S>?&GR5mx zw)ssPzx()lYDY@?&aIrTH_B%oLs^-e-`4io#{;oAi+Ba2Z%bq(_VoKPbyB z?2Io8MVh{Z`pjAF*nEQ;qD{H`Q-x0Rn^loesPZzh4)apmasCs7Dek7V*dC`S8^LD{ zP`54hx&KtdNyOZ8K~CVF4m>Q@MF^5gk=pfIoCdonG1QV9r|Y8c9bILz^h8u@rLTyY zM~+Dj@2D#0>GsJ$xg++c+(#9YY=1)oi6LL96!}uO>jP6cn10fL{z6cH+TSz@Oau@n&~5W?0zmo7Q{uxXfR1{_g~G79*pA|VN*efae%1WyT+IZ0EM5@O0o-qiaMMZ?%pwb-rk#i9TC zwTGa0X&S|s1J{@bx9CG(+SohjTZSEMpvV18oWyiaC1z97i(CMKAN2F)mxGXE^rPsz zBYk@Gb}i|BdXyjg;vu*MkW5g@&yRlDH#Ey5U;K)RQ?I57W>>e#i2WfBUMnY9gOv8Qb@S!8pSr;i@ru*pP{ zEQvr;#~-hba`!7WW9#W^Wgt!Te4b=sRCu8rQHj_~_)OoAYQVt?d39~ECzNUA0Y z@`6+ms!$>8w{I`;&UN;s@9ePOIQ^R%NFU4>-><%3q-Q1>;eXVj&nfgI6_QCtzyQSS zCEmO7#v^yy?|C{VDxSQO6#yr<@Rkmo9$)?TKvE%4fle}yG&JbE&nvFX%`eHv+72Dp zanE-*gC$UCKVF>d>P5rV6J>yCE0r<}U8sRq1W*IrRoUB@YQP$l_F(vR+s12!tsM`4 ztKc58=PoBx?LtrAQ+z6PhPU#2lNz=HyP5OSYKksOML+*sRA zv*+O{Umh{ukIpzzG{thchwQ98-r=D_ zW*SVpC0@Zxp78HWvm}RBH%_nHj;j^klmL2>!D^1a zhujhyeL$pFK?e7jpnaq-w#xo~a$vw5$;&G$zjM>Eq#Rz)ZZ{s{%3{RVZ9e*5DciY1 zRldO5Fuw#&*LDEgi8|!d?VHM=b0?+>=7U=l2+})0aZeEQu<1?Vv)2nR)8XY)DnfaQ zJb5BA+FfJj7*emwTni~4d4MsUtf=~Ck~a(6&2Sp&UBIVkm~&gRalj@Y6o3DO_M2uy zR~c^i2-6heB~creKf1vv{b-636$kPPjoV^d?v=@-rp2{QQ;+L~h2e2xuC|>q^q3Uq z>3nW*$amCn(EUPx?lw5UrYeVP4UrG`?@&z4PH+M6snfuWFA)G;9Y0%rL4gFjT!Zv? zv!_YeXx7o5Jhc|2O+Jiu5e_u#j4f~jtK{6Hj>zW4_DaL1PnFhrkE`BEv(`Fq*wZ*~ zPlf&smAyw`jy)S-?HghA5!5+YuPolfpy#G{qA6j{8~kR?dSfLf^sw)q1hQY2Ar*)% zUNYNN1byw>$C)|t2~U&_Q7_l5bBWgSa`&q&YA`S+fiF~Q2Qe3%VIFR?iEX1+!tdQQFdi?mrjr8JJJY)i; z5QC{`u3Q|1Tk$x3H6QiUJP zeEb;i!Eo?=f$POc0=yt(lkZ_262yrmKL+XYvMRZ!4T`P|SBQ*gL?@z4rcllTGYyCF z?rYOwwFB)}nJ3cr=Rc%stZ=Q0 zv$*$!Ca_Yq_f%8@wGjKN{2rq?qdtFc@>EAOJhOCKCik z3n8YT=M-0Gu=tQhxy(*4rZyo?oeUESPrAbUsKR@H0*sODeXtP5H%+1p?$knpE92iw}~8RH4DLeT3#%F{L!^Ym!2} z1at`(62QUh1+(bAI}!Zf?B&7i$)P;J6r4{u1x!EHumb;LhGZRYz_MB4djE|T2J41a zgZGe^@*)TFQk?h%JP%k6*>jFSAx9g~_xV!LtU%CF723dag5SzCppIiZ)dLD(pN|Zb zRRmL@vBeU_KJKDbYW90;uf|ZxyA!o5((er7`vS#V-+L za8oHLK`WJ(5b2Ts^@LYFq!CeZ2_(D{(@)a!DQs+qowHVLgiJ+OZ|dLu#cWOF*jLeY z1BjA+dA(DL;M;;qEz>Q?OHE)I#O9;KeVuzA z^Lpx4`~tir(lOQNN29jhLLDP=O&V?j6C=ITR8^(ZY*;@5d&EitVR|b9j+aBIm~)_Q zzL?Uw={1nHkOX4!H?#9kl!{bj_8XQQ#Lgon;L7K);y;po%#_FpQSiyTA|g-TZ4J_; zv{eTs{mKfKvyl=k>qK~`hU)m2p{}RiP`|5;>2VIRVF()nebX}IFd*{4BZqd|H z^xVYh5iq!3ApY7y&=-b2&}Cdv2~es({AuxSE-~Z9*GfVAk=CIm-&Z9tlxeAIwQ|{EP7XQu=);)(xobet#AGTCv9Aq>+nnEeL zLc#MxwJ%5u_*^xQJ@ZpK61x$H@rLu*p~;sTr$4?LIj=aAJ&QQr;@Wu3Qd_&P)3A$* zi)rb`tv5k+e`L*D{-l;$vs{roJ%Nj7%J#V0CZi?~zWo?)X9~coIcZWuX>At+1D-&OURs6BV!@ zGg5BGRI66tttkx&mgRwn?=iBnQ_3w=hbgI@wvBu>qAD1AQIB08EEv(N`#ZY`5_{|> z8{E+50R*>Fz7P+KcD8V=Gy6#P8`tCw%>T{)SLVy$xKQGEiW+*V@CVtegVk(?{VpjQx;F=MY!&?8*?-(UoO>( z1Pt$B8=Y@5yq%`%E9R14=+-0spGo;h*3s!W>-9plKI$EG!>66MPclF$2VR`QAHCl# zO-x*;`?jdwm_rQlZzyxx1w1?VWsm%29NU zQ_j80Yx`zlSs4^DvA<3_UTP*_7H}GUnxI7>`w<@(-hxls40$ihkY4Edcf*b^@K1GS z#$5#kg`t4~CL!Ct_ugYuNZT~&F@nOQInWBz>PJMv%MooQdC0$oxTBqEnT(`wHzf3~ zUSAPFqnt&p&HT36`-C5blMr|%frDf+^Vg*6vG_3Kzm*oN_89t|B;QRTZ3De(x~`@A~d%}i%GZ}_*jT3hVrBy|WM&X2z1zCbSM^~Y3SeSqYdx1Ou) z9gmem>vN!p#%?cC5xt5EKTFtftI3k^@e};%>OW$VQEx9gsHO86SdCv}0sKmgUC9{m z;KYt^VD4e_eyNm#neqoI-X^Sl=o8I_JC9TIDj#;l2GM zsn^jQF9UcDsTw3oAZIOkt8EovRp8Oi5blxaV^)C*e( z=gfqXYNuy64;Ev_&>Xbaie!Q+=SC1)r!=K0>5_|s`rcMvae;k`qwg}j*FisoE@ zJ+7{NRWa%=uJdez4(*vOpFvGHae&UxNA~p~1<3_d>nl&wr98~Uur&zQNqb{t5T<}O z$-h2%#9}ID#K|Bt9Q>#6_iziu4eH}O0@rI$*2+H zD08!71NBpYDVgR|2NfFd~l6wthHk%G>V|6M4{zq1|4Tu#bc`qU)s^2#5I0 zjQRzFf+cjZG=-*FQ=PXBdvyj{dPN@EW#{Av{G*|l)ZI%_i&Jo-QT^Ybe9tEL}Yc&y5^|MRw}K}ZRtdADJ9CM^2g>&~@5L9u>lWq4gjmJcvog##qj}M__>yvUiHU%JrzNAQWmW#V5)Xzs z7Wzf!xC%0kcca#pi=M5Q2qPJ73M%D^uu^bjxkDhZORt71Yer5M`tmb3z1zx`$Cg9s z&aCv^x@(%uGQD3v zO8y!5Rj|#}L`2hhCUTE^aKNe^Hc)`O-)W7)*jztGjP)G$;s@y>(xZ8Jo4!h8zEh|p z(ieT$F(s~v`$L1cbDOu1bSJZ0Vk^2nr28mX{;E7}nU+%FuV2BtVjA#Udgo)vka+(s zYU=Llen~s{W{#oI%{~YyTE<)6ct@-sQ=(0sr;R=Pbt6{r<%&lv!%VMtfmuuQn730P zcMEn?V$sE6sbo160IamWG9cq4YT6zvI^z{`blNl~vyoJgvfBYzUWY!y6w!^|@kZAd ziz+v@#B_aooFp_GW>)&ec}u3QG4{oSB!-Ty^2SHv?yFpmXy_}K;SHU&qU}iVAg^Jw zS!pk9cJ+bk-Q`;X7)nEL@4CkQF|@u!c%}IMK4`|7DiY*b>3>)pa};tzIv#|u?Q3OE z{I5BXM?~^VnytHsDn*OSYlRd`t>@s#2GqWd-kiw~|?NpODnJ6Kvg;%6+=vV{u`9T0rsHI*h3)$TPS--l>T=@hI>xMZ-Dh^@ z(p-@kwf;CC)5q;)%L+5iTW8m-z@u%OR=QHgHYTR<{$rQXg@3^Xncn*T`^7@hJMQ_5 z@nbL-)vcyOe+T#61p~t?*GPr0{jOU!c`jCjN=s4uP@vfvqvJZg&Dn{uO=IUZC{usR zS^whiA4JbMpl`$l%UEN@S$vW@4wA+czJ}>fh-v7WVNoX=TUzq|uwq^@6F@X2TAI-r z7C+E&S$@rLzdbVC|G}Boe{;J<>Rn<<|MYq)1H` z%=+C^`ee3@2RjS~Rkp@3>EccFCzt|a4efA|xF*0l^-L|SawWk|F=DE7#DmEWZc|22 ztZj%c=X%U@Rzp#aEzZ_}w-c`t~o_1+3 z-O2ptT@&|o;o%oo2$3BY^Yu;bDYq^|<(>JPN1a-l!XCu}hb=rQ-f! zbL&o=!*VB<7>aK$T*vsRUyeU9<63OeVq7YN#Piq*|AT4Eej$5Lg|U8oH)!%*yP4;{ zW2Y491$)-=QrD#a#oAh-X0CRPg-a>xy5u_T=WRU5d)l7(+<0(jjdJVjt4O10Zf%77 zS2lvb(csw1Q0NbO6zzjdtciLfnL+O^>1(TXvW%V>`K-#yqj}>sG?v6Q+5#t#(3zDMKLD0iP)$>|HB4h!k`g?35yRq^j!ltPM={5>E+fJwLkzz z<#1c%T@OQQKFlDxs8V`=`Q4d(+Y4O_ly)Cw87ENmCcfIDg+!q z;x%SonA^l#Fn_f8AV`>%@Ap+-^lm%k_w{SIXn#5E@P`+llC>XQpk&enKh<|1CQDxe zY6Ac>3(^+|2|35@g8az7Brk;sA};Z?j{vp1Qfhkj+65GM#Pk-WK(}-MgWK^y9wbM1 zJ%Ve{8V=yeR%XBfDJ7ZGs&J)a2-_K#KZV$4V#6$@lfjMf9KvoL$e~suSGAoQkNEH> zZSg@8pM$B0-9kr*JfN^ve|)=@%=D8K01fmpHA5*l{py~AgCCo^69uT`HxoETNko#E-HmNZC6oG%bA(7W@<3m_o{b`^DaKW}euQs?a+g zUd}lX8v0-S6$ik@=C_SG0H2ZYSDy)euuB`<>rk_|8o!)lIvgRhSo!Uq_067paQCK< z0NEfmA^`W+)7MZ)>gl|8jd=P_cK76KS;=!I%xf`x=bOGLd8A*w*kqlqskBwrjeiss z8>0V=owC(C5)xMz-Rt1+*W*CHhMwq-VoKfatY)Gv}9Vl4b+uE9Yn^PS7KO zq*57O*}KG=n*r9m-$X=n{=aYmbO7Mc0osoN7x3X9DL`9pJ?Xf_uRjhzUe2r2(V|ja z_W7L+R<;LaAc7p`j4cP-X)bciMV}F{Xc|YfW|N$-{cBob01-4aWnsKDt^LG~?0S-T zQy`xmjm-f3q0h~us&MNdNY;sv?X>f052@t=1`er;U7l8@jv5mU*cQO-Hx=ie{6Xh= zU~0^-M`(w}$cCL+_I%j)(iEWtM(d5(t6U?IZGWY8Gk)q!N!iyVtMOh+s?0yN@ph>U zIvutAS<4Z67;VBPZvCW(N~-Kp7PGg=3Yp%9%J!ysSJ@o%RatM3u=zr%fm?-o zw(3J6$NvwC%TCwe?2Pv4`Pd!9TKiXbO%B|LPA72z;Kc#-Fx4lV^ND6ga@$(e$sXY? zYSEc+0RaKE!j}r(b(8&NNjmHvK(8c~E^sW@B9~jA%$X7^1Wk~b(!NMs#_HKr65rL0a07NE0Fv zkDiic7&#HPc=%+8cQ}q5VF`gJx;Knje5wXTj z&j|h-VcZK-{|k^VB&ahQsSC^T8Bltwc=9QiHs-}SO9lHE#c&}2V?YJE zIm`-)Etr4O0u>X9FN1y;(lu}yx_zLSo&9x4nV6| zDr9pp&ll%eAy3kTuU-hyf(-~TXN=}DRp2wwl2sl}+DjjM52P5p)?SOExfCGKE_X`K z=nJMs_BIe8qG+EF2Bqu79$=P-$(f*lA|b>@FKxKWayUM2M}P#A!{nvXA7G415`V+f z0F&J@PI~a;uUJ1K%b?{*tsN@dv8DY)>7m4>FIPzKyMsr7DIO*~kjTJ3y)+wM4Ni0Z z&%qxzni0pT7Jd(Qo0zV1KDOkVLL5qYx~&YDLY=1Xf_F`JB%0rtwM9j&>a0fF>qpV< z%ajiGLlp*Z30Gc?Mo@Ok#}Z=~=oYqLTf@caX5c3G3Y~OS>Li!hR%J>~ZwB)XYLn~g zuaL=Xe{CytDSh*9^lT~%acwQQK;GVdtz>g`9S4V4T}A$(ULJpMB>U>rt+)W6peG6n zp03(?q&1b5gAaz}In4z+dhRFxo#;|jGL)GiUV=Ka^y*y2WxRR2ah!~MV?xEg zsMzXPHoo*7To$=60T3_T&H!<_X9R3)A5nObXsT3LFrjBI86Me-Xs|^h@xl;XXR-lc zLf5sNm0#T=BY*-8tJv>exzy7eK>NG{)u}Hv6Am=fd@X74a(fugLnoYM{>zaQU}Wo< zK+fg1N}h*KxPm5^C!qptWJsx*;$`UM4Ngh#6|4Ho6J!of&{bGV6zPQm?10mia;?Md z^5nI03?MzXq{lthkdeZ|!m4tXaz;6$k0nMlNN7rQe&_nO@<`}|=A`dTJTM%oQ0Mrf zqEZ*S5LaX2QX=zFB9-Z=iYlPk({kDFL7CsK{2-ni^NH^ji$WzURPjA!XB{ynEw-WR zM4Bf=J$CiZG9(W!HKc|={DOkKCY@1Im2iuvA8TPbHPB;|@K{0RZ;8~73I?3DH`6l) z%YmFhFPl|&5@tq-4pW843~jYRYBsd(-72;Fr(=`hsalQ2CE|ZTQe|L)^)c~RncoKB zIp~*1VEaOvi7Rb-i%y6YV#x^33EnsHy^ljQ%zt!QFO50vaqa7G0hP+Go_LJ|d3i37 zE1y7k{D|yDO1dB~Uns|lP!tfh#fZ2i>0MkQiCRJeCv;ZXmb5@EWOkyo;-kiT~7t0fZ@co`f64F-(dEkhztK6Yqg4P@{P1!H8Z%y`1frAa zbGwqnS^AL48B*^t^J8zcCBTek_jHgDOE0wKJJdg=6=xz~flmaSV9f9gx#}$W?D12!sQr=RC#2cXm_XTDa3hIH@Ry|8}s>%rR z=eQyHN7hJ=kGJ?97HijdfHU*HZZP6k$)Fq3P{4*=ZNgvhQSYLK^$%j@-gX=q1Lr`# z28?2F>i092jD7<6RL!8#O7@_w4r|#s!JnQH2ik`jpCPIMjDjiRLIl~rxFm3ktttR* zR2()(qah`K(?Cw4=0}o(hvUhwD;~yVlkuc8UpgJoK|X(TCs*Xs=`aV)lf@)6UyG9U z_I?!~U*n0;qkZbzov5K>VzS|b7A62`agjJe!{!*Fi5wR%8zUW^_Y_J+OpzsL;%;0d zP~G!`|M?Lx`$USg7nx;2_s;#MAvOv`Gr$ZOGlT8HXN|tJ79Y<2W(lJ0GQFnk+;0ZI zQ@pe?J?9Gip_VbYa?S;t35$B!Hyvxev!Dc90Es*xTth+#S`)9fJ_`Fw=*v~}%)xQi z;|JgPG9a2tLH>GF@ejb7$U8M+sI)nY0+jy!;lhkIBu%y$l$%(O7LB^B**~<*p)}0R z=lCV<@vA?;BO;2{{mq>hUkG+VWe@hXu~Jkdz0Bc+rDxsm1K0vc5395?T>N|^xsH7R z5NaeO=$jtF8=T*Vv+sd7oULQ?_mofAOR`>_+&b=632|NWyVIF*H@0hHSTg|;w#5o? zuvp_gv>JQ8WR>MrI*=D#2mo6cxiT_!>X>fg6)`nC-0tU>pqJpKL3K@n zzGbKsLRB_Bzphupw#=b=z>CZ6b)ET{v94dw{dU#%n7lC}4fbqJkCg%DY*erT43!uL z=sq+5qXf$J#-}_yDR}Pf4sm|qmWBl?H9Jmz(?-R7Ctj%(R{yPc!=$MCK2a``kk^m)H=#7Woe=@50SFbP9rQo3VC&Q!lni&5jmf*E2zVXQ~wtXub8 z*Ll8K#`#^aF@kJ>53lY_B}y?F7@atm-+h8CH*Vki@-jP`e7n|oc%d`4LrY7{P%%bQ z+1^3O*Nl0H>pYt0B)Gtn7OXY22x7^8u?(~x6RO*(-=an^7@E#X@xKwB^HLy{S`TyS zj-wnIA6K!qp3g+FIk1{~kC&M!I1|SaseQ~xD%v{`R2w|y``h9P!p{~`numCJ$HUa+ z9Cr#0L_&WXLq8N7b}?b%q3 zBkXXTaZrJ(s2G)^usZxeePYxWLR-QFp?%G#{q=xi(9V$RJfX$G`wrU))!4wJ?Z+@!Omno{% zf5`HXi4G4WOuc4szldaU@ThRpxWj@12;g&@jE3(r){!Qb?B6L)lnXsf_Gz3*L|J zh5lj3Jq*zLsdk-UgBlNw9EHg|DLjn)+(C#-5E=?>)?2O=i%TSE6F5%S_Qb@?G%_}z zuFTrP-$056fD)!bKG9MPAK>NEwWj%YObE{0^W@b*vzqp@$g(XAMTa1?K9t{NQzp ziw%0Ibe>IALE`Y7xg3P*3QtzJC{;DEUx@U#30}azvkr^T*?@$yG5Gw_E%EQM<8oOF zAt6tMqramoRC_q)@*B_n%oQEcr@*D8aG@`@6DR08F!91yp6gvE>JrFbnsvI1 zuQI*G1)6Ym;1k}(1j*ij3ruu=D~O#h((|qWRk;39``liFDU)dc9Rg(Lg<=HCUIpu> zkV&Re=VQ`CNnY3)#`@m*F7>tzF?uvt7*Tp z?VjKi>nqROeBt3w(Z{c_f5qnXx)-FVxsYpLewa)-8o*?_zrUOQ_vz1&Ge#w|YAaX& Si9;OlM@2zXzU+~C@c#kn*DP@W literal 0 HcmV?d00001 diff --git a/playbooks/SAA_Dynamic_Analysis.py b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py similarity index 57% rename from playbooks/SAA_Dynamic_Analysis.py rename to playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py index 975f9e46bf..4aa3de9054 100644 --- a/playbooks/SAA_Dynamic_Analysis.py +++ b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py @@ -35,7 +35,7 @@ def saa_input_filter(action=None, success=None, container=None, results=None, ha # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: - saa_url_detonation(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + url_detonation(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( @@ -47,14 +47,14 @@ def saa_input_filter(action=None, success=None, container=None, results=None, ha # call connected blocks if filtered artifacts or results if matched_artifacts_2 or matched_results_2: - saa_file_detonation(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_2, filtered_results=matched_results_2) + file_detonation(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_2, filtered_results=matched_results_2) return @phantom.playbook_block() -def saa_url_detonation(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("saa_url_detonation() called") +def url_detonation(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("url_detonation() called") # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) @@ -66,7 +66,7 @@ def saa_url_detonation(action=None, success=None, container=None, results=None, parameters = [] - # build parameters list for 'saa_url_detonation' call + # build parameters list for 'url_detonation' call for playbook_input_url_item in playbook_input_url: if playbook_input_url_item[0] is not None: parameters.append({ @@ -83,7 +83,7 @@ def saa_url_detonation(action=None, success=None, container=None, results=None, ## Custom Code End ################################################################################ - phantom.act("detonate url", parameters=parameters, name="saa_url_detonation", assets=["splunk attack analyzer"], callback=url_detonation_status_filter_1) + phantom.act("detonate url", parameters=parameters, name="url_detonation", assets=["saa"], callback=url_detonation_status_filter_1) return @@ -100,54 +100,20 @@ def file_detonation_status_filter(action=None, success=None, container=None, res matched_artifacts_1, matched_results_1 = phantom.condition( container=container, conditions=[ - ["saa_file_detonation:action_result.status", "==", "success"] + ["file_detonation:action_result.status", "==", "success"] ], name="file_detonation_status_filter:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: - get_jobid_of_file_detonation_output(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + get_file_forensics_output(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 get_jobid_of_url_detonation_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("get_jobid_of_url_detonation_output() called") - - ################################################################################ - # This block uses custom code for fetching JobID for URL(s) or file(s) detonation. - ################################################################################ - - saa_url_detonation_result_data = phantom.collect2(container=container, datapath=["saa_url_detonation:action_result.data.*.JobID"], action_results=results) - - saa_url_detonation_result_item_0 = [item[0] for item in saa_url_detonation_result_data] - - get_jobid_of_url_detonation_output__jobid = None - - ################################################################################ - ## Custom Code Start - ################################################################################ - - # Write your custom code here... - get_jobid_of_url_detonation_output__jobid = [] - - get_jobid_of_url_detonation_output__jobid.append(saa_url_detonation_result_item_0) - #phantom.debug("get_jobid_of_url_detonation_output__jobid: {}".format(get_jobid_of_url_detonation_output__jobid)) - ################################################################################ - ## Custom Code End - ################################################################################ - - phantom.save_run_data(key="get_jobid_of_url_detonation_output:jobid", value=json.dumps(get_jobid_of_url_detonation_output__jobid)) - - ssa_get_job_forensics_output(container=container) - - return - - -@phantom.playbook_block() -def ssa_get_job_forensics_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("ssa_get_job_forensics_output() called") +def get_url_forensics_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("get_url_forensics_output() called") # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) @@ -156,35 +122,39 @@ def ssa_get_job_forensics_output(action=None, success=None, container=None, resu # to be detonated. ################################################################################ - get_jobid_of_url_detonation_output__jobid = json.loads(_ if (_ := phantom.get_run_data(key="get_jobid_of_url_detonation_output:jobid")) != "" else "null") # pylint: disable=used-before-assignment + url_detonation_result_data = phantom.collect2(container=container, datapath=["url_detonation:action_result.data.*.JobID","url_detonation:action_result.parameter.context.artifact_id"], action_results=results) parameters = [] - if get_jobid_of_url_detonation_output__jobid is not None: - parameters.append({ - "job_id": get_jobid_of_url_detonation_output__jobid, - "timeout": 5, - }) + # build parameters list for 'get_url_forensics_output' call + for url_detonation_result_item in url_detonation_result_data: + if url_detonation_result_item[0] is not None: + parameters.append({ + "job_id": url_detonation_result_item[0], + "context": {'artifact_id': url_detonation_result_item[1]}, + }) ################################################################################ ## Custom Code Start ################################################################################ # Write your custom code here... - parameters = [] - for job_ids in get_jobid_of_url_detonation_output__jobid: - for job in job_ids: - if job is not None: - parameters.append({ - "job_id": job, - "timeout": 5, - }) + #parameters = [] + #for job_ids in url_jobid_detonation_output__jobid: + # for job in job_ids: + # if job is not None: + # parameters.append({ + # "job_id": job, + # "timeout": 5, + # }) #phantom.debug(parameters) ################################################################################ ## Custom Code End ################################################################################ - phantom.act("get job forensics", parameters=parameters, name="ssa_get_job_forensics_output", assets=["splunk attack analyzer"], callback=get_jobid_forensic_filter) + # calculate start time using delay of 2 minutes + start_time = datetime.now() + timedelta(minutes=2) + phantom.act("get job forensics", parameters=parameters, name="get_url_forensics_output", start_time=start_time, assets=["saa"], callback=get_jobid_forensic_filter) return @@ -201,38 +171,37 @@ def get_jobid_forensic_filter(action=None, success=None, container=None, results matched_artifacts_1, matched_results_1 = phantom.condition( container=container, conditions=[ - ["ssa_get_job_forensics_output:action_result.status", "==", "success"] + ["get_url_forensics_output:action_result.status", "==", "success"] ], name="get_jobid_forensic_filter:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: - normalized_job_forensic_report_output(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + normalized_url_forensic_output(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 normalized_job_forensic_report_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("normalized_job_forensic_report_output() called") +def normalized_url_forensic_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("normalized_url_forensic_output() called") ################################################################################ # This block uses custom code for normalizing score. Adjust the logic as desired # in the documented sections. ################################################################################ - ssa_get_job_forensics_output_result_data = phantom.collect2(container=container, datapath=["ssa_get_job_forensics_output:action_result.data.*.URLs.*.URL","ssa_get_job_forensics_output:action_result.data.*.DisplayScore","ssa_get_job_forensics_output:action_result.data.*.Detections.*.Description","ssa_get_job_forensics_output:action_result.data.*.Verdict","ssa_get_job_forensics_output:action_result.data"], action_results=results) + get_url_forensics_output_result_data = phantom.collect2(container=container, datapath=["get_url_forensics_output:action_result.data.*.URLs.*.URL","get_url_forensics_output:action_result.data.*.DisplayScore","get_url_forensics_output:action_result.data.*.Detections.*.Description","get_url_forensics_output:action_result.data.*.Verdict"], action_results=results) - ssa_get_job_forensics_output_result_item_0 = [item[0] for item in ssa_get_job_forensics_output_result_data] - ssa_get_job_forensics_output_result_item_1 = [item[1] for item in ssa_get_job_forensics_output_result_data] - ssa_get_job_forensics_output_result_item_2 = [item[2] for item in ssa_get_job_forensics_output_result_data] - ssa_get_job_forensics_output_result_item_3 = [item[3] for item in ssa_get_job_forensics_output_result_data] - ssa_get_job_forensics_output_result_item_4 = [item[4] for item in ssa_get_job_forensics_output_result_data] + get_url_forensics_output_result_item_0 = [item[0] for item in get_url_forensics_output_result_data] + get_url_forensics_output_result_item_1 = [item[1] for item in get_url_forensics_output_result_data] + get_url_forensics_output_result_item_2 = [item[2] for item in get_url_forensics_output_result_data] + get_url_forensics_output_result_item_3 = [item[3] for item in get_url_forensics_output_result_data] - normalized_job_forensic_report_output__url_score_object = None - normalized_job_forensic_report_output__scores = None - normalized_job_forensic_report_output__categories = None - normalized_job_forensic_report_output__confidence = None + normalized_url_forensic_output__url_score_object = None + normalized_url_forensic_output__scores = None + normalized_url_forensic_output__categories = None + normalized_url_forensic_output__score_id = None ################################################################################ ## Custom Code Start @@ -242,34 +211,34 @@ def normalized_job_forensic_report_output(action=None, success=None, container=N score_id =0 score_table = { "0":"Unknown", - "10":"Very_Safe", - "20":"Safe", - "30":"Probably_Safe", - "40":"Leans_Safe", - "50":"May_not_be_Safe", - "60":"Exercise_Caution", - "70":"Suspicious_or_Risky", - "80":"Possibly_Malicious", - "90":"Probably_Malicious", - "100":"Malicious" + "1":"Very_Safe", + "2":"Safe", + "3":"Probably_Safe", + "4":"Leans_Safe", + "5":"May_not_be_Safe", + "6":"Exercise_Caution", + "7":"Suspicious_or_Risky", + "8":"Possibly_Malicious", + "9":"Probably_Malicious", + "10":"Malicious" } #phantom.debug("url: {}".format(ssa_get_job_forensics_output_result_item_0)) #phantom.debug("DisplayScore: {}".format(ssa_get_job_forensics_output_result_item_1)) #phantom.debug("Category: {}".format(ssa_get_job_forensics_output_result_item_2)) #phantom.debug("verdict: {}".format(ssa_get_job_forensics_output_result_item_3)) #phantom.debug("action_data: {}".format(ssa_get_job_forensics_output_result_item_4)) - + #phantom.debug(get_url_forensics_output_result_item_4) - normalized_job_forensic_report_output__url_score_object = [] - normalized_job_forensic_report_output__scores = [] - normalized_job_forensic_report_output__categories = [] - normalized_job_forensic_report_output__confidence = [] + normalized_url_forensic_output__url_score_object = [] + normalized_url_forensic_output__scores = [] + normalized_url_forensic_output__categories = [] + normalized_url_forensic_output__score_id = [] ## normalized NoneType value to avoid enumeration failure - url_detonation_param_list = [(i or "") for i in ssa_get_job_forensics_output_result_item_0] - url_detonation_threat_score_list = [(i or 0) for i in ssa_get_job_forensics_output_result_item_1] - url_detonation_category_list = [(i or "") for i in ssa_get_job_forensics_output_result_item_2] - url_detonation_verdict_list = [(i or "") for i in ssa_get_job_forensics_output_result_item_3] + url_detonation_param_list = [(i or "") for i in get_url_forensics_output_result_item_0] + url_detonation_threat_score_list = [(i or 0) for i in get_url_forensics_output_result_item_1] + url_detonation_category_list = [(i or "") for i in get_url_forensics_output_result_item_2] + url_detonation_verdict_list = [(i or "") for i in get_url_forensics_output_result_item_3] ## get the set() or unique input url parameter. @@ -298,42 +267,43 @@ def normalized_job_forensic_report_output(action=None, success=None, container=N #phantom.debug("category_list: {} len: {}".format(category_list, len(category_list))) confidence_ = list(set(display_score_list))[0] categories = list(set(category_list)) - + + score = "" if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == ""): if confidence_ >= 0 and confidence_ < 10: score_id = 0 elif confidence_ >= 10 and confidence_ < 20: - score_id = 10 + score_id = 1 elif confidence_ >= 20 and confidence_ < 30: - score_id = 20 + score_id = 2 elif confidence_ >= 30 and confidence_ < 40: - score_id = 30 + score_id = 3 elif confidence_ >= 40 and confidence_ < 50: - score_id = 40 + score_id = 4 elif confidence_ >= 50 and confidence_ < 60: - score_id = 50 + score_id = 5 elif confidence_ >= 60 and confidence_ < 70: - score_id = 60 + score_id = 6 elif confidence_ >= 70 and confidence_ < 80: - score_id = 70 + score_id = 7 elif confidence_ >= 80 and confidence_ < 90: - score_id = 80 + score_id = 8 elif confidence_ >= 90 and confidence_ < 100: - score_id = 90 + score_id = 9 elif confidence_ >= 100: - score_id = 100 - - score = score_table[str(score_id)] - + score_id = 10 + #score = score_table[str(score_id)] else: - score = list(set(score_list))[0] - + score_id = round(confidence_/ 10) + + score = score_table[str(score_id)] + # Attach final object - normalized_job_forensic_report_output__url_score_object.append({'score': score, 'confidence': confidence_, 'categories': categories}) - normalized_job_forensic_report_output__scores.append(score) - normalized_job_forensic_report_output__categories.append(categories) - normalized_job_forensic_report_output__confidence.append(confidence_) - #phantom.debug("normalized_job_forensic_report_output__url_score_object: {}".format(normalized_job_forensic_report_output__url_score_object)) + normalized_url_forensic_output__url_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories}) + normalized_url_forensic_output__scores.append(score) + normalized_url_forensic_output__categories.append(", ".join(categories)) + normalized_url_forensic_output__score_id.append(score_id) + #phantom.debug("normalized_job_forensic_report_output__url_score_object: {}".format(normalized_url_forensic_output__url_score_object)) #phantom.debug("normalized_job_forensic_report_output__categories: {}".format(normalized_job_forensic_report_output__categories)) #phantom.debug("normalized_job_forensic_report_output__confidence: {}".format(normalized_job_forensic_report_output__confidence)) @@ -341,10 +311,10 @@ def normalized_job_forensic_report_output(action=None, success=None, container=N ## Custom Code End ################################################################################ - phantom.save_run_data(key="normalized_job_forensic_report_output:url_score_object", value=json.dumps(normalized_job_forensic_report_output__url_score_object)) - phantom.save_run_data(key="normalized_job_forensic_report_output:scores", value=json.dumps(normalized_job_forensic_report_output__scores)) - phantom.save_run_data(key="normalized_job_forensic_report_output:categories", value=json.dumps(normalized_job_forensic_report_output__categories)) - phantom.save_run_data(key="normalized_job_forensic_report_output:confidence", value=json.dumps(normalized_job_forensic_report_output__confidence)) + phantom.save_run_data(key="normalized_url_forensic_output:url_score_object", value=json.dumps(normalized_url_forensic_output__url_score_object)) + phantom.save_run_data(key="normalized_url_forensic_output:scores", value=json.dumps(normalized_url_forensic_output__scores)) + phantom.save_run_data(key="normalized_url_forensic_output:categories", value=json.dumps(normalized_url_forensic_output__categories)) + phantom.save_run_data(key="normalized_url_forensic_output:score_id", value=json.dumps(normalized_url_forensic_output__score_id)) format_url_report(container=container) @@ -359,15 +329,15 @@ def format_url_report(action=None, success=None, container=None, results=None, h # Format a summary table with the information gathered from the playbook. ################################################################################ - template = """SOAR analyzed URL(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| URL | Score | Confidence |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" + template = """SOAR analyzed URL(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" # parameter list for template variable replacement parameters = [ "playbook_input:url", - "normalized_job_forensic_report_output:custom_function:scores", - "normalized_job_forensic_report_output:custom_function:confidence", - "normalized_job_forensic_report_output:custom_function:categories", - "get_jobid_of_url_detonation_output:custom_function:jobid" + "normalized_url_forensic_output:custom_function:scores", + "normalized_url_forensic_output:custom_function:score_id", + "normalized_url_forensic_output:custom_function:categories", + "url_detonation:action_result.data.*.JobID" ] ################################################################################ @@ -397,10 +367,11 @@ def build_url_output(action=None, success=None, container=None, results=None, ha ################################################################################ playbook_input_url = phantom.collect2(container=container, datapath=["playbook_input:url"]) - get_jobid_of_url_detonation_output__jobid = json.loads(_ if (_ := phantom.get_run_data(key="get_jobid_of_url_detonation_output:jobid")) != "" else "null") # pylint: disable=used-before-assignment - normalized_job_forensic_report_output__url_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalized_job_forensic_report_output:url_score_object")) != "" else "null") # pylint: disable=used-before-assignment + url_detonation_result_data = phantom.collect2(container=container, datapath=["url_detonation:action_result.data.*.JobID"], action_results=results) + normalized_url_forensic_output__url_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalized_url_forensic_output:url_score_object")) != "" else "null") # pylint: disable=used-before-assignment playbook_input_url_values = [item[0] for item in playbook_input_url] + url_detonation_result_item_0 = [item[0] for item in url_detonation_result_data] build_url_output__observable_array = None @@ -413,17 +384,18 @@ def build_url_output(action=None, success=None, container=None, results=None, ha build_url_output__observable_array = [] #phantom.debug(playbook_input_url_values) # Build URL - for jobs_id in get_jobid_of_url_detonation_output__jobid: + - for url, external_id, url_object in zip(playbook_input_url_values, jobs_id, normalized_job_forensic_report_output__url_score_object): - parsed_url = urlparse(url) - #phantom.debug("url: {} jobs_id:{}".format(url, external_id)) - #phantom.debug("parsed_url: {}, url_object: {}".format(parsed_url, url_object)) - observable_object = { + for url, external_id, url_object in zip(playbook_input_url_values, url_detonation_result_item_0, normalized_url_forensic_output__url_score_object): + parsed_url = urlparse(url) + #phantom.debug("url: {} jobs_id:{}".format(url, external_id)) + #phantom.debug("parsed_url: {}, url_object: {}".format(parsed_url, url_object)) + observable_object = { "value": url, "type": "url", "reputation": { "score": url_object['score'], + "score_id": url_object['score_id'], "confidence": url_object['confidence'] }, "attributes": { @@ -435,15 +407,15 @@ def build_url_output(action=None, success=None, container=None, results=None, ha "source_link": f"https://app.twinwave.io/job/{external_id}" } - if parsed_url.path: - observable_object['attributes']['path'] = parsed_url.path - if parsed_url.query: - observable_object['attributes']['query'] = parsed_url.query - if parsed_url.port: - observable_object['attributes']['port'] = parsed_url.port + if parsed_url.path: + observable_object['attributes']['path'] = parsed_url.path + if parsed_url.query: + observable_object['attributes']['query'] = parsed_url.query + if parsed_url.port: + observable_object['attributes']['port'] = parsed_url.port - build_url_output__observable_array.append(observable_object) - #phantom.debug("build_url_output__observable_array: {}".format(build_url_output__observable_array)) + build_url_output__observable_array.append(observable_object) + #phantom.debug("build_url_output__observable_array: {}".format(build_url_output__observable_array)) ################################################################################ ## Custom Code End ################################################################################ @@ -454,8 +426,8 @@ def build_url_output(action=None, success=None, container=None, results=None, ha @phantom.playbook_block() -def saa_file_detonation(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("saa_file_detonation() called") +def file_detonation(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("file_detonation() called") # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) @@ -467,7 +439,7 @@ def saa_file_detonation(action=None, success=None, container=None, results=None, parameters = [] - # build parameters list for 'saa_file_detonation' call + # build parameters list for 'file_detonation' call for playbook_input_vault_id_item in playbook_input_vault_id: if playbook_input_vault_id_item[0] is not None: parameters.append({ @@ -484,48 +456,16 @@ def saa_file_detonation(action=None, success=None, container=None, results=None, ## Custom Code End ################################################################################ - phantom.act("detonate file", parameters=parameters, name="saa_file_detonation", assets=["splunk attack analyzer"], callback=file_detonation_status_filter) + # calculate start time using delay of 2 minutes + start_time = datetime.now() + timedelta(minutes=2) + phantom.act("detonate file", parameters=parameters, name="file_detonation", start_time=start_time, assets=["saa"], callback=file_detonation_status_filter) return @phantom.playbook_block() -def get_jobid_of_file_detonation_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("get_jobid_of_file_detonation_output() called") - - ################################################################################ - # This block uses custom code for fetching JobID for URL(s) or file(s) detonation. - ################################################################################ - - saa_file_detonation_result_data = phantom.collect2(container=container, datapath=["saa_file_detonation:action_result.data.*.JobID"], action_results=results) - - saa_file_detonation_result_item_0 = [item[0] for item in saa_file_detonation_result_data] - - get_jobid_of_file_detonation_output__jobid = None - - ################################################################################ - ## Custom Code Start - ################################################################################ - - # Write your custom code here... - get_jobid_of_file_detonation_output__jobid = [] - - get_jobid_of_file_detonation_output__jobid.append(saa_file_detonation_result_item_0) - #phantom.debug("get_jobid_of_file_detonation_output__jobid: {}".format(get_jobid_of_file_detonation_output__jobid)) - ################################################################################ - ## Custom Code End - ################################################################################ - - phantom.save_run_data(key="get_jobid_of_file_detonation_output:jobid", value=json.dumps(get_jobid_of_file_detonation_output__jobid)) - - saa_get_file_job_forensics_output(container=container) - - return - - -@phantom.playbook_block() -def saa_get_file_job_forensics_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("saa_get_file_job_forensics_output() called") +def get_file_forensics_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("get_file_forensics_output() called") # phantom.debug('Action: {0} {1}'.format(action['name'], ('SUCCEEDED' if success else 'FAILED'))) @@ -534,42 +474,46 @@ def saa_get_file_job_forensics_output(action=None, success=None, container=None, # to be detonated. ################################################################################ - get_jobid_of_file_detonation_output__jobid = json.loads(_ if (_ := phantom.get_run_data(key="get_jobid_of_file_detonation_output:jobid")) != "" else "null") # pylint: disable=used-before-assignment + file_detonation_result_data = phantom.collect2(container=container, datapath=["file_detonation:action_result.data.*.JobID","file_detonation:action_result.parameter.context.artifact_id"], action_results=results) parameters = [] - if get_jobid_of_file_detonation_output__jobid is not None: - parameters.append({ - "job_id": get_jobid_of_file_detonation_output__jobid, - "timeout": 5, - }) + # build parameters list for 'get_file_forensics_output' call + for file_detonation_result_item in file_detonation_result_data: + if file_detonation_result_item[0] is not None: + parameters.append({ + "job_id": file_detonation_result_item[0], + "context": {'artifact_id': file_detonation_result_item[1]}, + }) ################################################################################ ## Custom Code Start ################################################################################ # Write your custom code here... - parameters = [] - for job_ids in get_jobid_of_file_detonation_output__jobid: - for job in job_ids: - if job is not None: - parameters.append({ - "job_id": job, - "timeout": 5, - }) + #parameters = [] + #for job_ids in file_jobid_detonation_output__jobid: + # for job in job_ids: + # if job is not None: + # parameters.append({ + # "job_id": job, + # "timeout": 5, + # }) #phantom.debug(parameters) ################################################################################ ## Custom Code End ################################################################################ - phantom.act("get job forensics", parameters=parameters, name="saa_get_file_job_forensics_output", assets=["splunk attack analyzer"], callback=filter_6) + # calculate start time using delay of 2 minutes + start_time = datetime.now() + timedelta(minutes=2) + phantom.act("get job forensics", parameters=parameters, name="get_file_forensics_output", start_time=start_time, assets=["saa"], callback=filter_6) return @phantom.playbook_block() -def normalized_job_forensic_report_output_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("normalized_job_forensic_report_output_1() called") +def normalized_file_forensic_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("normalized_file_forensic_output() called") ################################################################################ # This block uses custom code for normalizing score. Adjust the logic as desired @@ -577,18 +521,17 @@ def normalized_job_forensic_report_output_1(action=None, success=None, container ################################################################################ playbook_input_vault_id = phantom.collect2(container=container, datapath=["playbook_input:vault_id"]) - saa_get_file_job_forensics_output_result_data = phantom.collect2(container=container, datapath=["saa_get_file_job_forensics_output:action_result.data.*.DisplayScore","saa_get_file_job_forensics_output:action_result.data.*.Detections.*.Description","saa_get_file_job_forensics_output:action_result.data.*.Verdict","saa_get_file_job_forensics_output:action_result.data"], action_results=results) + get_file_forensics_output_result_data = phantom.collect2(container=container, datapath=["get_file_forensics_output:action_result.data.*.DisplayScore","get_file_forensics_output:action_result.data.*.Detections.*.Description","get_file_forensics_output:action_result.data.*.Verdict"], action_results=results) playbook_input_vault_id_values = [item[0] for item in playbook_input_vault_id] - saa_get_file_job_forensics_output_result_item_0 = [item[0] for item in saa_get_file_job_forensics_output_result_data] - saa_get_file_job_forensics_output_result_item_1 = [item[1] for item in saa_get_file_job_forensics_output_result_data] - saa_get_file_job_forensics_output_result_item_2 = [item[2] for item in saa_get_file_job_forensics_output_result_data] - saa_get_file_job_forensics_output_result_item_3 = [item[3] for item in saa_get_file_job_forensics_output_result_data] + get_file_forensics_output_result_item_0 = [item[0] for item in get_file_forensics_output_result_data] + get_file_forensics_output_result_item_1 = [item[1] for item in get_file_forensics_output_result_data] + get_file_forensics_output_result_item_2 = [item[2] for item in get_file_forensics_output_result_data] - normalized_job_forensic_report_output_1__file_score_object = None - normalized_job_forensic_report_output_1__scores = None - normalized_job_forensic_report_output_1__categories = None - normalized_job_forensic_report_output_1__confidence = None + normalized_file_forensic_output__file_score_object = None + normalized_file_forensic_output__scores = None + normalized_file_forensic_output__categories = None + normalized_file_forensic_output__score_id = None ################################################################################ ## Custom Code Start @@ -598,34 +541,34 @@ def normalized_job_forensic_report_output_1(action=None, success=None, container score_id =0 score_table = { "0":"Unknown", - "10":"Very_Safe", - "20":"Safe", - "30":"Probably_Safe", - "40":"Leans_Safe", - "50":"May_not_be_Safe", - "60":"Exercise_Caution", - "70":"Suspicious_or_Risky", - "80":"Possibly_Malicious", - "90":"Probably_Malicious", - "100":"Malicious" + "1":"Very_Safe", + "2":"Safe", + "3":"Probably_Safe", + "4":"Leans_Safe", + "5":"May_not_be_Safe", + "6":"Exercise_Caution", + "7":"Suspicious_or_Risky", + "8":"Possibly_Malicious", + "9":"Probably_Malicious", + "10":"Malicious" } #phantom.debug("vault_id: {}".format(ssa_get_job_forensics_output_result_item_0)) #phantom.debug("DisplayScore: {}".format(ssa_get_job_forensics_output_result_item_1)) #phantom.debug("Category: {}".format(ssa_get_job_forensics_output_result_item_2)) #phantom.debug("verdict: {}".format(ssa_get_job_forensics_output_result_item_3)) #phantom.debug("action_data: {}".format(ssa_get_job_forensics_output_result_item_4)) - + #phantom.debug(get_file_forensics_output_result_item_3) - normalized_job_forensic_report_output_1__file_score_object = [] - normalized_job_forensic_report_output_1__scores = [] - normalized_job_forensic_report_output_1__categories = [] - normalized_job_forensic_report_output_1__confidence = [] + normalized_file_forensic_output__file_score_object = [] + normalized_file_forensic_output__scores = [] + normalized_file_forensic_output__categories = [] + normalized_file_forensic_output__score_id = [] ## normalized NoneType value to avoid enumeration failure file_detonation_param_list = [(i or "") for i in playbook_input_vault_id_values] - file_detonation_threat_score_list = [(i or 0) for i in saa_get_file_job_forensics_output_result_item_0] - file_detonation_category_list = [(i or "") for i in saa_get_file_job_forensics_output_result_item_1] - file_detonation_verdict_list = [(i or "") for i in saa_get_file_job_forensics_output_result_item_2] + file_detonation_threat_score_list = [(i or 0) for i in get_file_forensics_output_result_item_0] + file_detonation_category_list = [(i or "") for i in get_file_forensics_output_result_item_1] + file_detonation_verdict_list = [(i or "") for i in get_file_forensics_output_result_item_2] ## get the set() or unique input url parameter. @@ -654,41 +597,43 @@ def normalized_job_forensic_report_output_1(action=None, success=None, container #phantom.debug("category_list: {} len: {}".format(category_list, len(category_list))) confidence_ = list(set(display_score_list))[0] categories = list(set(category_list)) + #score_ = list(set(score_list))[0] + score = "" if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == ""): if confidence_ >= 0 and confidence_ < 10: score_id = 0 elif confidence_ >= 10 and confidence_ < 20: - score_id = 10 + score_id = 1 elif confidence_ >= 20 and confidence_ < 30: - score_id = 20 + score_id = 2 elif confidence_ >= 30 and confidence_ < 40: - score_id = 30 + score_id = 3 elif confidence_ >= 40 and confidence_ < 50: - score_id = 40 + score_id = 4 elif confidence_ >= 50 and confidence_ < 60: - score_id = 50 + score_id = 5 elif confidence_ >= 60 and confidence_ < 70: - score_id = 60 + score_id = 6 elif confidence_ >= 70 and confidence_ < 80: - score_id = 70 + score_id = 7 elif confidence_ >= 80 and confidence_ < 90: - score_id = 80 + score_id = 8 elif confidence_ >= 90 and confidence_ < 100: - score_id = 90 + score_id = 9 elif confidence_ >= 100: - score_id = 100 - - score = score_table[str(score_id)] + score_id = 10 else: - score = list(set(score_list))[0] - + score_id = round(confidence_/ 10) + + score = score_table[str(score_id)] + #phantom.debug("score: {} score_id {}".format(score, score_id)) # Attach final object - normalized_job_forensic_report_output_1__file_score_object.append({'score': score, 'confidence': confidence_, 'categories': categories}) - normalized_job_forensic_report_output_1__scores.append(score) - normalized_job_forensic_report_output_1__categories.append(categories) - normalized_job_forensic_report_output_1__confidence.append(confidence_) + normalized_file_forensic_output__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories}) + normalized_file_forensic_output__scores.append(score) + normalized_file_forensic_output__categories.append(", ".join(categories)) + normalized_file_forensic_output__score_id.append(score_id) #phantom.debug("normalized_job_forensic_report_output_1__file_score_object: {}".format(normalized_job_forensic_report_output_1__file_score_object)) #phantom.debug("normalized_job_forensic_report_output_1__scores: {}".format(normalized_job_forensic_report_output_1__scores)) #phantom.debug("normalized_job_forensic_report_output_1__categories: {}".format(normalized_job_forensic_report_output_1__categories)) @@ -696,10 +641,10 @@ def normalized_job_forensic_report_output_1(action=None, success=None, container ## Custom Code End ################################################################################ - phantom.save_run_data(key="normalized_job_forensic_report_output_1:file_score_object", value=json.dumps(normalized_job_forensic_report_output_1__file_score_object)) - phantom.save_run_data(key="normalized_job_forensic_report_output_1:scores", value=json.dumps(normalized_job_forensic_report_output_1__scores)) - phantom.save_run_data(key="normalized_job_forensic_report_output_1:categories", value=json.dumps(normalized_job_forensic_report_output_1__categories)) - phantom.save_run_data(key="normalized_job_forensic_report_output_1:confidence", value=json.dumps(normalized_job_forensic_report_output_1__confidence)) + phantom.save_run_data(key="normalized_file_forensic_output:file_score_object", value=json.dumps(normalized_file_forensic_output__file_score_object)) + phantom.save_run_data(key="normalized_file_forensic_output:scores", value=json.dumps(normalized_file_forensic_output__scores)) + phantom.save_run_data(key="normalized_file_forensic_output:categories", value=json.dumps(normalized_file_forensic_output__categories)) + phantom.save_run_data(key="normalized_file_forensic_output:score_id", value=json.dumps(normalized_file_forensic_output__score_id)) format_file_report(container=container) @@ -714,15 +659,15 @@ def format_file_report(action=None, success=None, container=None, results=None, # Format a summary table with the information gathered from the playbook. ################################################################################ - template = """SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| File hash | Score | Confidence |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" + template = """SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| vault_id | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" # parameter list for template variable replacement parameters = [ "playbook_input:vault_id", - "normalized_job_forensic_report_output_1:custom_function:scores", - "normalized_job_forensic_report_output_1:custom_function:confidence", - "normalized_job_forensic_report_output_1:custom_function:categories", - "get_jobid_of_file_detonation_output:custom_function:jobid" + "normalized_file_forensic_output:custom_function:scores", + "normalized_file_forensic_output:custom_function:score_id", + "normalized_file_forensic_output:custom_function:categories", + "file_detonation:action_result.data.*.JobID" ] ################################################################################ @@ -752,10 +697,11 @@ def build_file_output(action=None, success=None, container=None, results=None, h ################################################################################ playbook_input_vault_id = phantom.collect2(container=container, datapath=["playbook_input:vault_id"]) - get_jobid_of_file_detonation_output__jobid = json.loads(_ if (_ := phantom.get_run_data(key="get_jobid_of_file_detonation_output:jobid")) != "" else "null") # pylint: disable=used-before-assignment - normalized_job_forensic_report_output_1__file_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalized_job_forensic_report_output_1:file_score_object")) != "" else "null") # pylint: disable=used-before-assignment + file_detonation_result_data = phantom.collect2(container=container, datapath=["file_detonation:action_result.data.*.JobID"], action_results=results) + normalized_file_forensic_output__file_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalized_file_forensic_output:file_score_object")) != "" else "null") # pylint: disable=used-before-assignment playbook_input_vault_id_values = [item[0] for item in playbook_input_vault_id] + file_detonation_result_item_0 = [item[0] for item in file_detonation_result_data] build_file_output__observable_array = None @@ -765,15 +711,16 @@ def build_file_output(action=None, success=None, container=None, results=None, h # Write your custom code here... build_file_output__observable_array = [] - for jobs_id in get_jobid_of_file_detonation_output__jobid: - for _vault_id, external_id, file_object in zip(playbook_input_vault_id_values, jobs_id, normalized_job_forensic_report_output_1__file_score_object): - #phantom.debug("vault: {} id: {}".format(_vault_id, external_id)) - observable_object = { + + for _vault_id, external_id, file_object in zip(playbook_input_vault_id_values, file_detonation_result_item_0, normalized_file_forensic_output__file_score_object): + #phantom.debug("vault: {} id: {}".format(_vault_id, external_id)) + observable_object = { "value": _vault_id, "type": "hash", "reputation": { "score": file_object['score'], + "score_id": file_object['score_id'], "confidence": file_object['confidence'], }, @@ -786,8 +733,8 @@ def build_file_output(action=None, success=None, container=None, results=None, h "source": "Splunk Attack Analyzer (SAA)", "source_link":f"https://app.twinwave.io/job/{external_id}" } - build_file_output__observable_array.append(observable_object) - #phantom.debug("build_file_output__observable_array: {}".format(build_file_output__observable_array)) + build_file_output__observable_array.append(observable_object) + #phantom.debug("build_file_output__observable_array: {}".format(build_file_output__observable_array)) ################################################################################ ## Custom Code End ################################################################################ @@ -809,13 +756,13 @@ def url_detonation_status_filter_1(action=None, success=None, container=None, re matched_artifacts_1, matched_results_1 = phantom.condition( container=container, conditions=[ - ["saa_url_detonation:action_result.status", "==", "success"] + ["url_detonation:action_result.status", "==", "success"] ], name="url_detonation_status_filter_1:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: - get_jobid_of_url_detonation_output(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + get_url_forensics_output(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) return @@ -828,13 +775,13 @@ def filter_6(action=None, success=None, container=None, results=None, handle=Non matched_artifacts_1, matched_results_1 = phantom.condition( container=container, conditions=[ - ["saa_get_file_job_forensics_output:action_result.status", "==", "success"] + ["get_file_forensics_output:action_result.status", "==", "success"] ], name="filter_6:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: - normalized_job_forensic_report_output_1(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) + normalized_file_forensic_output(action=action, success=success, container=container, results=results, handle=handle, filtered_artifacts=matched_artifacts_1, filtered_results=matched_results_1) return diff --git a/playbooks/SAA_Dynamic_Analysis.yml b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.yml similarity index 83% rename from playbooks/SAA_Dynamic_Analysis.yml rename to playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.yml index 2c839210a3..0d61fb92c3 100644 --- a/playbooks/SAA_Dynamic_Analysis.yml +++ b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.yml @@ -1,11 +1,11 @@ -name: SAA Dynamic Analysis -id: 2754c122-c63b-4558-9b83-23c1e1b96177 +name: Splunk_Attack_Analyzer_Dynamic_Analysis +id: c77faffe-1339-43b0-b870-86582da9063e version: 1 date: '2023-03-24' author: Teoderick Contreras, Splunk type: Investigation description: "Accepts url link, domain or vault_id (hash) to be detonated using Splunk Attacker (SAA) API connector. This playbook produces a normalized output for each user and device." -playbook: SAA_Dynamic_Analysis +playbook: Splunk_Attack_Analyzer_Dynamic_Analysis how_to_implement: This input playbook requires the SAA API connector to be configured. It is designed to work in conjunction with the Dynamic Attribute Lookup playbook or other playbooks in the same style. references: [] app_list: From 7cf8ed5d94c7efe86e8030f607041073948e6ae2 Mon Sep 17 00:00:00 2001 From: Kelby Shelton Date: Fri, 28 Apr 2023 20:12:29 -0500 Subject: [PATCH 2/3] Recommended changes to attack analyzer playbook --- ...lunk_Attack_Analyzer_Dynamic_Analysis.json | 135 ++++---- ...Splunk_Attack_Analyzer_Dynamic_Analysis.py | 308 ++++++------------ 2 files changed, 165 insertions(+), 278 deletions(-) diff --git a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json index a3e71d5a4b..107384ef88 100644 --- a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json +++ b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json @@ -156,7 +156,7 @@ "targetPort": "15_in" } ], - "hash": "e8e4290e9196ed3b9dc21a9395efea9b415a12af", + "hash": "97d3aa1e96d0ec556004151086384b4841477f03", "nodes": { "0": { "data": { @@ -172,7 +172,7 @@ "type": "start", "warnings": {}, "x": 190, - "y": -5.755396159656812e-13 + "y": -7.673861546209082e-13 }, "1": { "data": { @@ -204,24 +204,20 @@ "functionName": "format_url_report", "id": "10", "parameters": [ - "playbook_input:url", + "normalized_url_forensic_output:custom_function:url", "normalized_url_forensic_output:custom_function:scores", "normalized_url_forensic_output:custom_function:score_id", "normalized_url_forensic_output:custom_function:categories", - "url_detonation:action_result.data.*.JobID" + "normalized_url_forensic_output:custom_function:job_id" ], - "template": "SOAR analyzed URL(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n", + "template": "SOAR analyzed URL(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Score Id | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n", "type": "format" }, "errors": {}, "id": "10", "type": "format", "userCode": "\n # Write your custom code here...\n #phantom.debug(phantom.format(container=container, template=template, parameters=parameters, name=\"format_report_url\"))\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "warnings": {}, "x": 0, "y": 1440 }, @@ -238,8 +234,8 @@ "functionName": "build_url_output", "id": "11", "inputParameters": [ - "playbook_input:url", - "url_detonation:action_result.data.*.JobID", + "normalized_url_forensic_output:custom_function:url", + "normalized_url_forensic_output:custom_function:job_id", "normalized_url_forensic_output:custom_function:url_score_object" ], "outputVariables": [ @@ -250,12 +246,8 @@ "errors": {}, "id": "11", "type": "code", - "userCode": "\n # Write your custom code here...\n from urllib.parse import urlparse\n build_url_output__observable_array = []\n #phantom.debug(playbook_input_url_values)\n # Build URL\n\n \n for url, external_id, url_object in zip(playbook_input_url_values, url_detonation_result_item_0, normalized_url_forensic_output__url_score_object):\n parsed_url = urlparse(url)\n #phantom.debug(\"url: {} jobs_id:{}\".format(url, external_id))\n #phantom.debug(\"parsed_url: {}, url_object: {}\".format(parsed_url, url_object))\n observable_object = {\n \"value\": url,\n \"type\": \"url\",\n \"reputation\": {\n \"score\": url_object['score'],\n \"score_id\": url_object['score_id'],\n \"confidence\": url_object['confidence']\n },\n \"attributes\": {\n \"hostname\": parsed_url.hostname,\n \"scheme\": parsed_url.scheme\n },\n \"categories\": url_object['categories'],\n \"source\": \"Splunk Attack Analyzer (SAA)\",\n \"source_link\": f\"https://app.twinwave.io/job/{external_id}\"\n }\n\n if parsed_url.path:\n observable_object['attributes']['path'] = parsed_url.path\n if parsed_url.query:\n observable_object['attributes']['query'] = parsed_url.query\n if parsed_url.port:\n observable_object['attributes']['port'] = parsed_url.port\n\n build_url_output__observable_array.append(observable_object)\n #phantom.debug(\"build_url_output__observable_array: {}\".format(build_url_output__observable_array))\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "userCode": "\n # Write your custom code here...\n from urllib.parse import urlparse\n build_url_output__observable_array = []\n #phantom.debug(playbook_input_url_values)\n # Build URL\n\n \n for url, external_id, url_object in zip(normalized_url_forensic_output__url, normalized_url_forensic_output__job_id, normalized_url_forensic_output__url_score_object):\n parsed_url = urlparse(url)\n #phantom.debug(\"url: {} jobs_id:{}\".format(url, external_id))\n #phantom.debug(\"parsed_url: {}, url_object: {}\".format(parsed_url, url_object))\n observable_object = {\n \"value\": url,\n \"type\": \"url\",\n \"reputation\": {\n \"base_score\": url_object['base_score'],\n \"score\": url_object['score'],\n \"score_id\": url_object['score_id'],\n \"confidence\": url_object['score_id'] #Attack Analyzer's score has confidence baked in.\n },\n \"attributes\": {\n \"hostname\": parsed_url.hostname,\n \"scheme\": parsed_url.scheme\n },\n \"categories\": url_object['categories'],\n \"source\": \"Splunk Attack Analyzer\",\n \"source_link\": f\"https://app.twinwave.io/job/{external_id}\"\n }\n\n if parsed_url.path:\n observable_object['attributes']['path'] = parsed_url.path\n if parsed_url.query:\n observable_object['attributes']['query'] = parsed_url.query\n if parsed_url.port:\n observable_object['attributes']['port'] = parsed_url.port\n\n build_url_output__observable_array.append(observable_object)\n #phantom.debug(\"build_url_output__observable_array: {}\".format(build_url_output__observable_array))\n", + "warnings": {}, "x": 0, "y": 1620 }, @@ -266,14 +258,14 @@ "advanced": { "customName": "file detonation", "customNameId": 0, - "delayTime": 2, + "delayTime": 0, "description": "Queries SAA for information about the provided vault_id(s)", "join": [], "note": "Queries SAA for information about the provided vault_id(s)" }, "connector": "Splunk Attack Analyzer", "connectorConfigs": [ - "saa" + "splunk_attack_analyzer" ], "connectorId": "de681fee-c552-45bf-9212-827b1c7529f8", "connectorVersion": "v1", @@ -309,14 +301,14 @@ "advanced": { "customName": "get file forensics output", "customNameId": 0, - "delayTime": 2, + "delayTime": 0, "description": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated.", "join": [], "note": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated." }, "connector": "Splunk Attack Analyzer", "connectorConfigs": [ - "saa" + "splunk_attack_analyzer" ], "connectorId": "de681fee-c552-45bf-9212-827b1c7529f8", "connectorVersion": "v1", @@ -324,7 +316,8 @@ "functionName": "get_file_forensics_output", "id": "15", "parameters": { - "job_id": "file_detonation:action_result.data.*.JobID" + "job_id": "filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID", + "timeout": "10" }, "requiredParameters": [ { @@ -355,28 +348,27 @@ "functionName": "normalized_file_forensic_output", "id": "17", "inputParameters": [ - "playbook_input:vault_id", - "get_file_forensics_output:action_result.data.*.DisplayScore", - "get_file_forensics_output:action_result.data.*.Detections.*.Description", - "get_file_forensics_output:action_result.data.*.Verdict" + "filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.parameter.file", + "filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID", + "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.parameter.job_id", + "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Score", + "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Detections" ], "outputVariables": [ "file_score_object", "scores", "categories", - "score_id" + "score_id", + "file", + "job_id" ], "type": "code" }, "errors": {}, "id": "17", "type": "code", - "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n #phantom.debug(\"vault_id: {}\".format(ssa_get_job_forensics_output_result_item_0))\n #phantom.debug(\"DisplayScore: {}\".format(ssa_get_job_forensics_output_result_item_1))\n #phantom.debug(\"Category: {}\".format(ssa_get_job_forensics_output_result_item_2))\n #phantom.debug(\"verdict: {}\".format(ssa_get_job_forensics_output_result_item_3))\n #phantom.debug(\"action_data: {}\".format(ssa_get_job_forensics_output_result_item_4))\n #phantom.debug(get_file_forensics_output_result_item_3)\n \n normalized_file_forensic_output__file_score_object = []\n normalized_file_forensic_output__scores = []\n normalized_file_forensic_output__categories = []\n normalized_file_forensic_output__score_id = []\n \n ## normalized NoneType value to avoid enumeration failure\n file_detonation_param_list = [(i or \"\") for i in playbook_input_vault_id_values] \n file_detonation_threat_score_list = [(i or 0) for i in get_file_forensics_output_result_item_0] \n file_detonation_category_list = [(i or \"\") for i in get_file_forensics_output_result_item_1] \n file_detonation_verdict_list = [(i or \"\") for i in get_file_forensics_output_result_item_2] \n \n ## get the set() or unique input url parameter.\n \n index_file_dict = {}\n set_file_inputs = set(file_detonation_param_list)\n \n for file_input in set_file_inputs:\n vaultid_list = []\n score_list = []\n display_score_list = []\n category_list = []\n \n ## getting the index of each detonation phase of the url group the result for each url detonation\n file_input_index = [indx for indx, vaultid_val in enumerate(file_detonation_param_list) if vaultid_val == file_input]\n index_file_dict[file_input] = file_input_index\n \n for idx,(_vaultid, _score, _display_score, _category) in enumerate(zip(file_detonation_param_list, file_detonation_verdict_list, file_detonation_threat_score_list, file_detonation_category_list)):\n if _vaultid == file_input and idx in index_file_dict[file_input]:\n vaultid_list.append(_vaultid)\n score_list.append(_score)\n display_score_list.append(_display_score)\n category_list.append(_category)\n \n ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation\n #phantom.debug(\"score_list: {} len: {}\".format(score_list, len(score_list)))\n #phantom.debug(\"category_list: {} len: {}\".format(category_list, len(category_list)))\n confidence_ = list(set(display_score_list))[0]\n categories = list(set(category_list))\n #score_ = list(set(score_list))[0]\n \n score = \"\"\n if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == \"\"):\n if confidence_ >= 0 and confidence_ < 10:\n score_id = 0\n elif confidence_ >= 10 and confidence_ < 20:\n score_id = 1\n elif confidence_ >= 20 and confidence_ < 30:\n score_id = 2\n elif confidence_ >= 30 and confidence_ < 40:\n score_id = 3\n elif confidence_ >= 40 and confidence_ < 50:\n score_id = 4\n elif confidence_ >= 50 and confidence_ < 60:\n score_id = 5\n elif confidence_ >= 60 and confidence_ < 70:\n score_id = 6\n elif confidence_ >= 70 and confidence_ < 80:\n score_id = 7\n elif confidence_ >= 80 and confidence_ < 90:\n score_id = 8\n elif confidence_ >= 90 and confidence_ < 100:\n score_id = 9\n elif confidence_ >= 100:\n score_id = 10\n \n else:\n score_id = round(confidence_/ 10)\n \n score = score_table[str(score_id)]\n #phantom.debug(\"score: {} score_id {}\".format(score, score_id))\n # Attach final object\n normalized_file_forensic_output__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories})\n normalized_file_forensic_output__scores.append(score)\n normalized_file_forensic_output__categories.append(\", \".join(categories))\n normalized_file_forensic_output__score_id.append(score_id)\n #phantom.debug(\"normalized_job_forensic_report_output_1__file_score_object: {}\".format(normalized_job_forensic_report_output_1__file_score_object))\n #phantom.debug(\"normalized_job_forensic_report_output_1__scores: {}\".format(normalized_job_forensic_report_output_1__scores))\n #phantom.debug(\"normalized_job_forensic_report_output_1__categories: {}\".format(normalized_job_forensic_report_output_1__categories))\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n \n normalized_file_forensic_output__file_score_object = []\n normalized_file_forensic_output__scores = []\n normalized_file_forensic_output__categories = []\n normalized_file_forensic_output__score_id = []\n normalized_file_forensic_output__file = []\n normalized_file_forensic_output__job_id = []\n \n ## pair forensic job results with url detonated\n job_file_dict = {}\n for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_file, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id):\n if orig_job == filtered_job:\n job_file_dict[filtered_job] = orig_url\n\n for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections):\n \n ## translate scores\n score_id = score_num/10 if score_num > 0 else 0\n score = score_table[str(score_id)]\n file = job_file_dict[job]\n categories = [item.get('Description') for item in detections]\n \n normalized_file_forensic_output__file_score_object.append({'value': file, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories})\n normalized_file_forensic_output__scores.append(score)\n normalized_file_forensic_output__categories.append(\", \".join(categories))\n normalized_file_forensic_output__score_id.append(score_id)\n normalized_file_forensic_output__file.append(file)\n normalized_file_forensic_output__job_id.append(job)\n \n", + "warnings": {}, "x": 340, "y": 1260 }, @@ -393,24 +385,20 @@ "functionName": "format_file_report", "id": "18", "parameters": [ - "playbook_input:vault_id", - "normalized_file_forensic_output:custom_function:scores", + "normalized_file_forensic_output:custom_function:file", + "normalized_file_forensic_output:custom_function:score", "normalized_file_forensic_output:custom_function:score_id", "normalized_file_forensic_output:custom_function:categories", - "file_detonation:action_result.data.*.JobID" + "normalized_file_forensic_output:custom_function:job_id" ], - "template": "SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| vault_id | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n", + "template": "SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| Vauld Id | Normalized Score | Score Id | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n", "type": "format" }, "errors": {}, "id": "18", "type": "format", "userCode": "\n # Write your custom code here...\n #phantom.debug(phantom.format(container=container, template=template, parameters=parameters, name=\"format_report_file\"))\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "warnings": {}, "x": 340, "y": 1440 }, @@ -427,8 +415,8 @@ "functionName": "build_file_output", "id": "19", "inputParameters": [ - "playbook_input:vault_id", - "file_detonation:action_result.data.*.JobID", + "normalized_file_forensic_output:custom_function:file", + "normalized_file_forensic_output:custom_function:job_id", "normalized_file_forensic_output:custom_function:file_score_object" ], "outputVariables": [ @@ -439,12 +427,8 @@ "errors": {}, "id": "19", "type": "code", - "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n\n for _vault_id, external_id, file_object in zip(playbook_input_vault_id_values, file_detonation_result_item_0, normalized_file_forensic_output__file_score_object):\n #phantom.debug(\"vault: {} id: {}\".format(_vault_id, external_id))\n observable_object = {\n\n \"value\": _vault_id,\n \"type\": \"hash\",\n \"reputation\": {\n \"score\": file_object['score'],\n \"score_id\": file_object['score_id'],\n \"confidence\": file_object['confidence'],\n\n },\n \"enrichment\": {\n \"provider\": \"Splunk Attack Analyzer\",\n \"type\": \"file\",\n\n },\n \"categories\": file_object['categories'],\n \"source\": \"Splunk Attack Analyzer (SAA)\",\n \"source_link\":f\"https://app.twinwave.io/job/{external_id}\"\n }\n build_file_output__observable_array.append(observable_object)\n #phantom.debug(\"build_file_output__observable_array: {}\".format(build_file_output__observable_array))\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n\n for _vault_id, external_id, file_object in zip(normalized_file_forensic_output__file, normalized_file_forensic_output__job_id, normalized_file_forensic_output__file_score_object):\n #phantom.debug(\"vault: {} id: {}\".format(_vault_id, external_id))\n observable_object = {\n\n \"value\": _vault_id,\n \"type\": \"hash\",\n \"reputation\": {\n \"base_score\": file_object['base_score'],\n \"score\": file_object['score'],\n \"score_id\": file_object['score_id'],\n \"confidence\": file_object['confidence'],\n \"confidence\": file_object['score_id'] #Attack Analyzer's score has confidence baked in.\n },\n \"enrichment\": {\n \"provider\": \"Splunk Attack Analyzer\",\n \"type\": \"file\",\n\n },\n \"categories\": file_object['categories'],\n \"source\": \"Splunk Attack Analyzer\",\n \"source_link\":f\"https://app.twinwave.io/job/{external_id}\"\n }\n build_file_output__observable_array.append(observable_object)\n \n", + "warnings": {}, "x": 340, "y": 1620 }, @@ -504,8 +488,8 @@ "22": { "data": { "advanced": { - "customName": "url detonation status filter", - "customNameId": 1, + "customName": "url status filter", + "customNameId": 0, "description": "Filters url detonation results.", "join": [], "note": "Filters url detonation results." @@ -526,7 +510,7 @@ } ], "functionId": 5, - "functionName": "url_detonation_status_filter_1", + "functionName": "url_status_filter", "id": "22", "type": "filter" }, @@ -540,7 +524,11 @@ "23": { "data": { "advanced": { - "join": [] + "customName": "file forensics filter", + "customNameId": 0, + "description": "Filters successful file detonation job forensic results.", + "join": [], + "note": "Filters successful file detonation job forensic results." }, "conditions": [ { @@ -558,7 +546,7 @@ } ], "functionId": 6, - "functionName": "filter_6", + "functionName": "file_forensics_filter", "id": "23", "type": "filter" }, @@ -583,7 +571,7 @@ }, "connector": "Splunk Attack Analyzer", "connectorConfigs": [ - "saa" + "splunk_attack_analyzer" ], "connectorId": "de681fee-c552-45bf-9212-827b1c7529f8", "connectorVersion": "v1", @@ -626,7 +614,7 @@ { "comparisons": [ { - "conditionIndex": 1, + "conditionIndex": 0, "op": "==", "param": "file_detonation:action_result.status", "value": "success" @@ -656,14 +644,15 @@ "advanced": { "customName": "get url forensics output", "customNameId": 0, - "delayTime": 2, + "delayTime": 0, "description": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated.", "join": [], - "note": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated." + "note": "Queries SAA Forensics data relative to the JobID of URL(s) or File(s) needs to be detonated.", + "reviewer": "" }, "connector": "Splunk Attack Analyzer", "connectorConfigs": [ - "saa" + "splunk_attack_analyzer" ], "connectorId": "de681fee-c552-45bf-9212-827b1c7529f8", "connectorVersion": "v1", @@ -671,7 +660,8 @@ "functionName": "get_url_forensics_output", "id": "6", "parameters": { - "job_id": "url_detonation:action_result.data.*.JobID" + "job_id": "filtered-data:url_status_filter:condition_1:url_detonation:action_result.data.*.JobID", + "timeout": "5" }, "requiredParameters": [ { @@ -694,9 +684,9 @@ "advanced": { "customName": "get jobid forensic filter", "customNameId": 0, - "description": "Filters successful url or file detonation job forensic results.", + "description": "Filters successful url detonation job forensic results.", "join": [], - "note": "Filters successful url or file detonation job forensic results." + "note": "Filters successful url detonation job forensic results." }, "conditions": [ { @@ -738,23 +728,26 @@ "functionName": "normalized_url_forensic_output", "id": "8", "inputParameters": [ - "get_url_forensics_output:action_result.data.*.URLs.*.URL", - "get_url_forensics_output:action_result.data.*.DisplayScore", - "get_url_forensics_output:action_result.data.*.Detections.*.Description", - "get_url_forensics_output:action_result.data.*.Verdict" + "filtered-data:url_status_filter:condition_1:url_detonation:action_result.parameter.url", + "filtered-data:url_status_filter:condition_1:url_detonation:action_result.data.*.JobID", + "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.parameter.job_id", + "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Score", + "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Detections" ], "outputVariables": [ "url_score_object", "scores", "categories", - "score_id" + "score_id", + "url", + "job_id" ], "type": "code" }, "errors": {}, "id": "8", "type": "code", - "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n #phantom.debug(\"url: {}\".format(ssa_get_job_forensics_output_result_item_0))\n #phantom.debug(\"DisplayScore: {}\".format(ssa_get_job_forensics_output_result_item_1))\n #phantom.debug(\"Category: {}\".format(ssa_get_job_forensics_output_result_item_2))\n #phantom.debug(\"verdict: {}\".format(ssa_get_job_forensics_output_result_item_3))\n #phantom.debug(\"action_data: {}\".format(ssa_get_job_forensics_output_result_item_4))\n #phantom.debug(get_url_forensics_output_result_item_4)\n \n normalized_url_forensic_output__url_score_object = []\n normalized_url_forensic_output__scores = []\n normalized_url_forensic_output__categories = []\n normalized_url_forensic_output__score_id = []\n \n ## normalized NoneType value to avoid enumeration failure\n url_detonation_param_list = [(i or \"\") for i in get_url_forensics_output_result_item_0] \n url_detonation_threat_score_list = [(i or 0) for i in get_url_forensics_output_result_item_1] \n url_detonation_category_list = [(i or \"\") for i in get_url_forensics_output_result_item_2] \n url_detonation_verdict_list = [(i or \"\") for i in get_url_forensics_output_result_item_3] \n \n ## get the set() or unique input url parameter.\n \n index_url_dict = {}\n set_url_inputs = set(url_detonation_param_list)\n \n for url_input in set_url_inputs:\n url_list = []\n score_list = []\n display_score_list = []\n category_list = []\n \n ## getting the index of each detonation phase of the url group the result for each url detonation\n url_input_index = [indx for indx, url_val in enumerate(url_detonation_param_list) if url_val == url_input]\n index_url_dict[url_input] = url_input_index\n\n for idx,(_url, _score, _display_score, _category) in enumerate(zip(url_detonation_param_list, url_detonation_verdict_list, url_detonation_threat_score_list, url_detonation_category_list)):\n if _url == url_input and idx in index_url_dict[url_input]:\n url_list.append(_url)\n score_list.append(_score)\n display_score_list.append(_display_score)\n category_list.append(_category)\n \n ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation\n #phantom.debug(\"score_list: {} len: {}\".format(score_list, len(score_list)))\n #phantom.debug(\"category_list: {} len: {}\".format(category_list, len(category_list)))\n confidence_ = list(set(display_score_list))[0]\n categories = list(set(category_list))\n \n score = \"\"\n if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == \"\"):\n if confidence_ >= 0 and confidence_ < 10:\n score_id = 0\n elif confidence_ >= 10 and confidence_ < 20:\n score_id = 1\n elif confidence_ >= 20 and confidence_ < 30:\n score_id = 2\n elif confidence_ >= 30 and confidence_ < 40:\n score_id = 3\n elif confidence_ >= 40 and confidence_ < 50:\n score_id = 4\n elif confidence_ >= 50 and confidence_ < 60:\n score_id = 5\n elif confidence_ >= 60 and confidence_ < 70:\n score_id = 6\n elif confidence_ >= 70 and confidence_ < 80:\n score_id = 7\n elif confidence_ >= 80 and confidence_ < 90:\n score_id = 8\n elif confidence_ >= 90 and confidence_ < 100:\n score_id = 9\n elif confidence_ >= 100:\n score_id = 10\n #score = score_table[str(score_id)]\n else:\n score_id = round(confidence_/ 10)\n \n score = score_table[str(score_id)]\n \n # Attach final object\n normalized_url_forensic_output__url_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories})\n normalized_url_forensic_output__scores.append(score)\n normalized_url_forensic_output__categories.append(\", \".join(categories))\n normalized_url_forensic_output__score_id.append(score_id)\n #phantom.debug(\"normalized_job_forensic_report_output__url_score_object: {}\".format(normalized_url_forensic_output__url_score_object))\n #phantom.debug(\"normalized_job_forensic_report_output__categories: {}\".format(normalized_job_forensic_report_output__categories))\n #phantom.debug(\"normalized_job_forensic_report_output__confidence: {}\".format(normalized_job_forensic_report_output__confidence))\n\n", + "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n \n normalized_url_forensic_output__url_score_object = []\n normalized_url_forensic_output__scores = []\n normalized_url_forensic_output__categories = []\n normalized_url_forensic_output__score_id = []\n normalized_url_forensic_output__url = []\n normalized_url_forensic_output__job_id = []\n\n ## pair forensic job results with url detonated\n job_url_dict = {}\n for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_url, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id):\n if orig_job == filtered_job:\n job_url_dict[filtered_job] = orig_url\n \n for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections):\n \n ## translate scores\n score_id = score_num/10 if score_num > 0 else 0\n score = score_table[str(score_id)]\n url = job_url_dict[job]\n categories = [item.get('Description') for item in detections]\n \n # Attach final object\n normalized_url_forensic_output__url_score_object.append({'value': url, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories})\n normalized_url_forensic_output__scores.append(score)\n normalized_url_forensic_output__categories.append(\", \".join(categories))\n normalized_url_forensic_output__score_id.append(score_id)\n normalized_url_forensic_output__url.append(url)\n normalized_url_forensic_output__job_id.append(job)\n\n", "warnings": {}, "x": 0, "y": 1260 @@ -807,7 +800,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-13T14:39:45.850050+00:00", + "create_time": "2023-04-29T01:07:14.801171+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py index 4aa3de9054..a4d8bb866b 100644 --- a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py +++ b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py @@ -83,7 +83,7 @@ def url_detonation(action=None, success=None, container=None, results=None, hand ## Custom Code End ################################################################################ - phantom.act("detonate url", parameters=parameters, name="url_detonation", assets=["saa"], callback=url_detonation_status_filter_1) + phantom.act("detonate url", parameters=parameters, name="url_detonation", assets=["splunk_attack_analyzer"], callback=url_status_filter) return @@ -122,16 +122,16 @@ def get_url_forensics_output(action=None, success=None, container=None, results= # to be detonated. ################################################################################ - url_detonation_result_data = phantom.collect2(container=container, datapath=["url_detonation:action_result.data.*.JobID","url_detonation:action_result.parameter.context.artifact_id"], action_results=results) + filtered_result_0_data_url_status_filter = phantom.collect2(container=container, datapath=["filtered-data:url_status_filter:condition_1:url_detonation:action_result.data.*.JobID"]) parameters = [] # build parameters list for 'get_url_forensics_output' call - for url_detonation_result_item in url_detonation_result_data: - if url_detonation_result_item[0] is not None: + for filtered_result_0_item_url_status_filter in filtered_result_0_data_url_status_filter: + if filtered_result_0_item_url_status_filter[0] is not None: parameters.append({ - "job_id": url_detonation_result_item[0], - "context": {'artifact_id': url_detonation_result_item[1]}, + "job_id": filtered_result_0_item_url_status_filter[0], + "timeout": 5, }) ################################################################################ @@ -152,9 +152,7 @@ def get_url_forensics_output(action=None, success=None, container=None, results= ## Custom Code End ################################################################################ - # calculate start time using delay of 2 minutes - start_time = datetime.now() + timedelta(minutes=2) - phantom.act("get job forensics", parameters=parameters, name="get_url_forensics_output", start_time=start_time, assets=["saa"], callback=get_jobid_forensic_filter) + phantom.act("get job forensics", parameters=parameters, name="get_url_forensics_output", assets=["splunk_attack_analyzer"], callback=get_jobid_forensic_filter) return @@ -164,7 +162,7 @@ def get_jobid_forensic_filter(action=None, success=None, container=None, results phantom.debug("get_jobid_forensic_filter() called") ################################################################################ - # Filters successful url or file detonation job forensic results. + # Filters successful url detonation job forensic results. ################################################################################ # collect filtered artifact ids and results for 'if' condition 1 @@ -191,17 +189,21 @@ def normalized_url_forensic_output(action=None, success=None, container=None, re # in the documented sections. ################################################################################ - get_url_forensics_output_result_data = phantom.collect2(container=container, datapath=["get_url_forensics_output:action_result.data.*.URLs.*.URL","get_url_forensics_output:action_result.data.*.DisplayScore","get_url_forensics_output:action_result.data.*.Detections.*.Description","get_url_forensics_output:action_result.data.*.Verdict"], action_results=results) + filtered_result_0_data_url_status_filter = phantom.collect2(container=container, datapath=["filtered-data:url_status_filter:condition_1:url_detonation:action_result.parameter.url","filtered-data:url_status_filter:condition_1:url_detonation:action_result.data.*.JobID"]) + filtered_result_1_data_get_jobid_forensic_filter = phantom.collect2(container=container, datapath=["filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.parameter.job_id","filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Score","filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Detections"]) - get_url_forensics_output_result_item_0 = [item[0] for item in get_url_forensics_output_result_data] - get_url_forensics_output_result_item_1 = [item[1] for item in get_url_forensics_output_result_data] - get_url_forensics_output_result_item_2 = [item[2] for item in get_url_forensics_output_result_data] - get_url_forensics_output_result_item_3 = [item[3] for item in get_url_forensics_output_result_data] + filtered_result_0_parameter_url = [item[0] for item in filtered_result_0_data_url_status_filter] + filtered_result_0_data___jobid = [item[1] for item in filtered_result_0_data_url_status_filter] + filtered_result_1_parameter_job_id = [item[0] for item in filtered_result_1_data_get_jobid_forensic_filter] + filtered_result_1_data___score = [item[1] for item in filtered_result_1_data_get_jobid_forensic_filter] + filtered_result_1_data___detections = [item[2] for item in filtered_result_1_data_get_jobid_forensic_filter] normalized_url_forensic_output__url_score_object = None normalized_url_forensic_output__scores = None normalized_url_forensic_output__categories = None normalized_url_forensic_output__score_id = None + normalized_url_forensic_output__url = None + normalized_url_forensic_output__job_id = None ################################################################################ ## Custom Code Start @@ -222,90 +224,35 @@ def normalized_url_forensic_output(action=None, success=None, container=None, re "9":"Probably_Malicious", "10":"Malicious" } - #phantom.debug("url: {}".format(ssa_get_job_forensics_output_result_item_0)) - #phantom.debug("DisplayScore: {}".format(ssa_get_job_forensics_output_result_item_1)) - #phantom.debug("Category: {}".format(ssa_get_job_forensics_output_result_item_2)) - #phantom.debug("verdict: {}".format(ssa_get_job_forensics_output_result_item_3)) - #phantom.debug("action_data: {}".format(ssa_get_job_forensics_output_result_item_4)) - #phantom.debug(get_url_forensics_output_result_item_4) normalized_url_forensic_output__url_score_object = [] normalized_url_forensic_output__scores = [] normalized_url_forensic_output__categories = [] normalized_url_forensic_output__score_id = [] - - ## normalized NoneType value to avoid enumeration failure - url_detonation_param_list = [(i or "") for i in get_url_forensics_output_result_item_0] - url_detonation_threat_score_list = [(i or 0) for i in get_url_forensics_output_result_item_1] - url_detonation_category_list = [(i or "") for i in get_url_forensics_output_result_item_2] - url_detonation_verdict_list = [(i or "") for i in get_url_forensics_output_result_item_3] - - ## get the set() or unique input url parameter. - - index_url_dict = {} - set_url_inputs = set(url_detonation_param_list) - - for url_input in set_url_inputs: - url_list = [] - score_list = [] - display_score_list = [] - category_list = [] - - ## getting the index of each detonation phase of the url group the result for each url detonation - url_input_index = [indx for indx, url_val in enumerate(url_detonation_param_list) if url_val == url_input] - index_url_dict[url_input] = url_input_index + normalized_url_forensic_output__url = [] + normalized_url_forensic_output__job_id = [] - for idx,(_url, _score, _display_score, _category) in enumerate(zip(url_detonation_param_list, url_detonation_verdict_list, url_detonation_threat_score_list, url_detonation_category_list)): - if _url == url_input and idx in index_url_dict[url_input]: - url_list.append(_url) - score_list.append(_score) - display_score_list.append(_display_score) - category_list.append(_category) - - ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation - #phantom.debug("score_list: {} len: {}".format(score_list, len(score_list))) - #phantom.debug("category_list: {} len: {}".format(category_list, len(category_list))) - confidence_ = list(set(display_score_list))[0] - categories = list(set(category_list)) - - score = "" - if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == ""): - if confidence_ >= 0 and confidence_ < 10: - score_id = 0 - elif confidence_ >= 10 and confidence_ < 20: - score_id = 1 - elif confidence_ >= 20 and confidence_ < 30: - score_id = 2 - elif confidence_ >= 30 and confidence_ < 40: - score_id = 3 - elif confidence_ >= 40 and confidence_ < 50: - score_id = 4 - elif confidence_ >= 50 and confidence_ < 60: - score_id = 5 - elif confidence_ >= 60 and confidence_ < 70: - score_id = 6 - elif confidence_ >= 70 and confidence_ < 80: - score_id = 7 - elif confidence_ >= 80 and confidence_ < 90: - score_id = 8 - elif confidence_ >= 90 and confidence_ < 100: - score_id = 9 - elif confidence_ >= 100: - score_id = 10 - #score = score_table[str(score_id)] - else: - score_id = round(confidence_/ 10) + ## pair forensic job results with url detonated + job_url_dict = {} + for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_url, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id): + if orig_job == filtered_job: + job_url_dict[filtered_job] = orig_url + + for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections): + ## translate scores + score_id = score_num/10 if score_num > 0 else 0 score = score_table[str(score_id)] + url = job_url_dict[job] + categories = [item.get('Description') for item in detections] # Attach final object - normalized_url_forensic_output__url_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories}) + normalized_url_forensic_output__url_score_object.append({'value': url, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories}) normalized_url_forensic_output__scores.append(score) normalized_url_forensic_output__categories.append(", ".join(categories)) normalized_url_forensic_output__score_id.append(score_id) - #phantom.debug("normalized_job_forensic_report_output__url_score_object: {}".format(normalized_url_forensic_output__url_score_object)) - #phantom.debug("normalized_job_forensic_report_output__categories: {}".format(normalized_job_forensic_report_output__categories)) - #phantom.debug("normalized_job_forensic_report_output__confidence: {}".format(normalized_job_forensic_report_output__confidence)) + normalized_url_forensic_output__url.append(url) + normalized_url_forensic_output__job_id.append(job) ################################################################################ ## Custom Code End @@ -315,6 +262,8 @@ def normalized_url_forensic_output(action=None, success=None, container=None, re phantom.save_run_data(key="normalized_url_forensic_output:scores", value=json.dumps(normalized_url_forensic_output__scores)) phantom.save_run_data(key="normalized_url_forensic_output:categories", value=json.dumps(normalized_url_forensic_output__categories)) phantom.save_run_data(key="normalized_url_forensic_output:score_id", value=json.dumps(normalized_url_forensic_output__score_id)) + phantom.save_run_data(key="normalized_url_forensic_output:url", value=json.dumps(normalized_url_forensic_output__url)) + phantom.save_run_data(key="normalized_url_forensic_output:job_id", value=json.dumps(normalized_url_forensic_output__job_id)) format_url_report(container=container) @@ -329,15 +278,15 @@ def format_url_report(action=None, success=None, container=None, results=None, h # Format a summary table with the information gathered from the playbook. ################################################################################ - template = """SOAR analyzed URL(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" + template = """SOAR analyzed URL(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Score Id | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" # parameter list for template variable replacement parameters = [ - "playbook_input:url", + "normalized_url_forensic_output:custom_function:url", "normalized_url_forensic_output:custom_function:scores", "normalized_url_forensic_output:custom_function:score_id", "normalized_url_forensic_output:custom_function:categories", - "url_detonation:action_result.data.*.JobID" + "normalized_url_forensic_output:custom_function:job_id" ] ################################################################################ @@ -366,13 +315,10 @@ def build_url_output(action=None, success=None, container=None, results=None, ha # the observables data path. ################################################################################ - playbook_input_url = phantom.collect2(container=container, datapath=["playbook_input:url"]) - url_detonation_result_data = phantom.collect2(container=container, datapath=["url_detonation:action_result.data.*.JobID"], action_results=results) + normalized_url_forensic_output__url = json.loads(_ if (_ := phantom.get_run_data(key="normalized_url_forensic_output:url")) != "" else "null") # pylint: disable=used-before-assignment + normalized_url_forensic_output__job_id = json.loads(_ if (_ := phantom.get_run_data(key="normalized_url_forensic_output:job_id")) != "" else "null") # pylint: disable=used-before-assignment normalized_url_forensic_output__url_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalized_url_forensic_output:url_score_object")) != "" else "null") # pylint: disable=used-before-assignment - playbook_input_url_values = [item[0] for item in playbook_input_url] - url_detonation_result_item_0 = [item[0] for item in url_detonation_result_data] - build_url_output__observable_array = None ################################################################################ @@ -386,7 +332,7 @@ def build_url_output(action=None, success=None, container=None, results=None, ha # Build URL - for url, external_id, url_object in zip(playbook_input_url_values, url_detonation_result_item_0, normalized_url_forensic_output__url_score_object): + for url, external_id, url_object in zip(normalized_url_forensic_output__url, normalized_url_forensic_output__job_id, normalized_url_forensic_output__url_score_object): parsed_url = urlparse(url) #phantom.debug("url: {} jobs_id:{}".format(url, external_id)) #phantom.debug("parsed_url: {}, url_object: {}".format(parsed_url, url_object)) @@ -394,16 +340,17 @@ def build_url_output(action=None, success=None, container=None, results=None, ha "value": url, "type": "url", "reputation": { + "base_score": url_object['base_score'], "score": url_object['score'], "score_id": url_object['score_id'], - "confidence": url_object['confidence'] + "confidence": url_object['score_id'] #Attack Analyzer's score has confidence baked in. }, "attributes": { "hostname": parsed_url.hostname, "scheme": parsed_url.scheme }, "categories": url_object['categories'], - "source": "Splunk Attack Analyzer (SAA)", + "source": "Splunk Attack Analyzer", "source_link": f"https://app.twinwave.io/job/{external_id}" } @@ -456,9 +403,7 @@ def file_detonation(action=None, success=None, container=None, results=None, han ## Custom Code End ################################################################################ - # calculate start time using delay of 2 minutes - start_time = datetime.now() + timedelta(minutes=2) - phantom.act("detonate file", parameters=parameters, name="file_detonation", start_time=start_time, assets=["saa"], callback=file_detonation_status_filter) + phantom.act("detonate file", parameters=parameters, name="file_detonation", assets=["splunk_attack_analyzer"], callback=file_detonation_status_filter) return @@ -474,16 +419,16 @@ def get_file_forensics_output(action=None, success=None, container=None, results # to be detonated. ################################################################################ - file_detonation_result_data = phantom.collect2(container=container, datapath=["file_detonation:action_result.data.*.JobID","file_detonation:action_result.parameter.context.artifact_id"], action_results=results) + filtered_result_0_data_file_detonation_status_filter = phantom.collect2(container=container, datapath=["filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID"]) parameters = [] # build parameters list for 'get_file_forensics_output' call - for file_detonation_result_item in file_detonation_result_data: - if file_detonation_result_item[0] is not None: + for filtered_result_0_item_file_detonation_status_filter in filtered_result_0_data_file_detonation_status_filter: + if filtered_result_0_item_file_detonation_status_filter[0] is not None: parameters.append({ - "job_id": file_detonation_result_item[0], - "context": {'artifact_id': file_detonation_result_item[1]}, + "job_id": filtered_result_0_item_file_detonation_status_filter[0], + "timeout": 10, }) ################################################################################ @@ -504,9 +449,7 @@ def get_file_forensics_output(action=None, success=None, container=None, results ## Custom Code End ################################################################################ - # calculate start time using delay of 2 minutes - start_time = datetime.now() + timedelta(minutes=2) - phantom.act("get job forensics", parameters=parameters, name="get_file_forensics_output", start_time=start_time, assets=["saa"], callback=filter_6) + phantom.act("get job forensics", parameters=parameters, name="get_file_forensics_output", assets=["splunk_attack_analyzer"], callback=file_forensics_filter) return @@ -520,18 +463,21 @@ def normalized_file_forensic_output(action=None, success=None, container=None, r # in the documented sections. ################################################################################ - playbook_input_vault_id = phantom.collect2(container=container, datapath=["playbook_input:vault_id"]) - get_file_forensics_output_result_data = phantom.collect2(container=container, datapath=["get_file_forensics_output:action_result.data.*.DisplayScore","get_file_forensics_output:action_result.data.*.Detections.*.Description","get_file_forensics_output:action_result.data.*.Verdict"], action_results=results) + filtered_result_0_data_file_detonation_status_filter = phantom.collect2(container=container, datapath=["filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.parameter.file","filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID"]) + filtered_result_1_data_file_forensics_filter = phantom.collect2(container=container, datapath=["filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.parameter.job_id","filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Score","filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Detections"]) - playbook_input_vault_id_values = [item[0] for item in playbook_input_vault_id] - get_file_forensics_output_result_item_0 = [item[0] for item in get_file_forensics_output_result_data] - get_file_forensics_output_result_item_1 = [item[1] for item in get_file_forensics_output_result_data] - get_file_forensics_output_result_item_2 = [item[2] for item in get_file_forensics_output_result_data] + filtered_result_0_parameter_file = [item[0] for item in filtered_result_0_data_file_detonation_status_filter] + filtered_result_0_data___jobid = [item[1] for item in filtered_result_0_data_file_detonation_status_filter] + filtered_result_1_parameter_job_id = [item[0] for item in filtered_result_1_data_file_forensics_filter] + filtered_result_1_data___score = [item[1] for item in filtered_result_1_data_file_forensics_filter] + filtered_result_1_data___detections = [item[2] for item in filtered_result_1_data_file_forensics_filter] normalized_file_forensic_output__file_score_object = None normalized_file_forensic_output__scores = None normalized_file_forensic_output__categories = None normalized_file_forensic_output__score_id = None + normalized_file_forensic_output__file = None + normalized_file_forensic_output__job_id = None ################################################################################ ## Custom Code Start @@ -552,91 +498,35 @@ def normalized_file_forensic_output(action=None, success=None, container=None, r "9":"Probably_Malicious", "10":"Malicious" } - #phantom.debug("vault_id: {}".format(ssa_get_job_forensics_output_result_item_0)) - #phantom.debug("DisplayScore: {}".format(ssa_get_job_forensics_output_result_item_1)) - #phantom.debug("Category: {}".format(ssa_get_job_forensics_output_result_item_2)) - #phantom.debug("verdict: {}".format(ssa_get_job_forensics_output_result_item_3)) - #phantom.debug("action_data: {}".format(ssa_get_job_forensics_output_result_item_4)) - #phantom.debug(get_file_forensics_output_result_item_3) normalized_file_forensic_output__file_score_object = [] normalized_file_forensic_output__scores = [] normalized_file_forensic_output__categories = [] normalized_file_forensic_output__score_id = [] + normalized_file_forensic_output__file = [] + normalized_file_forensic_output__job_id = [] - ## normalized NoneType value to avoid enumeration failure - file_detonation_param_list = [(i or "") for i in playbook_input_vault_id_values] - file_detonation_threat_score_list = [(i or 0) for i in get_file_forensics_output_result_item_0] - file_detonation_category_list = [(i or "") for i in get_file_forensics_output_result_item_1] - file_detonation_verdict_list = [(i or "") for i in get_file_forensics_output_result_item_2] - - ## get the set() or unique input url parameter. - - index_file_dict = {} - set_file_inputs = set(file_detonation_param_list) - - for file_input in set_file_inputs: - vaultid_list = [] - score_list = [] - display_score_list = [] - category_list = [] - - ## getting the index of each detonation phase of the url group the result for each url detonation - file_input_index = [indx for indx, vaultid_val in enumerate(file_detonation_param_list) if vaultid_val == file_input] - index_file_dict[file_input] = file_input_index - - for idx,(_vaultid, _score, _display_score, _category) in enumerate(zip(file_detonation_param_list, file_detonation_verdict_list, file_detonation_threat_score_list, file_detonation_category_list)): - if _vaultid == file_input and idx in index_file_dict[file_input]: - vaultid_list.append(_vaultid) - score_list.append(_score) - display_score_list.append(_display_score) - category_list.append(_category) - - ## if score_list is empty or it has one element but empty string, lets score it base on confidence score of its engine detonation - #phantom.debug("score_list: {} len: {}".format(score_list, len(score_list))) - #phantom.debug("category_list: {} len: {}".format(category_list, len(category_list))) - confidence_ = list(set(display_score_list))[0] - categories = list(set(category_list)) - #score_ = list(set(score_list))[0] - - score = "" - if len(score_list) == 0 or (len(set(score_list)) == 1 and score_list[0] == ""): - if confidence_ >= 0 and confidence_ < 10: - score_id = 0 - elif confidence_ >= 10 and confidence_ < 20: - score_id = 1 - elif confidence_ >= 20 and confidence_ < 30: - score_id = 2 - elif confidence_ >= 30 and confidence_ < 40: - score_id = 3 - elif confidence_ >= 40 and confidence_ < 50: - score_id = 4 - elif confidence_ >= 50 and confidence_ < 60: - score_id = 5 - elif confidence_ >= 60 and confidence_ < 70: - score_id = 6 - elif confidence_ >= 70 and confidence_ < 80: - score_id = 7 - elif confidence_ >= 80 and confidence_ < 90: - score_id = 8 - elif confidence_ >= 90 and confidence_ < 100: - score_id = 9 - elif confidence_ >= 100: - score_id = 10 - - else: - score_id = round(confidence_/ 10) + ## pair forensic job results with url detonated + job_file_dict = {} + for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_file, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id): + if orig_job == filtered_job: + job_file_dict[filtered_job] = orig_url + + for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections): + ## translate scores + score_id = score_num/10 if score_num > 0 else 0 score = score_table[str(score_id)] - #phantom.debug("score: {} score_id {}".format(score, score_id)) - # Attach final object - normalized_file_forensic_output__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence_, 'categories': categories}) + file = job_file_dict[job] + categories = [item.get('Description') for item in detections] + + normalized_file_forensic_output__file_score_object.append({'value': file, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories}) normalized_file_forensic_output__scores.append(score) normalized_file_forensic_output__categories.append(", ".join(categories)) normalized_file_forensic_output__score_id.append(score_id) - #phantom.debug("normalized_job_forensic_report_output_1__file_score_object: {}".format(normalized_job_forensic_report_output_1__file_score_object)) - #phantom.debug("normalized_job_forensic_report_output_1__scores: {}".format(normalized_job_forensic_report_output_1__scores)) - #phantom.debug("normalized_job_forensic_report_output_1__categories: {}".format(normalized_job_forensic_report_output_1__categories)) + normalized_file_forensic_output__file.append(file) + normalized_file_forensic_output__job_id.append(job) + ################################################################################ ## Custom Code End ################################################################################ @@ -645,6 +535,8 @@ def normalized_file_forensic_output(action=None, success=None, container=None, r phantom.save_run_data(key="normalized_file_forensic_output:scores", value=json.dumps(normalized_file_forensic_output__scores)) phantom.save_run_data(key="normalized_file_forensic_output:categories", value=json.dumps(normalized_file_forensic_output__categories)) phantom.save_run_data(key="normalized_file_forensic_output:score_id", value=json.dumps(normalized_file_forensic_output__score_id)) + phantom.save_run_data(key="normalized_file_forensic_output:file", value=json.dumps(normalized_file_forensic_output__file)) + phantom.save_run_data(key="normalized_file_forensic_output:job_id", value=json.dumps(normalized_file_forensic_output__job_id)) format_file_report(container=container) @@ -659,15 +551,15 @@ def format_file_report(action=None, success=None, container=None, results=None, # Format a summary table with the information gathered from the playbook. ################################################################################ - template = """SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| vault_id | Normalized Score | score id |Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} |https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" + template = """SOAR analyzed File(s) using Splunk Attack Analyzer. The table below shows a summary of the information gathered.\n\n| Vauld Id | Normalized Score | Score Id | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | {3} | https://app.twinwave.io/job/{4} | Splunk Attack Analyzer (SAA) |\n%%\n\n\n""" # parameter list for template variable replacement parameters = [ - "playbook_input:vault_id", - "normalized_file_forensic_output:custom_function:scores", + "normalized_file_forensic_output:custom_function:file", + "normalized_file_forensic_output:custom_function:score", "normalized_file_forensic_output:custom_function:score_id", "normalized_file_forensic_output:custom_function:categories", - "file_detonation:action_result.data.*.JobID" + "normalized_file_forensic_output:custom_function:job_id" ] ################################################################################ @@ -696,13 +588,10 @@ def build_file_output(action=None, success=None, container=None, results=None, h # the observables data path. ################################################################################ - playbook_input_vault_id = phantom.collect2(container=container, datapath=["playbook_input:vault_id"]) - file_detonation_result_data = phantom.collect2(container=container, datapath=["file_detonation:action_result.data.*.JobID"], action_results=results) + normalized_file_forensic_output__file = json.loads(_ if (_ := phantom.get_run_data(key="normalized_file_forensic_output:file")) != "" else "null") # pylint: disable=used-before-assignment + normalized_file_forensic_output__job_id = json.loads(_ if (_ := phantom.get_run_data(key="normalized_file_forensic_output:job_id")) != "" else "null") # pylint: disable=used-before-assignment normalized_file_forensic_output__file_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalized_file_forensic_output:file_score_object")) != "" else "null") # pylint: disable=used-before-assignment - playbook_input_vault_id_values = [item[0] for item in playbook_input_vault_id] - file_detonation_result_item_0 = [item[0] for item in file_detonation_result_data] - build_file_output__observable_array = None ################################################################################ @@ -712,17 +601,18 @@ def build_file_output(action=None, success=None, container=None, results=None, h # Write your custom code here... build_file_output__observable_array = [] - for _vault_id, external_id, file_object in zip(playbook_input_vault_id_values, file_detonation_result_item_0, normalized_file_forensic_output__file_score_object): + for _vault_id, external_id, file_object in zip(normalized_file_forensic_output__file, normalized_file_forensic_output__job_id, normalized_file_forensic_output__file_score_object): #phantom.debug("vault: {} id: {}".format(_vault_id, external_id)) observable_object = { "value": _vault_id, "type": "hash", "reputation": { + "base_score": file_object['base_score'], "score": file_object['score'], "score_id": file_object['score_id'], "confidence": file_object['confidence'], - + "confidence": file_object['score_id'] #Attack Analyzer's score has confidence baked in. }, "enrichment": { "provider": "Splunk Attack Analyzer", @@ -730,11 +620,11 @@ def build_file_output(action=None, success=None, container=None, results=None, h }, "categories": file_object['categories'], - "source": "Splunk Attack Analyzer (SAA)", + "source": "Splunk Attack Analyzer", "source_link":f"https://app.twinwave.io/job/{external_id}" } build_file_output__observable_array.append(observable_object) - #phantom.debug("build_file_output__observable_array: {}".format(build_file_output__observable_array)) + ################################################################################ ## Custom Code End ################################################################################ @@ -745,8 +635,8 @@ def build_file_output(action=None, success=None, container=None, results=None, h @phantom.playbook_block() -def url_detonation_status_filter_1(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("url_detonation_status_filter_1() called") +def url_status_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("url_status_filter() called") ################################################################################ # Filters url detonation results. @@ -758,7 +648,7 @@ def url_detonation_status_filter_1(action=None, success=None, container=None, re conditions=[ ["url_detonation:action_result.status", "==", "success"] ], - name="url_detonation_status_filter_1:condition_1") + name="url_status_filter:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: @@ -768,8 +658,12 @@ def url_detonation_status_filter_1(action=None, success=None, container=None, re @phantom.playbook_block() -def filter_6(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("filter_6() called") +def file_forensics_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("file_forensics_filter() called") + + ################################################################################ + # Filters successful file detonation job forensic results. + ################################################################################ # collect filtered artifact ids and results for 'if' condition 1 matched_artifacts_1, matched_results_1 = phantom.condition( @@ -777,7 +671,7 @@ def filter_6(action=None, success=None, container=None, results=None, handle=Non conditions=[ ["get_file_forensics_output:action_result.status", "==", "success"] ], - name="filter_6:condition_1") + name="file_forensics_filter:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: From a0ad583a553f34f0480d5f1921afc6453fa700e6 Mon Sep 17 00:00:00 2001 From: Kelby Shelton Date: Fri, 28 Apr 2023 20:29:24 -0500 Subject: [PATCH 3/3] Fixed bug with scoring and changed categories to classifications --- ...lunk_Attack_Analyzer_Dynamic_Analysis.json | 26 +++++----- ...Splunk_Attack_Analyzer_Dynamic_Analysis.py | 52 ++++++++----------- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json index 107384ef88..83bccbcefe 100644 --- a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json +++ b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.json @@ -156,7 +156,7 @@ "targetPort": "15_in" } ], - "hash": "97d3aa1e96d0ec556004151086384b4841477f03", + "hash": "d3278e0db9a8761705897f1307e410d32a715c77", "nodes": { "0": { "data": { @@ -246,7 +246,7 @@ "errors": {}, "id": "11", "type": "code", - "userCode": "\n # Write your custom code here...\n from urllib.parse import urlparse\n build_url_output__observable_array = []\n #phantom.debug(playbook_input_url_values)\n # Build URL\n\n \n for url, external_id, url_object in zip(normalized_url_forensic_output__url, normalized_url_forensic_output__job_id, normalized_url_forensic_output__url_score_object):\n parsed_url = urlparse(url)\n #phantom.debug(\"url: {} jobs_id:{}\".format(url, external_id))\n #phantom.debug(\"parsed_url: {}, url_object: {}\".format(parsed_url, url_object))\n observable_object = {\n \"value\": url,\n \"type\": \"url\",\n \"reputation\": {\n \"base_score\": url_object['base_score'],\n \"score\": url_object['score'],\n \"score_id\": url_object['score_id'],\n \"confidence\": url_object['score_id'] #Attack Analyzer's score has confidence baked in.\n },\n \"attributes\": {\n \"hostname\": parsed_url.hostname,\n \"scheme\": parsed_url.scheme\n },\n \"categories\": url_object['categories'],\n \"source\": \"Splunk Attack Analyzer\",\n \"source_link\": f\"https://app.twinwave.io/job/{external_id}\"\n }\n\n if parsed_url.path:\n observable_object['attributes']['path'] = parsed_url.path\n if parsed_url.query:\n observable_object['attributes']['query'] = parsed_url.query\n if parsed_url.port:\n observable_object['attributes']['port'] = parsed_url.port\n\n build_url_output__observable_array.append(observable_object)\n #phantom.debug(\"build_url_output__observable_array: {}\".format(build_url_output__observable_array))\n", + "userCode": "\n # Write your custom code here...\n from urllib.parse import urlparse\n build_url_output__observable_array = []\n #phantom.debug(playbook_input_url_values)\n # Build URL\n\n \n for url, external_id, url_object in zip(normalized_url_forensic_output__url, normalized_url_forensic_output__job_id, normalized_url_forensic_output__url_score_object):\n parsed_url = urlparse(url)\n #phantom.debug(\"url: {} jobs_id:{}\".format(url, external_id))\n #phantom.debug(\"parsed_url: {}, url_object: {}\".format(parsed_url, url_object))\n observable_object = {\n \"value\": url,\n \"type\": \"url\",\n \"reputation\": {\n \"base_score\": url_object['base_score'],\n \"score\": url_object['score'],\n \"score_id\": url_object['score_id'],\n \"confidence\": url_object['base_score'] #Attack Analyzer's score has confidence baked in.\n },\n \"attributes\": {\n \"hostname\": parsed_url.hostname,\n \"scheme\": parsed_url.scheme\n },\n \"classifications\": url_object['categories'],\n \"source\": \"Splunk Attack Analyzer\",\n \"source_link\": f\"https://app.twinwave.io/job/{external_id}\"\n }\n\n if parsed_url.path:\n observable_object['attributes']['path'] = parsed_url.path\n if parsed_url.query:\n observable_object['attributes']['query'] = parsed_url.query\n if parsed_url.port:\n observable_object['attributes']['port'] = parsed_url.port\n\n build_url_output__observable_array.append(observable_object)\n #phantom.debug(\"build_url_output__observable_array: {}\".format(build_url_output__observable_array))\n", "warnings": {}, "x": 0, "y": 1620 @@ -316,7 +316,7 @@ "functionName": "get_file_forensics_output", "id": "15", "parameters": { - "job_id": "filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID", + "job_id": "filtered-data:detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID", "timeout": "10" }, "requiredParameters": [ @@ -348,10 +348,10 @@ "functionName": "normalized_file_forensic_output", "id": "17", "inputParameters": [ - "filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.parameter.file", - "filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID", + "filtered-data:detonation_status_filter:condition_1:file_detonation:action_result.parameter.file", + "filtered-data:detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID", "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.parameter.job_id", - "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Score", + "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.DisplayScore", "filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Detections" ], "outputVariables": [ @@ -367,7 +367,7 @@ "errors": {}, "id": "17", "type": "code", - "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n \n normalized_file_forensic_output__file_score_object = []\n normalized_file_forensic_output__scores = []\n normalized_file_forensic_output__categories = []\n normalized_file_forensic_output__score_id = []\n normalized_file_forensic_output__file = []\n normalized_file_forensic_output__job_id = []\n \n ## pair forensic job results with url detonated\n job_file_dict = {}\n for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_file, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id):\n if orig_job == filtered_job:\n job_file_dict[filtered_job] = orig_url\n\n for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections):\n \n ## translate scores\n score_id = score_num/10 if score_num > 0 else 0\n score = score_table[str(score_id)]\n file = job_file_dict[job]\n categories = [item.get('Description') for item in detections]\n \n normalized_file_forensic_output__file_score_object.append({'value': file, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories})\n normalized_file_forensic_output__scores.append(score)\n normalized_file_forensic_output__categories.append(\", \".join(categories))\n normalized_file_forensic_output__score_id.append(score_id)\n normalized_file_forensic_output__file.append(file)\n normalized_file_forensic_output__job_id.append(job)\n \n", + "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n \n normalized_file_forensic_output__file_score_object = []\n normalized_file_forensic_output__scores = []\n normalized_file_forensic_output__categories = []\n normalized_file_forensic_output__score_id = []\n normalized_file_forensic_output__file = []\n normalized_file_forensic_output__job_id = []\n \n ## pair forensic job results with url detonated\n job_file_dict = {}\n for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_file, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id):\n if orig_job == filtered_job:\n job_file_dict[filtered_job] = orig_url\n\n for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___displayscore, filtered_result_1_data___detections):\n \n ## translate scores\n score_id = int(score_num/10) if score_num > 0 else 0\n score = score_table[str(score_id)]\n file = job_file_dict[job]\n categories = [item.get('Description') for item in detections]\n \n normalized_file_forensic_output__file_score_object.append({'value': file, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories})\n normalized_file_forensic_output__scores.append(score)\n normalized_file_forensic_output__categories.append(\", \".join(categories))\n normalized_file_forensic_output__score_id.append(score_id)\n normalized_file_forensic_output__file.append(file)\n normalized_file_forensic_output__job_id.append(job)\n \n", "warnings": {}, "x": 340, "y": 1260 @@ -427,7 +427,7 @@ "errors": {}, "id": "19", "type": "code", - "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n\n for _vault_id, external_id, file_object in zip(normalized_file_forensic_output__file, normalized_file_forensic_output__job_id, normalized_file_forensic_output__file_score_object):\n #phantom.debug(\"vault: {} id: {}\".format(_vault_id, external_id))\n observable_object = {\n\n \"value\": _vault_id,\n \"type\": \"hash\",\n \"reputation\": {\n \"base_score\": file_object['base_score'],\n \"score\": file_object['score'],\n \"score_id\": file_object['score_id'],\n \"confidence\": file_object['confidence'],\n \"confidence\": file_object['score_id'] #Attack Analyzer's score has confidence baked in.\n },\n \"enrichment\": {\n \"provider\": \"Splunk Attack Analyzer\",\n \"type\": \"file\",\n\n },\n \"categories\": file_object['categories'],\n \"source\": \"Splunk Attack Analyzer\",\n \"source_link\":f\"https://app.twinwave.io/job/{external_id}\"\n }\n build_file_output__observable_array.append(observable_object)\n \n", + "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n\n for _vault_id, external_id, file_object in zip(normalized_file_forensic_output__file, normalized_file_forensic_output__job_id, normalized_file_forensic_output__file_score_object):\n #phantom.debug(\"vault: {} id: {}\".format(_vault_id, external_id))\n observable_object = {\n\n \"value\": _vault_id,\n \"type\": \"hash\",\n \"reputation\": {\n \"base_score\": file_object['base_score'],\n \"score\": file_object['score'],\n \"score_id\": file_object['score_id'],\n \"confidence\": file_object['base_score'] #Attack Analyzer's score has confidence baked in.\n },\n \"classifications\": file_object['categories'],\n \"source\": \"Splunk Attack Analyzer\",\n \"source_link\":f\"https://app.twinwave.io/job/{external_id}\"\n }\n build_file_output__observable_array.append(observable_object)\n \n", "warnings": {}, "x": 340, "y": 1620 @@ -604,7 +604,7 @@ "4": { "data": { "advanced": { - "customName": "file detonation status filter", + "customName": "detonation status filter", "customNameId": 0, "description": "Filters successful file detonation results.", "join": [], @@ -626,7 +626,7 @@ } ], "functionId": 2, - "functionName": "file_detonation_status_filter", + "functionName": "detonation_status_filter", "id": "4", "type": "filter" }, @@ -731,7 +731,7 @@ "filtered-data:url_status_filter:condition_1:url_detonation:action_result.parameter.url", "filtered-data:url_status_filter:condition_1:url_detonation:action_result.data.*.JobID", "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.parameter.job_id", - "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Score", + "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.DisplayScore", "filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Detections" ], "outputVariables": [ @@ -747,7 +747,7 @@ "errors": {}, "id": "8", "type": "code", - "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n \n normalized_url_forensic_output__url_score_object = []\n normalized_url_forensic_output__scores = []\n normalized_url_forensic_output__categories = []\n normalized_url_forensic_output__score_id = []\n normalized_url_forensic_output__url = []\n normalized_url_forensic_output__job_id = []\n\n ## pair forensic job results with url detonated\n job_url_dict = {}\n for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_url, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id):\n if orig_job == filtered_job:\n job_url_dict[filtered_job] = orig_url\n \n for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections):\n \n ## translate scores\n score_id = score_num/10 if score_num > 0 else 0\n score = score_table[str(score_id)]\n url = job_url_dict[job]\n categories = [item.get('Description') for item in detections]\n \n # Attach final object\n normalized_url_forensic_output__url_score_object.append({'value': url, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories})\n normalized_url_forensic_output__scores.append(score)\n normalized_url_forensic_output__categories.append(\", \".join(categories))\n normalized_url_forensic_output__score_id.append(score_id)\n normalized_url_forensic_output__url.append(url)\n normalized_url_forensic_output__job_id.append(job)\n\n", + "userCode": "\n # Write your custom code here...\n score_id =0\n score_table = {\n \"0\":\"Unknown\",\n \"1\":\"Very_Safe\",\n \"2\":\"Safe\",\n \"3\":\"Probably_Safe\",\n \"4\":\"Leans_Safe\",\n \"5\":\"May_not_be_Safe\",\n \"6\":\"Exercise_Caution\",\n \"7\":\"Suspicious_or_Risky\",\n \"8\":\"Possibly_Malicious\",\n \"9\":\"Probably_Malicious\",\n \"10\":\"Malicious\"\n }\n \n normalized_url_forensic_output__url_score_object = []\n normalized_url_forensic_output__scores = []\n normalized_url_forensic_output__categories = []\n normalized_url_forensic_output__score_id = []\n normalized_url_forensic_output__url = []\n normalized_url_forensic_output__job_id = []\n\n ## pair forensic job results with url detonated\n job_url_dict = {}\n for orig_url, orig_job, filtered_job in zip(filtered_result_0_parameter_url, filtered_result_0_data___jobid, filtered_result_1_parameter_job_id):\n if orig_job == filtered_job:\n job_url_dict[filtered_job] = orig_url\n \n for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___displayscore, filtered_result_1_data___detections):\n \n ## translate scores\n score_id = int(score_num/10) if score_num > 0 else 0\n score = score_table[str(score_id)]\n url = job_url_dict[job]\n categories = [item.get('Description') for item in detections]\n \n # Attach final object\n normalized_url_forensic_output__url_score_object.append({'value': url, 'base_score': score_num, 'score': score, 'score_id': score_id, 'categories': categories})\n normalized_url_forensic_output__scores.append(score)\n normalized_url_forensic_output__categories.append(\", \".join(categories))\n normalized_url_forensic_output__score_id.append(score_id)\n normalized_url_forensic_output__url.append(url)\n normalized_url_forensic_output__job_id.append(job)\n\n", "warnings": {}, "x": 0, "y": 1260 @@ -800,7 +800,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-29T01:07:14.801171+00:00", + "create_time": "2023-04-29T01:27:41.839151+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py index a4d8bb866b..b0c7fbba89 100644 --- a/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py +++ b/playbooks/Splunk_Attack_Analyzer_Dynamic_Analysis.py @@ -89,8 +89,8 @@ def url_detonation(action=None, success=None, container=None, results=None, hand @phantom.playbook_block() -def file_detonation_status_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): - phantom.debug("file_detonation_status_filter() called") +def detonation_status_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("detonation_status_filter() called") ################################################################################ # Filters successful file detonation results. @@ -102,7 +102,7 @@ def file_detonation_status_filter(action=None, success=None, container=None, res conditions=[ ["file_detonation:action_result.status", "==", "success"] ], - name="file_detonation_status_filter:condition_1") + name="detonation_status_filter:condition_1") # call connected blocks if filtered artifacts or results if matched_artifacts_1 or matched_results_1: @@ -190,12 +190,12 @@ def normalized_url_forensic_output(action=None, success=None, container=None, re ################################################################################ filtered_result_0_data_url_status_filter = phantom.collect2(container=container, datapath=["filtered-data:url_status_filter:condition_1:url_detonation:action_result.parameter.url","filtered-data:url_status_filter:condition_1:url_detonation:action_result.data.*.JobID"]) - filtered_result_1_data_get_jobid_forensic_filter = phantom.collect2(container=container, datapath=["filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.parameter.job_id","filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Score","filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Detections"]) + filtered_result_1_data_get_jobid_forensic_filter = phantom.collect2(container=container, datapath=["filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.parameter.job_id","filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.DisplayScore","filtered-data:get_jobid_forensic_filter:condition_1:get_url_forensics_output:action_result.data.*.Detections"]) filtered_result_0_parameter_url = [item[0] for item in filtered_result_0_data_url_status_filter] filtered_result_0_data___jobid = [item[1] for item in filtered_result_0_data_url_status_filter] filtered_result_1_parameter_job_id = [item[0] for item in filtered_result_1_data_get_jobid_forensic_filter] - filtered_result_1_data___score = [item[1] for item in filtered_result_1_data_get_jobid_forensic_filter] + filtered_result_1_data___displayscore = [item[1] for item in filtered_result_1_data_get_jobid_forensic_filter] filtered_result_1_data___detections = [item[2] for item in filtered_result_1_data_get_jobid_forensic_filter] normalized_url_forensic_output__url_score_object = None @@ -238,10 +238,10 @@ def normalized_url_forensic_output(action=None, success=None, container=None, re if orig_job == filtered_job: job_url_dict[filtered_job] = orig_url - for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections): + for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___displayscore, filtered_result_1_data___detections): ## translate scores - score_id = score_num/10 if score_num > 0 else 0 + score_id = int(score_num/10) if score_num > 0 else 0 score = score_table[str(score_id)] url = job_url_dict[job] categories = [item.get('Description') for item in detections] @@ -343,13 +343,13 @@ def build_url_output(action=None, success=None, container=None, results=None, ha "base_score": url_object['base_score'], "score": url_object['score'], "score_id": url_object['score_id'], - "confidence": url_object['score_id'] #Attack Analyzer's score has confidence baked in. + "confidence": url_object['base_score'] #Attack Analyzer's score has confidence baked in. }, "attributes": { "hostname": parsed_url.hostname, "scheme": parsed_url.scheme }, - "categories": url_object['categories'], + "classifications": url_object['categories'], "source": "Splunk Attack Analyzer", "source_link": f"https://app.twinwave.io/job/{external_id}" } @@ -403,7 +403,7 @@ def file_detonation(action=None, success=None, container=None, results=None, han ## Custom Code End ################################################################################ - phantom.act("detonate file", parameters=parameters, name="file_detonation", assets=["splunk_attack_analyzer"], callback=file_detonation_status_filter) + phantom.act("detonate file", parameters=parameters, name="file_detonation", assets=["splunk_attack_analyzer"], callback=detonation_status_filter) return @@ -419,15 +419,15 @@ def get_file_forensics_output(action=None, success=None, container=None, results # to be detonated. ################################################################################ - filtered_result_0_data_file_detonation_status_filter = phantom.collect2(container=container, datapath=["filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID"]) + filtered_result_0_data_detonation_status_filter = phantom.collect2(container=container, datapath=["filtered-data:detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID"]) parameters = [] # build parameters list for 'get_file_forensics_output' call - for filtered_result_0_item_file_detonation_status_filter in filtered_result_0_data_file_detonation_status_filter: - if filtered_result_0_item_file_detonation_status_filter[0] is not None: + for filtered_result_0_item_detonation_status_filter in filtered_result_0_data_detonation_status_filter: + if filtered_result_0_item_detonation_status_filter[0] is not None: parameters.append({ - "job_id": filtered_result_0_item_file_detonation_status_filter[0], + "job_id": filtered_result_0_item_detonation_status_filter[0], "timeout": 10, }) @@ -463,13 +463,13 @@ def normalized_file_forensic_output(action=None, success=None, container=None, r # in the documented sections. ################################################################################ - filtered_result_0_data_file_detonation_status_filter = phantom.collect2(container=container, datapath=["filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.parameter.file","filtered-data:file_detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID"]) - filtered_result_1_data_file_forensics_filter = phantom.collect2(container=container, datapath=["filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.parameter.job_id","filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Score","filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Detections"]) + filtered_result_0_data_detonation_status_filter = phantom.collect2(container=container, datapath=["filtered-data:detonation_status_filter:condition_1:file_detonation:action_result.parameter.file","filtered-data:detonation_status_filter:condition_1:file_detonation:action_result.data.*.JobID"]) + filtered_result_1_data_file_forensics_filter = phantom.collect2(container=container, datapath=["filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.parameter.job_id","filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.DisplayScore","filtered-data:file_forensics_filter:condition_1:get_file_forensics_output:action_result.data.*.Detections"]) - filtered_result_0_parameter_file = [item[0] for item in filtered_result_0_data_file_detonation_status_filter] - filtered_result_0_data___jobid = [item[1] for item in filtered_result_0_data_file_detonation_status_filter] + filtered_result_0_parameter_file = [item[0] for item in filtered_result_0_data_detonation_status_filter] + filtered_result_0_data___jobid = [item[1] for item in filtered_result_0_data_detonation_status_filter] filtered_result_1_parameter_job_id = [item[0] for item in filtered_result_1_data_file_forensics_filter] - filtered_result_1_data___score = [item[1] for item in filtered_result_1_data_file_forensics_filter] + filtered_result_1_data___displayscore = [item[1] for item in filtered_result_1_data_file_forensics_filter] filtered_result_1_data___detections = [item[2] for item in filtered_result_1_data_file_forensics_filter] normalized_file_forensic_output__file_score_object = None @@ -512,10 +512,10 @@ def normalized_file_forensic_output(action=None, success=None, container=None, r if orig_job == filtered_job: job_file_dict[filtered_job] = orig_url - for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___score, filtered_result_1_data___detections): + for job, score_num, detections in zip(filtered_result_1_parameter_job_id, filtered_result_1_data___displayscore, filtered_result_1_data___detections): ## translate scores - score_id = score_num/10 if score_num > 0 else 0 + score_id = int(score_num/10) if score_num > 0 else 0 score = score_table[str(score_id)] file = job_file_dict[job] categories = [item.get('Description') for item in detections] @@ -611,15 +611,9 @@ def build_file_output(action=None, success=None, container=None, results=None, h "base_score": file_object['base_score'], "score": file_object['score'], "score_id": file_object['score_id'], - "confidence": file_object['confidence'], - "confidence": file_object['score_id'] #Attack Analyzer's score has confidence baked in. + "confidence": file_object['base_score'] #Attack Analyzer's score has confidence baked in. }, - "enrichment": { - "provider": "Splunk Attack Analyzer", - "type": "file", - - }, - "categories": file_object['categories'], + "classifications": file_object['categories'], "source": "Splunk Attack Analyzer", "source_link":f"https://app.twinwave.io/job/{external_id}" }