From 7efdf07489ca4f3aca3a6820876a13e75d231c3c Mon Sep 17 00:00:00 2001 From: tccontre Date: Thu, 23 Mar 2023 12:00:50 +0100 Subject: [PATCH 1/5] virustotal_v3_dynamic_analysis_playbooks --- playbooks/VirusTotal_v3_Dynamic_Analysis.json | 608 ++++++++++++++++++ playbooks/VirusTotal_v3_Dynamic_Analysis.png | Bin 0 -> 60006 bytes playbooks/VirusTotal_v3_Dynamic_Analysis.py | 601 +++++++++++++++++ playbooks/VirusTotal_v3_Dynamic_Analysis.yml | 26 + 4 files changed, 1235 insertions(+) create mode 100644 playbooks/VirusTotal_v3_Dynamic_Analysis.json create mode 100644 playbooks/VirusTotal_v3_Dynamic_Analysis.png create mode 100644 playbooks/VirusTotal_v3_Dynamic_Analysis.py create mode 100644 playbooks/VirusTotal_v3_Dynamic_Analysis.yml diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.json b/playbooks/VirusTotal_v3_Dynamic_Analysis.json new file mode 100644 index 0000000000..c93f96b00a --- /dev/null +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.json @@ -0,0 +1,608 @@ +{ + "blockly": false, + "blockly_xml": "", + "category": "Dynamic Analysis", + "coa": { + "data": { + "customCode": null, + "description": "Accepts a URL or vault_id and detonates the object in VirusTotal's sandbox. Generates a global report and a per observable sub-report and normalized score. The score can be customized based on a variety of factors.\n\nRef: https://d3fend.mitre.org/technique/d3f:DynamicAnalysis", + "edges": [ + { + "id": "port_0_to_port_2", + "sourceNode": "0", + "sourcePort": "0_out", + "targetNode": "2", + "targetPort": "2_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_2_to_port_3", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "3", + "targetPort": "3_in" + }, + { + "conditions": [ + { + "index": 1 + } + ], + "id": "port_2_to_port_4", + "sourceNode": "2", + "sourcePort": "2_out", + "targetNode": "4", + "targetPort": "4_in" + }, + { + "id": "port_7_to_port_8", + "sourceNode": "7", + "sourcePort": "7_out", + "targetNode": "8", + "targetPort": "8_in" + }, + { + "id": "port_8_to_port_1", + "sourceNode": "8", + "sourcePort": "8_out", + "targetNode": "1", + "targetPort": "1_in" + }, + { + "id": "port_4_to_port_10", + "sourceNode": "4", + "sourcePort": "4_out", + "targetNode": "10", + "targetPort": "10_in" + }, + { + "id": "port_12_to_port_13", + "sourceNode": "12", + "sourcePort": "12_out", + "targetNode": "13", + "targetPort": "13_in" + }, + { + "id": "port_13_to_port_1", + "sourceNode": "13", + "sourcePort": "13_out", + "targetNode": "1", + "targetPort": "1_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_10_to_port_11", + "sourceNode": "10", + "sourcePort": "10_out", + "targetNode": "11", + "targetPort": "11_in" + }, + { + "id": "port_6_to_port_7", + "sourceNode": "6", + "sourcePort": "6_out", + "targetNode": "7", + "targetPort": "7_in" + }, + { + "id": "port_3_to_port_15", + "sourceNode": "3", + "sourcePort": "3_out", + "targetNode": "15", + "targetPort": "15_in" + }, + { + "conditions": [ + { + "index": 0 + } + ], + "id": "port_15_to_port_6", + "sourceNode": "15", + "sourcePort": "15_out", + "targetNode": "6", + "targetPort": "6_in" + }, + { + "id": "port_11_to_port_12", + "sourceNode": "11", + "sourcePort": "11_out", + "targetNode": "12", + "targetPort": "12_in" + } + ], + "globalCustomCode": "\n\n\nfrom math import log", + "hash": "6d1c8bfd4d293929a88550feb9db8f98826d2e76", + "nodes": { + "0": { + "data": { + "advanced": { + "join": [] + }, + "functionName": "on_start", + "id": "0", + "type": "start" + }, + "errors": {}, + "id": "0", + "type": "start", + "warnings": {}, + "x": 190, + "y": 0 + }, + "1": { + "data": { + "advanced": { + "join": [] + }, + "functionName": "on_finish", + "id": "1", + "type": "end" + }, + "errors": {}, + "id": "1", + "type": "end", + "warnings": {}, + "x": 190, + "y": 1220 + }, + "10": { + "data": { + "advanced": { + "customName": "file detonate filter", + "customNameId": 0, + "description": "Filters successful file detonation results.", + "join": [], + "note": "Filters successful file detonation results." + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": "==", + "param": "file_detonation:action_result.status", + "value": "success" + } + ], + "conditionIndex": 0, + "customName": "success_vt_query", + "logic": "and" + } + ], + "functionId": 3, + "functionName": "file_detonate_filter", + "id": "10", + "type": "filter" + }, + "errors": {}, + "id": "10", + "type": "filter", + "warnings": {}, + "x": 400, + "y": 506 + }, + "11": { + "data": { + "advanced": { + "customName": "normalize score file", + "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": 3, + "functionName": "normalize_score_file", + "id": "11", + "inputParameters": [ + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.summary" + ], + "outputVariables": [ + "file_score_object", + "scores" + ], + "type": "code" + }, + "errors": {}, + "id": "11", + "type": "code", + "userCode": " # Reference for scores: https://schema.ocsf.io/objects/reputation\n #phantom.debug(\"filtered_result_0_summary: {}\".format(filtered_result_0_summary))\n #phantom.debug(\"filtered_result_0_data: {}\".format(filtered_result_0_data))\n #phantom.debug(\"filtered_result_0_data___scans: {}\".format(filtered_result_0_data___scans))\n #phantom.debug(\"filtered_result_0_data___attributes___category: {}\".format(filtered_result_0_data___attributes___category))\n #phantom.debug(\"vault_id_detonation_result_item_0: {}\".format(vault_id_detonation_result_item_0))\n \n\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 file_summary_list = filtered_result_0_summary\n normalize_score_file__file_score_object = []\n normalize_score_file__scores = []\n normalize_score_file__categories = []\n \n for summary_data in file_summary_list:\n # Set confidence based on percentage of vendors undetected\n # Reduce the confidence by percentage of vendors undetected.\n vendors = summary_data['harmless'] + summary_data['undetected'] + summary_data['malicious'] + summary_data['suspicious']\n confidence = 100 - int((summary_data['undetected']/vendors) * 100)\n \n phantom.debug(\"vendors: {}\".format(vendors))\n phantom.debug(\"confidence: {}\".format(confidence))\n\n # Normalize reputation on a 10 point scale based on number of malicious and suspicious divided by harmless vendors\n # This can be adjusted to include whatever logic is desired.\n suspect = summary_data['malicious'] + summary_data['suspicious']\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n if summary_data['harmless'] and not suspect:\n score_id = 1\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n elif not summary_data['harmless'] and not suspect:\n score_id = 0\n else:\n # customize score calculation as desired\n log_result = log((suspect/vendors) * 100, 100) # log imported from math in global code block\n score_id = int(log_result * 10) + 3\n\n phantom.debug(\"log_result: {}\".format(log_result))\n if score_id > 10:\n score_id = 10\n\n score = score_table[str(score_id)]\n\n normalize_score_file__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence})\n normalize_score_file__scores.append(score)\n phantom.debug(\"normalize_score_file__file_score_object: {}\".format(normalize_score_file__file_score_object))\n phantom.debug(\"normalize_score_file__scores: {}\".format(normalize_score_file__scores))\n\n", + "warnings": {}, + "x": 340, + "y": 686 + }, + "12": { + "data": { + "advanced": { + "customName": "format report file", + "customNameId": 0, + "description": "Format a summary table with the information gathered from the playbook.", + "join": [], + "note": "Format a summary table with the information gathered from the playbook." + }, + "functionId": 2, + "functionName": "format_report_file", + "id": "12", + "parameters": [ + "file_detonation:action_result.parameter.vault_id", + "normalize_score_file:custom_function:scores", + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data.*.attributes.meaningful_name", + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data.*.attributes.magic" + ], + "template": "SOAR analyzed file(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| File | VT Name | VT Decription | Normalized Score | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {2} | {3} | {1} | https://www.virustotal.com/gui/file/{0} | VirusTotal v3 |\n%%\n\n{1}\n{2}\n{3}\n", + "type": "format" + }, + "errors": {}, + "id": "12", + "type": "format", + "warnings": {}, + "x": 340, + "y": 864 + }, + "13": { + "data": { + "advanced": { + "customName": "build file output", + "customNameId": 0, + "description": "This block uses custom code to generate an observable dictionary to output into the observables data path.", + "join": [], + "note": "This block uses custom code to generate an observable dictionary to output into the observables data path." + }, + "functionId": 4, + "functionName": "build_file_output", + "id": "13", + "inputParameters": [ + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.parameter.vault_id", + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data", + "normalize_score_file:custom_function:file_score_object" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "13", + "type": "code", + "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n # Build file_hash\n for file_hash, file_data, file_object in zip(filtered_result_0_parameter_vault_id, filtered_result_0_data , normalize_score_file__file_score_object):\n observable_object = {\n \"value\": file_hash,\n \"type\": \"hash\",\n \"reputation\": {\n \"score_id\": file_object['score_id'],\n \"score\": file_object['score'],\n \"confidence\": file_object['confidence']\n },\n \"attributes\": {\n \"type\": file_data['attributes']['type_extension'],\n \"name\": file_data['attributes']['meaningful_name'],\n \"size\": file_data['attributes']['size'],\n \"md5\": file_data['attributes']['md5'],\n \"sha1\": file_data['attributes']['sha1'],\n \"sha256\": file_data['attributes']['sha256']\n },\n \"source\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/file/{file_hash}\"\n }\n \n build_file_output__observable_array.append(observable_object)\n \n", + "warnings": {}, + "x": 340, + "y": 1042 + }, + "15": { + "data": { + "advanced": { + "customName": "url detonate filter", + "customNameId": 0, + "description": "Filters successful url reputation results.", + "join": [], + "note": "Filters successful url reputation results." + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": "==", + "param": "url_detonation_1:action_result.status", + "value": "success" + } + ], + "conditionIndex": 0, + "customName": "success_url_vt_query", + "logic": "and" + } + ], + "functionId": 4, + "functionName": "url_detonate_filter", + "id": "15", + "type": "filter" + }, + "errors": {}, + "id": "15", + "type": "filter", + "warnings": {}, + "x": 60, + "y": 506 + }, + "2": { + "data": { + "advanced": { + "customName": "input filter", + "customNameId": 0, + "description": "Determine branches based on provided inputs.", + "join": [], + "note": "Determine branches based on provided inputs." + }, + "conditions": [ + { + "comparisons": [ + { + "conditionIndex": 0, + "op": "!=", + "param": "playbook_input:url", + "value": "" + } + ], + "conditionIndex": 0, + "customName": "url_input", + "logic": "and" + }, + { + "comparisons": [ + { + "conditionIndex": 1, + "op": "!=", + "param": "playbook_input:vault_id", + "value": "" + } + ], + "conditionIndex": 1, + "customName": "vauld_id_input", + "logic": "and" + } + ], + "functionId": 1, + "functionName": "input_filter", + "id": "2", + "type": "filter" + }, + "errors": {}, + "id": "2", + "type": "filter", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, + "x": 230, + "y": 148 + }, + "3": { + "data": { + "action": "detonate url", + "actionType": "investigate", + "advanced": { + "customName": "url detonation", + "customNameId": 1, + "description": "Queries VirusTotal for information about the provided URL(s)", + "join": [], + "note": "Queries VirusTotal for information about the provided URL(s)" + }, + "connector": "VirusTotal v3", + "connectorConfigs": [ + "virustotal_v3" + ], + "connectorId": "3fe4875d-a4a7-47d3-9ef1-f9e63a6653a4", + "connectorVersion": "v1", + "functionId": 1, + "functionName": "url_detonation_1", + "id": "3", + "parameters": { + "url": "playbook_input:url", + "wait_time": "" + }, + "requiredParameters": [ + { + "data_type": "string", + "field": "url" + } + ], + "type": "action" + }, + "errors": {}, + "id": "3", + "type": "action", + "userCode": " \n # Write your custom code here...\n\n", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, + "x": 0, + "y": 328 + }, + "4": { + "data": { + "action": "detonate file", + "actionType": "investigate", + "advanced": { + "customName": "file detonation", + "customNameId": 0, + "description": "Queries VirusTotal for information about the provided vault_id(s)", + "join": [], + "note": "Queries VirusTotal for information about the provided vault_id(s)" + }, + "connector": "VirusTotal v3", + "connectorConfigs": [ + "virustotal_v3" + ], + "connectorId": "3fe4875d-a4a7-47d3-9ef1-f9e63a6653a4", + "connectorVersion": "v1", + "functionId": 1, + "functionName": "file_detonation", + "id": "4", + "parameters": { + "vault_id": "playbook_input:vault_id" + }, + "requiredParameters": [ + { + "data_type": "string", + "field": "vault_id" + } + ], + "type": "action" + }, + "errors": {}, + "id": "4", + "type": "action", + "userCode": "\n # Write your custom code here...\n \n\n", + "warnings": { + "config": [ + "Reconfigure invalid datapath." + ] + }, + "x": 340, + "y": 328 + }, + "6": { + "customCode": null, + "data": { + "advanced": { + "customName": "normalize score url", + "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": 1, + "functionName": "normalize_score_url", + "id": "6", + "inputParameters": [ + "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.data.*.attributes.categories", + "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.summary" + ], + "outputVariables": [ + "url_score_object", + "score", + "categories" + ], + "type": "code" + }, + "errors": {}, + "id": "6", + "type": "code", + "userCode": "\n # Write your custom code here...\n #phantom.debug(\"filtered_result_0_data___attributes_categories: {}\".format(filtered_result_0_data___data_attributes_results___category))\n #phantom.debug(\"filtered_result_0_summary: {}\".format(filtered_result_0_summary))\n #phantom.debug(\"filtered_result_1_data___scans: {}\".format(filtered_result_1_data___scans))\n #phantom.debug(\"url_detonation_result_item_0: {}\".format(url_detonation_result_item_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 url_categories_list = filtered_result_0_data___attributes_categories\n url_summary_list = filtered_result_0_summary\n normalize_score_url__url_score_object = []\n normalize_score_url__score = []\n normalize_score_url__categories = []\n\n #for category, summary_data in zip(url_categories_list, url_summary_list):\n for category, summary_data in zip(url_categories_list, url_summary_list):\n \n # Set confidence based on percentage of vendors undetected\n # Reduce the confidence by percentage of vendors undetected.\n vendors = summary_data['harmless'] + summary_data['undetected'] + summary_data['malicious'] + summary_data['suspicious']\n confidence = 100 - int((summary_data['undetected']/vendors) * 100)\n \n #phantom.debug(\"vendors: {}\".format(vendors))\n #phantom.debug(\"confidence: {}\".format(confidence))\n\n # Normalize reputation on a 10 point scale based on number of malicious and suspicious divided by harmless vendors\n # This can be adjusted to include whatever logic is desired.\n suspect = summary_data['malicious'] + summary_data['suspicious']\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n if summary_data['harmless'] and not suspect:\n score_id = 1\n else:\n # customize score calculation as desired\n log_result = log((suspect/vendors) * 100, 100) # log imported from math in global code block\n score_id = int(log_result * 10) + 3\n #log_result = (suspect/vendors) # log imported from math in global code block\n #score_id = int(log_result * 100)\n #phantom.debug(\"log_result: {}\".format(log_result))\n \n if score_id > 10:\n score_id = 10\n \n if category != None:\n categories = [cat.lower() for cat in category.values()]\n categories = list(set(categories))\n else:\n categories = []\n \n score = score_table[str(score_id)]\n\n # Attach final object\n normalize_score_url__url_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence, 'categories': categories})\n normalize_score_url__score.append(score)\n normalize_score_url__categories.append(categories)\n #phantom.debug(\"normalize_score_url__url_score_object: {}\".format(normalize_score_url__url_score_object))\n #phantom.debug(\"normalize_score_url__score: {}\".format(normalize_score_url__score))\n #phantom.debug(\"normalize_score_url__categories: {}\".format(normalize_score_url__categories))\n\n\n", + "warnings": {}, + "x": 0, + "y": 686 + }, + "7": { + "data": { + "advanced": { + "customName": "format report url", + "customNameId": 0, + "description": "Format a summary table with the information gathered from the playbook.", + "join": [], + "note": "Format a summary table with the information gathered from the playbook." + }, + "functionId": 1, + "functionName": "format_report_url", + "id": "7", + "parameters": [ + "filtered-data:input_filter:condition_1:playbook_input:url", + "normalize_score_url:custom_function:score", + "normalize_score_url:custom_function:categories", + "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.data.*.id" + ], + "template": "SOAR analyzed URL(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | https://www.virustotal.com/gui/url/{3} | VirusTotal v3 |\n%%{1}{2}{3}", + "type": "format" + }, + "errors": {}, + "id": "7", + "type": "format", + "userCode": "\n # Write your custom code here...\n\n", + "warnings": {}, + "x": 0, + "y": 864 + }, + "8": { + "data": { + "advanced": { + "customName": "build url output", + "customNameId": 0, + "description": "This block uses custom code to generate an observable dictionary to output into the observables data path.", + "join": [], + "note": "This block uses custom code to generate an observable dictionary to output into the observables data path." + }, + "functionId": 2, + "functionName": "build_url_output", + "id": "8", + "inputParameters": [ + "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.parameter.url", + "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.data.*.id", + "normalize_score_url:custom_function:url_score_object" + ], + "outputVariables": [ + "observable_array" + ], + "type": "code" + }, + "errors": {}, + "id": "8", + "type": "code", + "userCode": " from urllib.parse import urlparse\n build_url_output__observable_array = []\n \n # Build URL\n for url, external_id, url_object in zip(filtered_result_0_parameter_url, filtered_result_0_data___id, normalize_score_url__url_score_object):\n parsed_url = urlparse(url)\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_id\": url_object['score_id'],\n \"score\": url_object['score'],\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\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/url/{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 \n \n", + "warnings": {}, + "x": 0, + "y": 1042 + } + }, + "notes": "Inputs: url, vault_id\nInteractions: VirusTotal v3\nActions: detonate url, detonate file\nOutputs: observable, markdown_report" + }, + "input_spec": [ + { + "contains": [ + "url" + ], + "description": "A URL provided for reputation analysis - Virustotal", + "name": "url" + }, + { + "contains": [ + "vault id" + ], + "description": "A file provided for reputation analysis", + "name": "vault_id" + } + ], + "output_spec": [ + { + "contains": [], + "datapaths": [ + "build_url_output:custom_function:observable_array", + "build_file_output:custom_function:observable_array" + ], + "deduplicate": false, + "description": "An array of observable dictionaries with value, type, score, score_id, and categories.", + "metadata": {}, + "name": "observable" + }, + { + "contains": [], + "datapaths": [ + "format_report_file:formatted_data", + "format_report_url:formatted_data" + ], + "deduplicate": false, + "description": "An array of reports. One report per detonation type.", + "metadata": {}, + "name": "markdown_report" + } + ], + "playbook_type": "data", + "python_version": "3", + "schema": "5.0.9", + "version": "6.0.0.114895" + }, + "create_time": "2023-03-23T00:01:47.766315+00:00", + "draft_mode": false, + "labels": [ + "*" + ], + "tags": [ + "url", + "VirusTotal_v3", + "sandbox", + "D3-DA", + "vault_id", + "D3-FA" + ] +} \ No newline at end of file diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.png b/playbooks/VirusTotal_v3_Dynamic_Analysis.png new file mode 100644 index 0000000000000000000000000000000000000000..a15b277c3630f0b536765637ac1351d81718917f GIT binary patch literal 60006 zcmdqJ1yCK&7cPk01PLy|;o=e;g1ZJMIKhJlx8N2M+}$O(yK8WV;1DDPcX!*lNq&EM zueM%o)q7i8n=0x~&rDBupFaJabG|;EAVqmeWJG*KC@3gosW)OuP*5;fP*9+3_~*bK zD&uSnC@5$Zb5T)6DN#|dqP>lYxur1_6it*^^*O3O}(6X4iY)4@WUx=@eQ1J_BO_ZdS#ReSsR%P-3S3#+jY zJdD~nmwVz2)b})mqnF!Xh#3kK4}l8#iW}Sig1Rshla!v&_#IE)$9s2Yoe}Ad`(mE`4=H`No^V}JWiQW%>T!O~ z7@Fl>R=<_(Qe6rb=iO&BI9{q->HX#j%P?7&Vp_M<1hW;hxby(R=G33Ni!>nw#H?nl zA!Q;b2So>5!$Uy_m_vbpD`?;kANYfUf{pfvf&+emxquSOg!$(w3|1!WKi8ma$cw_t zqEb@8ud<=Nv9Yy-nT@07TJ^aXDo}hbJiw)u zv7-Ul#mds!fyafP?Drcyz%}GH6B+pTD~=ZYWEygcU{M=;V=xCJGb1yZ03sL+=Ce04 z;ZYKk_*)$)@spW3I@iv2b&9GcmI=v9dA%Z!kExT00uJFjzZ~ z|M|#2-w`u*Ftj(fbu_oJ21CAU@ZQGBk)MnV($K&D{pqK%i}}A>vUd18EMS04kS9zm zjLb~``ZiFN4|11B(cHz@Qd7*_3J?#_h5!o-7boBE3jfoie>eFbRW<%y^)(yof7bku zC;wwjRR?2xQ5!3uO-F%$PtD)T|M~E5MLs6T(Emdef8_l8E+A+DL_Vf}O_~6rmf<0w zDnd|FV!|pe(EDj{sTAU^*WM}CXNUWT`x_n|MBMPspaBICIy$qy5Sr)F6BZX18#rBH zDu2vJj14UO3>r3Aj5285kuEPe}+0h6)-9@j1-zXF!P?vm>(~ zj}!*-Do{l0$ka!I{6cC+vOyySr=pYnEkgMg-TL>6f5i1DE&X+LKJ-R|0u6qSvNlXK zoPpRE)uW*uqYvszoGfMz?Yev7&3PAfl$2%Qq=(X|;%5;9glD3aPW^)sgzlEdz7hP; zcN)E@9{UJ$eXL$T4HY^BX7B~EQ$-C>gEfBl1883f^9{FX)H@LEKYgQdhQYjU-t3oIHs^q=DY>BfarJw?~Zc;VY-)J~jljVb6c z;6eM*3%#U$FC#kO1Qzvz41Mm%0ExnTyf>$~U5)9d;OI6Npf#lt$*TDLDQk|$XhSlb zpX0itFWtwJehp6G60~oKCwN>*txsYRmJxgzK8k*q(=F1m1frv(%V}#9?hPx(zE}e* zN`oXMBtqv`(pcHqX?FXBGgS~&5g$ucEut*p{i)KsOs z^=zc3(*{Mr@j`=?5p&(wq(3=e)_TLb5^c5e6*EfUr*I`I!%Yc6(s6)P3oi*TI1p6S zSO$6(k5Qm|tDSGF2(>;5zYT9a%;He5*xb)r&Y?x~5uydOk!E8#`|$4!CII?qD&nL38GCl zYJCsS)%>e^A{aj_zeB3eqH}F$!b(i<>@)zp}qF0F=>r9KaZ%<93@HlgKSrImU7|>;~b99tI9?sV*d(k+?5c_mHrZAA3 z;HC3;oiEt8K0JCi5i*v1y5f3%GLHaOUq(PWz>Ovn^>bwkfB{FM!T_t_^eqi5=}!|F z&Lzi??Tn^kuX}I%abdVk}o)U8A8R5K-- zZ_YA~;z;g~`%X)3yhP=>R=Em`ilVK4)DLmCyXNxU?K{|yEji;#s05VgG|Q*EKe}>n ztj8G7R~g&S>Rk0>t7}`wPtVLyMeyHN1jiYM^MAiz7ldevJVbv#k+DiA`_!^jkKa+n z?)bOB(lv?rg5O%X4d@2>vp=DSx^R zz2DED9r!NUuI%M8J-tcqr<$MQ?GS>5|GZPceJqAnnQExUTzRCRTHx5g^|8@g#?!Nv z@3_Oy{${q_0G|y1X3j8TD0)_xBi_gj=xW;-Mh`WlOMMU^_Qw#;ebccJYmWc2_f(=~ z@;E_vbR|2d>2sEM43%6oua|cUcUOh}H-ppd>o_tRwXu zbUrD8n*oQB)|XA(Y()7fY=YO{s@LZX$rYmX6>~(x*a1vz0=ZbS>nPUSb0nR|Y5p`|HHo?Tu(JC}(!-+&kPX7w$1SeV@A z^D%wqM6%c4zfg4;W`CiQca_J`_wSTS?x&-Lxp_C(k0nx+&sdVr`+no2vvZ$r-hn!- z7^lfE2%2%pLHTu>f9-#T2NU&xe)GdtlNDS*1DRDg_==4m(1C$}9gSFC03z*4ple9} zcn?3J07nB3Oa}9(sCWp>(x4}3-0Lrd!a`=vClLNGv^PSa{|a5`4kB4#>7bw$6xo<* z8&UVoF3>&7w};|xZGm8*n{(>y`YU_Lj|ajPVAD zB;PoBcf5}f%|-yTjqLsbz&4Wb<+?4ok|Bgpr}Mg@Vz~=&WGs}V4Rw!*eh?bqnY<-C z$V~ogr{J-o05gZas?7cy(1ki)0fTFqh^c}7J9sg`B#D1k_}+*K9}&9F2tq)!Mew!d zx5yzfuStUQcuyyz|d^$ckDQ8Iyu#Qd6uq89Ed zAV6W3iA7LuA0z3z^(y?DnC+FQx-m!?}8cKcuft4$54+3CLX zJ7nLtpgPMX3PAZy=Ex)X=m6v&D!9H5Aqczn#@(4RZ)4En=%Zf62Rs5c7}^z%cP4yh zjG)2z4wN-?ZEwl^`Ny7+g#O}QJws7HM|>NC8UB~NANM0o(_Q7uS`YJ2k42wC?cY_N zA}?3r=la6IY{>W|_{G_*OxnX!plzF74eignQ+=MDoyB8A2LNg1-HE^DO51DFR490% z%zFUxV)Zg6-^0&gn3UfE-BmzOQ??r!N%8-9Y%@CuiBs~twjoJ=SfVlJ_ zs4~QrD#w8Di~#C&mXE|^GZ`vUeI-W1)FTY|({Cie8%W|+vY#P^--zI=0Vs7PJEX?% z84H%Z5q}uzFVIj)U;7T)6`YEo@b$$A;xG4Svmc$+AvU29S z(ClcNuu?&q9t2h#{m(ft8k&*Y2>nzCNSXr#L|b#~l!$+Y3=N+MEMHvse{gDD7BE?+ zpW**vHUKK0SpG(SK0>3wtM(FsfcpNKGN2;t|92{87PV#BMG-yMvnamiePG3Si2#E6 zC}p)4X&55;6PBcYSr4#uzs_o$}0Rzm9iMzrrY%-k4P^?xO$)Lp<$A-9o z0u4V0u>uA$2{ix63#^X;gwNo=()*vnXTTix{woUqe-d~9zhCix5$P|wdB5i12pXHE zT3XU!<>Jy`dE5LRZ|pMh_dFb3^2hMa;E(+)G+kF>g%=O!r~hAurcf{&vicrdpd=Q< zx{`ObA^^OrjMA9#g24Ks0K9yW)BOr@Nq!61M+(qg-+xN~`*gQR81hYUdPgW;wb531 znu)Ela?H*A!~~tHs%l<)U74C%xh`Htm<0gxp%B8sZfnvJo`U?Rg%u`XR8-U?Lg8M? zxw{7dL5M9E1^?30#L}d(_!$CVy10RWbI=27+ylNFQ<2=j){I8LWvaHCk7Z%1`T&cJQpUoP2*C3*B9`vm=U)3yGGNZ__0pju&2%k zxzK3?OCJ|26w+~8U!!WQVR;I1aWEh@2%m!s$V&==A|+S2T%S@xogdRg}~N|7tFd zbRuFw+OaJ7FP;8mLH8n+RtCXu*5_dVA|4Ni7gDzH(N(-!hHMIlC_X_-_Osck&=}ZD zL9yr0Z;dA7<*Kaq;?1;MBu!tFxfxOj&}Cc1t%jz)Y3^o@w6|;K-l8$Z+0x$q>N?%; z;2$xW>R^%N-lq_LU|-T+;^i#kN%nhTv(@vG*Iz~wz@XWp`xX^_NaD8$KY3BWZ(Q2c zTt5&TXOsKdFiHs*0Sx6E4dIMVb>6^&eqxn{cBCr$<+{muJTBzrWxE8q)-HjAqC>|2rD4 zTxM>M>2pW?h(Hq0^S9aLeGNRDiP@o&gXA_ORvfxk;eJq{!4yDaU9SX71AI=BPo)b> zOJ7UvSN5fb2JW*^N4HsPr>Ur9%Cmlwd=KvuDm3Uo!#AUw3=2JrQBYd?N_uGuZTsMW zWk|Qu{>%NLvvZD4L|n0af>R#v6!{BghD>W5vCtbWslIBHT5_$LiChcQnurU6=i zs*r<2h>H5GQF;_%HasY7w&?2OA{=oI2sLO3p*Lw>^d(1wBZZ(0$)KfXpg+XYDgcqO zA|WsjJ*)DCXw0URiAlB}!8BvHO>U-N$dCc|B?K7ByTmsj=q-T9IP2NsQ>74rg6Sax zrl0;aR_hW3xVXk+U=pYzi@Ats)Uy81LUh3Q3gOXFefiLFmoFSe(z_E%=d!Z0s;qdC z1B6&0a%b65{-}vLo{#Yn30Ydl)6bA_VZoy}q=hU%r3X=`(Oz{w*Ysgf<;fP1{VD(^ zSn1r9B4BADQ`%HM&tIty0>p7dd#<-TreaSmhly#&Zm5$2oS1(CBXwcG656$t=}Cmv zW5WGAGD_btynlCj$J@f`MfgEoMkZt`oPe4XPWOr5d_rvAdBt;mx48Uu7~D{xM!j`f z%bw1Y0gBLt`}B^b0=R8D%vMv(y;ww_=jQ z-3>*Z$a-2zB0U&~^om)#`s0~A#z!$8=FC8FsgH{oWn6Jg>G>G*UwyQWiEK&DBb3ye3 zC^3tsmN>gDo$BY5VNR3H`-25{iOSQjZ?jr7c5$aiw1qOw0Ou&!Km8MFE?GF{ep~PbZ=*_uvxbNUQmseN zgR4Us%WDEY3U#~yKHoAPQu8ea#JZGOXezT^@;Pm_Vsx%VbJtXHI|-8uU@tvETyuk- zo+4pjG|HddzUprch>y1=WRVb29Vz6FDWFHQj*aRkXv)!kVsB-;M~5Y5yV^d3<^;NC zt8Al)1K*84uD9NF?u!`%Ybwu-0sg5A==$LS!H4J2F;E0(J$x;QD2r%lww%C{Y-;u{ zAOgw4Bw3FSKmYLs$BQGy@^qMOEZJMrSe^}2hRFOA86GJ3jgMRsBmzbQR*nFt*=UA$ z;{E2Po~csWTl4bP!{?2c3wx4ZxShU9$mYKRXxtmh4kU4}8!{r8aP(}aDMAjz#|?kqnjZTbMvQF zWeovZPGkR1!oVhmM5)BSMLZc0aQOhlW*7_E0Ja5WWK`Sr5{e zZYpZQ$D;arSdFWEs!U<;nJY0rEM<=88Rc4YoSf^QZ9`ztO5o`Ip^i>zkW8h0g>(@# z+7I`32?&`?9}NPM^WPWg@7XC)zc3)ri(!v?=KBk>_=w%5{e7}QQw9AY!j*eH25ynq z)tKQQ@jAgjX<-Ok4+;-+ST^K4b_^R!o!!>=77>;L%}2^&-B-SQ!N>9dT~Rs`-bMB0 zf*BcMP|`WLCy-hT9=K6T;5kQo1&H@2GK}TmxrE>ZBD@#^C?Fu)i_?8BaA$Kl%Nnp; zfSK7G?uUj}#~h~w)n~|kOTsK87EwWg(FD5Ik4TUN{%H+ky^`?r^0u+rxsV97CKOJAi&D+K&IKeFGUd{E^p7MM&m(` zF(9}alNy5k;bfH-H3U8b?HCO0xgeTxYa22-kMNofFjPKQ@Bou`tQ%vF4jK&b@p3h- z7Ot+U)cs(pfQLDilh>%E!)-l$ZG>*wEi3Vb8QvuTirUyMpUE5n6pb;S8zCz{Zpm{h zl;mB=Ndfkw<_RTU-H)KVjWT2P{-Si8=y(Ex0)J!z z7Q>*Q0YLN2qN}X}x#ECB6gfaIey2&D*!vj~kJIyOO}f2`%JQcH64o#TQ?$CuYLm4q7Mf#wWk;ZE-%n_3z^SIy+|Fz`IK_)V1HO zVFVxGQxAU}8&H26Gb7XyFVw_@O2FBu{DJCY|4)loMn+rPXQuccJXmwhZRYtJMBF}F zS1~9gyqkD!Wcdsohz}Zo0N~4Bb08uF&Azy~CNer5pE8-=p_rrDvH_vP;v(-9GK?!& zu^=I!aw;ldRFndw4FvR^J!zuTkZq78%kkkl4`9*76W)Lon;?d${-}T(R0S+pPvo2U z45zLEc!0MK1GI2?oD3J{kScOu9xJKlvnOWKI2i+|1sHWFucwp@I1=60gWl72Q%pif z2=Ly5fu7|z#qQf9It$}@NZcK3UkFfK?s z9B5|12@l)fry7(xCsgPkglV5@g*O5C)gl;wotVtAEzI_IQHf46-;Qm}v;EVaK2qxR z{%wov&`osjXBjSJ+2sx!$_9bOWq1$i;om9e^pKH|-f&sc1nDr&-h%tc^q&w+uN!vU zxdHyEU&9a?favnDxhw5pjekcLn>=3j^O;GOPa5#yr zPd5K1IXPu=P?QX?+078;{83+n1Y?f5y*+0o=Hc*t)_7GMXKr))T1LRzjy+6}%9|Yx z6+mz`;N8KZW^;N_G^UyPNfLTmniu!&cDB)K=L_R+pKhcuEgE`B9ERWiItsP?Wod0L zmmDty1Opxrs!>62QVE`dGBbt6!op&tf&Vg)-f+9es(v|DU!{O0Yc>9d;cS)UQUgz6 zfl!);_Vs5WZj1uPK8Z;aAiRwCA-2EMo}V0z-h_eDopOZFO{Um zmjQ`&(DcKrJZ_WYMf7E19$+6#7Eg_B`WI^VdS!74PA-2~7~h<7j%4~izImK;xd$q^ zLh!u&F|QWwjdZ5P-KE*}LH(1!ZvENf;}71i-*uLLeD#9FHLei5H0G5*jfN|(53psx zMz@(i|7m}={iIxdXmCV(!}q|afGGL2%^y|94jvd~q)@)BWx5 zq9tb(FPU`7%JIF!kdePeM60Q*L-wN9lT6cNEXwZ+`R>OBvs2YdJVZYpF5~N1<=M0 z_n^YUvM7T!>z%E_@EY!DG?)z$@CGqr#+Ydfd%^GJCIq%%?5~uBBe`xk zcvQ;HDlb=mhfJhsk@}gNd5-CodRtsPu|6ML`VvQap~A03$6Eopn^;L2+*n0sqbZ7d zav%AWqafFiD!wYf>QX`{BmSox3n`P4 zLI2akLSPl;kcsjAQ>FzXw;VAsn16UcB|t0RhzGj-s|;x0o1j3@KU``K*&l|5G3wvo zC)B}MXhmpmc>?e8b8Y5s=;$bR!Op3QtwOoZYU%y|Ex-0H^YbTC(ZM(NQs(|-eZ*KE zzjJB+;fzB`uIDf-0NztU++EVtI{uR z>mfn_Kk9)Us{K#T|7{Qc2v3|G@Z;Fev5@|V4#?#|LOeRBWbpkXYAlFh`97}9{6{h! zybws6OsE0g>QcaaRjha$MSj;K)%KAdH4><6$_dF+24u%Ki&8r3p7iZ zF-Gm>9T56i9LCp2N9hOG`rI${-=uNE$_j1m+OCnQe!k_8rk=L!k3FpE2Q1&<1gu+E z4&13+OJ1iJ$4DNb0PGhAqJOAQ)RM2}b)+FGvH;$3VL^q0B_;vl7YZ!a5<~1&D(*!+ zgjO{JN2R%O{#AXh37yR+gnq4Su7te)n%a)1d7-8IHtml4MHpXPY~$R2uP`G< z@Cae46XyD{x#>U)g5*$l9)C_=5#vZIMF=G`;Q}ea-AZh33UPvW`vct0d?hyrz98sS z0$6W0X6UJ}e0OK4l3Y)?Cq54I+GDHQ-_$Q(eJ;5@u?fwUFyzSXFGG9Y!%{6sdX(lI zq@t#R-B`O!C%}gh?XLHf#aDOw(&lyOOhdE~Kp&KqypB8N^G!y{7`}VN$+^jFOt-P9 zdd{)Wt&@zhDAPpjmA5S+F5^(l&dx5ar&sRi>`Yl~EpL#2`0$L|wfseB67^-^7Kl~K z#F(f4j0OO?1kO5vIErV&Xtv={&J_;yQR`q|O}*Qksj=i=ncWkZ)Ak+ONjE)b+RAo@ zu=Q(Bn#>RM++KC=3)?QRI%LP!^BJ+vCdVA5tP1?qf%WTBYbw*A^XN=MTN-ev)yZI6 zwoCM6*HA-Tm*D_Y9l3tA&F=7ehEv+XLFOe!wCVSau$ZeR8dr*}a7@LSVHoakY+n{Y z+u!T|bb^C^YF^5V&GDWeWLZ-d-(PGHpILONFGw}Ao@{-JwTckTpr>1_=$K)| zV@anQn7&!@R4F5?8Z7VaYH7SnbafKVYod+GA2eDUL%0er%$IVol2m$|a;w(ATb3Yx z1;urcuIo_WQg@P(-Hr0xZM|)_JbsyIfF#88!!*(9pg)|$D4Vp(yE@nltH2vGcpGt?}<}31j56^IMcC~Hz*HX3OOI| zx0y!Lar6>;aJ+d78~ki;%h-q0Iq1p`Dju@+_Or=OUA|lHr&MU`Wt=$nRZ-@VvKXJT z8+z2@`}eRLDu{RYS4>as{f2S5m*Ey3+nyO0J z=RIjKyMgs(Y_!>VZU1xKSw=?3NP-IA+=b(!Jo0jUeGWW8HAVw%u6us8$f(zTM02w_ z5Fw@Ykn^^2dG5AG`|P?;^2<&S?HOaV@}+Jfn%DMzOip{{wF24&{+%6tz1PFLp&`>C zNLIoLN=;F@#OFu1&2iZK*#gb1a@^vM8g)e^cEv`eoiHc}9J)OIjwFP}2b4;HD;OjBtkhp#j5 zS2t`1VrN|KyN6K+m@?Wr;O~5rW^Rv8uQ_-p?n8l}95=Qq_Afy%-;$=)dL=>6=yL{F<)Tm~Pp73ZhWNT?aME|Ys8P%6@lsn- zN3px{CNdu94!1X^$zDSRiiVwtAymO9E!Tr*80;qe!3R?X8b^wh;d~#2LIYee7U$vg z%N>s}pP4g``$K&8`Pw*GpN+ z#q9H+XxtpUNEj5r43AUot&&PZ&$FK2qzAgaj)m7c+QFK>a?S{~Ru|sUJ1VyxFA1ExXsEstI$` z3npm{uen{$SY6b@RkZXzw|A=M@oSBjYCL{uT5p@5W7??5aN{~(R{^tEYrgH80=Gn- z_bsf_bY#P@L4xYp@U=I|%QR$`mz5(qmO3<_i{iNce1c1KNAvqFUh&Bni1p zl0FF45Mw2kRN*to!V3b&|9Yl5ZJ5(i@7bO*1?ARXfJw7ZlY`?I67>t3hBEeY@~rI-{G9>SWTH9L=~v z^{n~M$hL>aaM$~)=1}7R0U2rFGG7;Ch4=Dx0vBBv-V3MJa>j_!cN(t6_pT4gMxSaq2=&7%O<&&s_AZg^_lzcj{ z;Wl*rxs;{rt7hH6R>v=T7xA^Bo56^0gGvxWXJR!&NDV9DHIxG?Q&NLfaq?1) z#qit-KXNZ!H{5H@4va5^nY~1vV*KD*ePdrd-L9`gnvbj~@nEeF_ zS0Smz?R(q=yG>oAjmVY!IfE|aP1k!!?O)%Syub z@vC3A?RX>jo~Pb4e3qkp+vz?>0v<&kTUp9RC;pP_Cvo*bwFyF++wKCJ3W8mWOm=}M zHGw=!e#Z@!`zMp$o(nBAPmV|3!B$ND3}c?(*?HJAHa5e&^9hYCJ6m?<$A%`0kAgu* z;dRkrx5HElB|a`EVX*Y>w=*$gJr&Hc$YRtrhm(H{ZRB_WZj}Uq2Isld;7W82VUdD>;Bjl2B7$e}Ph z{#>*5VPkHeHOuwmC*df$bd9X0##5gG>Lh=$5|%7;pwD6OulcH+-2DBu!h+^Fc96si z51)dqBpYv&vS?1y>*?LgM-_w|z0l{q{^P{0E3>{=T5( z(5okLqC5d*E%dq0*A8O^p#-^j%iDl^^MR|d2kLqgrUD`E){;KN&*RuVb7Rl&RnKoc zCZ?un_0Qv1Dufho39gp!+#(H^=5{OU*7#L!DhcZvjyMKm%tHjI?G`F_qSWeZX ze(;24W!@&{_FQ_HXuY*Uz`q#mbv3xB|8bhLogse|VUudU|AiUl+}_-~(dxc?#+0i@ zv<|5xSRgrh)aA7ET53jqAue?AkU9S*hn+?Qh?hf=O;4IDoz-diN$mHzzfr%=SS{0? zpI>3}>(#3(D=L(t3u`B0jLAMgyfihHrFp>=MzZ8nAgNPqf$~~PxkBm|S-_L~oY!79 z)$nmE_XP9Dn+0qjVeW{Xm9Y?QhFGSkxd6!0iu?@aMvoASf%HDB3d9>m_cyLx z1mdesD}E8rzqu@wZvfiof9rGb#y2(VV&)5UAdx11f3b2>?EBOd;X^7H>FLMOg{F6! zt!|VBkk%XGq1=XRbz+=)m}#mc)Kf#-W;jlq9i@$prwb5k>aN9}roMEMt`(eH?;P&n z$N9g2MC#UHA7TelCN4d=c)nEAWc8pA6i6t@^YK|HxR`Z;fuEs(%|0l8HL+_~dVD{! z2-`DCx31e-RzgB~I^C|R%ouf`PLK|}-&GgVRe5D#Ib%2XRDHq;m@slG~b z8rv76`v75utjT~5D%-5<;vfYt;=Ppn^zQ0bEP&@>$^t^i`YHjBU*#@Wm0SEia??y9 z07+*-`R_cJ`D45&^qpgc$f7}M1piJTx#b<8l6mcxil$xMNnGm`*;Twq0?nEfXWvXY zvCrqMU+&y4@^~qw|Jibx()&ifQ}^1OXPu%419|wfLO{DRm7{!QZ*PB=8eL9a(>s*8 zC$#xW!*Q1q#}4?Npuy4J7%9*Fb@IOOaNMhxR|!O<*K^-#4^I4utBtA*slR;}Y8c=Gy zP2Mw}9-KB=oGDE)-YBZam_Uc{Mw#QlY=5EBojZl>J6bx?FyQ%`tUc48%pYUTrjkzv zuRJZ+hwYgUL`$3I>xFaK`|{naE3=LZf{~HKCVtGuO!_b6JVOU|E`-3aFx!<#`f+AT z!~_h!;d{)w&rKQ?(!9%R>gzQVqy%{verzajW#EKOCIqcQG#o1i%B>{v{Dm^db8!Q% zEfwjZviiW?{jTqlBysU2hT+`O_i02X3!CvXHK&pAkO8;BXZD?n&+yx`&@4{kNLhSM zNd(TuLU{7T81@kjU-#jloN@wnYHV!B3yoBGfMq^GRCOk=I?0P8=lTcymN+`20t`O&l{SeE!Jj&1VCI=btce=fE_po5R7jcEA#kKsu0CI zhET-(FH#I(0})pyf-=o*JUf+zs_K_46_9y6ekXmzr2Pm?;ptc+#$Zf`h8umMNpVRD zoq}`?KFl+4$Acl@ZH3IV(DS=baf{)o0c7Lv5Ukix=nqqWRgfJXnZ#y-YqOwD`E)zmo}H@> zQ1Weehve0!Be8XL901UTm8?_FeKsET6VMea(}mDDlxDM z10#?z=0fS2eeQTAopI81V@YIRMY(n8vCZOqg<2beR&gF-iX3bh!Ts*ThY!Q`_V1pH zi;I6bnJ95E{v~FJ+Rr#UH%Fs7ts1?rv4>2+DXp$vKb*=fI)(NY2kH67v^q}8qJtcJag)%PjZZ9TE2l4I``6AP}H{Dt~)6@Zgp@B;_) zsO#NA7m+ypn^~jhw#xJmYf^siJ3pUnCftmsL9g-Su5}+BMV4yS50C~BTH<4h@_y{+ zzq*%Nc3G3F4w)WzS&#A_45B8a4j`z0al3ouEpVHdwe6Re?0wRcuwJZp6TM0jb>w*j zXF~iMcPepvZ!gB8tX24>?zyJ9xj9Q1qQ&C&tKPma0#nGbK@g1R!$a?|*detk88x)k z09qZt6-DKza0>&roggP>#tnC( z$m!4Lqx04JaQnyEVLyD#-6^QIIK1SQjyv|; z`{T}utM?EK1;zw`M%FB91{uT$q}Axc>HHcu4rS7|uN;^=x{^^P=lkR><`t<0$PWxiX=w^e-TUg*6I0V$N7G8M}G7v*OKueurv_DV~Z`Vtu zaIt6K2OMBZtOhqF{KDMw7+&PflA*R~yHQhFzP%tY(zj*)D+C^h5ERM6e!#noX3mYd zP^c^!M({IUtvqG&HSR^d^;B^jie~__wY=F$4ob?4V_L7$sp0n9y=xR4CyiTD1gw&W zfgGKauUXP(3J!~~As;?o{!ne)-Nj!+@b8ArT`;*>|NK<>Bu?_9ZJwceWAaRZ0kzzH?mi?; zX14t_YWlRZTu+|Ubz#R5K`abpfhx9uw}!Dh5zJ`eNFW2Q^cT}+Wh6zT9G{)F*l!Hl zZw-3>%PtiVu4m46<+)}Qt4QX`xM7!_8+|z&7PY*RzRT1G-lOG}()mGNpkPoNU!#K&GfeD@B}gLP>D+FuEu zCSXGMq1H_hC@z(3YHn@?FJo6#Ip^*~Jd8=_8zevf?b9%T=pQ~4JIF*RMgSQ&1;SaJ zS%*m#sfTHb^v-kpDXH7Cxr6_AjnJ zuEgD3oQ$x?og1vACL7N)0+Yu{dEVE?Gq2jblC<4+)RpswD1A}R^&WrapMFD4jZ+yu z@^((S-y4wJ0Ne#Bd<8Jz<-X|{aig!SYZiUHi3s?5d$5_}Rm=N6fk`XvynF2_(p&Jt zG%6ui(O1P(>wsaLkPj(9{!Iw-F@ZGMCq ztR;J%?}5jF_+e8K(hnR5lS2(Li??;v|WTB#~2h zL4}9*TwJxMdqSb)1@bf(y5yDO9Q5>#h{fPIqfVLKt;BaNn?|X0&r8>vPMmgn!Ke9n zd?>Gf{x5(F)-f07zJdHys`@3jm~A1Q=8tK7SNCVVEDO{_RmMZ>ogo6$5dyajC(>s? zu&e8ytMlcm^{CbUr1J%(S*kh9`xZ_18J+hnlIyMcZGA0S#INNKTFxiRhfey4Ua4!_ zHM(|fHwzNGvsaxv3|B@u@H}2MuW;BzW@QoC-`RLm*Dt#@+jVHf-0wsb^z~1(=^RMZ7WTZ$UMH< zeuIwtszo6PILVmI&hl;=NNgy0T2x3SC%K!G4BcsVYdiu@Q(nwIO>r4!YaO=jp-j#d zm6R}_%0~x~+yrQUoX7s|;R04<1R>`3sd%?MonH-*JlfW4dyY8SFS}+$o(Z~^Eqab+ ztTU4D_Y!#1%E^UoYVzC~Urv|nPnUsWZJD3cidLMh95y{$gw=@Y&X1u>YZu(jM#88wE zF@QJw&t8lu2R~iT3J!eMvH4hczMbnY@AfN*nv#J3;}%hS9B>RaMOC}@L#^iM_;_Tf zYB=llf_gilRdjl)CHL#hvfC3>9yuv?pGEA-Y+fvzbfV=hj9iMJ+n^x(w<-d-mY!4q$VV0K@Ro{9lbx#ALWFL%p2>K zOXCr5yGwgD!Zdf%tDhm@>82983`N&IkXQ;2@|Ww$lp!ST+8!4!l1J^_JtAR#I)7(F z^IGxT*pR29{XUnee~YoLUpr;;cF)pp^izQzT<(UO`<+K&b?AU@j-apK?KIs4IX#@w$pfF z)v5}Ji8zpwEidNe$&c!+euNzbR%~DS?dm!i#%0?rcs)#B7QZ!WY(8pIt`T(9`%<^M zU{S{EU9f9G$X<$Xw_pRflF>HWc1dxiIf4uiYutXFW$L519RbFD;Y23UaKtW(n@n62 z_tVx1rX>wA@@@$?6P4KD|cZ@uuduUmK-*27kl7|tB{thsjHpb^UKR* zuf5x}BfbeF!Hd@XkjKKwlrH<^#|}RfY|qt1eB|3_Pd`Ob2U@)!QcAQNC)mN~Tqn-B zoJ*?eIt+pwk5h#g#m3sU2J0imBsbgK_V)-!th=Y5DHrbVJIe6F2)u3`xVL|H4eLR> zA2e2Ay%1+Ut5%a#-*F_F0Y~ zMIN4>L{alO&h-zAS^Vm#c9wYagpg6$BHn}6!YgY^i<&ff>3H2P6`Vqp?}DB;?@M^s zfBFp%#VB~$s6_Pi_T4%4Hq_QmTc+%$?b%#wvJV6xlS@Z{NN}0GT=2Ya$t~kgC`+oL zizaJ`cc;p|6$H;pjEeipnh&450$!$3h(S&v@BQ&j-NvHx%EQ~|CjL54g>TsnIUD`JYP#5) zO;Ky2W72uDG!Gj7S+G$yM542#W5UBglECXLV7R!gSBApfh~cRBNFByR{6PA(I^nkG z0|V*({T62+qwy|=UoPr<3!A00lP0G34Z%~hvprN1w`)#H>}0SJxsr8w{{Fdr`&C+Q zDyG^heU1d>EEU=Z4gI?7f%t>YhEj=a^%T<$D`)EhI~GIOJY8b+)^#dUM0*c)g*tPNAe_B8WStM{MnM z_S(m-(dID#X-?_5f`~#+Bv06Ea)rFsA)BU#J zQrzR^;{y{5$1nG}gBIpCI;L2I!@Ap-W6~p@Wo4dSV2|xX@w+ljFc$AMZM^v_w>Bx# zmc`5Wu>=zfmRlNGXUfzIwZ`Yha;%ccY%*se$O|T)>-0E!0{hm-ZazHBX+6@qH*S?i zt1N-&t^nCqD&cUVem!zX@bqqfE8=4BA$Y>qOp)6urM#?<@lAb$bAOfn;$FXbXR4Fyk#ZIjX1o70lXdYJTs>s_T5J)s@9A66ErOq4bV(k=ey zh<1{rdC|I}Zi{bEr>^0!zN@i3jQawMeJyxwU>Txbk;z%!6?XJ~a)x6a$cw}h zK5SFdh@HuW5Zh*0bSs}YOw+mpI$``tW&l#yB3@ry3$bx|n#)PUMsk?@d(gdQat<5= zg)L^AgDEsO>wQyY%F*GHml^q$yAk0YpepnyDs&+-ZBu4t_>Ful#{|9c6PNjEz7nnW zSh1Yjv!E-bmgbSW50ivA^&?|eJvNb;aXnjth`ffo>SAFzr!;pBOZUwx6boGD_rr4c znMv=Cns37l=8wAw4R=Q_J82KL!`{b~;qKUa-#pwNP|R$X1IJOIQo4Km>=apS*tDm& zT({!O$YVV2V>S0p+tI@<;X7Ls7Xtd-;l}1>E`rZ2Q!~xY>h>$UV`!7|%5U_iCMFWZ z$m_jVg5 zsv%Rn8jc*64$(NM8-DK2r zNtCZ2N?aJX#43vG92Ge}8LOLgC9GHB(lDGNh{@nR8J`~3Pj$#Um>4)Z=3tWJ&b?&^ ztI;cZ9w%-^_7NCMf|APWAyT?t^tc{y`5A7EIu~M9MJtY`Qg1dlBDL!?1AG&{5KPus zS}}vWJdNu_76q3^uBJgB4Ms;u(e)tOFyZt1JgNjS+ehQY!^8GQ-JNVb{cME3_IPO( zyMvLd^0WNTkcq|eR%R(ifNX!c5#N}ZU{|@;gzNj#lVze`Yf~0ltcF>5oTRy+o}Wnx zp>RUrHpXqeL}d(U?6;0zUeFX$>$N_tg;d|(P1#%Qg`YLzJHDrU%#neHK@jjD4{Nm` zFzFEx6Ar`7MW*a%fWcEa!GaIz?u;V|Yjt>y`PQ570S@Emt0AooZW3(n&4|!!>{62O zkx0KHV@dsgfhCI6CU+~*?x;ZtPYgDESN#ZJ{)KBTVBi% zM%tQ89hjA@Br$@rC5zVM&OM7G2CcW8)RBmA7y%y5;Zzt%8|oZZRzlkxP}>Ysw(yuZ z-X1ybTt?DglG5BZ6JXj_ikXJOnL>Dj0~v&#{dz``UWr!MFRqL!6e`%NmbT4cex9eK zrn{3?F)Lsh$H1Av^x9Zn9Fz_LQvMSd(GU zd_7hRCL~sU0#yqzyt*d8{4})Rjr`i!s3vQvH|c+*I-(Jv&XfzziKGhwQjjxiy`f|-g^MwpLTekn zl%Csx@*4;0EPl#qJn7)Ln7{n?7~B?X}}DU{gbJAnw& zPZa`0uc0%fl|THFrX%TZ?C$v^!RAQ|uY6+|Y)x4hVoj-9i6OxPoNg$emR1;yH-7_J zJ3C)`Jr4Kg6QPfP>U^DOL5`3Tq*Xisx0}cwdS0uJNn98@c`V~24xFAa(ae3!csdpX zQeXzYz)Z{sIygQ+t9H`bx%cVg1%vj0E%(6@4S>cWbyU_}!|9+_CC6Z%tw7pf(|rTJ zY`Vnk)Ye~eX>we?Pvqz(tg6WR@hNhG+#W~MA`s&^a3Qnt=j%TdJBNJ=6*_MRXIig= zcBWj9EwaVtSyS_}fZqAy0%nrYce`0`~l7(03@mKo9d2u0JM7c@xeI4 zriawMgP2_oWI+^0XeA^hWZXFjXk2|5n&egL)Fe3^MF91h?||lP57suf)vF zo%{&TFD`nE${Gzeaq;}u={<901;NFEK$tGU!F0TVwsiYXi=x^F=ganMX|{!aQ%XWvu0Eq!3h?2YqoL0-ru z&siT(^Xiy&C_u$!HH%a;yDyoyS$ivES+t}{^;AgpPtHpRWC<{%+_ZZoozCpE19PLr z;eq&&LasRA=FJ6Tk7Z9d{F1kq3M*E*zsUb#ySEez_Oky})OvtYR*(V`;cd~^m0pi4 zTN~x%xa;h1M_LOnvefN;iS%6WV0NOybZ(Mi3Z-WdzM;$X0stri z7%~hGQ8ErtjS^7hZ4Btvf1HH$zY0MAxD2Q=2=`f3YV_q_7lR613I-~V;J@7G4tz$a4KZlpp#Zy7@oag$*aJ|BWzY&p&r051x4f+#>m=*eaZ&;OL>LH-}CkSwhA=dLOH#vi{?v;%8zrGtm z5@g$#yQxtA8JA^9k;>wpoGOqY`ZHPCz+S_PG!J-G$jE_eGr$cllLEDFe?}sG87u_| zp~(;x1;`PLfDr#rG1@;Xffs}?%4UTCnZDo%hcHk#b$Jv03eqHS#Q*fo{}xph{&&g^ zGbctMftXe%QQy#@XJJ8P!hT%cSHsgCK=IlAh2d4YlHjoJ#U;N>vD&%Ne`Hdcxv{lK zy9luVZ|VxR{9R>r7XDA&j`ht#Syo67i2&yjsY6xw6U->`DH7_xr2`&(zaSQm$Nuc^ z3<23a?^b75(V)K;{|utMV6lz?$@+rM`%M5TH8`hw>-A61Vn8Y>e?`a<{WC5QvN$|n{%V~DUmL$!qE9`=5A+TWpOTH{E`RjNq-^_K@k3RpF z*HHeywfKBGhX9OtCeMqDm+-LOxykkQPN$X4&6=vJsuJ!%D-4KYz1^}xflL@8EwD6P zlc;gk0VxLEG=&~F_q^lyZ9I5C5<_oLT^Db4m$H_Vl{I#z1Vl#l^>e;1)RPx6c0(p7 zQ5xjjY&EVKT1Sj9S4Hxoh0?_$iho`! zGgy#Vz=BkJ{^{`iS9*7fwhS3ri^`b(3H{FZmsoB`}zv);PLX$s?KHS8mL!=wzWa+?CwOq zO=9g?Ugmp`2dV?XZXFfu)=Pw~%B21gm1X$f^{M_9mCf!m+#TNmB$Cw8>0vU3PAJ84 z$uedaAiq;kR1{TOdQd)seLYqpMV--IP60!B6`iFi7*=<1YttTabQw*d?+elNCEYu_ zsBLzowX)h`GMOY;U9!c=6~*Pvkh@rN=N-cWtf2@97MjE=7|YEu8zRRPG7*kRvs6Xz z5UhM-@~61|A9r_(-lX-g0yHH^k$HN7AO#c8ksndKZe3=0R$5sW`Ud(t_hvuy z+8I9n(&IzzMmH(4RHEGw_f|L5^L=_`o-2RsrTKa4xf*k3g9H*-p;2)dh(GFP0%MyB z)xgIE>x0NatbhqvDkPwO)1cz<;Yg|vkVo{B^zW`Djf~Brw^E&diN9t^eF8LS%LhP7 zE@+7h*luk^m2&ExPUmyp{Hl3X@|K6GIis9klJ~x;#;BzGGR-YEbL-k9o&2 zsQ&#AP3GzZEHOh|YiPK;xcxwUl$$=Q@pd0wL`#d1ftImXTnFOAUV=NY!;R%+%JOWi zj5@t;RGl#sZTR8|;=Mcc`W0F}!tx&5&LqhoK5s(7_aj$ngN4 z3!gTGiqnS89h*!8D`-XcZ8PBK@UV0$8R;vC4~P64X}wuKvaYW8rAu1#LK>tJLa8l3 zr@Lq}m2zgEBSTu}3tHzm2yUIABON>-1cX>PjOA(79kfSBQ{8T`M|jIPi=E0Y-yK+! zg2=P7$%_Cti2}A``fI-mLWtd}QJ6^7n?&*j*Guj!(PT#bKw9N@OGu{`GM);&g>!Xa zC4t+f_2D>EKI|URy+C51+`ndPJ}U<@hce`@m#b9RNSf{IybbXS;0pHUn>zN`C-o32pl zH!j1vc4P-@U|--;Y#BeAzP)#Ze&06{Q;)acKQ_M~%+FxEd+3VZJ5kj6t)^rNi*sS2 zj(0|8cTHG@%({vOJ7z)q_sjrE0=*p1cDWvgoL@%QKDNr*yBvOP`dk+sTc%LCh8)|b zV<1R_|4b?bsf>LiXC5LkzzK$y@#y)P@=gCc%2C6OX;=wsUmdsgp3v&f2lz6c1?>a6 zp(hL?piGww-NM-WjT7wy#hz(HkJcAYFig;gY zoxm9ET2ES&U(gAqq-mKy<|uXYt%=dgWLcJ^9(Fu{+T#K&C*VKo=VW zr*2Rgp?ow)=9$tP?Wa*R4L+j$f0y7*SQnOP3Pau3sw0ad>^gBCKg!{&s%?YuReTwo zo>pus`{GMQi6uU9`f78T(Is@+y&gCfXZV4Ju0-|n#{x_|$@~B`A&jUG7Vl;5|g@v{$@kFd_q=tm6i38gN2gxu^K2DPiZ>43OSBS6w7_Ps~3rTwF8lk*=8_E zvc^vVEKaof7%kQlw)2zkSJWz%=p19e`zwb!gu@(NSDvLYtevn6{DRG8I7Wx^j3fo& zcpaU!f)4{F-tNo$oYXQZffS8+_vwsGUmqNdoqiT|hZVzt*aV?Zsj*yDO|xbo&;{BX z046)rEm!@;j`1>TXp3n-ylL=A^fN+xQ2L_IyxlS+r*y$=UaaTu*&OID-DMb`NQ4u)x%HGGkTj`|;1iDsX)T3_wGvAogSN*^Jxn{>OS)!5V#_ z41q$)1IGLKHa*%n+^Q!l8fPuHe$%#BnpH(nKE99erU3LQn(>$XN0T_sR8|a*TQs=+ z{v;O|5RM)a12(OcR`dr6Svtq)52W+TT`p+@C@skuus$3nB4mW(Hi{%m2fe~g5nvSq zu+!2drxR_1o23aIQXtobc=LtqdU_l8fd;H22m4Q*Ql%tzGB}7>eaCMM{n{xvzGC1M@>`xyiJky+f+qAMS zTCkyWN#zDYLvy9%Xwdos;Cd_?DYgROdO~q3oi3(dAoD%N-bGK*WKE*5Q*7uD!Wzxj zdVgH5@z5nHS66!aXOD2S*;lLEf$jwK>upE951_sp?3s4ND1emy3faJW z{hfhrgSfH~B{MT7TL7pqf;rR|ti+z(nNBH7-(Va2VK1gkSUu=lq7KAy0#COA|1}`6 z1N_@nH&VaVA3*W~HqeJJ0PHPdzI1IA%u*5J{wIWCARtE4(|EB?mT7&$F#2&P!4C-u z$)D#{ZsRSLnryt8L%`cBBpx0P9vt@1 z<|VPK;zH4&0+Rq>g?P_Lr2|Ekl67b5MDp`NzJBekqWaq2AC0vG2FeEfIhwKF^q$u7N3m4p!gRC|lf$)AZu=)tv!4y&9@9~o z=YR-8#r@T4!r>JYg}SfDE@%w7B(UsN{CNFyVA(KnLtn2Kr@{w13tVbm&-{TlfbmV` z6{vpWj|h~GSUqLd&$p^{{8;@7ixOwS0EP=>DK=LILyT}>1(A?yoveYT7ITYQoishz z7TdtB7y4NZXmMnV*5-mYI}FLf?k~O_cU9%$7HWB&xg__` zn@h0iQ+p@slmk-t~?o_;RdmXeY2qq#gA8o*wjOZw46IPf;+?MWI~w_8eV_ z#6>t?XU}=9?|e}Lx=LR%t}vv+RNf98R!X~!mZYfdwe|fPJ@`eEm&E8;2yj@5MHdst zS>_qtLCp%c`?(6U``N0~a0o*6FB=`-3bf@%R^o;RVu}%w8kam7|YEuLofm^GYl=JQTwt#Alrn5DUr?Ck9^&Glo`Apd!H3kAg9 z)3?pACk{&qs*4H>13!E*HV(X*_~LLHGNpgIIncTEipizr{vxEas7q9f7!xA#&c%~> zJI3*t)^zF-mD&ApHP^kFGVqg*7VTqZemn~5yEG_k$*mNiv%o#%5a^U@pPIUZWU;H= z`Ar3b>9Y_y=_h5zr0HY>dxM8_;~GOlLuV%|DXF2d3ntD5OicyXqTBPN!rekm>}43B z#sYIk1Z?s-8Tv<0e(m2vRWo0$@B`Z0Kh8Ee+oz4bdi@&ReLNX$iS8AuCqsr1oEy;- zXeSg8f-!Hav(um9!e?_o4dGSQ)bx@!`jlh=`aE=nmr-6Gp-8JmzE%_1QL+qf!AFz` zlirmnqLtRDExp~wo~^#S=j`-m(Lpy=t3uQL{;d+%c!a{tpQD_`hc4Up zBf$EEe)k?IEUXW#|Ej@wZBjOn^$d|`49HuSwH&_ZV$)Et%`tF+@dD@ZWyAA@2_{;c zZQRcHk=_V?gvZl)34;sk80!MRHfP?Fm%C|zBd@rb=+FbzV5cyONZk-Fv!pr0v*zn! zp+n!%fzHeUzOr4zYxHE$@{e&(&XjGgg&FO!Fe0*Xp=KJ-F48NkL`6i-sTXFAa!lt~ zwjg&x1$TON-MxwEO;X`PO2SP{>=}p8T-aK zzbtDvk=lyw!(0<1$umD`fH0wKzX8<~&01fx#WFf{%85q1$ zX&)AD!rEQ-(e;X1Q{m`7CL6uQb8$NT&GnQ}ilk}iR~{?!cKITgOA7Y+&GC$YcI8aZ zB<2kPN_duQfCuu`7xdwm9p_JZW?`xXEbl$SSfCE1H*S96A(%?~B6mz@tt7!cgMmW% z%?~`;^K>luix~k_(sMoreqbYiKAE2Y+=;P5YVx;NJvZc^)9 z#fR8=b!u8_GQS0W#UcfoLIbSzFRK}!uk{QJB03_4&(CegXJ^r>@PhdXZfmAwNKCphd4m9gj9>BXU6{36x`SH)BJcRiy zy&Z~&4Vh_>NqPpHkRdqi zjv)T~JwD81M+$5Qno{A@_3X5%x}3Cd*n?en7>HsbVPW!qZvlsu#*gqs}*5 zK{GCzM#T&r862;I6`Wo6e1BCjMs@kqa>O9`uI+^>9?J-Je^u!m`>x1r5s$l;IR)~3g?oKlF6P9O*}^LoN+wu0Sp&kD*jY$6nEn5$VSTrQ%`UVQUs zg9Nn{=e%p%KMHCJkOJNA5%ZV*y&G9H=VDXzN*IhTqvA2D&fQP*ZRI|Y3{ zV{aX@Dpr>s%+VAa`fXG>P8P}A9R|qTJp>dU>&%Q1BL%(%F%TAuKjQ`EX%ThGdC1us z)zol1D?x@^$1}H_0GsT$LxWkJIVLxHQB2wxFJ3iC9nujq`&dzg=G>Ua)jO|Y!H7%z zBq_cA79GuFyAJQ6K=?K?t=;YZ%YIw!Spa$vL3wH3m7iH>sz7^^F=n|xZ#rXAZ~7

MWsuy*;c-I_-d0L`oEDH2jiPXGTvi)1# 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 oUH1SZG-b= 10: + score_id = 10 + + if category != None: + categories = [cat.lower() for cat in category.values()] + categories = list(set(categories)) + else: + categories = [] + + score = score_table[str(score_id)] + + # Attach final object + normalize_score_url__url_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence, 'categories': categories}) + normalize_score_url__score.append(score) + normalize_score_url__categories.append(categories) + #phantom.debug("normalize_score_url__url_score_object: {}".format(normalize_score_url__url_score_object)) + #phantom.debug("normalize_score_url__score: {}".format(normalize_score_url__score)) + #phantom.debug("normalize_score_url__categories: {}".format(normalize_score_url__categories)) + + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="normalize_score_url:url_score_object", value=json.dumps(normalize_score_url__url_score_object)) + phantom.save_run_data(key="normalize_score_url:score", value=json.dumps(normalize_score_url__score)) + phantom.save_run_data(key="normalize_score_url:categories", value=json.dumps(normalize_score_url__categories)) + + format_report_url(container=container) + + return + + +@phantom.playbook_block() +def format_report_url(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("format_report_url() called") + + ################################################################################ + # Format a summary table with the information gathered from the playbook. + ################################################################################ + + template = """SOAR analyzed URL(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | https://www.virustotal.com/gui/url/{3} | VirusTotal v3 |\n%%{1}{2}{3}""" + + # parameter list for template variable replacement + parameters = [ + "filtered-data:input_filter:condition_1:playbook_input:url", + "normalize_score_url:custom_function:score", + "normalize_score_url:custom_function:categories", + "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.data.*.id" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="format_report_url") + + build_url_output(container=container) + + return + + +@phantom.playbook_block() +def build_url_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_url_output() called") + + ################################################################################ + # This block uses custom code to generate an observable dictionary to output into + # the observables data path. + ################################################################################ + + filtered_result_0_data_url_detonate_filter = phantom.collect2(container=container, datapath=["filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.parameter.url","filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.data.*.id"]) + normalize_score_url__url_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalize_score_url:url_score_object")) != "" else "null") # pylint: disable=used-before-assignment + + filtered_result_0_parameter_url = [item[0] for item in filtered_result_0_data_url_detonate_filter] + filtered_result_0_data___id = [item[1] for item in filtered_result_0_data_url_detonate_filter] + + build_url_output__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + from urllib.parse import urlparse + build_url_output__observable_array = [] + + # Build URL + for url, external_id, url_object in zip(filtered_result_0_parameter_url, filtered_result_0_data___id, normalize_score_url__url_score_object): + parsed_url = urlparse(url) + phantom.debug("parsed_url: {}, url_object: {}".format(parsed_url, url_object)) + observable_object = { + "value": url, + "type": "url", + "reputation": { + "score_id": url_object['score_id'], + "score": url_object['score'], + "confidence": url_object['confidence'] + }, + "attributes": { + "hostname": parsed_url.hostname, + "scheme": parsed_url.scheme + }, + "categories": url_object['categories'], + "source": "VirusTotal v3", + "source_link": f"https://www.virustotal.com/gui/url/{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 + + build_url_output__observable_array.append(observable_object) + + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="build_url_output:observable_array", value=json.dumps(build_url_output__observable_array)) + + return + + +@phantom.playbook_block() +def file_detonate_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("file_detonate_filter() called") + + ################################################################################ + # Filters successful file detonation results. + ################################################################################ + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["file_detonation:action_result.status", "==", "success"] + ], + name="file_detonate_filter:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + normalize_score_file(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 normalize_score_file(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("normalize_score_file() called") + + ################################################################################ + # This block uses custom code for normalizing score. Adjust the logic as desired + # in the documented sections. + ################################################################################ + + filtered_result_0_data_file_detonate_filter = phantom.collect2(container=container, datapath=["filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.summary"]) + + filtered_result_0_summary = [item[0] for item in filtered_result_0_data_file_detonate_filter] + + normalize_score_file__file_score_object = None + normalize_score_file__scores = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + # Reference for scores: https://schema.ocsf.io/objects/reputation + #phantom.debug("filtered_result_0_summary: {}".format(filtered_result_0_summary)) + #phantom.debug("filtered_result_0_data: {}".format(filtered_result_0_data)) + #phantom.debug("filtered_result_0_data___scans: {}".format(filtered_result_0_data___scans)) + #phantom.debug("filtered_result_0_data___attributes___category: {}".format(filtered_result_0_data___attributes___category)) + #phantom.debug("vault_id_detonation_result_item_0: {}".format(vault_id_detonation_result_item_0)) + + + score_table = { + "0":"Unknown", + "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" + } + + file_summary_list = filtered_result_0_summary + normalize_score_file__file_score_object = [] + normalize_score_file__scores = [] + normalize_score_file__categories = [] + + for summary_data in file_summary_list: + # Set confidence based on percentage of vendors undetected + # Reduce the confidence by percentage of vendors undetected. + vendors = summary_data['harmless'] + summary_data['undetected'] + summary_data['malicious'] + summary_data['suspicious'] + confidence = 100 - int((summary_data['undetected']/vendors) * 100) + + phantom.debug("vendors: {}".format(vendors)) + phantom.debug("confidence: {}".format(confidence)) + + # Normalize reputation on a 10 point scale based on number of malicious and suspicious divided by harmless vendors + # This can be adjusted to include whatever logic is desired. + suspect = summary_data['malicious'] + summary_data['suspicious'] + # If there are only harmless verdicts and no suspicious entries, set score_id to 1. + if summary_data['harmless'] and not suspect: + score_id = 1 + # If there are only harmless verdicts and no suspicious entries, set score_id to 1. + elif not summary_data['harmless'] and not suspect: + score_id = 0 + else: + # customize score calculation as desired + log_result = log((suspect/vendors) * 100, 100) # log imported from math in global code block + score_id = int(log_result * 10) + 3 + + phantom.debug("log_result: {}".format(log_result)) + if score_id > 10: + score_id = 10 + + score = score_table[str(score_id)] + + normalize_score_file__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence}) + normalize_score_file__scores.append(score) + phantom.debug("normalize_score_file__file_score_object: {}".format(normalize_score_file__file_score_object)) + phantom.debug("normalize_score_file__scores: {}".format(normalize_score_file__scores)) + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="normalize_score_file:file_score_object", value=json.dumps(normalize_score_file__file_score_object)) + phantom.save_run_data(key="normalize_score_file:scores", value=json.dumps(normalize_score_file__scores)) + + format_report_file(container=container) + + return + + +@phantom.playbook_block() +def format_report_file(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("format_report_file() called") + + ################################################################################ + # Format a summary table with the information gathered from the playbook. + ################################################################################ + + template = """SOAR analyzed file(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| File | VT Name | VT Decription | Normalized Score | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {2} | {3} | {1} | https://www.virustotal.com/gui/file/{0} | VirusTotal v3 |\n%%\n\n{1}\n{2}\n{3}\n""" + + # parameter list for template variable replacement + parameters = [ + "file_detonation:action_result.parameter.vault_id", + "normalize_score_file:custom_function:scores", + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data.*.attributes.meaningful_name", + "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data.*.attributes.magic" + ] + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.format(container=container, template=template, parameters=parameters, name="format_report_file") + + build_file_output(container=container) + + return + + +@phantom.playbook_block() +def build_file_output(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("build_file_output() called") + + ################################################################################ + # This block uses custom code to generate an observable dictionary to output into + # the observables data path. + ################################################################################ + + filtered_result_0_data_file_detonate_filter = phantom.collect2(container=container, datapath=["filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.parameter.vault_id","filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data"]) + normalize_score_file__file_score_object = json.loads(_ if (_ := phantom.get_run_data(key="normalize_score_file:file_score_object")) != "" else "null") # pylint: disable=used-before-assignment + + filtered_result_0_parameter_vault_id = [item[0] for item in filtered_result_0_data_file_detonate_filter] + filtered_result_0_data = [item[1] for item in filtered_result_0_data_file_detonate_filter] + + build_file_output__observable_array = None + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + build_file_output__observable_array = [] + # Build file_hash + for file_hash, file_data, file_object in zip(filtered_result_0_parameter_vault_id, filtered_result_0_data , normalize_score_file__file_score_object): + observable_object = { + "value": file_hash, + "type": "hash", + "reputation": { + "score_id": file_object['score_id'], + "score": file_object['score'], + "confidence": file_object['confidence'] + }, + "attributes": { + "type": file_data['attributes']['type_extension'], + "name": file_data['attributes']['meaningful_name'], + "size": file_data['attributes']['size'], + "md5": file_data['attributes']['md5'], + "sha1": file_data['attributes']['sha1'], + "sha256": file_data['attributes']['sha256'] + }, + "source": "VirusTotal v3", + "source_link": f"https://www.virustotal.com/gui/file/{file_hash}" + } + + build_file_output__observable_array.append(observable_object) + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_run_data(key="build_file_output:observable_array", value=json.dumps(build_file_output__observable_array)) + + return + + +@phantom.playbook_block() +def url_detonate_filter(action=None, success=None, container=None, results=None, handle=None, filtered_artifacts=None, filtered_results=None, custom_function=None, **kwargs): + phantom.debug("url_detonate_filter() called") + + ################################################################################ + # Filters successful url reputation results. + ################################################################################ + + # collect filtered artifact ids and results for 'if' condition 1 + matched_artifacts_1, matched_results_1 = phantom.condition( + container=container, + conditions=[ + ["url_detonation_1:action_result.status", "==", "success"] + ], + name="url_detonate_filter:condition_1") + + # call connected blocks if filtered artifacts or results + if matched_artifacts_1 or matched_results_1: + normalize_score_url(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 on_finish(container, summary): + phantom.debug("on_finish() called") + + format_report_file = phantom.get_format_data(name="format_report_file") + format_report_url = phantom.get_format_data(name="format_report_url") + build_url_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_url_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + build_file_output__observable_array = json.loads(_ if (_ := phantom.get_run_data(key="build_file_output:observable_array")) != "" else "null") # pylint: disable=used-before-assignment + + observable_combined_value = phantom.concatenate(build_url_output__observable_array, build_file_output__observable_array) + markdown_report_combined_value = phantom.concatenate(format_report_file, format_report_url) + + output = { + "observable": observable_combined_value, + "markdown_report": markdown_report_combined_value, + } + + ################################################################################ + ## Custom Code Start + ################################################################################ + + # Write your custom code here... + + ################################################################################ + ## Custom Code End + ################################################################################ + + phantom.save_playbook_output_data(output=output) + + return \ No newline at end of file diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.yml b/playbooks/VirusTotal_v3_Dynamic_Analysis.yml new file mode 100644 index 0000000000..de5a720310 --- /dev/null +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.yml @@ -0,0 +1,26 @@ +name: VirusTotal V3 Dynamic Analysis +id: 388ed434-a498-4d55-8de4-b2657825cb67 +version: 1 +date: '2023-03-23' +author: Teoderick Contreras, Splunk +type: Investigation +description: "Accepts a url link, domain or vault_id (hash) to be detonated using Virustotal V3 connector." +playbook: VirusTotal_v3_Dynamic_Analysis +how_to_implement: This input playbook requires the Virustotal V3 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: + - virustotal v3 +tags: + platform_tags: + - reputation + - url + - domain + - sandbox + - ip + - virustotal_v3 + playbook_type: Input + vpe_type: Modern + playbook_fields: [] + product: + - Splunk SOAR \ No newline at end of file From 0201de9659dc568cd84473c1420f4eee1083fa7c Mon Sep 17 00:00:00 2001 From: tccontre <26181693+tccontre@users.noreply.github.com> Date: Fri, 24 Mar 2023 10:04:53 +0100 Subject: [PATCH 2/5] Update VirusTotal_v3_Dynamic_Analysis.yml --- playbooks/VirusTotal_v3_Dynamic_Analysis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.yml b/playbooks/VirusTotal_v3_Dynamic_Analysis.yml index de5a720310..4cae148340 100644 --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.yml +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.yml @@ -17,10 +17,11 @@ tags: - url - domain - sandbox - - ip + - ip + - file_hash - virustotal_v3 playbook_type: Input vpe_type: Modern playbook_fields: [] product: - - Splunk SOAR \ No newline at end of file + - Splunk SOAR From 39347133ce7b9e15103a51956fef4b654c9bc675 Mon Sep 17 00:00:00 2001 From: tccontre <26181693+tccontre@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:11:36 +0100 Subject: [PATCH 3/5] Update VirusTotal_v3_Dynamic_Analysis.py --- playbooks/VirusTotal_v3_Dynamic_Analysis.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.py b/playbooks/VirusTotal_v3_Dynamic_Analysis.py index 8defa9887a..bac10c3b2e 100644 --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.py +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.py @@ -307,7 +307,7 @@ def build_url_output(action=None, success=None, container=None, results=None, ha observable_object = { "value": url, "type": "url", - "reputation": { + "sandbox": { "score_id": url_object['score_id'], "score": url_object['score'], "confidence": url_object['confidence'] @@ -518,7 +518,7 @@ def build_file_output(action=None, success=None, container=None, results=None, h observable_object = { "value": file_hash, "type": "hash", - "reputation": { + "sandbox": { "score_id": file_object['score_id'], "score": file_object['score'], "confidence": file_object['confidence'] @@ -598,4 +598,4 @@ def on_finish(container, summary): phantom.save_playbook_output_data(output=output) - return \ No newline at end of file + return From 3a5e110162570317b25fd39793833954154ab4c2 Mon Sep 17 00:00:00 2001 From: P4T12ICK Date: Mon, 3 Apr 2023 13:16:44 +0200 Subject: [PATCH 4/5] change observable.reputation to observable.sandbox --- playbooks/VirusTotal_v3_Dynamic_Analysis.json | 37 +++++++------------ playbooks/VirusTotal_v3_Dynamic_Analysis.py | 23 ++++++------ playbooks/VirusTotal_v3_Dynamic_Analysis.yml | 1 - 3 files changed, 25 insertions(+), 36 deletions(-) mode change 100644 => 100755 playbooks/VirusTotal_v3_Dynamic_Analysis.json mode change 100644 => 100755 playbooks/VirusTotal_v3_Dynamic_Analysis.py diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.json b/playbooks/VirusTotal_v3_Dynamic_Analysis.json old mode 100644 new mode 100755 index c93f96b00a..c49586aebb --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.json +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.json @@ -120,7 +120,7 @@ } ], "globalCustomCode": "\n\n\nfrom math import log", - "hash": "6d1c8bfd4d293929a88550feb9db8f98826d2e76", + "hash": "c62a83f2b885857ecb0ed931a92f20293e4829a0", "nodes": { "0": { "data": { @@ -136,7 +136,7 @@ "type": "start", "warnings": {}, "x": 190, - "y": 0 + "y": -6.394884621840902e-14 }, "1": { "data": { @@ -272,10 +272,10 @@ "errors": {}, "id": "13", "type": "code", - "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n # Build file_hash\n for file_hash, file_data, file_object in zip(filtered_result_0_parameter_vault_id, filtered_result_0_data , normalize_score_file__file_score_object):\n observable_object = {\n \"value\": file_hash,\n \"type\": \"hash\",\n \"reputation\": {\n \"score_id\": file_object['score_id'],\n \"score\": file_object['score'],\n \"confidence\": file_object['confidence']\n },\n \"attributes\": {\n \"type\": file_data['attributes']['type_extension'],\n \"name\": file_data['attributes']['meaningful_name'],\n \"size\": file_data['attributes']['size'],\n \"md5\": file_data['attributes']['md5'],\n \"sha1\": file_data['attributes']['sha1'],\n \"sha256\": file_data['attributes']['sha256']\n },\n \"source\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/file/{file_hash}\"\n }\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 # Build file_hash\n for file_hash, file_data, file_object in zip(filtered_result_0_parameter_vault_id, filtered_result_0_data , normalize_score_file__file_score_object):\n observable_object = {\n \"value\": file_hash,\n \"type\": \"hash\",\n \"sandbox\": {\n \"score_id\": file_object['score_id'],\n \"score\": file_object['score'],\n \"confidence\": file_object['confidence']\n },\n \"attributes\": {\n \"type\": file_data['attributes']['type_extension'],\n \"name\": file_data['attributes']['meaningful_name'],\n \"size\": file_data['attributes']['size'],\n \"md5\": file_data['attributes']['md5'],\n \"sha1\": file_data['attributes']['sha1'],\n \"sha256\": file_data['attributes']['sha256']\n },\n \"source\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/file/{file_hash}\"\n }\n \n build_file_output__observable_array.append(observable_object)\n \n", "warnings": {}, "x": 340, - "y": 1042 + "y": 1040 }, "15": { "data": { @@ -363,8 +363,8 @@ "Reconfigure invalid datapath." ] }, - "x": 230, - "y": 148 + "x": 220, + "y": 140 }, "3": { "data": { @@ -379,7 +379,7 @@ }, "connector": "VirusTotal v3", "connectorConfigs": [ - "virustotal_v3" + "virus_total" ], "connectorId": "3fe4875d-a4a7-47d3-9ef1-f9e63a6653a4", "connectorVersion": "v1", @@ -387,8 +387,7 @@ "functionName": "url_detonation_1", "id": "3", "parameters": { - "url": "playbook_input:url", - "wait_time": "" + "url": "filtered-data:input_filter:condition_1:playbook_input:url" }, "requiredParameters": [ { @@ -402,11 +401,7 @@ "id": "3", "type": "action", "userCode": " \n # Write your custom code here...\n\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "warnings": {}, "x": 0, "y": 328 }, @@ -423,7 +418,7 @@ }, "connector": "VirusTotal v3", "connectorConfigs": [ - "virustotal_v3" + "virus_total" ], "connectorId": "3fe4875d-a4a7-47d3-9ef1-f9e63a6653a4", "connectorVersion": "v1", @@ -431,7 +426,7 @@ "functionName": "file_detonation", "id": "4", "parameters": { - "vault_id": "playbook_input:vault_id" + "vault_id": "filtered-data:input_filter:condition_2:playbook_input:vault_id" }, "requiredParameters": [ { @@ -445,11 +440,7 @@ "id": "4", "type": "action", "userCode": "\n # Write your custom code here...\n \n\n", - "warnings": { - "config": [ - "Reconfigure invalid datapath." - ] - }, + "warnings": {}, "x": 340, "y": 328 }, @@ -539,7 +530,7 @@ "errors": {}, "id": "8", "type": "code", - "userCode": " from urllib.parse import urlparse\n build_url_output__observable_array = []\n \n # Build URL\n for url, external_id, url_object in zip(filtered_result_0_parameter_url, filtered_result_0_data___id, normalize_score_url__url_score_object):\n parsed_url = urlparse(url)\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_id\": url_object['score_id'],\n \"score\": url_object['score'],\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\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/url/{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 \n \n", + "userCode": " from urllib.parse import urlparse\n build_url_output__observable_array = []\n \n # Build URL\n for url, external_id, url_object in zip(filtered_result_0_parameter_url, filtered_result_0_data___id, normalize_score_url__url_score_object):\n parsed_url = urlparse(url)\n phantom.debug(\"parsed_url: {}, url_object: {}\".format(parsed_url, url_object))\n observable_object = {\n \"value\": url,\n \"type\": \"url\",\n \"sandbox\": {\n \"score_id\": url_object['score_id'],\n \"score\": url_object['score'],\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\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/url/{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 \n \n", "warnings": {}, "x": 0, "y": 1042 @@ -592,7 +583,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-03-23T00:01:47.766315+00:00", + "create_time": "2023-04-03T11:10:18.424298+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.py b/playbooks/VirusTotal_v3_Dynamic_Analysis.py old mode 100644 new mode 100755 index bac10c3b2e..4cb062c3ca --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.py +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.py @@ -73,16 +73,15 @@ def url_detonation_1(action=None, success=None, container=None, results=None, ha # Queries VirusTotal for information about the provided URL(s) ################################################################################ - playbook_input_url = phantom.collect2(container=container, datapath=["playbook_input:url"]) + filtered_input_0_url = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_1:playbook_input:url"]) parameters = [] # build parameters list for 'url_detonation_1' call - for playbook_input_url_item in playbook_input_url: - if playbook_input_url_item[0] is not None: + for filtered_input_0_url_item in filtered_input_0_url: + if filtered_input_0_url_item[0] is not None: parameters.append({ - "url": playbook_input_url_item[0], - "wait_time": "", + "url": filtered_input_0_url_item[0], }) ################################################################################ @@ -95,7 +94,7 @@ def url_detonation_1(action=None, success=None, container=None, results=None, ha ## Custom Code End ################################################################################ - phantom.act("detonate url", parameters=parameters, name="url_detonation_1", assets=["virustotal_v3"], callback=url_detonate_filter) + phantom.act("detonate url", parameters=parameters, name="url_detonation_1", assets=["virus_total"], callback=url_detonate_filter) return @@ -110,15 +109,15 @@ def file_detonation(action=None, success=None, container=None, results=None, han # Queries VirusTotal for information about the provided vault_id(s) ################################################################################ - playbook_input_vault_id = phantom.collect2(container=container, datapath=["playbook_input:vault_id"]) + filtered_input_0_vault_id = phantom.collect2(container=container, datapath=["filtered-data:input_filter:condition_2:playbook_input:vault_id"]) parameters = [] # 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: + for filtered_input_0_vault_id_item in filtered_input_0_vault_id: + if filtered_input_0_vault_id_item[0] is not None: parameters.append({ - "vault_id": playbook_input_vault_id_item[0], + "vault_id": filtered_input_0_vault_id_item[0], }) ################################################################################ @@ -132,7 +131,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=["virustotal_v3"], callback=file_detonate_filter) + phantom.act("detonate file", parameters=parameters, name="file_detonation", assets=["virus_total"], callback=file_detonate_filter) return @@ -598,4 +597,4 @@ def on_finish(container, summary): phantom.save_playbook_output_data(output=output) - return + return \ No newline at end of file diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.yml b/playbooks/VirusTotal_v3_Dynamic_Analysis.yml index 4cae148340..8b514e204c 100644 --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.yml +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.yml @@ -13,7 +13,6 @@ app_list: - virustotal v3 tags: platform_tags: - - reputation - url - domain - sandbox From d0dd4475a280783d74ad90b617ab55698e6e9e19 Mon Sep 17 00:00:00 2001 From: P4T12ICK Date: Wed, 12 Apr 2023 13:35:40 +0200 Subject: [PATCH 5/5] Update VT playbooks --- playbooks/VirusTotal_v3_Dynamic_Analysis.json | 18 ++++++------- playbooks/VirusTotal_v3_Dynamic_Analysis.py | 27 +++++++++---------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.json b/playbooks/VirusTotal_v3_Dynamic_Analysis.json index c49586aebb..dc8fdd2e2b 100755 --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.json +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.json @@ -135,8 +135,8 @@ "id": "0", "type": "start", "warnings": {}, - "x": 190, - "y": -6.394884621840902e-14 + "x": 180, + "y": 0 }, "1": { "data": { @@ -214,7 +214,7 @@ "errors": {}, "id": "11", "type": "code", - "userCode": " # Reference for scores: https://schema.ocsf.io/objects/reputation\n #phantom.debug(\"filtered_result_0_summary: {}\".format(filtered_result_0_summary))\n #phantom.debug(\"filtered_result_0_data: {}\".format(filtered_result_0_data))\n #phantom.debug(\"filtered_result_0_data___scans: {}\".format(filtered_result_0_data___scans))\n #phantom.debug(\"filtered_result_0_data___attributes___category: {}\".format(filtered_result_0_data___attributes___category))\n #phantom.debug(\"vault_id_detonation_result_item_0: {}\".format(vault_id_detonation_result_item_0))\n \n\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 file_summary_list = filtered_result_0_summary\n normalize_score_file__file_score_object = []\n normalize_score_file__scores = []\n normalize_score_file__categories = []\n \n for summary_data in file_summary_list:\n # Set confidence based on percentage of vendors undetected\n # Reduce the confidence by percentage of vendors undetected.\n vendors = summary_data['harmless'] + summary_data['undetected'] + summary_data['malicious'] + summary_data['suspicious']\n confidence = 100 - int((summary_data['undetected']/vendors) * 100)\n \n phantom.debug(\"vendors: {}\".format(vendors))\n phantom.debug(\"confidence: {}\".format(confidence))\n\n # Normalize reputation on a 10 point scale based on number of malicious and suspicious divided by harmless vendors\n # This can be adjusted to include whatever logic is desired.\n suspect = summary_data['malicious'] + summary_data['suspicious']\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n if summary_data['harmless'] and not suspect:\n score_id = 1\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n elif not summary_data['harmless'] and not suspect:\n score_id = 0\n else:\n # customize score calculation as desired\n log_result = log((suspect/vendors) * 100, 100) # log imported from math in global code block\n score_id = int(log_result * 10) + 3\n\n phantom.debug(\"log_result: {}\".format(log_result))\n if score_id > 10:\n score_id = 10\n\n score = score_table[str(score_id)]\n\n normalize_score_file__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence})\n normalize_score_file__scores.append(score)\n phantom.debug(\"normalize_score_file__file_score_object: {}\".format(normalize_score_file__file_score_object))\n phantom.debug(\"normalize_score_file__scores: {}\".format(normalize_score_file__scores))\n\n", + "userCode": " # Reference for scores: https://schema.ocsf.io/objects/reputation\n #phantom.debug(\"filtered_result_0_summary: {}\".format(filtered_result_0_summary))\n #phantom.debug(\"filtered_result_0_data: {}\".format(filtered_result_0_data))\n #phantom.debug(\"filtered_result_0_data___scans: {}\".format(filtered_result_0_data___scans))\n #phantom.debug(\"filtered_result_0_data___attributes___category: {}\".format(filtered_result_0_data___attributes___category))\n #phantom.debug(\"vault_id_detonation_result_item_0: {}\".format(vault_id_detonation_result_item_0))\n \n\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 file_summary_list = filtered_result_0_summary\n normalize_score_file__file_score_object = []\n normalize_score_file__scores = []\n normalize_score_file__categories = []\n \n for summary_data in file_summary_list:\n # Set confidence based on percentage of vendors undetected\n # Reduce the confidence by percentage of vendors undetected.\n vendors = summary_data['harmless'] + summary_data['undetected'] + summary_data['malicious'] + summary_data['suspicious']\n confidence = 100 - int((summary_data['undetected']/vendors) * 100)\n\n # Normalize reputation on a 10 point scale based on number of malicious and suspicious divided by harmless vendors\n # This can be adjusted to include whatever logic is desired.\n suspect = summary_data['malicious'] + summary_data['suspicious']\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n if summary_data['harmless'] and not suspect:\n score_id = 1\n # If there are only harmless verdicts and no suspicious entries, set score_id to 1.\n elif not summary_data['harmless'] and not suspect:\n score_id = 0\n else:\n # customize score calculation as desired\n log_result = log((suspect/vendors) * 100, 100) # log imported from math in global code block\n score_id = int(log_result * 10) + 3\n\n phantom.debug(\"log_result: {}\".format(log_result))\n if score_id > 10:\n score_id = 10\n\n score = score_table[str(score_id)]\n\n normalize_score_file__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence})\n normalize_score_file__scores.append(score)\n #phantom.debug(\"normalize_score_file__file_score_object: {}\".format(normalize_score_file__file_score_object))\n #phantom.debug(\"normalize_score_file__scores: {}\".format(normalize_score_file__scores))\n\n", "warnings": {}, "x": 340, "y": 686 @@ -237,7 +237,7 @@ "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data.*.attributes.meaningful_name", "filtered-data:file_detonate_filter:condition_1:file_detonation:action_result.data.*.attributes.magic" ], - "template": "SOAR analyzed file(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| File | VT Name | VT Decription | Normalized Score | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {2} | {3} | {1} | https://www.virustotal.com/gui/file/{0} | VirusTotal v3 |\n%%\n\n{1}\n{2}\n{3}\n", + "template": "SOAR analyzed file(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| File | VT Name | VT Decription | Normalized Score | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {2} | {3} | {1} | https://www.virustotal.com/gui/file/{0} | VirusTotal v3 |\n%%\n", "type": "format" }, "errors": {}, @@ -245,7 +245,7 @@ "type": "format", "warnings": {}, "x": 340, - "y": 864 + "y": 860 }, "13": { "data": { @@ -272,7 +272,7 @@ "errors": {}, "id": "13", "type": "code", - "userCode": "\n # Write your custom code here...\n build_file_output__observable_array = []\n # Build file_hash\n for file_hash, file_data, file_object in zip(filtered_result_0_parameter_vault_id, filtered_result_0_data , normalize_score_file__file_score_object):\n observable_object = {\n \"value\": file_hash,\n \"type\": \"hash\",\n \"sandbox\": {\n \"score_id\": file_object['score_id'],\n \"score\": file_object['score'],\n \"confidence\": file_object['confidence']\n },\n \"attributes\": {\n \"type\": file_data['attributes']['type_extension'],\n \"name\": file_data['attributes']['meaningful_name'],\n \"size\": file_data['attributes']['size'],\n \"md5\": file_data['attributes']['md5'],\n \"sha1\": file_data['attributes']['sha1'],\n \"sha256\": file_data['attributes']['sha256']\n },\n \"source\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/file/{file_hash}\"\n }\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 # Build file_hash\n for file_hash, file_data, file_object in zip(filtered_result_0_parameter_vault_id, filtered_result_0_data , normalize_score_file__file_score_object):\n observable_object = {\n \"value\": file_hash,\n \"type\": \"hash\",\n \"reputation\": {\n \"score_id\": file_object['score_id'],\n \"score\": file_object['score'],\n \"confidence\": file_object['confidence']\n },\n \"attributes\": {\n \"type\": file_data[0]['attributes'].get('type_extension'),\n \"name\": file_data[0]['attributes'].get('meaningful_name'),\n \"size\": file_data[0]['attributes'].get('size'),\n \"md5\": file_data[0]['attributes'].get('md5'),\n \"sha1\": file_data[0]['attributes'].get('sha1'),\n \"sha256\": file_data[0]['attributes'].get('sha256')\n },\n \"source\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/file/{file_hash}\"\n }\n \n build_file_output__observable_array.append(observable_object)\n \n", "warnings": {}, "x": 340, "y": 1040 @@ -494,7 +494,7 @@ "normalize_score_url:custom_function:categories", "filtered-data:url_detonate_filter:condition_1:url_detonation_1:action_result.data.*.id" ], - "template": "SOAR analyzed URL(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | https://www.virustotal.com/gui/url/{3} | VirusTotal v3 |\n%%{1}{2}{3}", + "template": "SOAR analyzed URL(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | https://www.virustotal.com/gui/url/{3} | VirusTotal v3 |\n%%", "type": "format" }, "errors": {}, @@ -530,7 +530,7 @@ "errors": {}, "id": "8", "type": "code", - "userCode": " from urllib.parse import urlparse\n build_url_output__observable_array = []\n \n # Build URL\n for url, external_id, url_object in zip(filtered_result_0_parameter_url, filtered_result_0_data___id, normalize_score_url__url_score_object):\n parsed_url = urlparse(url)\n phantom.debug(\"parsed_url: {}, url_object: {}\".format(parsed_url, url_object))\n observable_object = {\n \"value\": url,\n \"type\": \"url\",\n \"sandbox\": {\n \"score_id\": url_object['score_id'],\n \"score\": url_object['score'],\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\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/url/{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 \n \n", + "userCode": " from urllib.parse import urlparse\n build_url_output__observable_array = []\n \n # Build URL\n for url, external_id, url_object in zip(filtered_result_0_parameter_url, filtered_result_0_data___id, normalize_score_url__url_score_object):\n parsed_url = urlparse(url)\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_id\": url_object['score_id'],\n \"score\": url_object['score'],\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\": \"VirusTotal v3\",\n \"source_link\": f\"https://www.virustotal.com/gui/url/{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 \n \n", "warnings": {}, "x": 0, "y": 1042 @@ -583,7 +583,7 @@ "schema": "5.0.9", "version": "6.0.0.114895" }, - "create_time": "2023-04-03T11:10:18.424298+00:00", + "create_time": "2023-04-12T11:31:47.902551+00:00", "draft_mode": false, "labels": [ "*" diff --git a/playbooks/VirusTotal_v3_Dynamic_Analysis.py b/playbooks/VirusTotal_v3_Dynamic_Analysis.py index 4cb062c3ca..a6e370baa3 100755 --- a/playbooks/VirusTotal_v3_Dynamic_Analysis.py +++ b/playbooks/VirusTotal_v3_Dynamic_Analysis.py @@ -249,7 +249,7 @@ def format_report_url(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 VirusTotal. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | https://www.virustotal.com/gui/url/{3} | VirusTotal v3 |\n%%{1}{2}{3}""" + template = """SOAR analyzed URL(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| URL | Normalized Score | Categories | Report Link | Source |\n| --- | --- | --- | --- | --- |\n%%\n| `{0}` | {1} | {2} | https://www.virustotal.com/gui/url/{3} | VirusTotal v3 |\n%%""" # parameter list for template variable replacement parameters = [ @@ -306,7 +306,7 @@ def build_url_output(action=None, success=None, container=None, results=None, ha observable_object = { "value": url, "type": "url", - "sandbox": { + "reputation": { "score_id": url_object['score_id'], "score": url_object['score'], "confidence": url_object['confidence'] @@ -413,9 +413,6 @@ def normalize_score_file(action=None, success=None, container=None, results=None # Reduce the confidence by percentage of vendors undetected. vendors = summary_data['harmless'] + summary_data['undetected'] + summary_data['malicious'] + summary_data['suspicious'] confidence = 100 - int((summary_data['undetected']/vendors) * 100) - - phantom.debug("vendors: {}".format(vendors)) - phantom.debug("confidence: {}".format(confidence)) # Normalize reputation on a 10 point scale based on number of malicious and suspicious divided by harmless vendors # This can be adjusted to include whatever logic is desired. @@ -439,8 +436,8 @@ def normalize_score_file(action=None, success=None, container=None, results=None normalize_score_file__file_score_object.append({'score': score, 'score_id': score_id, 'confidence': confidence}) normalize_score_file__scores.append(score) - phantom.debug("normalize_score_file__file_score_object: {}".format(normalize_score_file__file_score_object)) - phantom.debug("normalize_score_file__scores: {}".format(normalize_score_file__scores)) + #phantom.debug("normalize_score_file__file_score_object: {}".format(normalize_score_file__file_score_object)) + #phantom.debug("normalize_score_file__scores: {}".format(normalize_score_file__scores)) ################################################################################ ## Custom Code End @@ -462,7 +459,7 @@ def format_report_file(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 VirusTotal. The table below shows a summary of the information gathered.\n\n| File | VT Name | VT Decription | Normalized Score | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {2} | {3} | {1} | https://www.virustotal.com/gui/file/{0} | VirusTotal v3 |\n%%\n\n{1}\n{2}\n{3}\n""" + template = """SOAR analyzed file(s) using VirusTotal. The table below shows a summary of the information gathered.\n\n| File | VT Name | VT Decription | Normalized Score | Report Link | Source |\n| --- | --- | --- | --- | --- | --- |\n%%\n| `{0}` | {2} | {3} | {1} | https://www.virustotal.com/gui/file/{0} | VirusTotal v3 |\n%%\n""" # parameter list for template variable replacement parameters = [ @@ -517,18 +514,18 @@ def build_file_output(action=None, success=None, container=None, results=None, h observable_object = { "value": file_hash, "type": "hash", - "sandbox": { + "reputation": { "score_id": file_object['score_id'], "score": file_object['score'], "confidence": file_object['confidence'] }, "attributes": { - "type": file_data['attributes']['type_extension'], - "name": file_data['attributes']['meaningful_name'], - "size": file_data['attributes']['size'], - "md5": file_data['attributes']['md5'], - "sha1": file_data['attributes']['sha1'], - "sha256": file_data['attributes']['sha256'] + "type": file_data[0]['attributes'].get('type_extension'), + "name": file_data[0]['attributes'].get('meaningful_name'), + "size": file_data[0]['attributes'].get('size'), + "md5": file_data[0]['attributes'].get('md5'), + "sha1": file_data[0]['attributes'].get('sha1'), + "sha256": file_data[0]['attributes'].get('sha256') }, "source": "VirusTotal v3", "source_link": f"https://www.virustotal.com/gui/file/{file_hash}"