From 422ac630f07f55e31377f5707c447fc728221f18 Mon Sep 17 00:00:00 2001 From: Felipe Garrido Date: Fri, 21 Jul 2023 10:56:26 -0400 Subject: [PATCH 01/23] Add ZeroFox data connector It also adds analytic rules to map ZeroFox alerts to Microsoft Sentinel Incidents based on severity. --- .../ZF_Alerts_HighSeverityRule.yml | 26 ++++ .../ZF_Alerts_InformationalSeverityRule.yml | 26 ++++ .../ZF_Alerts_LowSeverityRule.yml | 26 ++++ .../ZF_Alerts_MediumSeverityRule.yml | 26 ++++ .../Alerts/alerts_connector.json | 128 ++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yml create mode 100644 Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yml create mode 100644 Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yml create mode 100644 Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yml create mode 100644 Solutions/ZeroFox/Data Connectors/Alerts/alerts_connector.json diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yml new file mode 100644 index 00000000000..b46cc65b28a --- /dev/null +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yml @@ -0,0 +1,26 @@ +name: ZeroFox Alerts - High Severity Alerts +description: | + 'Detects high severity alerts from ZeroFox' +severity: High +status: Available +requiredDataConnectors: + - connectorId: ZeroFox_Alert_Polling + dataTypes: + - ZeroFoxAlertPoller_CL +queryFrequency: 5m +queryPeriod: 5m +triggerOperator: gt +triggerThreshold: 0 +tactics: +query: | + ZeroFoxAlertPoller_CL + | where Severity in (5) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: entity_name_s +eventGroupingSettings: + aggregationKind: AlertPerResult +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yml new file mode 100644 index 00000000000..990c15d264d --- /dev/null +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yml @@ -0,0 +1,26 @@ +name: ZeroFox Alerts - Informational Severity Alerts +description: | + 'Detects informational severity alerts from ZeroFox' +severity: Informational +status: Available +requiredDataConnectors: + - connectorId: ZeroFox_Alert_Polling + dataTypes: + - ZeroFoxAlertPoller_CL +queryFrequency: 5m +queryPeriod: 5m +triggerOperator: gt +triggerThreshold: 0 +tactics: +query: | + ZeroFoxAlertPoller_CL + | where Severity in (1,2) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: entity_name_s +eventGroupingSettings: + aggregationKind: AlertPerResult +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yml new file mode 100644 index 00000000000..c23b6e993de --- /dev/null +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yml @@ -0,0 +1,26 @@ +name: ZeroFox Alerts - Low Severity Alerts +description: | + 'Detects low severity alerts from ZeroFox' +severity: Low +status: Available +requiredDataConnectors: + - connectorId: ZeroFox_Alert_Polling + dataTypes: + - ZeroFoxAlertPoller_CL +queryFrequency: 5m +queryPeriod: 5m +triggerOperator: gt +triggerThreshold: 0 +tactics: +query: | + ZeroFoxAlertPoller_CL + | where Severity in (3) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: entity_name_s +eventGroupingSettings: + aggregationKind: AlertPerResult +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yml new file mode 100644 index 00000000000..5e023ac0d4a --- /dev/null +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yml @@ -0,0 +1,26 @@ +name: ZeroFox Alerts - Medium Severity Alerts +description: | + 'Detects medium severity alerts from ZeroFox' +severity: Medium +status: Available +requiredDataConnectors: + - connectorId: ZeroFox_Alert_Polling + dataTypes: + - ZeroFoxAlertPoller_CL +queryFrequency: 5m +queryPeriod: 5m +triggerOperator: gt +triggerThreshold: 0 +tactics: +query: | + ZeroFoxAlertPoller_CL + | where Severity in (4) +entityMappings: + - entityType: Account + fieldMappings: + - identifier: FullName + columnName: entity_name_s +eventGroupingSettings: + aggregationKind: AlertPerResult +kind: Scheduled +version: 1.0.0 diff --git a/Solutions/ZeroFox/Data Connectors/Alerts/alerts_connector.json b/Solutions/ZeroFox/Data Connectors/Alerts/alerts_connector.json new file mode 100644 index 00000000000..861e945c1e3 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/Alerts/alerts_connector.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workspace": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "id": "[concat('/subscriptions/',subscription().subscriptionId,'/resourceGroups/',resourceGroup().name,'/providers/Microsoft.OperationalInsights/workspaces/',parameters('workspace'),'/providers/Microsoft.SecurityInsights/dataConnectors/',guid(subscription().subscriptionId))]", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',guid(subscription().subscriptionId))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "kind": "APIPolling", + "properties": { + "connectorUiConfig": { + "id":"ZeroFox_Alert_Polling", + "title": "ZeroFox Enterprise - Alerts (Polling CCP)", + "publisher": "ZeroFox Enterprise", + "descriptionMarkdown": "Collects alerts from ZeroFox API.", + "graphQueriesTableName": "ZeroFoxAlertPoller_CL", + "graphQueries": [ + { + "metricName": "Total alerts received", + "legend": "ZeroFox Alerts", + "baseQuery": "{{graphQueriesTableName}}" + } + ], + "sampleQueries": [ + { + "description": "List all ZeroFox alerts", + "query": "{{graphQueriesTableName}}\n| sort by TimeGenerated asc" + }, + { + "description": "Count alerts by network type", + "query": "{{graphQueriesTableName}}\n| summarize Count = count() by ThreatSource=network_s" + }, + { + "description": "Count alerts by entity", + "query": "{{graphQueriesTableName}}\n| summarize Count = count() by Entity=entity_name_s" + } + ], + "dataTypes": [ + { + "name": "{{graphQueriesTableName}}", + "lastDataReceivedQuery": "{{graphQueriesTableName}}\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriteria": [ + { + "type": "SentinelKindsV2", + "value": [ + "APIPolling" + ] + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/solutions", + "permissionsDisplayText": "read and write permissions are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "action": true, + "write": true, + "read": true, + "delete": true + } + } + ], + "customs": [ + { + "name": "ZeroFox Personal Access Token (PAT)", + "description": "A ZeroFox PAT is required. You can get it in Data Connectors > [API Data Feeds](https://cloud.zerofox.com/data_connectors/api)." + } + ] + }, + "instructionSteps": [ + { + "title": "Connect ZeroFox to Microsoft Sentinel", + "description": "Provide your ZeroFox PAT", + "instructions": [ + { + "type": "APIKey" + } + ] + } + ] + }, + "pollingConfig": { + "auth": { + "authType": "APIKey", + "APIKeyName": "Authorization", + "APIKeyIdentifier": "Token" + }, + "request": { + "apiEndpoint": "https://api.zerofox.com/1.0/alerts/", + "httpMethod": "Get", + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "min_timestamp", + "endTimeAttributeName": "max_timestamp", + "queryParameters": { + "sort_direction": "asc" + } + }, + "response": { + "eventsJsonPaths": [ + "$.alerts[*]" + ] + }, + "paging": { + "pagingType": "Offset", + "offsetParaName": "offset", + "pageSizeParaName": "limit", + "pageSize": 100 + } + } + } + } + ] +} From 9da3e33903a5be6fdb1a79af48a710a0cb507b5a Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Mon, 24 Jul 2023 12:38:20 -0400 Subject: [PATCH 02/23] Add data connectors for ZeroFox CTI Adds all ZeroFox connectors to CTI feeds by way of Azure Functions, as per task ZFE-73125 --- .../advanced_dark_web_connector/__init__.py | 61 ++++++ .../advanced_dark_web_connector/function.json | 11 + .../__init__.py | 61 ++++++ .../function.json | 11 + .../connectors/botnet_connector/__init__.py | 64 ++++++ .../connectors/botnet_connector/function.json | 11 + .../connectors/breaches_connector/__init__.py | 62 ++++++ .../breaches_connector/function.json | 11 + .../c2_domains_connector/__init__.py | 61 ++++++ .../c2_domains_connector/function.json | 11 + .../__init__.py | 64 ++++++ .../function.json | 11 + .../CTI/connectors/connections/__init__.py | 0 .../CTI/connectors/connections/sentinel.py | 162 +++++++++++++++ .../CTI/connectors/connections/zerofox.py | 192 ++++++++++++++++++ .../credit_cards_connector/__init__.py | 64 ++++++ .../credit_cards_connector/function.json | 11 + .../connectors/dark_web_connector/__init__.py | 62 ++++++ .../dark_web_connector/function.json | 11 + .../connectors/discord_connector/__init__.py | 62 ++++++ .../discord_connector/function.json | 11 + .../disruption_connector/__init__.py | 62 ++++++ .../disruption_connector/function.json | 11 + .../email_addresses_connector/__init__.py | 61 ++++++ .../email_addresses_connector/function.json | 11 + .../connectors/exploits_connector/__init__.py | 65 ++++++ .../exploits_connector/function.json | 11 + .../identity_breach_connector/__init__.py | 61 ++++++ .../identity_breach_connector/function.json | 11 + .../CTI/connectors/irc_connector/__init__.py | 62 ++++++ .../connectors/irc_connector/function.json | 11 + .../connectors/malware_connector/__init__.py | 62 ++++++ .../malware_connector/function.json | 11 + .../national_ids_connector/__init__.py | 64 ++++++ .../national_ids_connector/function.json | 11 + .../connectors/phishing_connector/__init__.py | 62 ++++++ .../phishing_connector/function.json | 11 + .../phone_numbers_connector/__init__.py | 64 ++++++ .../phone_numbers_connector/function.json | 11 + .../ransomware_connector/__init__.py | 62 ++++++ .../ransomware_connector/function.json | 11 + .../connectors/telegram_connector/__init__.py | 64 ++++++ .../telegram_connector/function.json | 11 + .../threat_actors_connector/__init__.py | 62 ++++++ .../threat_actors_connector/function.json | 11 + .../vulnerabilities_connector/__init__.py | 64 ++++++ .../vulnerabilities_connector/function.json | 11 + .../ZeroFox/Data Connectors/CTI/host.json | 15 ++ .../Data Connectors/CTI/requirements.txt | 6 + .../Data Connectors/Logo/foxy-mark.svg | 1 + 50 files changed, 1994 insertions(+) create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/sentinel.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/__init__.py create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/function.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/host.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/requirements.txt create mode 100644 Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/__init__.py new file mode 100644 index 00000000000..f536db5c037 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/__init__.py @@ -0,0 +1,61 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_advanced_dark_web(zf_client, created_after = query_from, created_before = query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_advanced_dark_web" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_advanced_dark_web(client: ZeroFoxClient, created_from, created_after): + url_suffix = "advanced-dark-web/" + params = dict(created_after=created_after, created_before=created_from) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/__init__.py new file mode 100644 index 00000000000..ea76e6b81c7 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/__init__.py @@ -0,0 +1,61 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_botnet_compromised_credentials(zf_client, created_after = query_from, created__before = query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_botnet_compromised_credentials" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_botnet_compromised_credentials(client: ZeroFoxClient, created_from, created_after): + url_suffix = "botnet-compromised-credentials/" + params = dict(created_after=created_after, created_before=created_from) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/__init__.py new file mode 100644 index 00000000000..c16d87bd4be --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_botnet(zf_client, listed_after=query_from, listed_before=query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_botnet" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_botnet(client: ZeroFoxClient, listed_after: str, listed_before: str): + """ + :param domain: The domain to lookup in botnet CTI Feed + :return: HTTP request content. + """ + url_suffix = "botnet/" + params = dict(listed_after=listed_after, listed_before=listed_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/__init__.py new file mode 100644 index 00000000000..4194007489a --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_breaches(zf_client, created_at_after= query_from, created_at_before= query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_breaches" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_breaches(client: ZeroFoxClient, created_at_after: str, created_at_before: str): + url_suffix = "breaches/" + params = dict(created_at_after=created_at_after, created_at_before=created_at_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/__init__.py new file mode 100644 index 00000000000..4f24723ffa9 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/__init__.py @@ -0,0 +1,61 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_c2_domains(zf_client, created_after= query_from, created_before= query_to) + + logging.info("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_C2" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_c2_domains(client: ZeroFoxClient, created_after: str, created_before: str): + url_suffix = "c2-domains/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/__init__.py new file mode 100644 index 00000000000..c5beb7cf3e2 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_compromised_credentials( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_compromised_credentials" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_compromised_credentials( + client: ZeroFoxClient, created_after: str, created_before: str +): + url_suffix = "compromised-credentials/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/sentinel.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/sentinel.py new file mode 100644 index 00000000000..4c4fa26e97a --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/sentinel.py @@ -0,0 +1,162 @@ +import base64 +import datetime +import hashlib +import hmac +import json +import logging +from threading import Thread +import requests + + +class SentinelConnector: + def __init__( + self, + customer_id, + shared_key, + log_type, + queue_size=200, + bulks_number=10, + queue_size_bytes=25 * (2**20), + ): + self.log_analytics_uri = f"https://{customer_id}.ods.opinsights.azure.com" + self.customer_id = customer_id + self.shared_key = shared_key + self.log_type = log_type + self.queue_size = queue_size + self.bulks_number = bulks_number + self.queue_size_bytes = queue_size_bytes + self._queue = [] + self._bulks_list = [] + self.successfull_sent_events_number = 0 + self.failed_sent_events_number = 0 + + def send(self, event): + self._queue.append(event) + if len(self._queue) >= self.queue_size: + self.flush(force=False) + + def flush(self, force=True): + self._bulks_list.append(self._queue) + if force: + self._flush_bulks() + else: + if len(self._bulks_list) >= self.bulks_number: + self._flush_bulks() + + self._queue = [] + + def _flush_bulks(self): + jobs = [] + for queue in self._bulks_list: + if queue: + queue_list = self._split_big_request(queue) + for q in queue_list: + jobs.append( + Thread( + target=self._post_data, + args=( + self.customer_id, + self.shared_key, + q, + self.log_type, + ), + ) + ) + + for job in jobs: + job.start() + + for job in jobs: + job.join() + + self._bulks_list = [] + + def __enter__(self): + pass + + def __exit__(self, type, value, traceback): + self.flush() + + def _build_signature( + self, + customer_id, + shared_key, + date, + content_length, + method, + content_type, + resource, + ): + x_headers = "x-ms-date:" + date + string_to_hash = ( + method + + "\n" + + str(content_length) + + "\n" + + content_type + + "\n" + + x_headers + + "\n" + + resource + ) + bytes_to_hash = bytes(string_to_hash, encoding="utf-8") + decoded_key = base64.b64decode(shared_key) + encoded_hash = base64.b64encode( + hmac.new(decoded_key, bytes_to_hash, digestmod=hashlib.sha256).digest() + ).decode() + authorization = "SharedKey {}:{}".format(customer_id, encoded_hash) + return authorization + + def _post_data(self, customer_id, shared_key, body, log_type): + events_number = len(body) + body = json.dumps(body) + method = "POST" + content_type = "application/json" + resource = "/api/logs" + rfc1123date = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT") + content_length = len(body) + signature = self._build_signature( + customer_id, + shared_key, + rfc1123date, + content_length, + method, + content_type, + resource, + ) + uri = self.log_analytics_uri + resource + "?api-version=2016-04-01" + + headers = { + "content-type": content_type, + "Authorization": signature, + "Log-Type": log_type, + "x-ms-date": rfc1123date, + } + + response = requests.post(uri, data=body, headers=headers) + if response.status_code >= 200 and response.status_code <= 299: + logging.info( + "{} events have been successfully sent to Microsoft Sentinel".format( + events_number + ) + ) + self.successfull_sent_events_number += events_number + else: + logging.error( + f"Error during sending events to Microsoft Sentinel. Response code: {response.status_code}, with message: {response.text}" + ) + self.failed_sent_events_number += events_number + + def _check_size(self, queue): + data_bytes_len = len(json.dumps(queue).encode()) + return data_bytes_len < self.queue_size_bytes + + def _split_big_request(self, queue): + if self._check_size(queue): + return [queue] + else: + middle = int(len(queue) / 2) + queues_list = [queue[:middle], queue[middle:]] + return self._split_big_request(queues_list[0]) + self._split_big_request( + queues_list[1] + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py new file mode 100644 index 00000000000..972d05d2531 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py @@ -0,0 +1,192 @@ +from typing import Dict +import requests +import json + + +class ZeroFoxClient: + def __init__(self, user, token) -> None: + self.credentials = {"username": user, "password": token} + self._base_url = "https://api.zerofox.com" + + def cti_request( + self, + method: str, + url_suffix: str, + params: Dict = None, + data: Dict = None, + error_handler=None, + ): + """ + :param method: HTTP request type + :param url_suffix: The suffix of the URL + :param params: The request's query parameters + :param data: The request's body parameters + :param version: api prefix to consider, default is to use version '1.0' + :param res_type: Selects the decoder of the response. It can be + `json` (default), `xml`, `text`, `content`, `response` + :param empty_response: Indicates if the response data is empty or not + :param error_handler: Function that receives the response and manage + the error + :return: Returns the content of the response received from the API. + """ + headers = self._get_cti_request_header() + + response = self._http_request( + method=method, + url_suffix=f"/cti/{url_suffix}", + headers=headers, + params=params, + data=data, + empty_valid_codes=(200, 201), + error_handler=error_handler, + ) + + for result in response["results"]: + yield result + while response["next"]: + response = response = self._http_request( + method="GET", + headers=headers, + full_address=response["next"], + ) + for result in response["results"]: + yield result + + def _client_error_handler(self, res: requests.Response): + err_msg = f"Error in API call [{res.status_code}] - {res.reason}" + try: + # Try to parse json error response + error_entry = res.json() + err_msg += f"\n{json.dumps(error_entry)}" + raise Exception(err_msg, res=res) + except ValueError: + err_msg += f"\n{res.text}" + raise Exception(err_msg, res=res) + + def _http_request( + self, + method, + url_suffix="", + full_address=None, + headers=None, + auth=None, + json_data=None, + params=None, + data=None, + files=None, + timeout=None, + ok_codes=None, + return_empty_response=False, + retries=0, + error_handler=None, + empty_valid_codes=None, + **kwargs, + ): + try: + # Replace params if supplied + address = full_address if full_address else f"{self._base_url}{url_suffix}" + if not timeout: + timeout = 10 + + # Execute + res = requests.request( + method, + address, + params=params, + data=data, + json=json_data, + files=files, + headers=headers, + auth=auth, + timeout=timeout, + **kwargs, + ) + # Handle error responses gracefully + if not self._is_status_code_valid(res, ok_codes): + if error_handler: + error_handler(res) + else: + self._client_error_handler(res) + + if not empty_valid_codes: + empty_valid_codes = [204] + is_response_empty_and_successful = res.status_code in empty_valid_codes + if is_response_empty_and_successful and return_empty_response: + return res + + try: + return res.json() + except ValueError as exception: + raise Exception( + f"Failed to parse object from response: {res.content}", + exception, + res, + ) + except requests.exceptions.ConnectTimeout as exception: + err_msg = ( + "Connection Timeout Error - potential reasons might be that the Server URL parameter" + " is incorrect or that the Server is not accessible from your host." + ) + raise Exception(err_msg, exception) + except requests.exceptions.SSLError as exception: + # in case the "Trust any certificate" is already checked + err_msg = ( + "SSL Certificate Verification Failed - try selecting 'Trust any certificate' checkbox in" + " the integration configuration." + ) + raise Exception(err_msg, exception) + except requests.exceptions.ProxyError as exception: + err_msg = ( + "Proxy Error - if the 'Use system proxy' checkbox in the integration configuration is" + " selected, try clearing the checkbox." + ) + raise Exception(err_msg, exception) + except requests.exceptions.ConnectionError as exception: + # Get originating Exception in Exception chain + error_class = str(exception.__class__) + err_type = f"""<{error_class[error_class.find("'") + 1 : error_class.rfind("'")]}>""" + err_msg = ( + "Verify that the server URL parameter" + " is correct and that you have access to the server from your host." + f"\nError Type: {err_type}\nError Number: [{exception.errno}]\nMessage: {exception.strerror}\n" + ) + raise Exception(err_msg, exception) + except requests.exceptions.RetryError as exception: + try: + reason = f"Reason: {exception.args[0].reason.args[0]}" + except Exception: # noqa: disable=broad-except + reason = "" + err_msg = f"Max Retries Error- Request attempts with {retries} retries failed. \n{reason}" + raise Exception(err_msg, exception) + + def _is_status_code_valid(self, response: requests.Response, codes): + return codes is None or response.status_code in codes + + def _get_new_access_token(self): + url_suffix: str = "/auth/token/" + try: + response_content: Dict = self._http_request( + method="POST", + url_suffix=url_suffix, + data=self.credentials, + ) + return response_content.get("access", "") + except Exception as e: + raise e + + def get_cti_authorization_token(self) -> str: + """ + :return: returns the authorization token for the CTI feed + """ + token = self._get_new_access_token() + if not token: + raise Exception("Unable to retrieve token.") + return token + + def _get_cti_request_header(self): + token: str = self.get_cti_authorization_token() + return { + "Authorization": f"Bearer {token}", + "Content-Type": "application/json", + "Accept": "application/json", + } diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/__init__.py new file mode 100644 index 00000000000..5e83358fcb6 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_credit_cards( + zf_client, created_After=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_credit_cards" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_credit_cards( + client: ZeroFoxClient, created_after: str, created_before: str +): + url_suffix = "credit-cards/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/__init__.py new file mode 100644 index 00000000000..7d6e0f0af54 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_dark_web( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_dark_web" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_dark_web(client: ZeroFoxClient, created_after: str, created_before: str): + url_suffix = "dark-web/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/__init__.py new file mode 100644 index 00000000000..eb66c2b7d4d --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_discord( + zf_client, timestamp_after=query_from, timestamp_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_discord" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_discord(client: ZeroFoxClient, timestamp_after: str, timestamp_before: str): + url_suffix = "discord/" + params = dict(timestamp_after=timestamp_after, timestamp_before=timestamp_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/__init__.py new file mode 100644 index 00000000000..1f77c9cfba2 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_disruption( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_disruption" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_disruption(client: ZeroFoxClient, created_after: str, created_before: str): + url_suffix = "disruption/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/__init__.py new file mode 100644 index 00000000000..d98a716f333 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/__init__.py @@ -0,0 +1,61 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_email_addresses(zf_client, created_after=query_from, created_before=query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_email_addresses" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_email_addresses(client: ZeroFoxClient, created_before, created_after): + url_suffix = "email-addresses/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/__init__.py new file mode 100644 index 00000000000..c5b670e5bc6 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/__init__.py @@ -0,0 +1,65 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_exploits( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_exploits" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_exploits(client: ZeroFoxClient, created_after: str, created_before: str): + """ + :return: HTTP request content. + """ + url_suffix = "exploits/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/__init__.py new file mode 100644 index 00000000000..a90fbf0a566 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/__init__.py @@ -0,0 +1,61 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_identity_breach(zf_client, created_after=query_from, created_before=query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_identity_breach" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_identity_breach(client: ZeroFoxClient, created_before, created_after): + url_suffix = "identity-breach/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/__init__.py new file mode 100644 index 00000000000..1072beec6fc --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_irc(zf_client, timestamp_after= query_from, timestamp_before= query_to) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_irc" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_irc(client: ZeroFoxClient, timestamp_after: str, timestamp_before: str): + + url_suffix = "irc/" + params = dict(timestamp_after=timestamp_after, timestamp_before=timestamp_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) + diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py new file mode 100644 index 00000000000..b7a8eb09478 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_malware( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_malware" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_malware(client: ZeroFoxClient, created_before, created_after): + url_suffix = "malware/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/__init__.py new file mode 100644 index 00000000000..0c31aabad56 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_national_ids( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_national_ids" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_national_ids( + client: ZeroFoxClient, created_after: str, created_before: str +): + url_suffix = "national-ids/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/__init__.py new file mode 100644 index 00000000000..bc17192f504 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_phishing( + zf_client, scanned_after=query_from, scanned_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_phishing" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_phishing(client: ZeroFoxClient, scanned_before, scanned_after): + url_suffix = "phishing/" + params = dict(scanned_after=scanned_after, scanned_before=scanned_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/__init__.py new file mode 100644 index 00000000000..7fd0951fa07 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_phone_numbers( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_phone_numbers" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_phone_numbers( + client: ZeroFoxClient, created_after: str, created_before: str +): + url_suffix = "phone-numbers/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/__init__.py new file mode 100644 index 00000000000..acf39514af5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_ransomware( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_ransomware" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_ransomware(client: ZeroFoxClient, created_after: str, created_before: str): + url_suffix = "ransomware/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/__init__.py new file mode 100644 index 00000000000..90b140b7a31 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_telegram( + zf_client, timestamp_after=query_from, timestamp_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_telegram" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_telegram( + client: ZeroFoxClient, timestamp_after: str, timestamp_before: str +): + url_suffix = "telegram/" + params = dict(timestamp_after=timestamp_after, timestamp_before=timestamp_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/__init__.py new file mode 100644 index 00000000000..0c52869d6c9 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/__init__.py @@ -0,0 +1,62 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_threat_actors( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_threat_actors" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info("Python timer trigger function ran at %s", utc_timestamp) + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_threat_actors(client: ZeroFoxClient, created_after, created_before): + url_suffix = "threat-actors/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/function.json new file mode 100644 index 00000000000..c84ce78a5a5 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/__init__.py new file mode 100644 index 00000000000..43409c31165 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/__init__.py @@ -0,0 +1,64 @@ +import datetime +import logging +import os + +import azure.functions as func + +from connections.sentinel import SentinelConnector +from connections.zerofox import ZeroFoxClient + + +def main(mytimer: func.TimerRequest) -> None: + utc_timestamp = ( + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + ) + + if mytimer.past_due: + logging.info("The timer is past due!") + + # Update the customer ID to your Log Analytics workspace ID + customer_id = os.environ.get("WorkspaceID") + + # For the shared key, use either the primary or the secondary Connected Sources client authentication key + shared_key = os.environ.get("WorkspaceKey") + + query_from = mytimer.schedule_status["Last"] + query_to = datetime.datetime.utcnow() - datetime.timedelta(minutes=1) + + zf_client = get_zf_client() + + results = get_cti_vulnerabilities( + zf_client, created_after=query_from, created_before=query_to + ) + + logging.debug("Trigger function retrieved results") + + # The log type is the name of the event that is being submitted + log_type = "ZeroFox_CTI_vulnerabilities" + + sentinel_client = SentinelConnector( + customer_id=customer_id, shared_key=shared_key, log_type=log_type + ) + + for result in results: + sentinel_client.send(result) + + logging.info(f"Python timer trigger function ran at {utc_timestamp}") + + +def get_zf_client(): + user = os.environ.get("zf_username") + token = os.environ.get("token") + return ZeroFoxClient(user, token) + + +def get_cti_vulnerabilities( + client: ZeroFoxClient, created_after: str, created_before: str +): + url_suffix = "vulnerabilities/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/function.json new file mode 100644 index 00000000000..0fb7c296f13 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/function.json @@ -0,0 +1,11 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "name": "mytimer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 0 18 * * *" + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/host.json b/Solutions/ZeroFox/Data Connectors/CTI/host.json new file mode 100644 index 00000000000..d6e08a8bf78 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/host.json @@ -0,0 +1,15 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[3.*, 4.0.0)" + } + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt b/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt new file mode 100644 index 00000000000..5458bfc65c9 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt @@ -0,0 +1,6 @@ +# Do not include azure-functions-worker in this file +# The Python Worker is managed by the Azure Functions platform +# Manually managing azure-functions-worker may cause unexpected issues + +azure-functions +requests \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg b/Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg new file mode 100644 index 00000000000..0038c4381bd --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg @@ -0,0 +1 @@ + foxy-mark \ No newline at end of file From fe4b5aefc2f7ece6d1edbf66c30179b1d6ed5b3a Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Tue, 25 Jul 2023 13:19:18 -0400 Subject: [PATCH 03/23] Add configuration json file for CTI connector Adds configuration info for CTI connectors according to ZF-73126 --- Logos/foxy-mark.svg | 1 + .../CTI/ZeroFox_CTI_FunctionApp.json | 159 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 Logos/foxy-mark.svg create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json diff --git a/Logos/foxy-mark.svg b/Logos/foxy-mark.svg new file mode 100644 index 00000000000..0038c4381bd --- /dev/null +++ b/Logos/foxy-mark.svg @@ -0,0 +1 @@ + foxy-mark \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json b/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json new file mode 100644 index 00000000000..a318835a5ec --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json @@ -0,0 +1,159 @@ +{ + "id": "ZeroFoxCTIDataConnector", + "title": "ZeroFox CTI", + "publisher": "ZeroFox", + "descriptionMarkdown": "The ZeroFox CTI data connector provides the capability to ingest [ZeroFox](https://docs.umbrella.com/) cyber threat intelligence events into Azure Sentinel.", + "additionalRequirementBanner": "These queries and workbooks are dependent on a parser based on Kusto to work as expected. Follow the steps to use this Kusto functions alias **Cisco_Umbrella** in queries and workbooks. [Follow steps to get this Kusto functions>](https://aka.ms/sentinel-ciscoumbrella-function) ", + "graphQueries": [ + { + "metricName": "ZeroFox CTI C2 Domains Logs", + "legend": "ZeroFox_CTI_C2_CL", + "baseQuery": "ZeroFox_CTI_C2_CL" + }, + { + "metricName": "ZeroFox CTI Email Addresses Logs", + "legend": "ZeroFox_CTI_email_addresses_CL", + "baseQuery": "ZeroFox_CTI_email_addresses_CL" + } + ], + "sampleQueries": [ + { + "description": "All Cisco Umbrella Logs", + "query": "Cisco_Umbrella\n| sort by TimeGenerated desc" + }, + { + "description": "Cisco Umbrella DNS Logs", + "query": "Cisco_Umbrella\n | where EventType == 'dnslogs'\n| sort by TimeGenerated desc" + } + + ], + "dataTypes": [ + { + "name": "Cisco_Umbrella_dns_CL", + "lastDataReceivedQuery": "Cisco_Umbrella_dns_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "Cisco_Umbrella_proxy_CL", + "lastDataReceivedQuery": "Cisco_Umbrella_proxy_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "Cisco_Umbrella_ip_CL", + "lastDataReceivedQuery": "Cisco_Umbrella_ip_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "Cisco_Umbrella_cloudfirewall_CL", + "lastDataReceivedQuery": "Cisco_Umbrella_cloudfirewall_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriterias": [ + { + "type": "IsConnectedQuery", + "value": [ + "Cisco_Umbrella_dns_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "Cisco_Umbrella_proxy_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "Cisco_Umbrella_ip_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "Cisco_Umbrella_cloudfirewall_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)" + ] + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "read and write permissions on the workspace are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + }, + { + "provider": "Microsoft.OperationalInsights/workspaces/sharedKeys", + "permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key).", + "providerDisplayName": "Keys", + "scope": "Workspace", + "requiredPermissions": { + "action": true + } + } + ], + "customs": [ + { + "name": "Microsoft.Web/sites permissions", + "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." + }, + { + "name": "Amazon S3 REST API Credentials/permissions", + "description": "**AWS Access Key Id**, **AWS Secret Access Key**, **AWS S3 Bucket Name** are required for Amazon S3 REST API." + } + ] + }, + "instructionSteps": [ + { + "title": "", + "description": ">**NOTE:** This connector uses Azure Functions to connect to the Amazon S3 REST API to pull logs into Microsoft Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details." + }, + { + "title": "", + "description": ">**NOTE:** This connector has been updated to support [cisco umbrella version 5 and version 6.](https://docs.umbrella.com/deployment-umbrella/docs/log-formats-and-versioning)" + }, + { + "title": "", + "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + }, + { + "title": "", + "description": ">**NOTE:** This connector uses a parser based on a Kusto Function to normalize fields. [Follow these steps](https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Parsers/CiscoUmbrella/Cisco_Umbrella) to create the Kusto function alias **Cisco_Umbrella**." + }, + { + "title": "", + "description": "**STEP 1 - Configuration of the Cisco Umbrella logs collection**\n\n[See documentation](https://docs.umbrella.com/deployment-umbrella/docs/log-management#section-logging-to-amazon-s-3) and follow the instructions for set up logging and obtain credentials." + }, + { + "title": "", + "description": "**STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the Cisco Umbrella data connector, have the Workspace ID and Workspace Primary Key (can be copied from the following), as well as the Amazon S3 REST API Authorization credentials, readily available.", + "instructions": [ + { + "parameters": { + "fillWith": [ + "WorkspaceId" + ], + "label": "Workspace ID" + }, + "type": "CopyableLabel" + }, + { + "parameters": { + "fillWith": [ + "PrimaryKey" + ], + "label": "Primary Key" + }, + "type": "CopyableLabel" + } + ] + }, + { + "title": "Option 1 - Azure Resource Manager (ARM) Template", + "description": "Use this method for automated deployment of the Cisco Umbrella data connector using an ARM Tempate.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinelciscoumbrellaazuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **S3Bucket**, **AWSAccessKeyId**, **AWSSecretAccessKey**\n**Note:** For the S3Bucket use the value that Cisco referrs to as the _S3 Bucket Data Path_ and add a / (forward slash) to the end of the value\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy." + }, + { + "title": "Option 2 - Manual Deployment of Azure Functions", + "description": "Use the following step-by-step instructions to deploy the Cisco Umbrella data connector manually with Azure Functions (Deployment via Visual Studio Code)." + }, + { + "title": "", + "description": "**1. Deploy a Function App**\n\n> **NOTE:** You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/create-first-function-vs-code-python) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-CiscoUmbrellaConn-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. UmbrellaXYZ).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft Sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + }, + { + "title": "", + "description": "**2. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): \n\t\tWorkspaceID\n\t\tWorkspaceKey\n\t\tS3Bucket\n\t\tAWSAccessKeyId\n\t\tAWSSecretAccessKey\n\t\tlogAnalyticsUri (optional)\n> - Use logAnalyticsUri to override the log analytics API endpoint for dedicated cloud. For example, for public cloud, leave the value empty; for Azure GovUS cloud environment, specify the value in the following format: `https://.ods.opinsights.azure.us`.\n3. Once all application settings have been entered, click **Save**." + } + ] +} From 238284b5472231cbb69c1ba08c1e6ff4dce8db46 Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Thu, 27 Jul 2023 16:05:28 -0400 Subject: [PATCH 04/23] Organize data connectors and include configuration file for azure functions * Add configuration file ZeroFox_CTI_FunctionApp.json for azure functions * Add unit test for ZeroFox Client * Move Data Connectors from connectors folder According to task ZFE-73126 --- .../CTI/ZeroFox_CTI_FunctionApp.json | 249 +++++++++++++++--- .../advanced_dark_web_connector/__init__.py | 0 .../advanced_dark_web_connector/function.json | 0 .../__init__.py | 0 .../function.json | 0 .../botnet_connector/__init__.py | 0 .../botnet_connector/function.json | 0 .../breaches_connector/__init__.py | 0 .../breaches_connector/function.json | 0 .../c2_domains_connector/__init__.py | 0 .../c2_domains_connector/function.json | 0 .../__init__.py | 0 .../function.json | 0 .../{connectors => }/connections/__init__.py | 0 .../{connectors => }/connections/sentinel.py | 0 .../{connectors => }/connections/zerofox.py | 12 +- .../credit_cards_connector/__init__.py | 0 .../credit_cards_connector/function.json | 0 .../dark_web_connector/__init__.py | 0 .../dark_web_connector/function.json | 0 .../discord_connector/__init__.py | 0 .../discord_connector/function.json | 0 .../disruption_connector/__init__.py | 0 .../disruption_connector/function.json | 0 .../email_addresses_connector/__init__.py | 0 .../email_addresses_connector/function.json | 0 .../exploits_connector/__init__.py | 0 .../exploits_connector/function.json | 0 .../identity_breach_connector/__init__.py | 0 .../identity_breach_connector/function.json | 0 .../irc_connector/__init__.py | 0 .../irc_connector/function.json | 0 .../malware_connector/__init__.py | 3 +- .../malware_connector/function.json | 0 .../national_ids_connector/__init__.py | 0 .../national_ids_connector/function.json | 0 .../phishing_connector/__init__.py | 0 .../phishing_connector/function.json | 0 .../phone_numbers_connector/__init__.py | 0 .../phone_numbers_connector/function.json | 0 .../ransomware_connector/__init__.py | 0 .../ransomware_connector/function.json | 0 .../Data Connectors/CTI/requirements.txt | 4 +- .../telegram_connector/__init__.py | 0 .../telegram_connector/function.json | 0 .../Data Connectors/CTI/tests/ZeroFox_test.py | 57 ++++ .../threat_actors_connector/__init__.py | 0 .../threat_actors_connector/function.json | 0 .../vulnerabilities_connector/__init__.py | 0 .../vulnerabilities_connector/function.json | 0 .../Data Connectors/Logo/foxy-mark.svg | 1 - 51 files changed, 288 insertions(+), 38 deletions(-) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/advanced_dark_web_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/advanced_dark_web_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/botnet_compromised_credentials_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/botnet_compromised_credentials_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/botnet_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/botnet_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/breaches_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/breaches_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/c2_domains_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/c2_domains_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/compromised_credentials_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/compromised_credentials_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/connections/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/connections/sentinel.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/connections/zerofox.py (96%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/credit_cards_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/credit_cards_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/dark_web_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/dark_web_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/discord_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/discord_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/disruption_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/disruption_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/email_addresses_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/email_addresses_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/exploits_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/exploits_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/identity_breach_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/identity_breach_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/irc_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/irc_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/malware_connector/__init__.py (93%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/malware_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/national_ids_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/national_ids_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/phishing_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/phishing_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/phone_numbers_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/phone_numbers_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/ransomware_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/ransomware_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/telegram_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/telegram_connector/function.json (100%) create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/tests/ZeroFox_test.py rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/threat_actors_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/threat_actors_connector/function.json (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/vulnerabilities_connector/__init__.py (100%) rename Solutions/ZeroFox/Data Connectors/CTI/{connectors => }/vulnerabilities_connector/function.json (100%) delete mode 100644 Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg diff --git a/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json b/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json index a318835a5ec..ce58a3d7287 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json +++ b/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json @@ -2,57 +2,242 @@ "id": "ZeroFoxCTIDataConnector", "title": "ZeroFox CTI", "publisher": "ZeroFox", - "descriptionMarkdown": "The ZeroFox CTI data connector provides the capability to ingest [ZeroFox](https://docs.umbrella.com/) cyber threat intelligence events into Azure Sentinel.", - "additionalRequirementBanner": "These queries and workbooks are dependent on a parser based on Kusto to work as expected. Follow the steps to use this Kusto functions alias **Cisco_Umbrella** in queries and workbooks. [Follow steps to get this Kusto functions>](https://aka.ms/sentinel-ciscoumbrella-function) ", + "descriptionMarkdown": "The ZeroFox CTI data connectors provide the capability to ingest the different [ZeroFox](https://www.zerofox.com/threat-intelligence/) cyber threat intelligence alerts into Azure Sentinel.", "graphQueries": [ + { + "metricName": "ZeroFox CTI Advanced Dark Web Logs", + "legend": "ZeroFox_CTI_advanced_dark_web_CL", + "baseQuery": "ZeroFox_CTI_advanced_dark_web_CL" + }, + { + "metricName": "ZeroFox CTI Botnet Logs", + "baseQuery": "ZeroFox_CTI_botnet_CL" + }, + { + "metricName": "ZeroFox CTI Breaches Logs", + "legend": "ZeroFox_CTI_breaches_CL", + "baseQuery": "ZeroFox_CTI_breaches_CL" + }, { "metricName": "ZeroFox CTI C2 Domains Logs", "legend": "ZeroFox_CTI_C2_CL", "baseQuery": "ZeroFox_CTI_C2_CL" }, + { + "metricName": "ZeroFox CTI Compromised Credentials Logs", + "legend": "ZeroFox_CTI_compromised_credentials_CL", + "baseQuery": "ZeroFox_CTI_compromised_credentials_CL" + }, + { + "metricName": "ZeroFox CTI Credit Cards Logs", + "legend": "ZeroFox_CTI_credit_cards_CL", + "baseQuery": "ZeroFox_CTI_credit_cards_CL" + }, + { + "metricName": "ZeroFox CTI Dark Web Logs", + "legend": "ZeroFox_CTI_dark_web_CL", + "baseQuery": "ZeroFox_CTI_dark_web_CL" + }, + { + "metricName": "ZeroFox CTI Discord Logs", + "legend": "ZeroFox_CTI_discord_CL", + "baseQuery": "ZeroFox_CTI_discord_CL" + }, + { + "metricName": "ZeroFox CTI Disruption Logs", + "legend": "ZeroFox_CTI_disruption_CL", + "baseQuery": "ZeroFox_CTI_disruption_CL" + }, { "metricName": "ZeroFox CTI Email Addresses Logs", "legend": "ZeroFox_CTI_email_addresses_CL", "baseQuery": "ZeroFox_CTI_email_addresses_CL" - } + }, + { + "metricName": "ZeroFox CTI Exploits Logs", + "legend": "ZeroFox_CTI_exploits_CL", + "baseQuery": "ZeroFox_CTI_exploits_CL" + }, + { + "metricName": "ZeroFox CTI Identity Breach Logs", + "legend": "ZeroFox_CTI_identity_breach_CL", + "baseQuery": "ZeroFox_CTI_identity_breach_CL" + }, + { + "metricName": "ZeroFox CTI IRC Logs", + "legend": "ZeroFox_CTI_irc_CL", + "baseQuery": "ZeroFox_CTI_irc_CL" + }, + { + "metricName": "ZeroFox CTI Malware Logs", + "legend": "ZeroFox_CTI_malware_CL", + "baseQuery": "ZeroFox_CTI_malware_CL" + }, + { + "metricName": "ZeroFox CTI National Ids Logs", + "legend": "ZeroFox_CTI_national_ids_CL", + "baseQuery": "ZeroFox_CTI_national_ids_CL" + }, + { + "metricName": "ZeroFox CTI Phishing Logs", + "legend": "ZeroFox_CTI_phishing_CL", + "baseQuery": "ZeroFox_CTI_phishing_CL" + }, + { + "metricName": "ZeroFox CTI Phone Numbers Logs", + "legend": "ZeroFox_CTI_phone_numbers_CL", + "baseQuery": "ZeroFox_CTI_phone_numbers_CL" + }, + { + "metricName": "ZeroFox CTI Ransomware Logs", + "legend": "ZeroFox_CTI_ransomware_CL", + "baseQuery": "ZeroFox_CTI_ransomware_CL" + }, + { + "metricName": "ZeroFox CTI Telegram Logs", + "legend": "ZeroFox_CTI_telegram_CL", + "baseQuery": "ZeroFox_CTI_telegram_CL" + }, + { + "metricName": "ZeroFox CTI Threat Actors Logs", + "legend": "ZeroFox_CTI_threat_actors_CL", + "baseQuery": "ZeroFox_CTI_threat_actors_CL" + }, + { + "metricName": "ZeroFox CTI Vulnerabilities Logs", + "legend": "ZeroFox_CTI_vulnerabilities_CL", + "baseQuery": "ZeroFox_CTI_vulnerabilities_CL" + } + ], "sampleQueries": [ { - "description": "All Cisco Umbrella Logs", - "query": "Cisco_Umbrella\n| sort by TimeGenerated desc" + "description": "ZeroFox CTI C2-domains Logs", + "query": "ZeroFox_CTI_C2_CL\n | sort by TimeGenerated desc" + }, + { + "description": "ZeroFox CTI Email Addresses Logs", + "query": "ZeroFox_CTI_email_addresses_CL\n | sort by TimeGenerated desc" }, { - "description": "Cisco Umbrella DNS Logs", - "query": "Cisco_Umbrella\n | where EventType == 'dnslogs'\n| sort by TimeGenerated desc" + "description": "ZeroFox CTI Malware Logs", + "query": "ZeroFox_CTI_malware_CL\n | sort by TimeGenerated desc" } + ], "dataTypes": [ { - "name": "Cisco_Umbrella_dns_CL", - "lastDataReceivedQuery": "Cisco_Umbrella_dns_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + "name": "ZeroFox_CTI_advanced_dark_web_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_advanced_dark_web_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_botnet_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_botnet_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_breaches_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_breaches_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_C2_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_C2_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_compromised_credentials_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_compromised_credentials_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_credit_cards_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_credit_cards_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_dark_web_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_dark_web_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_discord_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_discord_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_disruption_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_disruption_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_email_addresses_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_email_addresses_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_exploits_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_exploits_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" }, { - "name": "Cisco_Umbrella_proxy_CL", - "lastDataReceivedQuery": "Cisco_Umbrella_proxy_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + "name": "ZeroFox_CTI_identity_breach_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_identity_breach_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" }, { - "name": "Cisco_Umbrella_ip_CL", - "lastDataReceivedQuery": "Cisco_Umbrella_ip_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + "name": "ZeroFox_CTI_irc_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_irc_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" }, { - "name": "Cisco_Umbrella_cloudfirewall_CL", - "lastDataReceivedQuery": "Cisco_Umbrella_cloudfirewall_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + "name": "ZeroFox_CTI_malware_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_malware_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_national_ids_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_national_ids_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_phishing_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_phishing_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_phone_numbers_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_phone_numbers_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_ransomware_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_ransomware_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_telegram_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_telegram_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_threat_actors_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_threat_actors_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_vulnerabilities_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_vulnerabilities_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" } + ], "connectivityCriterias": [ { "type": "IsConnectedQuery", "value": [ - "Cisco_Umbrella_dns_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", - "Cisco_Umbrella_proxy_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", - "Cisco_Umbrella_ip_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", - "Cisco_Umbrella_cloudfirewall_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)" + "ZeroFox_CTI_advanced_dark_web_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_botnet_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_breaches_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_C2_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_compromised_credentials_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_credit_cards_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_dark_web_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_discord_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_disruption_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_email_addresses_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_exploits_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_identity_breach_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_irc_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_malware_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_national_ids_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_phishing_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_phone_numbers_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_ransomware_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_telegram_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_threat_actors_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_vulnerabilities_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)" ] } ], @@ -89,35 +274,39 @@ "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." }, { - "name": "Amazon S3 REST API Credentials/permissions", - "description": "**AWS Access Key Id**, **AWS Secret Access Key**, **AWS S3 Bucket Name** are required for Amazon S3 REST API." + "name": "ZeroFox API Credentials/permissions", + "description": "**ZeroFox Username**, **ZeroFox Personal Access Token** are required for ZeroFox CTI REST API." } ] }, "instructionSteps": [ { "title": "", - "description": ">**NOTE:** This connector uses Azure Functions to connect to the Amazon S3 REST API to pull logs into Microsoft Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details." + "description": ">**NOTE:** This connector uses Azure Functions to connect to the ZeroFox CTI REST API to pull logs into Microsoft Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details." }, { "title": "", - "description": ">**NOTE:** This connector has been updated to support [cisco umbrella version 5 and version 6.](https://docs.umbrella.com/deployment-umbrella/docs/log-formats-and-versioning)" + "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." }, { "title": "", - "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + "description": "**STEP 1 - Retrieval of ZeroFox credentials:**\n\n Follow these instructions for set up logging and obtain credentials." + }, + { + "title": "", + "description": "**STEP 1.1 - [Log into ZeroFox's website.](https://cloud.zerofox.com/login) using your username and password" }, { "title": "", - "description": ">**NOTE:** This connector uses a parser based on a Kusto Function to normalize fields. [Follow these steps](https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Parsers/CiscoUmbrella/Cisco_Umbrella) to create the Kusto function alias **Cisco_Umbrella**." + "description": "**STEP 1.2 - Click into the Settings button and go to the Data Connectors Section." }, { "title": "", - "description": "**STEP 1 - Configuration of the Cisco Umbrella logs collection**\n\n[See documentation](https://docs.umbrella.com/deployment-umbrella/docs/log-management#section-logging-to-amazon-s-3) and follow the instructions for set up logging and obtain credentials." + "description": "**STEP 1.3 - Select the API DATA FEEDS tab and head to the bottom of the page, select <> in the API Information box, to obtain a Personal Access Token to be used along with your username." }, { "title": "", - "description": "**STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the Cisco Umbrella data connector, have the Workspace ID and Workspace Primary Key (can be copied from the following), as well as the Amazon S3 REST API Authorization credentials, readily available.", + "description": "**STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the ZeroFox CTI data connector, have the Workspace ID and Workspace Primary Key (can be copied from the following), as well as the Amazon S3 REST API Authorization credentials, readily available.", "instructions": [ { "parameters": { @@ -141,15 +330,15 @@ }, { "title": "Option 1 - Azure Resource Manager (ARM) Template", - "description": "Use this method for automated deployment of the Cisco Umbrella data connector using an ARM Tempate.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinelciscoumbrellaazuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **S3Bucket**, **AWSAccessKeyId**, **AWSSecretAccessKey**\n**Note:** For the S3Bucket use the value that Cisco referrs to as the _S3 Bucket Data Path_ and add a / (forward slash) to the end of the value\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy." + "description": "Use this method for automated deployment of the ZeroFox CTI data connectors using an ARM Tempate.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinelCiscoUmbrellaazuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **ZeroFox Username**, **ZeroFox Personal Access Token**\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy." }, { "title": "Option 2 - Manual Deployment of Azure Functions", - "description": "Use the following step-by-step instructions to deploy the Cisco Umbrella data connector manually with Azure Functions (Deployment via Visual Studio Code)." + "description": "Use the following step-by-step instructions to deploy the ZeroFox CTI data connector manually with Azure Functions (Deployment via Visual Studio Code)." }, { "title": "", - "description": "**1. Deploy a Function App**\n\n> **NOTE:** You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/create-first-function-vs-code-python) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-CiscoUmbrellaConn-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. UmbrellaXYZ).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft Sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + "description": "**1. Deploy a Function App**\n\n> **NOTE:** You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/create-first-function-vs-code-python) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-ZeroFoxCTIConn-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. CTIXYZ).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft Sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." }, { "title": "", diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/advanced_dark_web_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_compromised_credentials_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/botnet_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/botnet_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/botnet_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/botnet_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/botnet_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/breaches_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/breaches_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/breaches_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/breaches_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/breaches_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/c2_domains_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/c2_domains_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/c2_domains_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/c2_domains_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/c2_domains_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/compromised_credentials_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/compromised_credentials_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/compromised_credentials_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/compromised_credentials_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/compromised_credentials_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/connections/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/connections/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/sentinel.py b/Solutions/ZeroFox/Data Connectors/CTI/connections/sentinel.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/sentinel.py rename to Solutions/ZeroFox/Data Connectors/CTI/connections/sentinel.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py b/Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py similarity index 96% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py rename to Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py index 972d05d2531..fa57e5343ad 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/connectors/connections/zerofox.py +++ b/Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py @@ -12,8 +12,8 @@ def cti_request( self, method: str, url_suffix: str, - params: Dict = None, - data: Dict = None, + params=None, + data=None, error_handler=None, ): """ @@ -58,10 +58,10 @@ def _client_error_handler(self, res: requests.Response): # Try to parse json error response error_entry = res.json() err_msg += f"\n{json.dumps(error_entry)}" - raise Exception(err_msg, res=res) + raise Exception(err_msg) except ValueError: err_msg += f"\n{res.text}" - raise Exception(err_msg, res=res) + raise Exception(err_msg) def _http_request( self, @@ -148,7 +148,9 @@ def _http_request( err_msg = ( "Verify that the server URL parameter" " is correct and that you have access to the server from your host." - f"\nError Type: {err_type}\nError Number: [{exception.errno}]\nMessage: {exception.strerror}\n" + f"\nError Type: {err_type}\n" + "Error Number: [{exception.errno}]\n" + "Message: {exception.strerror}\n" ) raise Exception(err_msg, exception) except requests.exceptions.RetryError as exception: diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/credit_cards_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/dark_web_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/dark_web_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/dark_web_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/dark_web_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/dark_web_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/discord_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/discord_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/discord_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/discord_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/discord_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/disruption_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/disruption_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/disruption_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/disruption_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/disruption_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/email_addresses_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/email_addresses_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/email_addresses_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/email_addresses_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/email_addresses_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/exploits_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/exploits_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/exploits_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/exploits_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/exploits_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/identity_breach_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/identity_breach_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/identity_breach_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/identity_breach_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/identity_breach_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/irc_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/irc_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/irc_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/irc_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/irc_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/malware_connector/__init__.py similarity index 93% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/malware_connector/__init__.py index b7a8eb09478..1da817fc5c3 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/__init__.py +++ b/Solutions/ZeroFox/Data Connectors/CTI/malware_connector/__init__.py @@ -19,7 +19,8 @@ def main(mytimer: func.TimerRequest) -> None: # Update the customer ID to your Log Analytics workspace ID customer_id = os.environ.get("WorkspaceID") - # For the shared key, use either the primary or the secondary Connected Sources client authentication key + # For the shared key, use either the primary + # or the secondary Connected Sources client authentication key shared_key = os.environ.get("WorkspaceKey") query_from = mytimer.schedule_status["Last"] diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/malware_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/malware_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/malware_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/national_ids_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/national_ids_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/national_ids_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/national_ids_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/national_ids_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/phishing_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/phishing_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/phishing_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/phishing_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/phishing_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/phone_numbers_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/phone_numbers_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/phone_numbers_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/phone_numbers_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/phone_numbers_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/ransomware_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/ransomware_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/ransomware_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/ransomware_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/ransomware_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt b/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt index 5458bfc65c9..67bd1d64791 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt +++ b/Solutions/ZeroFox/Data Connectors/CTI/requirements.txt @@ -3,4 +3,6 @@ # Manually managing azure-functions-worker may cause unexpected issues azure-functions -requests \ No newline at end of file +pytest +requests +responses \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/telegram_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/telegram_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/telegram_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/telegram_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/telegram_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/tests/ZeroFox_test.py b/Solutions/ZeroFox/Data Connectors/CTI/tests/ZeroFox_test.py new file mode 100644 index 00000000000..e9fe24224f7 --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/tests/ZeroFox_test.py @@ -0,0 +1,57 @@ +from itertools import zip_longest + +import responses +from responses import matchers +from connections.zerofox import ZeroFoxClient + +USER = "user" +TOKEN = "token" +ENDPOINT = "dummy-endpoint/" +CTI_TOKEN = "cti_token" +SECOND_PAGE_URL = "https://second_page" +URL = "https://api.zerofox.com" + + +class TestZeroFoxCTI(): + @responses.activate + def test_cti_generator_is_provided(self): + zf_client = ZeroFoxClient(user=USER, token=TOKEN) + self.build_cti_responses() + + output = zf_client.cti_request(method="GET", url_suffix=ENDPOINT) + expected = (dict(index=f"r{i}") for i in range(4)) + all_match = all(a == b for a, b in zip_longest(output, expected)) + assert all_match + + def build_cti_responses(self): + """Prepare mock responses for queries through the requests package.""" + responses.post( + url=f"{URL}/auth/token/", + match=[matchers.urlencoded_params_matcher( + dict(username=USER, password=TOKEN)) + ], + json=dict(access=CTI_TOKEN), + ) + + cti_header = { + "Authorization": f"Bearer {CTI_TOKEN}", + "Content-Type": "application/json", + "Accept": "application/json", + } + + endpoint_first_page_json = dict( + next=SECOND_PAGE_URL, results=[ + dict(index=f"r{i}") for i in range(2)] + ) + responses.get( + url=f"{URL}/cti/{ENDPOINT}", headers=cti_header, + json=endpoint_first_page_json + ) + + endpoint_second_page_json = dict( + next=None, results=[dict(index=f"r{i}") for i in range(2, 4)] + ) + responses.get( + url=SECOND_PAGE_URL, headers=cti_header, + json=endpoint_second_page_json + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/threat_actors_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/threat_actors_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/threat_actors_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/threat_actors_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/threat_actors_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/vulnerabilities_connector/__init__.py similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/__init__.py rename to Solutions/ZeroFox/Data Connectors/CTI/vulnerabilities_connector/__init__.py diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/function.json b/Solutions/ZeroFox/Data Connectors/CTI/vulnerabilities_connector/function.json similarity index 100% rename from Solutions/ZeroFox/Data Connectors/CTI/connectors/vulnerabilities_connector/function.json rename to Solutions/ZeroFox/Data Connectors/CTI/vulnerabilities_connector/function.json diff --git a/Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg b/Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg deleted file mode 100644 index 0038c4381bd..00000000000 --- a/Solutions/ZeroFox/Data Connectors/Logo/foxy-mark.svg +++ /dev/null @@ -1 +0,0 @@ - foxy-mark \ No newline at end of file From d1889c77396fa0fa3a015da2b2b3c6a527a42b0e Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 28 Jul 2023 11:09:17 -0400 Subject: [PATCH 05/23] Add solution Metadata Add Data folder and solution metadata file as per task ZF-7126 --- Solutions/ZeroFox/Data/Solution_ZeroFox.json | 22 ++++++++++++++++++++ Solutions/ZeroFox/SolutionMetadata.json | 16 ++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 Solutions/ZeroFox/Data/Solution_ZeroFox.json create mode 100644 Solutions/ZeroFox/SolutionMetadata.json diff --git a/Solutions/ZeroFox/Data/Solution_ZeroFox.json b/Solutions/ZeroFox/Data/Solution_ZeroFox.json new file mode 100644 index 00000000000..855b5e98c06 --- /dev/null +++ b/Solutions/ZeroFox/Data/Solution_ZeroFox.json @@ -0,0 +1,22 @@ +{ + "Name": "ZeroFox", + "Author": "Microsoft - support@microsoft.com", + "Logo": "", + "Description": "The [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)", + "Data Connectors": [ + "Data Connectors/CTI/ZeroFox_API_FunctionApp.json", + "Data Connectors/Alerts/alerts_connector.json" + ], + "Analytic Rules": [ + "Analytic Rules/ZF_Alerts_HighSeverityRule.yaml", + "Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml", + "Analytic Rules/ZF_Alerts_LowSeverityRule.yaml", + "Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml" + ], + "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\ZeroFox", + "Version": "1.0.0", + "Metadata": "SolutionMetadata.json", + "TemplateSpec": true, + "Is1Pconnector": false + } + \ No newline at end of file diff --git a/Solutions/ZeroFox/SolutionMetadata.json b/Solutions/ZeroFox/SolutionMetadata.json new file mode 100644 index 00000000000..51193dd17d5 --- /dev/null +++ b/Solutions/ZeroFox/SolutionMetadata.json @@ -0,0 +1,16 @@ +{ + "publisherId": "azuresentinel", + "offerId": "azure-sentinel-solution-zerofox", + "firstPublishDate": "2023-07-28", + "providers": [ "ZeroFox" ], + "categories": { + "domains" : ["Security - Threat Protection","Security - Automation (SOAR)"], + "verticals": [] + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } +} \ No newline at end of file From 5ed4294cb0f39d1812a327530d9bc221cf03cecc Mon Sep 17 00:00:00 2001 From: Diego Ramirez R Date: Mon, 31 Jul 2023 18:03:20 -0400 Subject: [PATCH 06/23] Add Azure-Sentinel CI fix (#20) * Add fixes shown by Azure's CI pipeline * Add custom log schemas for CTI data connectors * Add custom table specifications for CTI tables Table description specifications added: - Identity breach - IRC - Malware - National IDs - Phishing - Phone Numbers - Ransomware - Telegram - Threat Actors - Vulnerabilities * Fix comments * Fix types on custom tables schemas --------- Co-authored-by: Diego Ramirez Co-authored-by: Felipe Garrido --- .../CustomTables/ZeroFox_CTI_C2_CL.json | 33 +++++++ .../ZeroFox_CTI_advanced_dark_web_CL.json | 78 ++++++++++++++++ .../CustomTables/ZeroFox_CTI_botnet_CL.json | 89 +++++++++++++++++++ .../CustomTables/ZeroFox_CTI_breaches_CL.json | 85 ++++++++++++++++++ ...eroFox_CTI_compromised_credentials_CL.json | 41 +++++++++ .../ZeroFox_CTI_credit_cards_CL.json | 45 ++++++++++ .../CustomTables/ZeroFox_CTI_dark_web_CL.json | 82 +++++++++++++++++ .../CustomTables/ZeroFox_CTI_discord_CL.json | 33 +++++++ .../ZeroFox_CTI_disruption_CL.json | 61 +++++++++++++ .../ZeroFox_CTI_email_addresses_CL.json | 25 ++++++ .../CustomTables/ZeroFox_CTI_exploits_CL.json | 25 ++++++ .../ZeroFox_CTI_identity_breach_CL.json | 65 ++++++++++++++ .../CustomTables/ZeroFox_CTI_irc_CL.json | 49 ++++++++++ .../CustomTables/ZeroFox_CTI_malware_CL.json | 41 +++++++++ .../ZeroFox_CTI_national_ids_CL.json | 37 ++++++++ .../CustomTables/ZeroFox_CTI_phishing_CL.json | 41 +++++++++ .../ZeroFox_CTI_phone_numbers_CL.json | 13 +++ .../ZeroFox_CTI_ransomware_CL.json | 49 ++++++++++ .../CustomTables/ZeroFox_CTI_telegram_CL.json | 33 +++++++ .../ZeroFox_CTI_threat_actors_CL.json | 53 +++++++++++ .../ZeroFox_CTI_vulnerabilities_CL.json | 49 ++++++++++ ...le.yml => ZF_Alerts_HighSeverityRule.yaml} | 0 ... ZF_Alerts_InformationalSeverityRule.yaml} | 0 ...ule.yml => ZF_Alerts_LowSeverityRule.yaml} | 0 ....yml => ZF_Alerts_MediumSeverityRule.yaml} | 0 .../CTI/ZeroFox_CTI_FunctionApp.json | 2 +- 26 files changed, 1028 insertions(+), 1 deletion(-) create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_C2_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_advanced_dark_web_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_breaches_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_compromised_credentials_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_credit_cards_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_dark_web_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_discord_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_disruption_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_exploits_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json rename Solutions/ZeroFox/Analytic Rules/{ZF_Alerts_HighSeverityRule.yml => ZF_Alerts_HighSeverityRule.yaml} (100%) rename Solutions/ZeroFox/Analytic Rules/{ZF_Alerts_InformationalSeverityRule.yml => ZF_Alerts_InformationalSeverityRule.yaml} (100%) rename Solutions/ZeroFox/Analytic Rules/{ZF_Alerts_LowSeverityRule.yml => ZF_Alerts_LowSeverityRule.yaml} (100%) rename Solutions/ZeroFox/Analytic Rules/{ZF_Alerts_MediumSeverityRule.yml => ZF_Alerts_MediumSeverityRule.yaml} (100%) diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_C2_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_C2_CL.json new file mode 100644 index 00000000000..3ce0888f464 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_C2_CL.json @@ -0,0 +1,33 @@ +{ + "Name": "ZeroFox_CTI_C2_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "domain_s", + "Type": "String" + }, + { + "Name": "port_d", + "Type": "Double" + }, + { + "Name": "tags_s", + "Type": "String" + }, + { + "Name": "ip_addresses_s", + "Type": "String" + }, + { + "Name": "updated_at_t", + "Type": "DateTime" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_advanced_dark_web_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_advanced_dark_web_CL.json new file mode 100644 index 00000000000..0e0195429a3 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_advanced_dark_web_CL.json @@ -0,0 +1,78 @@ +{ + "Name": "ZeroFox_CTI_advanced_dark_web_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "id_d", + "Type": "Double" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "title_s", + "Type": "String" + }, + { + "Name": "confidence_s", + "Type": "String" + }, + { + "Name": "reliability_s", + "Type": "String" + }, + { + "Name": "tlp", + "Type": "String" + }, + { + "Name": "contents_s", + "Type": "String" + }, + { + "Name": "comments_s", + "Type": "String" + }, + { + "Name": "threat_types_s", + "Type": "String" + }, + { + "Name": "target_targets_s", + "Type": "String" + }, + { + "Name": "target_regions_s", + "Type": "String" + }, + { + "Name": "target_industries_s", + "Type": "String" + }, + { + "Name": "languages_s", + "Type": "String" + }, + { + "Name": "actors_s", + "Type": "String" + }, + { + "Name": "tags_s", + "Type": "String" + } + , + { + "Name": "source_urls_s", + "Type": "String" + }, + { + "Name": "source_names_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json new file mode 100644 index 00000000000..d728bd65a13 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json @@ -0,0 +1,89 @@ +{ + "Name": "ZeroFox_CTI_C2_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "listed_at_t", + "Type": "DateTime" + }, + { + "Name": "bot_name_s", + "Type": "String" + }, + { + "Name": "c2_ip_address_s", + "Type": "String" + }, + { + "Name": "c2_domain_s", + "Type": "String" + }, + { + "Name": "is_common_domain_b", + "Type": "Boolean" + }, + { + "Name": "file_location_s", + "Type": "String" + }, + { + "Name": "operating_system_s", + "Type": "String" + }, + { + "Name": "anti_viruses_s", + "Type": "String" + }, + { + "Name": "country_code_s", + "Type": "String" + }, + { + "Name": "zip_code_s", + "Type": "String" + }, + { + "Name": "location_s", + "Type": "String" + }, + { + "Name": "current_language_s", + "Type": "String" + }, + { + "Name": "available_keyboards_s", + "Type": "String" + }, + { + "Name": "uac_s", + "Type": "String" + }, + { + "Name": "process_elevation_s", + "Type": "String" + }, + { + "Name": "acquired_at_t", + "Type": "DateTime" + }, + { + "Name": "logged_at_t", + "Type": "DateTime" + }, + { + "Name": "estimated_infected_at_t", + "Type": "DateTime" + }, + { + "Name": "breached_at", + "Type": "DateTime" + }, + { + "Name": "tags_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_breaches_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_breaches_CL.json new file mode 100644 index 00000000000..15af661b0c7 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_breaches_CL.json @@ -0,0 +1,85 @@ +{ + "Name": "ZeroFox_CTI_breaches_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "id_s", + "Type": "Double" + }, + { + "Name": "name_s", + "Type": "String" + }, + { + "Name": "description_s", + "Type": "String" + }, + { + "Name": "breach_date_t", + "Type": "DateTime" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "included_fields_s", + "Type": "String" + }, + { + "Name": "record_count_d", + "Type": "Double" + }, + { + "Name": "threat_type_s", + "Type": "String" + }, + { + "Name": "geography_region_code_s", + "Type": "String" + }, + { + "Name": "geography_sub_region_code_s", + "Type": "String" + }, + { + "Name": "geography_country_code_s", + "Type": "String" + }, + { + "Name": "geography_country_iso_alpha3_code_s", + "Type": "String" + }, + { + "Name": "geography_region_s", + "Type": "String" + }, + { + "Name": "geography_sub_region_s", + "Type": "String" + }, + { + "Name": "geography_country_s", + "Type": "String" + }, + { + "Name": "confidence_s", + "Type": "String" + }, + { + "Name": "reliability_s", + "Type": "String" + }, + { + "Name": "tlp_s", + "Type": "String" + }, + { + "Name": "industry_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_compromised_credentials_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_compromised_credentials_CL.json new file mode 100644 index 00000000000..40e45795106 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_compromised_credentials_CL.json @@ -0,0 +1,41 @@ +{ + "Name": "ZeroFox_CTI_compromised_credentials_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "domain_s", + "Type": "String" + }, + { + "Name": "email_s", + "Type": "String" + }, + { + "Name": "username_s", + "Type": "String" + }, + { + "Name": "password_s", + "Type": "String" + }, + { + "Name": "breach_name_s", + "Type": "String" + }, + { + "Name": "breach_id_s", + "Type": "String" + }, + { + "Name": "impacted_domain_s", + "Type": "String" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_credit_cards_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_credit_cards_CL.json new file mode 100644 index 00000000000..e2abb880e00 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_credit_cards_CL.json @@ -0,0 +1,45 @@ +{ + "Name": "ZeroFox_CTI_credit_cards_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "cc_num_s", + "Type": "String" + }, + { + "Name": "month_s", + "Type": "String" + }, + { + "Name": "year_s", + "Type": "String" + }, + { + "Name": "cvv_s", + "Type": "Double" + }, + { + "Name": "issuer_s", + "Type": "String" + }, + { + "Name": "source_s", + "Type": "String" + }, + { + "Name": "cc_bin_s", + "Type": "String" + }, + { + "Name": "breach_name_s", + "Type": "String" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_dark_web_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_dark_web_CL.json new file mode 100644 index 00000000000..553a9a49131 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_dark_web_CL.json @@ -0,0 +1,82 @@ +{ + "Name": "ZeroFox_CTI_dark_web_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "content_audience_s", + "Type": "String" + }, + { + "Name": "forum_name_s", + "Type": "String" + }, + { + "Name": "forum_uuid_g", + "Type": "GUID" + }, + { + "Name": "general_topic_s", + "Type": "String" + }, + { + "Name": "language_code_s", + "Type": "String" + }, + { + "Name": "network_type_s", + "Type": "String" + }, + { + "Name": "parent_uuid_g", + "Type": "GUID" + }, + { + "Name": "post_body_s", + "Type": "String" + }, + { + "Name": "timestamp_t", + "Type": "DateTime" + }, + { + "Name": "post_member_name_s", + "Type": "String" + }, + { + "Name": "post_type_s", + "Type": "String" + }, + { + "Name": "post_uuid_g", + "Type": "GUID" + }, + { + "Name": "sequence_number_d", + "Type": "Double" + }, + { + "Name": "thread_name_s", + "Type": "String" + }, + { + "Name": "thread_url_s", + "Type": "String" + }, + { + "Name": "thread_uuid_g", + "Type": "GUID" + }, + { + "Name": "domain_s", + "Type": "String" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + } + + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_discord_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_discord_CL.json new file mode 100644 index 00000000000..e79a1a0ea0c --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_discord_CL.json @@ -0,0 +1,33 @@ +{ + "Name": "ZeroFox_CTI_discord_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "author_id_s", + "Type": "String" + }, + { + "Name": "author_username_s", + "Type": "String" + }, + { + "Name": "channel_name_s", + "Type": "String" + }, + { + "Name": "content_s", + "Type": "String" + }, + { + "Name": "server_name_s", + "Type": "String" + }, + { + "Name": "timestamp_t", + "Type": "DateTime" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_disruption_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_disruption_CL.json new file mode 100644 index 00000000000..0fadbf26ca6 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_disruption_CL.json @@ -0,0 +1,61 @@ +{ + "Name": "ZeroFox_CTI_disruption_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "url_s", + "Type": "String" + }, + { + "Name": "fqdn_s", + "Type": "String" + }, + { + "Name": "ip_s", + "Type": "String" + }, + { + "Name": "host_s", + "Type": "String" + }, + { + "Name": "registrar_s", + "Type": "String" + }, + { + "Name": "threat_type_s", + "Type": "String" + }, + { + "Name": "http_status_d", + "Type": "Double" + }, + { + "Name": "asn_d", + "Type": "Double" + }, + { + "Name": "iana_d", + "Type": "Double" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "updated_at_t", + "Type": "DateTime" + }, + { + "Name": "category_s", + "Type": "String" + }, + { + "Name": "network_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json new file mode 100644 index 00000000000..e39da3c6a17 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json @@ -0,0 +1,25 @@ +{ + "Name": "ZeroFox_CTI_C2_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "email_s", + "Type": "String" + }, + { + "Name": "domain_s", + "Type": "String" + }, + { + "Name": "tags_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_exploits_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_exploits_CL.json new file mode 100644 index 00000000000..1d0ab2af1af --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_exploits_CL.json @@ -0,0 +1,25 @@ +{ + "Name": "ZeroFox_CTI_exploits_CL", + "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "cve_s", + "Type": "String" + }, + { + "Name": "urls_s", + "Type": "String" + }, + { + "Name": "exploit_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json new file mode 100644 index 00000000000..e955ed98e19 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json @@ -0,0 +1,65 @@ +{ + "Name": "ZeroFox_CTI_identity_breach_CL", + "Properties": [ + { + "Name": "cac_username_hash_s", + "Type": "String" + }, + { + "Name": "cac_email_hash_s", + "Type": "String" + }, + { + "Name": "cac_password_hash_s", + "Type": "String" + }, + { + "Name": "ncid_id_hash_s", + "Type": "String" + }, + { + "Name": "cc_num_hash_s", + "Type": "String" + }, + { + "Name": "passport_id_hash_s", + "Type": "String" + }, + { + "Name": "bank_account_id_hash_s", + "Type": "String" + }, + { + "Name": "medical_account_id_hash_s", + "Type": "String" + }, + { + "Name": "phone_number_e164_hash_s", + "Type": "String" + }, + { + "Name": "dl_id_hash_s", + "Type": "String" + }, + { + "Name": "ui_discovered_ts_s", + "Type": "String" + }, + { + "Name": "ui_insertion_ts_s", + "Type": "String" + }, + { + "Name": "ui_breach_id_d", + "Type": "Double" + }, + { + "Name": "ui_breach_name_s", + "Type": "String" + }, + { + "Name": "ui_breach_description_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json new file mode 100644 index 00000000000..8823502317a --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json @@ -0,0 +1,49 @@ +{ + "Name": "ZeroFox_CTI_irc_CL", + "Properties": [ + { + "Name": "channel_s", + "Type": "String" + }, + { + "Name": "message_s", + "Type": "String" + }, + { + "Name": "sender_s", + "Type": "String" + }, + { + "Name": "timestamp_t", + "Type": "DateTime" + }, + { + "Name": "username_s", + "Type": "String" + }, + { + "Name": "hostname_s", + "Type": "String" + }, + { + "Name": "real_name_s", + "Type": "String" + }, + { + "Name": "server_s", + "Type": "String" + }, + { + "Name": "server_info_s", + "Type": "String" + }, + { + "Name": "secure_b", + "Type": "Boolean" + }, + { + "Name": "account_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json new file mode 100644 index 00000000000..c9d27d882d4 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json @@ -0,0 +1,41 @@ +{ + "Name": "ZeroFox_CTI_malware_CL", + "Properties": [ + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "family_s", + "Type": "String" + }, + { + "Name": "md5_s", + "Type": "String" + }, + { + "Name": "sha1_s", + "Type": "String" + }, + { + "Name": "sha256_s", + "Type": "String" + }, + { + "Name": "sha512_s", + "Type": "String" + }, + { + "Name": "tags_s", + "Type": "String" + }, + { + "Name": "botnet_s", + "Type": "String" + }, + { + "Name": "c2_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json new file mode 100644 index 00000000000..55c2ebc7ae9 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json @@ -0,0 +1,37 @@ +{ + "Name": "ZeroFox_CTI_national_ids_CL", + "Properties": [ + { + "Name": "national_identifier_s", + "Type": "String" + }, + { + "Name": "country_s", + "Type": "String" + }, + { + "Name": "first_name_s", + "Type": "String" + }, + { + "Name": "last_name_s", + "Type": "String" + }, + { + "Name": "person_name_s", + "Type": "String" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "source_s", + "Type": "String" + }, + { + "Name": "breach_name_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json new file mode 100644 index 00000000000..fa9ccf1effc --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json @@ -0,0 +1,41 @@ +{ + "Name": "ZeroFox_CTI_phishing_CL", + "Properties": [ + { + "Name": "scanned_t", + "Type": "DateTime" + }, + { + "Name": "domain_s", + "Type": "String" + }, + { + "Name": "url_s", + "Type": "String" + }, + { + "Name": "cert_authority_s", + "Type": "String" + }, + { + "Name": "cert_fingerprint_s", + "Type": "String" + }, + { + "Name": "cert_issued_s", + "Type": "String" + }, + { + "Name": "host_ip_s", + "Type": "String" + }, + { + "Name": "host_asn_d", + "Type": "Double" + }, + { + "Name": "host_geo_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json new file mode 100644 index 00000000000..e05e6322fb6 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json @@ -0,0 +1,13 @@ +{ + "Name": "ZeroFox_CTI_phone_numbers_CL", + "Properties": [ + { + "Name": "phone_number_s", + "Type": "String" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json new file mode 100644 index 00000000000..49d1fc72f7f --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json @@ -0,0 +1,49 @@ +{ + "Name": "ZeroFox_CTI_ransomware_CL", + "Properties": [ + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "md5_s", + "Type": "String" + }, + { + "Name": "sha1_s", + "Type": "String" + }, + { + "Name": "sha256_s", + "Type": "String" + }, + { + "Name": "sha512_s", + "Type": "String" + }, + { + "Name": "emails_s", + "Type": "String" + }, + { + "Name": "ransom_note_s", + "Type": "String" + }, + { + "Name": "note_urls_s", + "Type": "String" + }, + { + "Name": "crypto_wallets_s", + "Type": "String" + }, + { + "Name": "ransomware_name_s", + "Type": "String" + }, + { + "Name": "tags_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json new file mode 100644 index 00000000000..69471d98dfd --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json @@ -0,0 +1,33 @@ +{ + "Name": "ZeroFox_CTI_telegram_CL", + "Properties": [ + { + "Name": "channel_name_s", + "Type": "String" + }, + { + "Name": "timestamp_t", + "Type": "DateTime" + }, + { + "Name": "first_name_s", + "Type": "String" + }, + { + "Name": "last_name_s", + "Type": "String" + }, + { + "Name": "message_s", + "Type": "String" + }, + { + "Name": "user_s", + "Type": "String" + }, + { + "Name": "message_url_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json new file mode 100644 index 00000000000..ec4dcbbc19c --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json @@ -0,0 +1,53 @@ +{ + "Name": "ZeroFox_CTI_threat_actors_CL", + "Properties": [ + { + "Name": "id_d", + "Type": "Double" + }, + { + "Name": "mitre_id_s", + "Type": "String" + }, + { + "Name": "name_s", + "Type": "String" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "updated_at_t", + "Type": "DateTime" + }, + { + "Name": "description_s", + "Type": "String" + }, + { + "Name": "references_s", + "Type": "String" + }, + { + "Name": "software_s", + "Type": "String" + }, + { + "Name": "associated_groups_s", + "Type": "String" + }, + { + "Name": "target_geo_s", + "Type": "String" + }, + { + "Name": "target_industries_s", + "Type": "String" + }, + { + "Name": "mitre_ttps_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json new file mode 100644 index 00000000000..fb18333ee72 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json @@ -0,0 +1,49 @@ +{ + "Name": "ZeroFox_CTI_vulnerabilities_CL", + "Properties": [ + { + "Name": "base_score_d", + "Type": "Double" + }, + { + "Name": "description_s", + "Type": "String" + }, + { + "Name": "exploitability_score_d", + "Type": "Double" + }, + { + "Name": "impact_score_d", + "Type": "Double" + }, + { + "Name": "created_at_t", + "Type": "DateTime" + }, + { + "Name": "updated_at_t", + "Type": "DateTime" + }, + { + "Name": "vector_string_s", + "Type": "String" + }, + { + "Name": "cve_s", + "Type": "String" + }, + { + "Name": "summary_s", + "Type": "String" + }, + { + "Name": "remediation_s", + "Type": "String" + }, + { + "Name": "products_s", + "Type": "String" + } + ] +} diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml similarity index 100% rename from Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yml rename to Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml similarity index 100% rename from Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yml rename to Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml similarity index 100% rename from Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yml rename to Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml similarity index 100% rename from Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yml rename to Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml diff --git a/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json b/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json index ce58a3d7287..5adb812dd36 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json +++ b/Solutions/ZeroFox/Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json @@ -2,7 +2,7 @@ "id": "ZeroFoxCTIDataConnector", "title": "ZeroFox CTI", "publisher": "ZeroFox", - "descriptionMarkdown": "The ZeroFox CTI data connectors provide the capability to ingest the different [ZeroFox](https://www.zerofox.com/threat-intelligence/) cyber threat intelligence alerts into Azure Sentinel.", + "descriptionMarkdown": "The ZeroFox CTI data connectors provide the capability to ingest the different [ZeroFox](https://www.zerofox.com/threat-intelligence/) cyber threat intelligence alerts into Microsoft Sentinel.", "graphQueries": [ { "metricName": "ZeroFox CTI Advanced Dark Web Logs", From 45902b1c9473b018ac3be7f46899b541514cba65 Mon Sep 17 00:00:00 2001 From: Felipe Garrido Date: Tue, 1 Aug 2023 11:01:38 -0400 Subject: [PATCH 07/23] Add unique ids to analytic rules --- Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml | 1 + .../Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml | 1 + Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml | 1 + .../ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml index b46cc65b28a..035ed6ecc43 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml @@ -1,3 +1,4 @@ +id: deb45e6d-892f-40bf-9118-e2a6f26b788d name: ZeroFox Alerts - High Severity Alerts description: | 'Detects high severity alerts from ZeroFox' diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml index 990c15d264d..f4b48872d53 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml @@ -1,3 +1,4 @@ +id: 6f7a7413-b72f-4361-84ee-897baeb9c6d4 name: ZeroFox Alerts - Informational Severity Alerts description: | 'Detects informational severity alerts from ZeroFox' diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml index c23b6e993de..eba3d6f6f64 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml @@ -1,3 +1,4 @@ +id: e0c7a91a-7aa1-498a-9c20-cd6c721f9345 name: ZeroFox Alerts - Low Severity Alerts description: | 'Detects low severity alerts from ZeroFox' diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml index 5e023ac0d4a..c16ea5de071 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml @@ -1,3 +1,4 @@ +id: a6496de5-911b-4199-b7db-d34ac9d70df3 name: ZeroFox Alerts - Medium Severity Alerts description: | 'Detects medium severity alerts from ZeroFox' From 97ba40d9f5c7bceb6c5da41e2b9c0f51f7cf5f40 Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Tue, 1 Aug 2023 11:21:19 -0400 Subject: [PATCH 08/23] Fix code issues in CTI connectors --- .../advanced_dark_web_connector/__init__.py | 26 +++++++++---------- .../__init__.py | 24 +++++++++-------- .../CTI/connections/zerofox.py | 2 +- .../CTI/credit_cards_connector/__init__.py | 5 ++-- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/__init__.py index f536db5c037..986c2342652 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/__init__.py +++ b/Solutions/ZeroFox/Data Connectors/CTI/advanced_dark_web_connector/__init__.py @@ -10,16 +10,14 @@ def main(mytimer: func.TimerRequest) -> None: utc_timestamp = ( - datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) + .isoformat() ) if mytimer.past_due: logging.info("The timer is past due!") - # Update the customer ID to your Log Analytics workspace ID customer_id = os.environ.get("WorkspaceID") - - # For the shared key, use either the primary or the secondary Connected Sources client authentication key shared_key = os.environ.get("WorkspaceKey") query_from = mytimer.schedule_status["Last"] @@ -27,7 +25,8 @@ def main(mytimer: func.TimerRequest) -> None: zf_client = get_zf_client() - results = get_cti_advanced_dark_web(zf_client, created_after = query_from, created_before = query_to) + results = get_cti_advanced_dark_web( + zf_client, created_after=query_from, created_before=query_to) logging.debug("Trigger function retrieved results") @@ -50,12 +49,11 @@ def get_zf_client(): return ZeroFoxClient(user, token) -def get_cti_advanced_dark_web(client: ZeroFoxClient, created_from, created_after): - url_suffix = "advanced-dark-web/" - params = dict(created_after=created_after, created_before=created_from) - return client.cti_request( - "GET", - url_suffix, - params=params, - ) - +def get_cti_advanced_dark_web(client: ZeroFoxClient, created_before, created_after): + url_suffix = "advanced-dark-web/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/__init__.py index ea76e6b81c7..0e95735d9ca 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/__init__.py +++ b/Solutions/ZeroFox/Data Connectors/CTI/botnet_compromised_credentials_connector/__init__.py @@ -10,7 +10,8 @@ def main(mytimer: func.TimerRequest) -> None: utc_timestamp = ( - datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) + .isoformat() ) if mytimer.past_due: @@ -27,7 +28,8 @@ def main(mytimer: func.TimerRequest) -> None: zf_client = get_zf_client() - results = get_cti_botnet_compromised_credentials(zf_client, created_after = query_from, created__before = query_to) + results = get_cti_botnet_compromised_credentials( + zf_client, created_after=query_from, created_before=query_to) logging.debug("Trigger function retrieved results") @@ -50,12 +52,12 @@ def get_zf_client(): return ZeroFoxClient(user, token) -def get_cti_botnet_compromised_credentials(client: ZeroFoxClient, created_from, created_after): - url_suffix = "botnet-compromised-credentials/" - params = dict(created_after=created_after, created_before=created_from) - return client.cti_request( - "GET", - url_suffix, - params=params, - ) - +def get_cti_botnet_compromised_credentials(client: ZeroFoxClient, + created_before, created_after): + url_suffix = "botnet-compromised-credentials/" + params = dict(created_after=created_after, created_before=created_before) + return client.cti_request( + "GET", + url_suffix, + params=params, + ) diff --git a/Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py b/Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py index fa57e5343ad..fa2a25a6092 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py +++ b/Solutions/ZeroFox/Data Connectors/CTI/connections/zerofox.py @@ -44,7 +44,7 @@ def cti_request( for result in response["results"]: yield result while response["next"]: - response = response = self._http_request( + response = self._http_request( method="GET", headers=headers, full_address=response["next"], diff --git a/Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/__init__.py b/Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/__init__.py index 5e83358fcb6..7967877600c 100644 --- a/Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/__init__.py +++ b/Solutions/ZeroFox/Data Connectors/CTI/credit_cards_connector/__init__.py @@ -10,7 +10,8 @@ def main(mytimer: func.TimerRequest) -> None: utc_timestamp = ( - datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).isoformat() + datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc) + .isoformat() ) if mytimer.past_due: @@ -28,7 +29,7 @@ def main(mytimer: func.TimerRequest) -> None: zf_client = get_zf_client() results = get_cti_credit_cards( - zf_client, created_After=query_from, created_before=query_to + zf_client, created_after=query_from, created_before=query_to ) logging.debug("Trigger function retrieved results") From 29bd2ae8526eda3f385d1e4f6eb85bf54a949aba Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Tue, 1 Aug 2023 12:44:43 -0400 Subject: [PATCH 09/23] Fix ZeroFox's logo formattin --- Logos/foxy-mark.svg | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Logos/foxy-mark.svg b/Logos/foxy-mark.svg index 0038c4381bd..b6e3319b75b 100644 --- a/Logos/foxy-mark.svg +++ b/Logos/foxy-mark.svg @@ -1 +1,12 @@ - foxy-mark \ No newline at end of file + + + foxy-mark + + + + + + + + \ No newline at end of file From ffe6939ac0d587879e9b5691f1c8828abf6864f7 Mon Sep 17 00:00:00 2001 From: Felipe Garrido Date: Tue, 1 Aug 2023 13:50:14 -0400 Subject: [PATCH 10/23] Add connector id and alert table schema --- .../CustomTables/ZeroFoxAlertPoller_CL.json | 201 ++++++++++++++++++ .../CustomTables/ZeroFox_CTI_botnet_CL.json | 2 +- .../ValidConnectorIds.json | 5 +- 3 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 .script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json new file mode 100644 index 00000000000..8b79b95f654 --- /dev/null +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json @@ -0,0 +1,201 @@ +{ + "_Name": "ZeroFoxAlertPoller_CL", + "Properties": [ + { + "Name": "alert_type_s", + "Type": "String" + }, + { + "Name": "logs_s", + "Type": "String" + }, + { + "Name": "offending_content_url_s", + "Type": "String" + }, + { + "Name": "asset_term_s", + "Type": "String" + }, + { + "Name": "assignee_s", + "Type": "String" + }, + { + "Name": "entity_id_d", + "Type": "Double" + }, + { + "Name": "entity_name_s", + "Type": "String" + }, + { + "Name": "entity_image_s", + "Type": "String" + }, + { + "Name": "entity_labels_s", + "Type": "String" + }, + { + "Name": "entity_entity_group_id_d", + "Type": "Double" + }, + { + "Name": "entity_entity_group_name_s", + "Type": "String" + }, + { + "Name": "entity_term_s", + "Type": "String" + }, + { + "Name": "content_created_at_t", + "Type": "DateTime" + }, + { + "Name": "id_d", + "Type": "Double" + }, + { + "Name": "Severity", + "Type": "Double" + }, + { + "Name": "perpetrator_name_s", + "Type": "String" + }, + { + "Name": "perpetrator_display_name_s", + "Type": "String" + }, + { + "Name": "perpetrator_id_d", + "Type": "Double" + }, + { + "Name": "perpetrator_url_s", + "Type": "String" + }, + { + "Name": "perpetrator_content_s", + "Type": "String" + }, + { + "Name": "perpetrator_type_s", + "Type": "String" + }, + { + "Name": "perpetrator_timestamp_t", + "Type": "DateTime" + }, + { + "Name": "perpetrator_network_s", + "Type": "String" + }, + { + "Name": "rule_group_id_d", + "Type": "Double" + }, + { + "Name": "asset_id_d", + "Type": "Double" + }, + { + "Name": "asset_name_s", + "Type": "String" + }, + { + "Name": "asset_image_s", + "Type": "String" + }, + { + "Name": "asset_labels_s", + "Type": "String" + }, + { + "Name": "asset_entity_group_id_d", + "Type": "Double" + }, + { + "Name": "asset_entity_group_name_s", + "Type": "String" + }, + { + "Name": "entered_by_s", + "Type": "String" + }, + { + "Name": "metadata_s", + "Type": "String" + }, + { + "Name": "status_s", + "Type": "String" + }, + { + "Name": "timestamp_t", + "Type": "DateTime" + }, + { + "Name": "rule_name_s", + "Type": "String" + }, + { + "Name": "last_modified_t", + "Type": "DateTime" + }, + { + "Name": "protected_locations_s", + "Type": "String" + }, + { + "Name": "darkweb_term_s", + "Type": "String" + }, + { + "Name": "business_network_s", + "Type": "String" + }, + { + "Name": "reviewed_b", + "Type": "Boolean" + }, + { + "Name": "escalated_b", + "Type": "Boolean" + }, + { + "Name": "network_s", + "Type": "String" + }, + { + "Name": "protected_social_object_s", + "Type": "String" + }, + { + "Name": "notes_s", + "Type": "String" + }, + { + "Name": "reviews_s", + "Type": "String" + }, + { + "Name": "rule_id_d", + "Type": "Double" + }, + { + "Name": "entity_account_s", + "Type": "String" + }, + { + "Name": "entity_email_receiver_id_s", + "Type": "String" + }, + { + "Name": "tags_s", + "Type": "String" + } + ] +} diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json index d728bd65a13..f6e37a670be 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_botnet_CL.json @@ -1,5 +1,5 @@ { - "Name": "ZeroFox_CTI_C2_CL", + "Name": "ZeroFox_CTI_botnet_CL", "Properties": [ { "Name": "TimeGenerated", diff --git a/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json b/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json index 4c8b67a6968..f5dc4521e3c 100644 --- a/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json +++ b/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json @@ -189,5 +189,6 @@ "DynatraceAttacks", "DynatraceAuditLogs", "DynatraceProblems", - "MicrosoftDefenderThreatIntelligence" -] \ No newline at end of file + "MicrosoftDefenderThreatIntelligence", + "ZeroFox_Alert_Polling" +] From 5793b05f6058009f92e2e08dc806cc11a139a0ad Mon Sep 17 00:00:00 2001 From: Felipe Garrido Date: Tue, 1 Aug 2023 15:44:29 -0400 Subject: [PATCH 11/23] Fix typo in alerts schema --- .../CustomTables/ZeroFoxAlertPoller_CL.json | 2 +- Logos/foxy-mark.svg | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json index 8b79b95f654..1f4a908b3b6 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json @@ -1,5 +1,5 @@ { - "_Name": "ZeroFoxAlertPoller_CL", + "Name": "ZeroFoxAlertPoller_CL", "Properties": [ { "Name": "alert_type_s", diff --git a/Logos/foxy-mark.svg b/Logos/foxy-mark.svg index b6e3319b75b..625541fa8b1 100644 --- a/Logos/foxy-mark.svg +++ b/Logos/foxy-mark.svg @@ -1,7 +1,6 @@ - foxy-mark @@ -9,4 +8,4 @@ - \ No newline at end of file + From 97929651cc017f010ca511eabf15d06f4e47a33c Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Wed, 2 Aug 2023 14:17:51 -0400 Subject: [PATCH 12/23] Fix custom table scheme duplicate --- .../CustomTables/ZeroFox_CTI_email_addresses_CL.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json index e39da3c6a17..f5e171df153 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json @@ -1,5 +1,5 @@ { - "Name": "ZeroFox_CTI_C2_CL", + "Name": "ZeroFox_email_addresses_CL", "Properties": [ { "Name": "TimeGenerated", From 4ff313783440a88a3d25bdabea3cb8b4475122bc Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Wed, 2 Aug 2023 15:34:03 -0400 Subject: [PATCH 13/23] Fix typo related to email addresses connector --- .../CustomTables/ZeroFox_CTI_email_addresses_CL.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json index f5e171df153..b9b6d1cc173 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_email_addresses_CL.json @@ -1,5 +1,5 @@ { - "Name": "ZeroFox_email_addresses_CL", + "Name": "ZeroFox_CTI_email_addresses_CL", "Properties": [ { "Name": "TimeGenerated", From e16ebd05230aa24c85e2fe9e7ee4bc8465a954af Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 4 Aug 2023 15:07:20 -0400 Subject: [PATCH 14/23] Add release files Add release notes, versioning files in Package folder, as well as template fails from ARM toolkit --- .../azuredeploy_ZeroFox_CTI_FunctionApp.json | 197 ++++++++++++++++++ .../ZeroFox/Data Connectors/CTI/proxies.json | 4 + Solutions/ZeroFox/Package/3.0.0.zip | Bin 0 -> 3506 bytes .../ZeroFox/Package/createUiDefinition.json | 89 ++++++++ Solutions/ZeroFox/Package/mainTemplate.json | 91 ++++++++ Solutions/ZeroFox/ReleaseNotes.md | 3 + 6 files changed, 384 insertions(+) create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/azuredeploy_ZeroFox_CTI_FunctionApp.json create mode 100644 Solutions/ZeroFox/Data Connectors/CTI/proxies.json create mode 100644 Solutions/ZeroFox/Package/3.0.0.zip create mode 100644 Solutions/ZeroFox/Package/createUiDefinition.json create mode 100644 Solutions/ZeroFox/Package/mainTemplate.json create mode 100644 Solutions/ZeroFox/ReleaseNotes.md diff --git a/Solutions/ZeroFox/Data Connectors/CTI/azuredeploy_ZeroFox_CTI_FunctionApp.json b/Solutions/ZeroFox/Data Connectors/CTI/azuredeploy_ZeroFox_CTI_FunctionApp.json new file mode 100644 index 00000000000..adceb17bffd --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/azuredeploy_ZeroFox_CTI_FunctionApp.json @@ -0,0 +1,197 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "FunctionName": { + "defaultValue": "ZeroFoxCTI", + "minLength": 1, + "type": "string" + }, + "ZeroFoxToken": { + "type": "securestring", + "defaultValue": "" + }, + "ZeroFoxUsername": { + "type": "string", + "defaultValue": "" + }, + "WorkspaceID": { + "type": "string", + "defaultValue": "" + }, + "WorkspaceKey": { + "type": "securestring", + "defaultValue": "" + } + }, + "variables": { + "FunctionName": "[concat(toLower(parameters('FunctionName')), uniqueString(resourceGroup().id))]", + "StorageAccountName":"[substring(tolower(variables('FunctionName')), 0, 22)]", + "StorageSuffix": "[environment().suffixes.storage]", + "LogAnaltyicsUri": "[replace(environment().portal, 'https://portal', concat('https://', toLower(parameters('WorkspaceID')), '.ods.opinsights'))]" + }, + "resources": [ + { + "type": "Microsoft.Insights/components", + "apiVersion": "2015-05-01", + "name": "[variables('FunctionName')]", + "location": "[resourceGroup().location]", + "kind": "web", + "properties": { + "Application_Type": "web", + "ApplicationId": "[variables('FunctionName')]" + } + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2019-06-01", + "name": "[variables('StorageAccountName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "kind": "StorageV2", + "properties": { + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [], + "ipRules": [], + "defaultAction": "Allow" + }, + "supportsHttpsTrafficOnly": true, + "encryption": { + "services": { + "file": { + "keyType": "Account", + "enabled": true + }, + "blob": { + "keyType": "Account", + "enabled": true + } + }, + "keySource": "Microsoft.Storage" + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2019-06-01", + "name": "[concat(variables('StorageAccountName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + }, + "deleteRetentionPolicy": { + "enabled": false + } + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices", + "apiVersion": "2019-06-01", + "name": "[concat(variables('StorageAccountName'), '/default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" + ], + "sku": { + "name": "Standard_LRS", + "tier": "Standard" + }, + "properties": { + "cors": { + "corsRules": [] + } + } + }, + { + "type": "Microsoft.Web/sites", + "apiVersion": "2018-11-01", + "name": "[variables('FunctionName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]", + "[resourceId('Microsoft.Insights/components', variables('FunctionName'))]" + ], + "kind": "functionapp,linux", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "name": "[variables('FunctionName')]", + "httpsOnly": true, + "clientAffinityEnabled": true, + "alwaysOn": true, + "reserved": true, + "siteConfig": { + "linuxFxVersion": "python|3.8" + } + }, + "resources": [ + { + "apiVersion": "2018-11-01", + "type": "config", + "name": "appsettings", + "dependsOn": [ + "[concat('Microsoft.Web/sites/', variables('FunctionName'))]" + ], + "properties": { + "FUNCTIONS_EXTENSION_VERSION": "~4", + "FUNCTIONS_WORKER_RUNTIME": "python", + "APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.insights/components', variables('FunctionName')), '2015-05-01').InstrumentationKey]", + "APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('microsoft.insights/components', variables('FunctionName')), '2015-05-01').ConnectionString]", + "AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('StorageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName')), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]", + "logAnalyticsUri": "[variables('LogAnaltyicsUri')]", + "WorkspaceID": "[parameters('WorkspaceID')]", + "WorkspaceKey": "[parameters('WorkspaceKey')]", + "WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/sentinel-ZeroFoxCTI-functionapp" + } + } + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(variables('StorageAccountName'), '/default/azure-webjobs-hosts')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices/containers", + "apiVersion": "2019-06-01", + "name": "[concat(variables('StorageAccountName'), '/default/azure-webjobs-secrets')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" + ], + "properties": { + "publicAccess": "None" + } + }, + { + "type": "Microsoft.Storage/storageAccounts/fileServices/shares", + "apiVersion": "2019-06-01", + "name": "[concat(variables('StorageAccountName'), '/default/', variables('StorageAccountName'))]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('StorageAccountName'), 'default')]", + "[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]" + ], + "properties": { + "shareQuota": 5120 + } + } + ] +} \ No newline at end of file diff --git a/Solutions/ZeroFox/Data Connectors/CTI/proxies.json b/Solutions/ZeroFox/Data Connectors/CTI/proxies.json new file mode 100644 index 00000000000..a1695a9b97a --- /dev/null +++ b/Solutions/ZeroFox/Data Connectors/CTI/proxies.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json.schemastore.org/proxies", + "proxies": {} + } \ No newline at end of file diff --git a/Solutions/ZeroFox/Package/3.0.0.zip b/Solutions/ZeroFox/Package/3.0.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..217e82d9ef088bf709aa493e733de9289a1a8eac GIT binary patch literal 3506 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 literal 0 HcmV?d00001 diff --git a/Solutions/ZeroFox/Package/createUiDefinition.json b/Solutions/ZeroFox/Package/createUiDefinition.json new file mode 100644 index 00000000000..9d038d83891 --- /dev/null +++ b/Solutions/ZeroFox/Package/createUiDefinition.json @@ -0,0 +1,89 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#", + "handler": "Microsoft.Azure.CreateUIDef", + "version": "0.1.2-preview", + "parameters": { + "config": { + "isWizard": false, + "basics": { + "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "subscription": { + "resourceProviders": [ + "Microsoft.OperationsManagement/solutions", + "Microsoft.OperationalInsights/workspaces/providers/alertRules", + "Microsoft.Insights/workbooks", + "Microsoft.Logic/workflows" + ] + }, + "location": { + "metadata": { + "hidden": "Hiding location, we get it from the log analytics workspace" + }, + "visible": false + }, + "resourceGroup": { + "allowExisting": true + } + } + }, + "basics": [ + { + "name": "getLAWorkspace", + "type": "Microsoft.Solutions.ArmApiControl", + "toolTip": "This filters by workspaces that exist in the Resource Group selected", + "condition": "[greater(length(resourceGroup().name),0)]", + "request": { + "method": "GET", + "path": "[concat(subscription().id,'/providers/Microsoft.OperationalInsights/workspaces?api-version=2020-08-01')]" + } + }, + { + "name": "workspace", + "type": "Microsoft.Common.DropDown", + "label": "Workspace", + "placeholder": "Select a workspace", + "toolTip": "This dropdown will list only workspace that exists in the Resource Group selected", + "constraints": { + "allowedValues": "[map(filter(basics('getLAWorkspace').value, (filter) => contains(toLower(filter.id), toLower(resourceGroup().name))), (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.name, '\"}')))]", + "required": true + }, + "visible": true + } + ], + "steps": [ + { + "name": "analytics", + "label": "Analytics", + "subLabel": { + "preValidation": "Configure the analytics", + "postValidation": "Done" + }, + "bladeTitle": "Analytics", + "elements": [ + { + "name": "analytics-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This solution installs the following analytic rule templates. After installing the solution, create and enable analytic rules in Manage solution view." + } + }, + { + "name": "analytics-link", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more", + "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-detect-threats-custom?WT.mc_id=Portal-Microsoft_Azure_CreateUIDef" + } + } + } + ] + } + ], + "outputs": { + "workspace-location": "[first(map(filter(basics('getLAWorkspace').value, (filter) => and(contains(toLower(filter.id), toLower(resourceGroup().name)),equals(filter.name,basics('workspace')))), (item) => item.location))]", + "location": "[location()]", + "workspace": "[basics('workspace')]" + } + } +} diff --git a/Solutions/ZeroFox/Package/mainTemplate.json b/Solutions/ZeroFox/Package/mainTemplate.json new file mode 100644 index 00000000000..880f4c6fbd1 --- /dev/null +++ b/Solutions/ZeroFox/Package/mainTemplate.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "author": "Microsoft - support@microsoft.com", + "comments": "Solution template for ZeroFox" + }, + "parameters": { + "location": { + "type": "string", + "minLength": 1, + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Not used, but needed to pass arm-ttk test `Location-Should-Not-Be-Hardcoded`. We instead use the `workspace-location` which is derived from the LA workspace" + } + }, + "workspace-location": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "[concat('Region to deploy solution resources -- separate from location selection',parameters('location'))]" + } + }, + "workspace": { + "defaultValue": "", + "type": "string", + "metadata": { + "description": "Workspace name for Log Analytics where Microsoft Sentinel is setup" + } + } + }, + "variables": { + "email": "support@microsoft.com", + "_email": "[variables('email')]", + "_solutionName": "ZeroFox", + "_solutionVersion": "3.0.0", + "solutionId": "azuresentinel.azure-sentinel-solution-zerofox", + "_solutionId": "[variables('solutionId')]", + "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" + }, + "resources": { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.0", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "ZeroFox", + "publisherDisplayName": "Microsoft Sentinel, Microsoft Corporation", + "descriptionHtml": "

Note: There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The ZeroFox solution for Microsoft Sentinel enables you to ingest ZeroFox Alerts and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n
    \n
  1. Azure Monitor HTTP Data Collector API

    \n
  2. \n
  3. Azure Functions

    \n
  4. \n
\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "tier": "Microsoft", + "link": "https://support.microsoft.com/" + }, + "dependencies": { + "operator": "AND", + "criteria": [] + }, + "firstPublishDate": "2023-07-28", + "providers": [ + "ZeroFox" + ], + "categories": { + "domains": [ + "Security - Threat Protection", + "Security - Automation (SOAR)" + ] + } + }, + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + }, + "outputs": {} +} diff --git a/Solutions/ZeroFox/ReleaseNotes.md b/Solutions/ZeroFox/ReleaseNotes.md new file mode 100644 index 00000000000..350f160c528 --- /dev/null +++ b/Solutions/ZeroFox/ReleaseNotes.md @@ -0,0 +1,3 @@ +| **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | +|-------------|--------------------------------|---------------------------------------------| +| 1.0.0 | 04-08-2023 | **Data Connector** Added Data Connectors for ZeroFox's Alerts and CTI feeds \ No newline at end of file From 2059213474a8f51805c2e31828c3972eb261e65c Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 11 Aug 2023 11:02:53 -0400 Subject: [PATCH 15/23] Add PR comments regarding Azure's KqlValidations Add PR comments regarding field TimeGenerated so that KqlValidations pass Also, correct versioning so that it matches generated package --- .../CustomTables/ZeroFoxAlertPoller_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_identity_breach_CL.json | 4 ++++ .../KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_malware_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_national_ids_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_phishing_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_phone_numbers_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_ransomware_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_telegram_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_threat_actors_CL.json | 4 ++++ .../CustomTables/ZeroFox_CTI_vulnerabilities_CL.json | 4 ++++ Solutions/ZeroFox/Data/Solution_ZeroFox.json | 2 +- Solutions/ZeroFox/ReleaseNotes.md | 2 +- 13 files changed, 46 insertions(+), 2 deletions(-) diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json index 1f4a908b3b6..067face6d25 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFoxAlertPoller_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFoxAlertPoller_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "alert_type_s", "Type": "String" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json index e955ed98e19..0360e33a818 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_identity_breach_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_identity_breach_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "cac_username_hash_s", "Type": "String" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json index 8823502317a..75021e8b88d 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_irc_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_irc_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "channel_s", "Type": "String" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json index c9d27d882d4..4252ed14975 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_malware_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_malware_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "created_at_t", "Type": "DateTime" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json index 55c2ebc7ae9..e566dc95ed1 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_national_ids_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_national_ids_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "national_identifier_s", "Type": "String" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json index fa9ccf1effc..d077710bb2a 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phishing_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_phishing_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "scanned_t", "Type": "DateTime" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json index e05e6322fb6..7dd99d14813 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_phone_numbers_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_phone_numbers_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "phone_number_s", "Type": "String" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json index 49d1fc72f7f..e8bc0e1f94e 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_ransomware_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_ransomware_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "created_at_t", "Type": "DateTime" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json index 69471d98dfd..add588d3a8b 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_telegram_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_telegram_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "channel_name_s", "Type": "String" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json index ec4dcbbc19c..4b4c2181aa2 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_threat_actors_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_threat_actors_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "id_d", "Type": "Double" diff --git a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json index fb18333ee72..cb4736155b1 100644 --- a/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json +++ b/.script/tests/KqlvalidationsTests/CustomTables/ZeroFox_CTI_vulnerabilities_CL.json @@ -1,6 +1,10 @@ { "Name": "ZeroFox_CTI_vulnerabilities_CL", "Properties": [ + { + "Name": "TimeGenerated", + "Type": "DateTime" + }, { "Name": "base_score_d", "Type": "Double" diff --git a/Solutions/ZeroFox/Data/Solution_ZeroFox.json b/Solutions/ZeroFox/Data/Solution_ZeroFox.json index 855b5e98c06..76521611a0e 100644 --- a/Solutions/ZeroFox/Data/Solution_ZeroFox.json +++ b/Solutions/ZeroFox/Data/Solution_ZeroFox.json @@ -14,7 +14,7 @@ "Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml" ], "BasePath": "C:\\GitHub\\Azure-Sentinel\\Solutions\\ZeroFox", - "Version": "1.0.0", + "Version": "3.0.0", "Metadata": "SolutionMetadata.json", "TemplateSpec": true, "Is1Pconnector": false diff --git a/Solutions/ZeroFox/ReleaseNotes.md b/Solutions/ZeroFox/ReleaseNotes.md index 350f160c528..869ddfa7c38 100644 --- a/Solutions/ZeroFox/ReleaseNotes.md +++ b/Solutions/ZeroFox/ReleaseNotes.md @@ -1,3 +1,3 @@ | **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | |-------------|--------------------------------|---------------------------------------------| -| 1.0.0 | 04-08-2023 | **Data Connector** Added Data Connectors for ZeroFox's Alerts and CTI feeds \ No newline at end of file +| 3.0.0 | 04-08-2023 | **Data Connector** Added Data Connectors for ZeroFox's Alerts and CTI feeds \ No newline at end of file From 2f8b63e008a36b374a23d1e9f7555732e0efee95 Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 18 Aug 2023 10:04:49 -0400 Subject: [PATCH 16/23] Add comma tu validConnectorIds file --- .../detectionTemplateSchemaValidation/ValidConnectorIds.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json b/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json index af3e95c0e8a..977bb0a50d2 100644 --- a/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json +++ b/.script/tests/detectionTemplateSchemaValidation/ValidConnectorIds.json @@ -194,6 +194,6 @@ "DynatraceAuditLogs", "DynatraceProblems", "MicrosoftDefenderThreatIntelligence", - "ZeroFox_Alert_Polling" + "ZeroFox_Alert_Polling", "CortexXDR" ] From 7747494456617164c630663088b572ef7afad6a4 Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 18 Aug 2023 10:30:02 -0400 Subject: [PATCH 17/23] Attempt at fixing the mainTemplate file --- Solutions/ZeroFox/Package/mainTemplate.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Solutions/ZeroFox/Package/mainTemplate.json b/Solutions/ZeroFox/Package/mainTemplate.json index 880f4c6fbd1..84fc585d7e4 100644 --- a/Solutions/ZeroFox/Package/mainTemplate.json +++ b/Solutions/ZeroFox/Package/mainTemplate.json @@ -38,7 +38,8 @@ "_solutionId": "[variables('solutionId')]", "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" }, - "resources": { + "resources": [ + { "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", "apiVersion": "2023-04-01-preview", "location": "[parameters('workspace-location')]", @@ -86,6 +87,7 @@ } }, "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" - }, + + }], "outputs": {} } From 4bb0c132e2edfa7105c3452bafbb2f92663ec2d7 Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Mon, 21 Aug 2023 12:50:37 -0400 Subject: [PATCH 18/23] Fix formatting in mainTemplate file according to arm-ttk guidelines --- Solutions/ZeroFox/Package/mainTemplate.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Solutions/ZeroFox/Package/mainTemplate.json b/Solutions/ZeroFox/Package/mainTemplate.json index 84fc585d7e4..f8b8b34c585 100644 --- a/Solutions/ZeroFox/Package/mainTemplate.json +++ b/Solutions/ZeroFox/Package/mainTemplate.json @@ -32,11 +32,11 @@ "variables": { "email": "support@microsoft.com", "_email": "[variables('email')]", - "_solutionName": "ZeroFox", "_solutionVersion": "3.0.0", "solutionId": "azuresentinel.azure-sentinel-solution-zerofox", "_solutionId": "[variables('solutionId')]", - "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" + "solutioncontentProductId":"[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]", + "_solutioncontentProductId": "[variables('solutioncontentProductId')]" }, "resources": [ { @@ -72,8 +72,7 @@ "link": "https://support.microsoft.com/" }, "dependencies": { - "operator": "AND", - "criteria": [] + "operator": "AND" }, "firstPublishDate": "2023-07-28", "providers": [ From 20df5111becf34d96b3a9b164d6be51220259c3e Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 25 Aug 2023 10:55:49 -0400 Subject: [PATCH 19/23] Repackage solution by running solutionPackagerV3 and fix arm-template --- Solutions/ZeroFox/Package/3.0.0.zip | Bin 3506 -> 5268 bytes .../ZeroFox/Package/createUiDefinition.json | 58 +- Solutions/ZeroFox/Package/mainTemplate.json | 549 ++++++++++++++++-- 3 files changed, 559 insertions(+), 48 deletions(-) diff --git a/Solutions/ZeroFox/Package/3.0.0.zip b/Solutions/ZeroFox/Package/3.0.0.zip index 217e82d9ef088bf709aa493e733de9289a1a8eac..36bb0284a8335f50f2c563ed00adecb1f4c4c86e 100644 GIT binary patch delta 5189 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 delta 3432 zcmV-u4VUtiDY6?FP)h>@6aWAK2mp3y1Xs5Y54vLp003GMkr*9+tylYR+cpsXJ%0tE z4zL~Qkp>tx1WSu7=_5e01+Cixvx9(^&K4I+)JV#X+xdUrk$RZ2<+XbtKoU{MdwzHK z>B|uz-ghcaA*0?I@lve|brwa{Ntn@`E+B(khxBtPA&f;XyY)C^6UJg;nh+sAQKQ6{<0^ z9aGiYf=Zf!Mi~_Nf+u(ui+s)&?K7S*^@)9^GQqPsT;Zg|3bM z#IglZGCrTSX_0glE|^ZsS*bvxIc-v7l44`1eh2UIkmbOCBg_&y7~P14P|;jGt%Hop z$51U7Q;)1zqEpoU!}rA#KBmAHscxTNO!H}eeEf^haCUr59>yt1AQ@eg8IZ|iE><~V zN|m7QhmCji5i6!5Rhw6WmnIfvr7PhPDL`t970MSx3!+n|$opE*Cj|$pfJm5wtX@Ng z2Im58vs`I^$~j&PAB=P85t;l3QoI&VTOU@dRrnbXFU{1N60h9*_XG9nlRb#IbENEs>i-1ws>&6mI65ITg*OfE@qMSh7 z(fUPyER)324Da#_FuK`nt?x+ag~{49F#m0eiTSkDiI8IfRwfXnrO)KxIxc(<^i) zO5FpYyq$kc1+NJ$n&>TO_eiutZnBVzQgOHlrh{%v1Ca# zODB#1amVzpO$pU)yB4C>tYh-fJ<6|hW$>b+m5`6BpeCn9V>_az)!&sk3=S*nXqXA{ z_&2gRcUf!&=h#KcbL8&Z|2IymxQJcJ*3Tn>)6JP##=JQa5y&GtJmqKhHs=Oi(SvDnIEs781`Xq z7NeG>qvgo`TkG6z#s&z^hRkv0nqV=on~P7a|Bg3$T|jrcxA+Y&WOh-oOWc}L@XjS6 z_?Q{zJv$lajGM(yX6tQ|6lRcW0wxxJGiXdc-_@ZL8#_^88XXc>HSXwYAQ|P8h2 z$RS_obl6GVa1@&GM*hj@-s|M|4_utO2NS6>DMtO%7$gCb@whD$db_QRjRlcWx-r3X}_{50WTRs^~J%8$1Px{|(2B`YJ3r+)PCL!4y z22QrNW5*GxuytQg2_&|~SB6A8>3Xqq(f1ZR<)#+s)Cjo<)`um{boMSS(>E*J0HDv3 zxrIEVd+kc(&a`zyo^hJMm}whzzjIKrpxqt0vk~^9N6z%5cbC+`ejB5I@XxqJ9(Qku zqFS@NE66A_2AU3PS!K=F)eezt{2~$;Ez-)&5NJ>#xtJqZ9hPtJsrJ(#wxiQB&x)~k zHQ8pYc)`{M!;eC*g>9&rV)MGOUnP6Mr&aI+y(8HobBkfVx?s6H}pZK+EBJg zmq<$)(&_|1WMdsvpCc-Nh)bnK_VUv>%;E=@oZlfUN2xZ-2g_a`+%M)GZ#IXM-u{2z z+t^4si?TB%w;LmH2$DCMGpY3OUk*#e(eOVEOdqEhGNR^&op_D;)zL?yZE$R1bY(7Tb8l{)SZ#0HHW2>aUqQG7>;RUPq$|1wp0TV&*I;QIG+7HQ z2^vN^+ngy-=DoDP+8J#5z;wa(fiV z<;o!AiccT|)~ONY+2EF4;5t^&ix+OWUC=-2v72j%a*-JBbawh`5T%Cgi6cO}Sj1=rj%2U2UD z5o%HyB-}!sf||G~ZXVRDDU5j`-A679f9leJ)rCpmccY8Ew>JnTt{a**BT#9wi4l1P zr}IubR#-^;?6z=B0i=*Jr&-ReW!z+ub2IeVI(BnfnTXD(x{zsvN2A{$dc#ecXgqc^ zVC)kxp{xUco|0DProe6%+RSXu6Nm!H8@8B=WXgnPDHt(FlVhVZFMfK=>I0n$_Pzi2 zzPglcecuw-%?N&HoGy`WXsp-$025CF%}RPQ8+Z|}#Igu!1w;fQ2{{&k;56rZ&!E@P z^tq{js{ed{uiuTj{jQZ={|hUW_@{t#fBC&i$Ij#aXsB+bf(K<;+in$6BydIH^1|z! zSY3FaW=)^L`y7m?KbI%UipkX3xaPZ!bED@1>x7L%hqI20yzwL_BxF9r-_?&3?Kum6bXAthpgi4$JVzpDjKlhz(2L&k`g~k4| zZ09fmDsH8)n1Zj?1-J9D-^kII+F*L|bwrKCf8#QrU&DMf#HyjyWP}ACj)rmd#fFfL zE_~)^e93Ns4LO?{82@rrH+Vk7?r!6dB!L`uru0GyYYVVfoss`y<>xTwBS!J%LLpgy zsjj*ymXQ#>KFcL=Y$a$2Ot}piTF0tX_8KlF9vlo~gg1=y(Un3M5N8X##bPn2($&u# zh^30Ihf!B@s(3Bc4U}g+Tk3*)gvdggk^5TTSoMw*e z>)zw49qw}%2_zPVVFD@vvhqFRgkcYVok5RUd@iiN6%1#M{On{<$2m~tPlX98mn6GV zaskOyX{jfeJATMmk*RS})HCYQ3hNw>9n@L@HL7IEXmv_WNJ_o%SQ=XUK6qARm(pXB zL!#7Y$tnn3G=Z=}wQ<&uple+xu6j`{VZvoxm63>6rJq~5jIeSb(=pa(kYYsf-h!X%#BQMX z9+f|%Oe^8Ei7{R_rp_NF7)hK#w2828qV8joV{c12_U7W^jJ-lREEts>-8gwyA&U{o z19)1)31%*+l#dJLrymC}1ZCAxvYt^Xo6u40=qZ+|)HBPqM z)b=6sCQ^B_m<>fRd!0ZH#jd`~A{x7v{REKTw%@V%uL0K=$SN!q2 zS50F$$RPOyJ#8+pw^WXQg|Y6eoSd+{!D>=`5gq>2;8GYiNzF=mIr\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n**Analytic Rules:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", @@ -76,6 +76,62 @@ "uri": "https://docs.microsoft.com/azure/sentinel/tutorial-detect-threats-custom?WT.mc_id=Portal-Microsoft_Azure_CreateUIDef" } } + }, + { + "name": "analytic1", + "type": "Microsoft.Common.Section", + "label": "ZeroFox Alerts - High Severity Alerts", + "elements": [ + { + "name": "analytic1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Detects high severity alerts from ZeroFox" + } + } + ] + }, + { + "name": "analytic2", + "type": "Microsoft.Common.Section", + "label": "ZeroFox Alerts - Informational Severity Alerts", + "elements": [ + { + "name": "analytic2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Detects informational severity alerts from ZeroFox" + } + } + ] + }, + { + "name": "analytic3", + "type": "Microsoft.Common.Section", + "label": "ZeroFox Alerts - Low Severity Alerts", + "elements": [ + { + "name": "analytic3-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Detects low severity alerts from ZeroFox" + } + } + ] + }, + { + "name": "analytic4", + "type": "Microsoft.Common.Section", + "label": "ZeroFox Alerts - Medium Severity Alerts", + "elements": [ + { + "name": "analytic4-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "Detects medium severity alerts from ZeroFox" + } + } + ] } ] } diff --git a/Solutions/ZeroFox/Package/mainTemplate.json b/Solutions/ZeroFox/Package/mainTemplate.json index f8b8b34c585..4266cf2e5c1 100644 --- a/Solutions/ZeroFox/Package/mainTemplate.json +++ b/Solutions/ZeroFox/Package/mainTemplate.json @@ -32,61 +32,516 @@ "variables": { "email": "support@microsoft.com", "_email": "[variables('email')]", + "_solutionName": "ZeroFox", "_solutionVersion": "3.0.0", "solutionId": "azuresentinel.azure-sentinel-solution-zerofox", "_solutionId": "[variables('solutionId')]", - "solutioncontentProductId":"[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]", + "analyticRuleVersion1": "1.0.0", + "analyticRulecontentId1": "deb45e6d-892f-40bf-9118-e2a6f26b788d", + "_analyticRulecontentId1": "[variables('analyticRulecontentId1')]", + "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('_analyticRulecontentId1'))]", + "analyticRuleTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId1'))))]", + "analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId1'),'-', variables('analyticRuleVersion1'))))]", + "_analyticRulecontentProductId1": "[variables('analyticRulecontentProductId1')]", + "analyticRuleVersion2": "1.0.0", + "analyticRulecontentId2": "6f7a7413-b72f-4361-84ee-897baeb9c6d4", + "_analyticRulecontentId2": "[variables('analyticRulecontentId2')]", + "analyticRuleId2": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('analyticRulecontentId2'))]", + "analyticRuleTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId2'))))]", + "analyticRulecontentProductId2": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId2'),'-', variables('analyticRuleVersion2'))))]", + "_analyticRulecontentProductId2": "[variables('analyticRulecontentProductId2')]", + "analyticRuleVersion3": "1.0.0", + "analyticRulecontentId3": "e0c7a91a-7aa1-498a-9c20-cd6c721f9345", + "_analyticRulecontentId3": "[variables('analyticRulecontentId3')]", + "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('_analyticRulecontentId3'))]", + "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId3'))))]", + "analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId3'),'-', variables('analyticRuleVersion3'))))]", + "_analyticRulecontentProductId3": "[variables('analyticRulecontentProductId3')]", + "analyticRuleVersion4": "1.0.0", + "analyticRulecontentId4": "a6496de5-911b-4199-b7db-d34ac9d70df3", + "_analyticRulecontentId4": "[variables('analyticRulecontentId4')]", + "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('_analyticRulecontentId4'))]", + "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId4'))))]", + "analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId4'),'-', variables('analyticRuleVersion4'))))]", + "_analyticRulecontentProductId4": "[variables('analyticRulecontentProductId4')]", + "solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]", "_solutioncontentProductId": "[variables('solutioncontentProductId')]" }, "resources": [ - { - "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", - "apiVersion": "2023-04-01-preview", - "location": "[parameters('workspace-location')]", - "properties": { - "version": "3.0.0", - "kind": "Solution", - "contentSchemaVersion": "3.0.0", - "displayName": "ZeroFox", - "publisherDisplayName": "Microsoft Sentinel, Microsoft Corporation", - "descriptionHtml": "

Note: There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The ZeroFox solution for Microsoft Sentinel enables you to ingest ZeroFox Alerts and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n
    \n
  1. Azure Monitor HTTP Data Collector API

    \n
  2. \n
  3. Azure Functions

    \n
  4. \n
\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", - "contentKind": "Solution", - "contentProductId": "[variables('_solutioncontentProductId')]", - "id": "[variables('_solutioncontentProductId')]", - "icon": "", - "contentId": "[variables('_solutionId')]", - "parentId": "[variables('_solutionId')]", - "source": { - "kind": "Solution", - "name": "ZeroFox", - "sourceId": "[variables('_solutionId')]" - }, - "author": { - "name": "Microsoft", - "email": "[variables('_email')]" - }, - "support": { - "name": "Microsoft Corporation", - "email": "support@microsoft.com", - "tier": "Microsoft", - "link": "https://support.microsoft.com/" - }, - "dependencies": { - "operator": "AND" - }, - "firstPublishDate": "2023-07-28", - "providers": [ - "ZeroFox" + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleTemplateSpecName1')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], - "categories": { - "domains": [ - "Security - Threat Protection", - "Security - Automation (SOAR)" - ] + "properties": { + "description": "ZF_Alerts_HighSeverityRule_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleVersion1')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRulecontentId1')]", + "apiVersion": "2022-04-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Detects high severity alerts from ZeroFox", + "displayName": "ZeroFox Alerts - High Severity Alerts", + "enabled": false, + "query": "ZeroFoxAlertPoller_CL\n| where Severity in (5)\n", + "queryFrequency": "PT5M", + "queryPeriod": "PT5M", + "severity": "High", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "ZeroFoxAlertPoller_CL" + ], + "connectorId": "ZeroFox_Alert_Polling" + } + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "columnName": "entity_name_s", + "identifier": "FullName" + } + ] + } + ], + "eventGroupingSettings": { + "aggregationKind": "AlertPerResult" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleId1'),'/'))))]", + "properties": { + "description": "ZeroFox Analytics Rule 1", + "parentId": "[variables('analyticRuleId1')]", + "contentId": "[variables('_analyticRulecontentId1')]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleVersion1')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_analyticRulecontentId1')]", + "contentKind": "AnalyticsRule", + "displayName": "ZeroFox Alerts - High Severity Alerts", + "contentProductId": "[variables('_analyticRulecontentProductId1')]", + "id": "[variables('_analyticRulecontentProductId1')]", + "version": "[variables('analyticRuleVersion1')]" } }, - "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" - - }], + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleTemplateSpecName2')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ZF_Alerts_InformationalSeverityRule_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleVersion2')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRulecontentId2')]", + "apiVersion": "2022-04-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Detects informational severity alerts from ZeroFox", + "displayName": "ZeroFox Alerts - Informational Severity Alerts", + "enabled": false, + "query": "ZeroFoxAlertPoller_CL\n| where Severity in (1,2)\n", + "queryFrequency": "PT5M", + "queryPeriod": "PT5M", + "severity": "Informational", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "ZeroFoxAlertPoller_CL" + ], + "connectorId": "ZeroFox_Alert_Polling" + } + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "columnName": "entity_name_s", + "identifier": "FullName" + } + ] + } + ], + "eventGroupingSettings": { + "aggregationKind": "AlertPerResult" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleId2'),'/'))))]", + "properties": { + "description": "ZeroFox Analytics Rule 2", + "parentId": "[variables('analyticRuleId2')]", + "contentId": "[variables('_analyticRulecontentId2')]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleVersion2')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_analyticRulecontentId2')]", + "contentKind": "AnalyticsRule", + "displayName": "ZeroFox Alerts - Informational Severity Alerts", + "contentProductId": "[variables('_analyticRulecontentProductId2')]", + "id": "[variables('_analyticRulecontentProductId2')]", + "version": "[variables('analyticRuleVersion2')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleTemplateSpecName3')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ZF_Alerts_LowSeverityRule_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleVersion3')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRulecontentId3')]", + "apiVersion": "2022-04-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Detects low severity alerts from ZeroFox", + "displayName": "ZeroFox Alerts - Low Severity Alerts", + "enabled": false, + "query": "ZeroFoxAlertPoller_CL\n| where Severity in (3)\n", + "queryFrequency": "PT5M", + "queryPeriod": "PT5M", + "severity": "Low", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "ZeroFoxAlertPoller_CL" + ], + "connectorId": "ZeroFox_Alert_Polling" + } + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "columnName": "entity_name_s", + "identifier": "FullName" + } + ] + } + ], + "eventGroupingSettings": { + "aggregationKind": "AlertPerResult" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleId3'),'/'))))]", + "properties": { + "description": "ZeroFox Analytics Rule 3", + "parentId": "[variables('analyticRuleId3')]", + "contentId": "[variables('_analyticRulecontentId3')]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleVersion3')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_analyticRulecontentId3')]", + "contentKind": "AnalyticsRule", + "displayName": "ZeroFox Alerts - Low Severity Alerts", + "contentProductId": "[variables('_analyticRulecontentProductId3')]", + "id": "[variables('_analyticRulecontentProductId3')]", + "version": "[variables('analyticRuleVersion3')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('analyticRuleTemplateSpecName4')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ZF_Alerts_MediumSeverityRule_AnalyticalRules Analytics Rule with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('analyticRuleVersion4')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "type": "Microsoft.SecurityInsights/AlertRuleTemplates", + "name": "[variables('analyticRulecontentId4')]", + "apiVersion": "2022-04-01-preview", + "kind": "Scheduled", + "location": "[parameters('workspace-location')]", + "properties": { + "description": "Detects medium severity alerts from ZeroFox", + "displayName": "ZeroFox Alerts - Medium Severity Alerts", + "enabled": false, + "query": "ZeroFoxAlertPoller_CL\n| where Severity in (4)\n", + "queryFrequency": "PT5M", + "queryPeriod": "PT5M", + "severity": "Medium", + "suppressionDuration": "PT1H", + "suppressionEnabled": false, + "triggerOperator": "GreaterThan", + "triggerThreshold": 0, + "status": "Available", + "requiredDataConnectors": [ + { + "dataTypes": [ + "ZeroFoxAlertPoller_CL" + ], + "connectorId": "ZeroFox_Alert_Polling" + } + ], + "entityMappings": [ + { + "entityType": "Account", + "fieldMappings": [ + { + "columnName": "entity_name_s", + "identifier": "FullName" + } + ] + } + ], + "eventGroupingSettings": { + "aggregationKind": "AlertPerResult" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('AnalyticsRule-', last(split(variables('analyticRuleId4'),'/'))))]", + "properties": { + "description": "ZeroFox Analytics Rule 4", + "parentId": "[variables('analyticRuleId4')]", + "contentId": "[variables('_analyticRulecontentId4')]", + "kind": "AnalyticsRule", + "version": "[variables('analyticRuleVersion4')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_analyticRulecontentId4')]", + "contentKind": "AnalyticsRule", + "displayName": "ZeroFox Alerts - Medium Severity Alerts", + "contentProductId": "[variables('_analyticRulecontentProductId4')]", + "id": "[variables('_analyticRulecontentProductId4')]", + "version": "[variables('analyticRuleVersion4')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentPackages", + "apiVersion": "2023-04-01-preview", + "location": "[parameters('workspace-location')]", + "properties": { + "version": "3.0.0", + "kind": "Solution", + "contentSchemaVersion": "3.0.0", + "displayName": "ZeroFox", + "publisherDisplayName": "Microsoft Sentinel, Microsoft Corporation", + "descriptionHtml": "

Note: There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The ZeroFox solution for Microsoft Sentinel enables you to ingest ZeroFox Alerts and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n
    \n
  1. Azure Monitor HTTP Data Collector API

    \n
  2. \n
  3. Azure Functions

    \n
  4. \n
\n

Analytic Rules: 4

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "contentKind": "Solution", + "contentProductId": "[variables('_solutioncontentProductId')]", + "id": "[variables('_solutioncontentProductId')]", + "icon": "", + "contentId": "[variables('_solutionId')]", + "parentId": "[variables('_solutionId')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "tier": "Microsoft", + "link": "https://support.microsoft.com/" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRulecontentId1')]", + "version": "[variables('analyticRuleVersion1')]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRulecontentId2')]", + "version": "[variables('analyticRuleVersion2')]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRulecontentId3')]", + "version": "[variables('analyticRuleVersion3')]" + }, + { + "kind": "AnalyticsRule", + "contentId": "[variables('analyticRulecontentId4')]", + "version": "[variables('analyticRuleVersion4')]" + } + ] + }, + "firstPublishDate": "2023-07-28", + "providers": [ + "ZeroFox" + ], + "categories": { + "domains": [ + "Security - Threat Protection", + "Security - Automation (SOAR)" + ] + } + }, + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/', variables('_solutionId'))]" + } + ], "outputs": {} } From 8b02186f03e1559d30b0c36c89bdb127a566dbed Mon Sep 17 00:00:00 2001 From: Diego Ramirez Date: Fri, 25 Aug 2023 13:59:47 -0400 Subject: [PATCH 20/23] Fix description in uiDefinition.json file --- Solutions/ZeroFox/Package/createUiDefinition.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Solutions/ZeroFox/Package/createUiDefinition.json b/Solutions/ZeroFox/Package/createUiDefinition.json index d5e4d6d4678..10930999df9 100644 --- a/Solutions/ZeroFox/Package/createUiDefinition.json +++ b/Solutions/ZeroFox/Package/createUiDefinition.json @@ -6,7 +6,7 @@ "config": { "isWizard": false, "basics": { - "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n**Analytic Rules:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n**Analytic Rules:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", From 219bec76e924ca35acccf10949aa5d5ddec03d1a Mon Sep 17 00:00:00 2001 From: Diego Ramirez R Date: Wed, 30 Aug 2023 10:23:13 -0400 Subject: [PATCH 21/23] Fix CTI connector filename in Data packaging file (#41) Co-authored-by: Diego Ramirez --- Solutions/ZeroFox/Data/Solution_ZeroFox.json | 2 +- Solutions/ZeroFox/Package/3.0.0.zip | Bin 5268 -> 11803 bytes .../ZeroFox/Package/createUiDefinition.json | 33 +- Solutions/ZeroFox/Package/mainTemplate.json | 1148 ++++++++++++++++- 4 files changed, 1169 insertions(+), 14 deletions(-) diff --git a/Solutions/ZeroFox/Data/Solution_ZeroFox.json b/Solutions/ZeroFox/Data/Solution_ZeroFox.json index 76521611a0e..2653e26d97d 100644 --- a/Solutions/ZeroFox/Data/Solution_ZeroFox.json +++ b/Solutions/ZeroFox/Data/Solution_ZeroFox.json @@ -4,7 +4,7 @@ "Logo": "", "Description": "The [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)", "Data Connectors": [ - "Data Connectors/CTI/ZeroFox_API_FunctionApp.json", + "Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json", "Data Connectors/Alerts/alerts_connector.json" ], "Analytic Rules": [ diff --git a/Solutions/ZeroFox/Package/3.0.0.zip b/Solutions/ZeroFox/Package/3.0.0.zip index 36bb0284a8335f50f2c563ed00adecb1f4c4c86e..4b0c6173e49f219455f850c1494df4c27abbec62 100644 GIT binary patch literal 11803 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

L>DSt~QLUeAudO9dW7_B7awC4T z{Hsd-itcNy&nABUUrNP@CMYq_Vo6}pop?1}!0LW%AK+bMLE?g&lwD)9U{=?SfKj2F z^&Lob5qc5Ls@43_4p$JJPA5xW?U0g7%V=R2@ia%so3#x$Fv0%qqy*`PEZ*)|ER3@0 zaK53?J~|1nw}SgVsV6HwyG&<)VQmfbb7Ae48$mu4b}4WIJ4RMDR}c|KahC9(q3t>j z5BgM49=;U3YCYwa3flVI7Epb7a=9Fh<>;pcgecP|<1CA2jZmOjZOk_S(4(T{S zACf>bdAGm6!gucV8w`OKS<+pR!#*pGnf+(*#+?jMG6z6vmh#BP%x!3U1Q(qrUB~SM zc;hcgT}HL8$Y#XBmeR;22hoD3crnN2Uag>*o*%yQKb86Y}9&)KK2py9k_6MxDQ1yo=XU;5w$?*_3zL% zPBo|%Yet}b6z8>P7h)X@anui}+LV9mUcUqw)2rIeHIwLLi0+e60O1}n3^7!ygMp{t z#(0N&KNm$(yDfh1Z=nVO;T}YA_B%FwoQo;ygT6&qmAU>jB{tE5)4JdpcB(1(CVNij z)ygfKa31EEYIGkqX*1xM3ZMaDnYErbO#Jv-qOn~tajtGF)jE@C@|^(?N?%(0m(tY_ z$S3qkq}WR8E$DsCAJqpY?e-w-KU=xYeG|{EKaO_p63Ln+WCiWY zl`1nO5*5+jGQMy9buYA-EKnq5dSW8?6TFRSxEq-~q>25;PMoDEQuia`i|J<~HVIIPozWK!wNjt$X6P1Rqd z#^76nzs)A2n5K(13N~Z*3Vd8lAELWR5OGBYXl z;WhOl>sYD~c`m~mCYMbQ^Y3&*os64}%HL2o!}@0Dw~eLXeo>VO!bpV{l|D84GaedlXjD^GU8hCj zG&a@gByr(cbAIJDTj_FDHni(9e;PmvDGR4JwwKZyWQ2?sbT2nk9XPRb0h3i6w>zp6 z(`%!oO@j-}Cc}qSCK5=>MBvZ0!+9#2hULFn!~K1U`KrJigSh4#+5hQY5_s8b*8%r} znNiFqngGiK&$lHR_&}CQVK2sWbNl>knV6-N+D9UJ3@{hiDheVk=GF4pFK{NE@J)ED zer(=CB&|D54*oIH?xfQR9?Q}p^E4WizIT^ap~Ht>M)?WzI}f ztKRXrppP35C~d=Db$gYjQ)GBa90`7X?teE;R<30%OLw|MiV6ue>Un7lv7E?H-^*>E zV~OlFl%OQ*yofWjn1tKH!AwTBqg%uIw|&fshmsm)Q^QMFW>j!k5EEApEeccn%)-+g zY^z(22UNo^AE8$$A!_7s{g>C!6)UCo*?C*hdyYjAv^v8&YNjwsn22#r;98;ui!XLD z0&BubU-M9?Gw!aw9 zl54vB!j-OIdt5)Y5{YjS7p%1Hlmjs}KPE{*D4mz(0`P$fbd>_uUtd^_kxcFi`zVPu zO>Pi(O;H|9!T*Bc80F$jMH*bvw*jZNT5*DixwD3LvftaQH{bo0WFeujq5pRT`yZk8 zf1?QUKk0t~vj3;Z|75)WD**vf1iAN*?*9LXFD2P8u>TnX^{<5d%l|mifAN0+HD$ay delta 5192 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 C!So6M diff --git a/Solutions/ZeroFox/Package/createUiDefinition.json b/Solutions/ZeroFox/Package/createUiDefinition.json index 10930999df9..e9c1b20dfb0 100644 --- a/Solutions/ZeroFox/Package/createUiDefinition.json +++ b/Solutions/ZeroFox/Package/createUiDefinition.json @@ -6,7 +6,7 @@ "config": { "isWizard": false, "basics": { - "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n**Analytic Rules:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** _There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing._\n\nThe [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)\n\n**Data Connectors:** 2, **Analytic Rules:** 4\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", @@ -51,6 +51,37 @@ } ], "steps": [ + { + "name": "dataconnectors", + "label": "Data Connectors", + "bladeTitle": "Data Connectors", + "elements": [ + { + "name": "dataconnectors1-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Solution installs the data connector for ZeroFox. You can get ZeroFox custom log data in your Microsoft Sentinel workspace. After installing the solution, configure and enable this data connector by following guidance in Manage solution view." + } + }, + { + "name": "dataconnectors2-text", + "type": "Microsoft.Common.TextBlock", + "options": { + "text": "This Solution installs the data connector for ZeroFox. You can get ZeroFox custom log data in your Microsoft Sentinel workspace. After installing the solution, configure and enable this data connector by following guidance in Manage solution view." + } + }, + { + "name": "dataconnectors-link2", + "type": "Microsoft.Common.TextBlock", + "options": { + "link": { + "label": "Learn more about connecting data sources", + "uri": "https://docs.microsoft.com/azure/sentinel/connect-data-sources" + } + } + } + ] + }, { "name": "analytics", "label": "Analytics", diff --git a/Solutions/ZeroFox/Package/mainTemplate.json b/Solutions/ZeroFox/Package/mainTemplate.json index 4266cf2e5c1..f28712c4cb5 100644 --- a/Solutions/ZeroFox/Package/mainTemplate.json +++ b/Solutions/ZeroFox/Package/mainTemplate.json @@ -36,10 +36,30 @@ "_solutionVersion": "3.0.0", "solutionId": "azuresentinel.azure-sentinel-solution-zerofox", "_solutionId": "[variables('solutionId')]", + "uiConfigId1": "ZeroFoxCTIDataConnector", + "_uiConfigId1": "[variables('uiConfigId1')]", + "dataConnectorContentId1": "ZeroFoxCTIDataConnector", + "_dataConnectorContentId1": "[variables('dataConnectorContentId1')]", + "dataConnectorId1": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId1'))]", + "_dataConnectorId1": "[variables('dataConnectorId1')]", + "dataConnectorTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentId1'))))]", + "dataConnectorVersion1": "1.0.0", + "dataConnectorcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','dc','-', uniqueString(concat(variables('_solutionId'),'-','DataConnector','-',variables('_dataConnectorContentId1'),'-', variables('dataConnectorVersion1'))))]", + "_dataConnectorcontentProductId1": "[variables('dataConnectorcontentProductId1')]", + "uiConfigId2": "ZeroFox_Alert_Polling", + "_uiConfigId2": "[variables('uiConfigId2')]", + "dataConnectorContentId2": "ZeroFox_Alert_Polling", + "_dataConnectorContentId2": "[variables('dataConnectorContentId2')]", + "dataConnectorId2": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId2'))]", + "_dataConnectorId2": "[variables('dataConnectorId2')]", + "dataConnectorTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-dc-',uniquestring(variables('_dataConnectorContentId2'))))]", + "dataConnectorVersion2": "1.0.0", + "dataConnectorcontentProductId2": "[concat(take(variables('_solutionId'),50),'-','dc','-', uniqueString(concat(variables('_solutionId'),'-','DataConnector','-',variables('_dataConnectorContentId2'),'-', variables('dataConnectorVersion2'))))]", + "_dataConnectorcontentProductId2": "[variables('dataConnectorcontentProductId2')]", "analyticRuleVersion1": "1.0.0", "analyticRulecontentId1": "deb45e6d-892f-40bf-9118-e2a6f26b788d", "_analyticRulecontentId1": "[variables('analyticRulecontentId1')]", - "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('_analyticRulecontentId1'))]", + "analyticRuleId1": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('analyticRulecontentId1'))]", "analyticRuleTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId1'))))]", "analyticRulecontentProductId1": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId1'),'-', variables('analyticRuleVersion1'))))]", "_analyticRulecontentProductId1": "[variables('analyticRulecontentProductId1')]", @@ -53,14 +73,14 @@ "analyticRuleVersion3": "1.0.0", "analyticRulecontentId3": "e0c7a91a-7aa1-498a-9c20-cd6c721f9345", "_analyticRulecontentId3": "[variables('analyticRulecontentId3')]", - "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('_analyticRulecontentId3'))]", + "analyticRuleId3": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('analyticRulecontentId3'))]", "analyticRuleTemplateSpecName3": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId3'))))]", "analyticRulecontentProductId3": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId3'),'-', variables('analyticRuleVersion3'))))]", "_analyticRulecontentProductId3": "[variables('analyticRulecontentProductId3')]", "analyticRuleVersion4": "1.0.0", "analyticRulecontentId4": "a6496de5-911b-4199-b7db-d34ac9d70df3", "_analyticRulecontentId4": "[variables('analyticRulecontentId4')]", - "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('_analyticRulecontentId4'))]", + "analyticRuleId4": "[resourceId('Microsoft.SecurityInsights/AlertRuleTemplates', variables('analyticRulecontentId4'))]", "analyticRuleTemplateSpecName4": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-ar-',uniquestring(variables('_analyticRulecontentId4'))))]", "analyticRulecontentProductId4": "[concat(take(variables('_solutionId'),50),'-','ar','-', uniqueString(concat(variables('_solutionId'),'-','AnalyticsRule','-',variables('_analyticRulecontentId4'),'-', variables('analyticRuleVersion4'))))]", "_analyticRulecontentProductId4": "[variables('analyticRulecontentProductId4')]", @@ -68,6 +88,1100 @@ "_solutioncontentProductId": "[variables('solutioncontentProductId')]" }, "resources": [ + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('dataConnectorTemplateSpecName1')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ZeroFox data connector with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorVersion1')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId1'))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "GenericUI", + "properties": { + "connectorUiConfig": { + "id": "[variables('_uiConfigId1')]", + "title": "ZeroFox CTI (using Azure Functions)", + "publisher": "ZeroFox", + "descriptionMarkdown": "The ZeroFox CTI data connectors provide the capability to ingest the different [ZeroFox](https://www.zerofox.com/threat-intelligence/) cyber threat intelligence alerts into Microsoft Sentinel.", + "graphQueries": [ + { + "metricName": "ZeroFox CTI Advanced Dark Web Logs", + "legend": "ZeroFox_CTI_advanced_dark_web_CL", + "baseQuery": "ZeroFox_CTI_advanced_dark_web_CL" + }, + { + "metricName": "ZeroFox CTI Botnet Logs", + "baseQuery": "ZeroFox_CTI_botnet_CL" + }, + { + "metricName": "ZeroFox CTI Breaches Logs", + "legend": "ZeroFox_CTI_breaches_CL", + "baseQuery": "ZeroFox_CTI_breaches_CL" + }, + { + "metricName": "ZeroFox CTI C2 Domains Logs", + "legend": "ZeroFox_CTI_C2_CL", + "baseQuery": "ZeroFox_CTI_C2_CL" + }, + { + "metricName": "ZeroFox CTI Compromised Credentials Logs", + "legend": "ZeroFox_CTI_compromised_credentials_CL", + "baseQuery": "ZeroFox_CTI_compromised_credentials_CL" + }, + { + "metricName": "ZeroFox CTI Credit Cards Logs", + "legend": "ZeroFox_CTI_credit_cards_CL", + "baseQuery": "ZeroFox_CTI_credit_cards_CL" + }, + { + "metricName": "ZeroFox CTI Dark Web Logs", + "legend": "ZeroFox_CTI_dark_web_CL", + "baseQuery": "ZeroFox_CTI_dark_web_CL" + }, + { + "metricName": "ZeroFox CTI Discord Logs", + "legend": "ZeroFox_CTI_discord_CL", + "baseQuery": "ZeroFox_CTI_discord_CL" + }, + { + "metricName": "ZeroFox CTI Disruption Logs", + "legend": "ZeroFox_CTI_disruption_CL", + "baseQuery": "ZeroFox_CTI_disruption_CL" + }, + { + "metricName": "ZeroFox CTI Email Addresses Logs", + "legend": "ZeroFox_CTI_email_addresses_CL", + "baseQuery": "ZeroFox_CTI_email_addresses_CL" + }, + { + "metricName": "ZeroFox CTI Exploits Logs", + "legend": "ZeroFox_CTI_exploits_CL", + "baseQuery": "ZeroFox_CTI_exploits_CL" + }, + { + "metricName": "ZeroFox CTI Identity Breach Logs", + "legend": "ZeroFox_CTI_identity_breach_CL", + "baseQuery": "ZeroFox_CTI_identity_breach_CL" + }, + { + "metricName": "ZeroFox CTI IRC Logs", + "legend": "ZeroFox_CTI_irc_CL", + "baseQuery": "ZeroFox_CTI_irc_CL" + }, + { + "metricName": "ZeroFox CTI Malware Logs", + "legend": "ZeroFox_CTI_malware_CL", + "baseQuery": "ZeroFox_CTI_malware_CL" + }, + { + "metricName": "ZeroFox CTI National Ids Logs", + "legend": "ZeroFox_CTI_national_ids_CL", + "baseQuery": "ZeroFox_CTI_national_ids_CL" + }, + { + "metricName": "ZeroFox CTI Phishing Logs", + "legend": "ZeroFox_CTI_phishing_CL", + "baseQuery": "ZeroFox_CTI_phishing_CL" + }, + { + "metricName": "ZeroFox CTI Phone Numbers Logs", + "legend": "ZeroFox_CTI_phone_numbers_CL", + "baseQuery": "ZeroFox_CTI_phone_numbers_CL" + }, + { + "metricName": "ZeroFox CTI Ransomware Logs", + "legend": "ZeroFox_CTI_ransomware_CL", + "baseQuery": "ZeroFox_CTI_ransomware_CL" + }, + { + "metricName": "ZeroFox CTI Telegram Logs", + "legend": "ZeroFox_CTI_telegram_CL", + "baseQuery": "ZeroFox_CTI_telegram_CL" + }, + { + "metricName": "ZeroFox CTI Threat Actors Logs", + "legend": "ZeroFox_CTI_threat_actors_CL", + "baseQuery": "ZeroFox_CTI_threat_actors_CL" + }, + { + "metricName": "ZeroFox CTI Vulnerabilities Logs", + "legend": "ZeroFox_CTI_vulnerabilities_CL", + "baseQuery": "ZeroFox_CTI_vulnerabilities_CL" + } + ], + "sampleQueries": [ + { + "description": "ZeroFox CTI C2-domains Logs", + "query": "ZeroFox_CTI_C2_CL\n | sort by TimeGenerated desc" + }, + { + "description": "ZeroFox CTI Email Addresses Logs", + "query": "ZeroFox_CTI_email_addresses_CL\n | sort by TimeGenerated desc" + }, + { + "description": "ZeroFox CTI Malware Logs", + "query": "ZeroFox_CTI_malware_CL\n | sort by TimeGenerated desc" + } + ], + "dataTypes": [ + { + "name": "ZeroFox_CTI_advanced_dark_web_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_advanced_dark_web_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_botnet_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_botnet_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_breaches_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_breaches_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_C2_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_C2_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_compromised_credentials_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_compromised_credentials_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_credit_cards_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_credit_cards_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_dark_web_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_dark_web_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_discord_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_discord_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_disruption_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_disruption_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_email_addresses_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_email_addresses_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_exploits_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_exploits_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_identity_breach_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_identity_breach_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_irc_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_irc_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_malware_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_malware_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_national_ids_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_national_ids_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_phishing_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_phishing_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_phone_numbers_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_phone_numbers_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_ransomware_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_ransomware_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_telegram_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_telegram_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_threat_actors_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_threat_actors_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_vulnerabilities_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_vulnerabilities_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriterias": [ + { + "type": "IsConnectedQuery", + "value": [ + "ZeroFox_CTI_advanced_dark_web_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_botnet_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_breaches_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_C2_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_compromised_credentials_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_credit_cards_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_dark_web_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_discord_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_disruption_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_email_addresses_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_exploits_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_identity_breach_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_irc_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_malware_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_national_ids_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_phishing_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_phone_numbers_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_ransomware_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_telegram_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_threat_actors_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_vulnerabilities_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)" + ] + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "read and write permissions on the workspace are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + }, + { + "provider": "Microsoft.OperationalInsights/workspaces/sharedKeys", + "permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key).", + "providerDisplayName": "Keys", + "scope": "Workspace", + "requiredPermissions": { + "action": true + } + } + ], + "customs": [ + { + "name": "Microsoft.Web/sites permissions", + "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." + }, + { + "name": "ZeroFox API Credentials/permissions", + "description": "**ZeroFox Username**, **ZeroFox Personal Access Token** are required for ZeroFox CTI REST API." + } + ] + }, + "instructionSteps": [ + { + "description": ">**NOTE:** This connector uses Azure Functions to connect to the ZeroFox CTI REST API to pull logs into Microsoft Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details." + }, + { + "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + }, + { + "description": "**STEP 1 - Retrieval of ZeroFox credentials:**\n\n Follow these instructions for set up logging and obtain credentials." + }, + { + "description": "**STEP 1.1 - [Log into ZeroFox's website.](https://cloud.zerofox.com/login) using your username and password" + }, + { + "description": "**STEP 1.2 - Click into the Settings button and go to the Data Connectors Section." + }, + { + "description": "**STEP 1.3 - Select the API DATA FEEDS tab and head to the bottom of the page, select <> in the API Information box, to obtain a Personal Access Token to be used along with your username." + }, + { + "description": "**STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the ZeroFox CTI data connector, have the Workspace ID and Workspace Primary Key (can be copied from the following), as well as the Amazon S3 REST API Authorization credentials, readily available.", + "instructions": [ + { + "parameters": { + "fillWith": [ + "WorkspaceId" + ], + "label": "Workspace ID" + }, + "type": "CopyableLabel" + }, + { + "parameters": { + "fillWith": [ + "PrimaryKey" + ], + "label": "Primary Key" + }, + "type": "CopyableLabel" + } + ] + }, + { + "description": "Use this method for automated deployment of the ZeroFox CTI data connectors using an ARM Tempate.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinelCiscoUmbrellaazuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **ZeroFox Username**, **ZeroFox Personal Access Token**\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy.", + "title": "Option 1 - Azure Resource Manager (ARM) Template" + }, + { + "description": "Use the following step-by-step instructions to deploy the ZeroFox CTI data connector manually with Azure Functions (Deployment via Visual Studio Code).", + "title": "Option 2 - Manual Deployment of Azure Functions" + }, + { + "description": "**1. Deploy a Function App**\n\n> **NOTE:** You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/create-first-function-vs-code-python) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-ZeroFoxCTIConn-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. CTIXYZ).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft Sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + }, + { + "description": "**2. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): \n\t\tWorkspaceID\n\t\tWorkspaceKey\n\t\tS3Bucket\n\t\tAWSAccessKeyId\n\t\tAWSSecretAccessKey\n\t\tlogAnalyticsUri (optional)\n> - Use logAnalyticsUri to override the log analytics API endpoint for dedicated cloud. For example, for public cloud, leave the value empty; for Azure GovUS cloud environment, specify the value in the following format: `https://.ods.opinsights.azure.us`.\n3. Once all application settings have been entered, click **Save**." + } + ] + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', last(split(variables('_dataConnectorId1'),'/'))))]", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId1'))]", + "contentId": "[variables('_dataConnectorContentId1')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorVersion1')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_dataConnectorContentId1')]", + "contentKind": "DataConnector", + "displayName": "ZeroFox CTI (using Azure Functions)", + "contentProductId": "[variables('_dataConnectorcontentProductId1')]", + "id": "[variables('_dataConnectorcontentProductId1')]", + "version": "[variables('dataConnectorVersion1')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', last(split(variables('_dataConnectorId1'),'/'))))]", + "dependsOn": [ + "[variables('_dataConnectorId1')]" + ], + "location": "[parameters('workspace-location')]", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId1'))]", + "contentId": "[variables('_dataConnectorContentId1')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorVersion1')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId1'))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "GenericUI", + "properties": { + "connectorUiConfig": { + "title": "ZeroFox CTI (using Azure Functions)", + "publisher": "ZeroFox", + "descriptionMarkdown": "The ZeroFox CTI data connectors provide the capability to ingest the different [ZeroFox](https://www.zerofox.com/threat-intelligence/) cyber threat intelligence alerts into Microsoft Sentinel.", + "graphQueries": [ + { + "metricName": "ZeroFox CTI Advanced Dark Web Logs", + "legend": "ZeroFox_CTI_advanced_dark_web_CL", + "baseQuery": "ZeroFox_CTI_advanced_dark_web_CL" + }, + { + "metricName": "ZeroFox CTI Botnet Logs", + "baseQuery": "ZeroFox_CTI_botnet_CL" + }, + { + "metricName": "ZeroFox CTI Breaches Logs", + "legend": "ZeroFox_CTI_breaches_CL", + "baseQuery": "ZeroFox_CTI_breaches_CL" + }, + { + "metricName": "ZeroFox CTI C2 Domains Logs", + "legend": "ZeroFox_CTI_C2_CL", + "baseQuery": "ZeroFox_CTI_C2_CL" + }, + { + "metricName": "ZeroFox CTI Compromised Credentials Logs", + "legend": "ZeroFox_CTI_compromised_credentials_CL", + "baseQuery": "ZeroFox_CTI_compromised_credentials_CL" + }, + { + "metricName": "ZeroFox CTI Credit Cards Logs", + "legend": "ZeroFox_CTI_credit_cards_CL", + "baseQuery": "ZeroFox_CTI_credit_cards_CL" + }, + { + "metricName": "ZeroFox CTI Dark Web Logs", + "legend": "ZeroFox_CTI_dark_web_CL", + "baseQuery": "ZeroFox_CTI_dark_web_CL" + }, + { + "metricName": "ZeroFox CTI Discord Logs", + "legend": "ZeroFox_CTI_discord_CL", + "baseQuery": "ZeroFox_CTI_discord_CL" + }, + { + "metricName": "ZeroFox CTI Disruption Logs", + "legend": "ZeroFox_CTI_disruption_CL", + "baseQuery": "ZeroFox_CTI_disruption_CL" + }, + { + "metricName": "ZeroFox CTI Email Addresses Logs", + "legend": "ZeroFox_CTI_email_addresses_CL", + "baseQuery": "ZeroFox_CTI_email_addresses_CL" + }, + { + "metricName": "ZeroFox CTI Exploits Logs", + "legend": "ZeroFox_CTI_exploits_CL", + "baseQuery": "ZeroFox_CTI_exploits_CL" + }, + { + "metricName": "ZeroFox CTI Identity Breach Logs", + "legend": "ZeroFox_CTI_identity_breach_CL", + "baseQuery": "ZeroFox_CTI_identity_breach_CL" + }, + { + "metricName": "ZeroFox CTI IRC Logs", + "legend": "ZeroFox_CTI_irc_CL", + "baseQuery": "ZeroFox_CTI_irc_CL" + }, + { + "metricName": "ZeroFox CTI Malware Logs", + "legend": "ZeroFox_CTI_malware_CL", + "baseQuery": "ZeroFox_CTI_malware_CL" + }, + { + "metricName": "ZeroFox CTI National Ids Logs", + "legend": "ZeroFox_CTI_national_ids_CL", + "baseQuery": "ZeroFox_CTI_national_ids_CL" + }, + { + "metricName": "ZeroFox CTI Phishing Logs", + "legend": "ZeroFox_CTI_phishing_CL", + "baseQuery": "ZeroFox_CTI_phishing_CL" + }, + { + "metricName": "ZeroFox CTI Phone Numbers Logs", + "legend": "ZeroFox_CTI_phone_numbers_CL", + "baseQuery": "ZeroFox_CTI_phone_numbers_CL" + }, + { + "metricName": "ZeroFox CTI Ransomware Logs", + "legend": "ZeroFox_CTI_ransomware_CL", + "baseQuery": "ZeroFox_CTI_ransomware_CL" + }, + { + "metricName": "ZeroFox CTI Telegram Logs", + "legend": "ZeroFox_CTI_telegram_CL", + "baseQuery": "ZeroFox_CTI_telegram_CL" + }, + { + "metricName": "ZeroFox CTI Threat Actors Logs", + "legend": "ZeroFox_CTI_threat_actors_CL", + "baseQuery": "ZeroFox_CTI_threat_actors_CL" + }, + { + "metricName": "ZeroFox CTI Vulnerabilities Logs", + "legend": "ZeroFox_CTI_vulnerabilities_CL", + "baseQuery": "ZeroFox_CTI_vulnerabilities_CL" + } + ], + "dataTypes": [ + { + "name": "ZeroFox_CTI_advanced_dark_web_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_advanced_dark_web_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_botnet_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_botnet_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_breaches_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_breaches_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_C2_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_C2_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_compromised_credentials_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_compromised_credentials_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_credit_cards_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_credit_cards_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_dark_web_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_dark_web_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_discord_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_discord_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_disruption_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_disruption_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_email_addresses_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_email_addresses_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_exploits_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_exploits_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_identity_breach_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_identity_breach_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_irc_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_irc_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_malware_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_malware_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_national_ids_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_national_ids_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_phishing_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_phishing_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_phone_numbers_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_phone_numbers_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_ransomware_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_ransomware_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_telegram_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_telegram_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_threat_actors_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_threat_actors_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + }, + { + "name": "ZeroFox_CTI_vulnerabilities_CL", + "lastDataReceivedQuery": "ZeroFox_CTI_vulnerabilities_CL\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriterias": [ + { + "type": "IsConnectedQuery", + "value": [ + "ZeroFox_CTI_advanced_dark_web_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_botnet_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_breaches_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_C2_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_compromised_credentials_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_credit_cards_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_dark_web_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_discord_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_disruption_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_email_addresses_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_exploits_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_identity_breach_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_irc_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_malware_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_national_ids_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_phishing_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_phone_numbers_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_ransomware_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_telegram_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_threat_actors_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)", + "ZeroFox_CTI_vulnerabilities_CL\n | summarize LastLogReceived = max(TimeGenerated)\n | project IsConnected = LastLogReceived > ago(1d)" + ] + } + ], + "sampleQueries": [ + { + "description": "ZeroFox CTI C2-domains Logs", + "query": "ZeroFox_CTI_C2_CL\n | sort by TimeGenerated desc" + }, + { + "description": "ZeroFox CTI Email Addresses Logs", + "query": "ZeroFox_CTI_email_addresses_CL\n | sort by TimeGenerated desc" + }, + { + "description": "ZeroFox CTI Malware Logs", + "query": "ZeroFox_CTI_malware_CL\n | sort by TimeGenerated desc" + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/workspaces", + "permissionsDisplayText": "read and write permissions on the workspace are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "write": true, + "read": true, + "delete": true + } + }, + { + "provider": "Microsoft.OperationalInsights/workspaces/sharedKeys", + "permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key).", + "providerDisplayName": "Keys", + "scope": "Workspace", + "requiredPermissions": { + "action": true + } + } + ], + "customs": [ + { + "name": "Microsoft.Web/sites permissions", + "description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)." + }, + { + "name": "ZeroFox API Credentials/permissions", + "description": "**ZeroFox Username**, **ZeroFox Personal Access Token** are required for ZeroFox CTI REST API." + } + ] + }, + "instructionSteps": [ + { + "description": ">**NOTE:** This connector uses Azure Functions to connect to the ZeroFox CTI REST API to pull logs into Microsoft Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details." + }, + { + "description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App." + }, + { + "description": "**STEP 1 - Retrieval of ZeroFox credentials:**\n\n Follow these instructions for set up logging and obtain credentials." + }, + { + "description": "**STEP 1.1 - [Log into ZeroFox's website.](https://cloud.zerofox.com/login) using your username and password" + }, + { + "description": "**STEP 1.2 - Click into the Settings button and go to the Data Connectors Section." + }, + { + "description": "**STEP 1.3 - Select the API DATA FEEDS tab and head to the bottom of the page, select <> in the API Information box, to obtain a Personal Access Token to be used along with your username." + }, + { + "description": "**STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the ZeroFox CTI data connector, have the Workspace ID and Workspace Primary Key (can be copied from the following), as well as the Amazon S3 REST API Authorization credentials, readily available.", + "instructions": [ + { + "parameters": { + "fillWith": [ + "WorkspaceId" + ], + "label": "Workspace ID" + }, + "type": "CopyableLabel" + }, + { + "parameters": { + "fillWith": [ + "PrimaryKey" + ], + "label": "Primary Key" + }, + "type": "CopyableLabel" + } + ] + }, + { + "description": "Use this method for automated deployment of the ZeroFox CTI data connectors using an ARM Tempate.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://aka.ms/sentinelCiscoUmbrellaazuredeploy)\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **ZeroFox Username**, **ZeroFox Personal Access Token**\n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**.\n5. Click **Purchase** to deploy.", + "title": "Option 1 - Azure Resource Manager (ARM) Template" + }, + { + "description": "Use the following step-by-step instructions to deploy the ZeroFox CTI data connector manually with Azure Functions (Deployment via Visual Studio Code).", + "title": "Option 2 - Manual Deployment of Azure Functions" + }, + { + "description": "**1. Deploy a Function App**\n\n> **NOTE:** You will need to [prepare VS code](https://docs.microsoft.com/azure/azure-functions/create-first-function-vs-code-python) for Azure function development.\n\n1. Download the [Azure Function App](https://aka.ms/sentinel-ZeroFoxCTIConn-functionapp) file. Extract archive to your local development computer.\n2. Start VS Code. Choose File in the main menu and select Open Folder.\n3. Select the top level folder from extracted files.\n4. Choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose the **Deploy to function app** button.\nIf you aren't already signed in, choose the Azure icon in the Activity bar, then in the **Azure: Functions** area, choose **Sign in to Azure**\nIf you're already signed in, go to the next step.\n5. Provide the following information at the prompts:\n\n\ta. **Select folder:** Choose a folder from your workspace or browse to one that contains your function app.\n\n\tb. **Select Subscription:** Choose the subscription to use.\n\n\tc. Select **Create new Function App in Azure** (Don't choose the Advanced option)\n\n\td. **Enter a globally unique name for the function app:** Type a name that is valid in a URL path. The name you type is validated to make sure that it's unique in Azure Functions. (e.g. CTIXYZ).\n\n\te. **Select a runtime:** Choose Python 3.8.\n\n\tf. Select a location for new resources. For better performance and lower costs choose the same [region](https://azure.microsoft.com/regions/) where Microsoft Sentinel is located.\n\n6. Deployment will begin. A notification is displayed after your function app is created and the deployment package is applied.\n7. Go to Azure Portal for the Function App configuration." + }, + { + "description": "**2. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following application settings individually, with their respective string values (case-sensitive): \n\t\tWorkspaceID\n\t\tWorkspaceKey\n\t\tS3Bucket\n\t\tAWSAccessKeyId\n\t\tAWSSecretAccessKey\n\t\tlogAnalyticsUri (optional)\n> - Use logAnalyticsUri to override the log analytics API endpoint for dedicated cloud. For example, for public cloud, leave the value empty; for Azure GovUS cloud environment, specify the value in the following format: `https://.ods.opinsights.azure.us`.\n3. Once all application settings have been entered, click **Save**." + } + ], + "id": "[variables('_uiConfigId1')]" + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('dataConnectorTemplateSpecName2')]", + "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], + "properties": { + "description": "ZeroFox data connector with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('dataConnectorVersion2')]", + "parameters": {}, + "variables": {}, + "resources": [ + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId2'))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "APIPolling", + "properties": { + "connectorUiConfig": { + "id": "[variables('_uiConfigId2')]", + "title": "ZeroFox Enterprise - Alerts (Polling CCP)", + "publisher": "ZeroFox Enterprise", + "descriptionMarkdown": "Collects alerts from ZeroFox API.", + "graphQueriesTableName": "ZeroFoxAlertPoller_CL", + "graphQueries": [ + { + "metricName": "Total alerts received", + "legend": "ZeroFox Alerts", + "baseQuery": "{{graphQueriesTableName}}" + } + ], + "sampleQueries": [ + { + "description": "List all ZeroFox alerts", + "query": "{{graphQueriesTableName}}\n| sort by TimeGenerated asc" + }, + { + "description": "Count alerts by network type", + "query": "{{graphQueriesTableName}}\n| summarize Count = count() by ThreatSource=network_s" + }, + { + "description": "Count alerts by entity", + "query": "{{graphQueriesTableName}}\n| summarize Count = count() by Entity=entity_name_s" + } + ], + "dataTypes": [ + { + "name": "{{graphQueriesTableName}}", + "lastDataReceivedQuery": "{{graphQueriesTableName}}\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriteria": [ + { + "type": "SentinelKindsV2", + "value": [ + "APIPolling" + ] + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/solutions", + "permissionsDisplayText": "read and write permissions are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "action": true, + "write": true, + "read": true, + "delete": true + } + } + ], + "customs": [ + { + "name": "ZeroFox Personal Access Token (PAT)", + "description": "A ZeroFox PAT is required. You can get it in Data Connectors > [API Data Feeds](https://cloud.zerofox.com/data_connectors/api)." + } + ] + }, + "instructionSteps": [ + { + "description": "Provide your ZeroFox PAT", + "instructions": [ + { + "type": "APIKey" + } + ], + "title": "Connect ZeroFox to Microsoft Sentinel" + } + ] + }, + "pollingConfig": { + "auth": { + "authType": "APIKey", + "APIKeyName": "Authorization", + "APIKeyIdentifier": "Token" + }, + "request": { + "apiEndpoint": "https://api.zerofox.com/1.0/alerts/", + "httpMethod": "Get", + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "min_timestamp", + "endTimeAttributeName": "max_timestamp", + "queryParameters": { + "sort_direction": "asc" + } + }, + "response": { + "eventsJsonPaths": [ + "$.alerts[*]" + ] + }, + "paging": { + "pagingType": "Offset", + "offsetParaName": "offset", + "pageSizeParaName": "limit", + "pageSize": 100 + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', last(split(variables('_dataConnectorId2'),'/'))))]", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId2'))]", + "contentId": "[variables('_dataConnectorContentId2')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorVersion2')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + } + ] + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_dataConnectorContentId2')]", + "contentKind": "DataConnector", + "displayName": "ZeroFox Enterprise - Alerts (Polling CCP)", + "contentProductId": "[variables('_dataConnectorcontentProductId2')]", + "id": "[variables('_dataConnectorcontentProductId2')]", + "version": "[variables('dataConnectorVersion2')]" + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2023-04-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('DataConnector-', last(split(variables('_dataConnectorId2'),'/'))))]", + "dependsOn": [ + "[variables('_dataConnectorId2')]" + ], + "location": "[parameters('workspace-location')]", + "properties": { + "parentId": "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/dataConnectors', variables('_dataConnectorContentId2'))]", + "contentId": "[variables('_dataConnectorContentId2')]", + "kind": "DataConnector", + "version": "[variables('dataConnectorVersion2')]", + "source": { + "kind": "Solution", + "name": "ZeroFox", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Microsoft", + "email": "[variables('_email')]" + }, + "support": { + "tier": "Microsoft", + "name": "Microsoft Corporation", + "email": "support@microsoft.com", + "link": "https://support.microsoft.com/" + } + } + }, + { + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',variables('_dataConnectorContentId2'))]", + "apiVersion": "2021-03-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/dataConnectors", + "location": "[parameters('workspace-location')]", + "kind": "APIPolling", + "properties": { + "connectorUiConfig": { + "id": "[variables('_uiConfigId2')]", + "title": "ZeroFox Enterprise - Alerts (Polling CCP)", + "publisher": "ZeroFox Enterprise", + "descriptionMarkdown": "Collects alerts from ZeroFox API.", + "graphQueriesTableName": "ZeroFoxAlertPoller_CL", + "graphQueries": [ + { + "metricName": "Total alerts received", + "legend": "ZeroFox Alerts", + "baseQuery": "{{graphQueriesTableName}}" + } + ], + "sampleQueries": [ + { + "description": "List all ZeroFox alerts", + "query": "{{graphQueriesTableName}}\n| sort by TimeGenerated asc" + }, + { + "description": "Count alerts by network type", + "query": "{{graphQueriesTableName}}\n| summarize Count = count() by ThreatSource=network_s" + }, + { + "description": "Count alerts by entity", + "query": "{{graphQueriesTableName}}\n| summarize Count = count() by Entity=entity_name_s" + } + ], + "dataTypes": [ + { + "name": "{{graphQueriesTableName}}", + "lastDataReceivedQuery": "{{graphQueriesTableName}}\n | summarize Time = max(TimeGenerated)\n | where isnotempty(Time)" + } + ], + "connectivityCriteria": [ + { + "type": "SentinelKindsV2", + "value": [ + "APIPolling" + ] + } + ], + "availability": { + "status": 1, + "isPreview": false + }, + "permissions": { + "resourceProvider": [ + { + "provider": "Microsoft.OperationalInsights/solutions", + "permissionsDisplayText": "read and write permissions are required.", + "providerDisplayName": "Workspace", + "scope": "Workspace", + "requiredPermissions": { + "action": true, + "write": true, + "read": true, + "delete": true + } + } + ], + "customs": [ + { + "name": "ZeroFox Personal Access Token (PAT)", + "description": "A ZeroFox PAT is required. You can get it in Data Connectors > [API Data Feeds](https://cloud.zerofox.com/data_connectors/api)." + } + ] + }, + "instructionSteps": [ + { + "description": "Provide your ZeroFox PAT", + "instructions": [ + { + "type": "APIKey" + } + ], + "title": "Connect ZeroFox to Microsoft Sentinel" + } + ] + }, + "pollingConfig": { + "auth": { + "authType": "APIKey", + "APIKeyName": "Authorization", + "APIKeyIdentifier": "Token" + }, + "request": { + "apiEndpoint": "https://api.zerofox.com/1.0/alerts/", + "httpMethod": "Get", + "queryTimeFormat": "yyyy-MM-ddTHH:mm:ssZ", + "startTimeAttributeName": "min_timestamp", + "endTimeAttributeName": "max_timestamp", + "queryParameters": { + "sort_direction": "asc" + } + }, + "response": { + "eventsJsonPaths": [ + "$.alerts[*]" + ] + }, + "paging": { + "pagingType": "Offset", + "offsetParaName": "offset", + "pageSizeParaName": "limit", + "pageSize": 100 + } + } + } + }, { "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", "apiVersion": "2023-04-01-preview", @@ -105,10 +1219,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "ZeroFox_Alert_Polling", "dataTypes": [ "ZeroFoxAlertPoller_CL" - ], - "connectorId": "ZeroFox_Alert_Polling" + ] } ], "entityMappings": [ @@ -206,10 +1320,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "ZeroFox_Alert_Polling", "dataTypes": [ "ZeroFoxAlertPoller_CL" - ], - "connectorId": "ZeroFox_Alert_Polling" + ] } ], "entityMappings": [ @@ -307,10 +1421,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "ZeroFox_Alert_Polling", "dataTypes": [ "ZeroFoxAlertPoller_CL" - ], - "connectorId": "ZeroFox_Alert_Polling" + ] } ], "entityMappings": [ @@ -408,10 +1522,10 @@ "status": "Available", "requiredDataConnectors": [ { + "connectorId": "ZeroFox_Alert_Polling", "dataTypes": [ "ZeroFoxAlertPoller_CL" - ], - "connectorId": "ZeroFox_Alert_Polling" + ] } ], "entityMappings": [ @@ -482,7 +1596,7 @@ "contentSchemaVersion": "3.0.0", "displayName": "ZeroFox", "publisherDisplayName": "Microsoft Sentinel, Microsoft Corporation", - "descriptionHtml": "

Note: There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The ZeroFox solution for Microsoft Sentinel enables you to ingest ZeroFox Alerts and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n
    \n
  1. Azure Monitor HTTP Data Collector API

    \n
  2. \n
  3. Azure Functions

    \n
  4. \n
\n

Analytic Rules: 4

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", + "descriptionHtml": "

Note: There may be known issues pertaining to this Solution, please refer to them before installing.

\n

The ZeroFox solution for Microsoft Sentinel enables you to ingest ZeroFox Alerts and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API.

\n

Underlying Microsoft Technologies used:

\n

This solution takes a dependency on the following technologies, and some of these dependencies either may be in Preview state or might result in additional ingestion or operational costs:

\n
    \n
  1. Azure Monitor HTTP Data Collector API

    \n
  2. \n
  3. Azure Functions

    \n
  4. \n
\n

Data Connectors: 2, Analytic Rules: 4

\n

Learn more about Microsoft Sentinel | Learn more about Solutions

\n", "contentKind": "Solution", "contentProductId": "[variables('_solutioncontentProductId')]", "id": "[variables('_solutioncontentProductId')]", @@ -507,6 +1621,16 @@ "dependencies": { "operator": "AND", "criteria": [ + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentId1')]", + "version": "[variables('dataConnectorVersion1')]" + }, + { + "kind": "DataConnector", + "contentId": "[variables('_dataConnectorContentId2')]", + "version": "[variables('dataConnectorVersion2')]" + }, { "kind": "AnalyticsRule", "contentId": "[variables('analyticRulecontentId1')]", From 5c154a41a1ab14e37b1fc5a8a6e443735c1b9e1c Mon Sep 17 00:00:00 2001 From: Felipe Garrido Date: Tue, 5 Sep 2023 10:36:24 -0300 Subject: [PATCH 22/23] Add required tactics and techniques (#42) --- .../ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml | 6 ++++++ .../Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml | 6 ++++++ .../ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml | 6 ++++++ .../Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml index 035ed6ecc43..2f15873cb34 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_HighSeverityRule.yaml @@ -13,6 +13,12 @@ queryPeriod: 5m triggerOperator: gt triggerThreshold: 0 tactics: + - ResourceDevelopment + - InitialAccess +relevantTechniques: + - T1583 + - T1586 + - T1566 query: | ZeroFoxAlertPoller_CL | where Severity in (5) diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml index f4b48872d53..b27fcbd22d6 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_InformationalSeverityRule.yaml @@ -13,6 +13,12 @@ queryPeriod: 5m triggerOperator: gt triggerThreshold: 0 tactics: + - ResourceDevelopment + - InitialAccess +relevantTechniques: + - T1583 + - T1586 + - T1566 query: | ZeroFoxAlertPoller_CL | where Severity in (1,2) diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml index eba3d6f6f64..d36f396b2b3 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_LowSeverityRule.yaml @@ -13,6 +13,12 @@ queryPeriod: 5m triggerOperator: gt triggerThreshold: 0 tactics: + - ResourceDevelopment + - InitialAccess +relevantTechniques: + - T1583 + - T1586 + - T1566 query: | ZeroFoxAlertPoller_CL | where Severity in (3) diff --git a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml index c16ea5de071..17e586c53fd 100644 --- a/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml +++ b/Solutions/ZeroFox/Analytic Rules/ZF_Alerts_MediumSeverityRule.yaml @@ -13,6 +13,12 @@ queryPeriod: 5m triggerOperator: gt triggerThreshold: 0 tactics: + - ResourceDevelopment + - InitialAccess +relevantTechniques: + - T1583 + - T1586 + - T1566 query: | ZeroFoxAlertPoller_CL | where Severity in (4) From 42a18d3213f6751cef7b0aa06f61641da51d9127 Mon Sep 17 00:00:00 2001 From: Diego Ramirez R Date: Tue, 5 Sep 2023 11:50:35 -0300 Subject: [PATCH 23/23] Repackage solution to include analytic rules tactics (#43) Co-authored-by: Diego Ramirez --- Solutions/ZeroFox/Data/Solution_ZeroFox.json | 2 +- Solutions/ZeroFox/Package/3.0.0.zip | Bin 11803 -> 11866 bytes Solutions/ZeroFox/Package/mainTemplate.json | 54 +++++++++++++++---- 3 files changed, 46 insertions(+), 10 deletions(-) diff --git a/Solutions/ZeroFox/Data/Solution_ZeroFox.json b/Solutions/ZeroFox/Data/Solution_ZeroFox.json index 2653e26d97d..e1c86233adf 100644 --- a/Solutions/ZeroFox/Data/Solution_ZeroFox.json +++ b/Solutions/ZeroFox/Data/Solution_ZeroFox.json @@ -2,7 +2,7 @@ "Name": "ZeroFox", "Author": "Microsoft - support@microsoft.com", "Logo": "", - "Description": "The [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events]](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)", + "Description": "The [ZeroFox](https://www.zerofox.com/) solution for Microsoft Sentinel enables you to ingest [ZeroFox Alerts](https://www.zerofox.com/platform/) and [ZeroFox CTI events](https://www.zerofox.com/threat-intelligence/) into Microsoft Sentinel using the ZeroFox API. \n\n**Underlying Microsoft Technologies used:**\n\nThis solution takes a dependency on the following technologies, and some of these dependencies either may be in [Preview](https://azure.microsoft.com/support/legal/preview-supplemental-terms/) state or might result in additional ingestion or operational costs:\n\na. [Azure Monitor HTTP Data Collector API](https://docs.microsoft.com/azure/azure-monitor/logs/data-collector-api)\n\nb. [Azure Functions](https://azure.microsoft.com/services/functions/#overview)", "Data Connectors": [ "Data Connectors/CTI/ZeroFox_CTI_FunctionApp.json", "Data Connectors/Alerts/alerts_connector.json" diff --git a/Solutions/ZeroFox/Package/3.0.0.zip b/Solutions/ZeroFox/Package/3.0.0.zip index 4b0c6173e49f219455f850c1494df4c27abbec62..9a8b1fde9a7a4533f7f0956eb551eea2e71f96e5 100644 GIT binary patch delta 9777 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 LH3q;f000006$jT% delta 9699 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