diff --git a/Solutions/Intel471/Data/Solution_Intel471.json b/Solutions/Intel471/Data/Solution_Intel471.json index 5478d77b7ba..2b00b82af97 100644 --- a/Solutions/Intel471/Data/Solution_Intel471.json +++ b/Solutions/Intel471/Data/Solution_Intel471.json @@ -2,13 +2,14 @@ "Name": "Intel471", "Author": "Intel 471 Inc.", "Logo": "", - "Description": "Intel 471 Threat Intelligence integration ingests malware indicators into Microsoft Graph Security.", + "Description": "Intel 471 Threat Intelligence integration ingests malware indicators into Log Analytics workspace.", "PlaybooksBladeDescription": "This solution installs the following Playbook templates. After installing the solution, playbooks can be managed in the Manage solution view. ", "Playbooks": [ + "Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/azuredeploy.json", "Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/azuredeploy.json" ], "BasePath": "/Users/mmolenda/Code/Azure-Sentinel", - "Version": "2.0.0", + "Version": "3.0.0", "Metadata": "SolutionMetadata.json", "TemplateSpec": true, "Is1PConnector": false diff --git a/Solutions/Intel471/Package/3.0.0.zip b/Solutions/Intel471/Package/3.0.0.zip new file mode 100644 index 00000000000..92d73ba112f Binary files /dev/null and b/Solutions/Intel471/Package/3.0.0.zip differ diff --git a/Solutions/Intel471/Package/createUiDefinition.json b/Solutions/Intel471/Package/createUiDefinition.json index fe52a368e3c..9a59bb4f4aa 100644 --- a/Solutions/Intel471/Package/createUiDefinition.json +++ b/Solutions/Intel471/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\nIntel 471 Threat Intelligence integration ingests malware indicators into Microsoft Graph Security.\n\n**Playbooks:** 1\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", + "description": "\n\n**Note:** Please refer to the following before installing the solution: \r \n • Review the solution [Release Notes](https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Intel471/ReleaseNotes.md)\r \n • There may be [known issues](https://aka.ms/sentinelsolutionsknownissues) pertaining to this Solution, please refer to them before installing.\n\nIntel 471 Threat Intelligence integration ingests malware indicators into Log Analytics workspace.\n\n**Playbooks:** 2\n\n[Learn more about Microsoft Sentinel](https://aka.ms/azuresentinel) | [Learn more about Solutions](https://aka.ms/azuresentinelsolutionsdoc)", "subscription": { "resourceProviders": [ "Microsoft.OperationsManagement/solutions", diff --git a/Solutions/Intel471/Package/mainTemplate.json b/Solutions/Intel471/Package/mainTemplate.json index cb8c135761e..3cae5c4ab8f 100644 --- a/Solutions/Intel471/Package/mainTemplate.json +++ b/Solutions/Intel471/Package/mainTemplate.json @@ -30,72 +30,66 @@ } }, "variables": { + "_solutionName": "Intel471", + "_solutionVersion": "3.0.0", "solutionId": "intel471inc1641226539011.microsoft-sentinel-solution-intel471", "_solutionId": "[variables('solutionId')]", - "Intel471-ImportMalwareIntelligenceToSentinel": "Intel471-ImportMalwareIntelligenceToSentinel", - "_Intel471-ImportMalwareIntelligenceToSentinel": "[variables('Intel471-ImportMalwareIntelligenceToSentinel')]", + "Intel471-ImportMalwareIntelligenceToGraphSecurity": "Intel471-ImportMalwareIntelligenceToGraphSecurity", + "_Intel471-ImportMalwareIntelligenceToGraphSecurity": "[variables('Intel471-ImportMalwareIntelligenceToGraphSecurity')]", "TemplateEmptyArray": "[json('[]')]", "blanks": "[replace('b', 'b', '')]", "playbookVersion1": "1.0", - "playbookContentId1": "Intel471-ImportMalwareIntelligenceToSentinel", + "playbookContentId1": "Intel471-ImportMalwareIntelligenceToGraphSecurity", "_playbookContentId1": "[variables('playbookContentId1')]", "playbookId1": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId1'))]", - "playbookTemplateSpecName1": "[concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId1')))]", - "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]" + "playbookTemplateSpecName1": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId1'))))]", + "workspaceResourceId": "[resourceId('microsoft.OperationalInsights/Workspaces', parameters('workspace'))]", + "_playbookcontentProductId1": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId1'),'-', variables('playbookVersion1'))))]", + "Intel471-ImportMalwareIntelligenceToSentinel": "Intel471-ImportMalwareIntelligenceToSentinel", + "_Intel471-ImportMalwareIntelligenceToSentinel": "[variables('Intel471-ImportMalwareIntelligenceToSentinel')]", + "playbookVersion2": "1.0", + "playbookContentId2": "Intel471-ImportMalwareIntelligenceToSentinel", + "_playbookContentId2": "[variables('playbookContentId2')]", + "playbookId2": "[resourceId('Microsoft.Logic/workflows', variables('playbookContentId2'))]", + "playbookTemplateSpecName2": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat(parameters('workspace'),'-pl-',uniquestring(variables('_playbookContentId2'))))]", + "_playbookcontentProductId2": "[concat(take(variables('_solutionId'),50),'-','pl','-', uniqueString(concat(variables('_solutionId'),'-','Playbook','-',variables('_playbookContentId2'),'-', variables('playbookVersion2'))))]", + "_solutioncontentProductId": "[concat(take(variables('_solutionId'),50),'-','sl','-', uniqueString(concat(variables('_solutionId'),'-','Solution','-',variables('_solutionId'),'-', variables('_solutionVersion'))))]" }, "resources": [ { - "type": "Microsoft.Resources/templateSpecs", - "apiVersion": "2022-02-01", + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", "name": "[variables('playbookTemplateSpecName1')]", "location": "[parameters('workspace-location')]", - "tags": { - "hidden-sentinelWorkspaceId": "[variables('workspaceResourceId')]", - "hidden-sentinelContentType": "Playbook" - }, - "properties": { - "description": "Intel471-ImportMalwareIntelligenceToSentinel playbook", - "displayName": "Intel471-ImportMalwareIntelligenceToSentinel playbook" - } - }, - { - "type": "Microsoft.Resources/templateSpecs/versions", - "apiVersion": "2022-02-01", - "name": "[concat(variables('playbookTemplateSpecName1'),'/',variables('playbookVersion1'))]", - "location": "[parameters('workspace-location')]", - "tags": { - "hidden-sentinelWorkspaceId": "[variables('workspaceResourceId')]", - "hidden-sentinelContentType": "Playbook" - }, "dependsOn": [ - "[resourceId('Microsoft.Resources/templateSpecs', variables('playbookTemplateSpecName1'))]" + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" ], "properties": { - "description": "Intel471-ImportMalwareIntelligenceToSentinel Playbook with template version 2.0.0", + "description": "Intel471-ImportMalwareIntelligenceToGraphSecurity Playbook with template version 3.0.0", "mainTemplate": { "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", "contentVersion": "[variables('playbookVersion1')]", "parameters": { "PlaybookName": { - "defaultValue": "Intel471-ImportMalwareIntelligenceToSentinel", + "defaultValue": "Intel471-ImportMalwareIntelligenceToGraphSecurity", "type": "String" }, "StorageAccountName": { "type": "String", "metadata": { - "description": "Pre-existing Storage Account where Logic App can store helper data." + "description": "Name of the pre-existing Storage Account where Logic App can store helper data." } }, "StorageAccountContainerName": { "type": "String", "metadata": { - "description": "Pre-existing Container inside provided Storage Account." + "description": "Name of the pre-existing blob container inside provided Storage Account." } }, "KeyVaultName": { "type": "String", "metadata": { - "description": "Pre-existing Key Vault with Titan API credentials. The credentials are expected under following keys: TitanUserName nad TitanAPIKey" + "description": "Name of the pre-existing Key Vault with Titan API credentials. The credentials are expected under following keys: TitanUserNameGraph nad TitanAPIKeyGraph" } }, "TargetProduct": { @@ -218,11 +212,11 @@ "type": "String" }, "BlobNameCursor": { - "defaultValue": "cursor.txt", + "defaultValue": "cursorGraph.txt", "type": "String" }, "BlobNameFromDate": { - "defaultValue": "fromdate.txt", + "defaultValue": "fromdateGraph.txt", "type": "String" }, "LookBackDays": { @@ -409,7 +403,7 @@ "username": "@body('GetUsername')?['value']" }, "headers": { - "User-Agent": "Intel 471 - Malware Intelligence - Azure Logic App 1.0.0" + "User-Agent": "Intel 471 - Malware Intelligence Graph - Azure Logic App 1.0.0" }, "method": "GET", "queries": "@if(equals(variables('cursor'), 'null'), variables('payload'), setProperty(variables('payload'), 'cursor', variables('cursor')))", @@ -706,7 +700,7 @@ } }, "method": "get", - "path": "/secrets/@{encodeURIComponent('TitanAPIKey')}/value" + "path": "/secrets/@{encodeURIComponent('TitanAPIKeyGraph')}/value" }, "runAfter": { "GetUsername": [ @@ -763,7 +757,7 @@ } }, "method": "get", - "path": "/secrets/@{encodeURIComponent('TitanUserName')}/value" + "path": "/secrets/@{encodeURIComponent('TitanUserNameGraph')}/value" }, "type": "ApiConnection" }, @@ -1058,13 +1052,14 @@ } ], "metadata": { - "title": "Intel 471 Malware Intelligence", - "description": "This playbook ingests malware indicators from Intel 471's Titan API into ThreatIntelligenceIndicator table.", + "title": "Intel 471 Malware Intelligence to Graph Security", + "description": "This playbook ingests malware indicators from Intel 471's Titan API into Microsoft Graph Security as tiIndicator resource type.", "prerequisites": [ "1. An active account in Titan platform, which is available as part of Intel 471's subscriptions. For more information, please contact sales@intel471.com.", "2. Titan API credentials.", - "3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserName` and `TitanAPIKey` keys.", - "4. Pre-existing [Blob storage](https://docs.microsoft.com/azure/storage/blobs/storage-blobs-introduction) with blob container for persisting data such as cursor between the API calls." + "3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserNameGraph` and `TitanAPIKeyGraph` keys.", + "4. Pre-existing [Storage account](https://learn.microsoft.com/azure/storage/common/storage-account-overview) with blob container already created for persisting data such as cursor between the API calls.", + "5. Threat Intelligence connector enabled in Sentinel. Go to Sentinel instance → 'Content hub' and install 'Threat Intelligence' solution." ], "postDeployment": [ "1. Go to the Key Vault. Select `Access control (IAM)` → `+ Add` → `Add role assignment`. Choose `Key Vault Secrets User`. On the next screen hit `+ Select members`, search for Intel 471 and select newly created logic app. Select it and proceed with granting access rights.", @@ -1089,40 +1084,1080 @@ ] } } - } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId1')]", + "contentKind": "Playbook", + "displayName": "Intel471-ImportMalwareIntelligenceToGraphSecurity", + "contentProductId": "[variables('_playbookcontentProductId1')]", + "id": "[variables('_playbookcontentProductId1')]", + "version": "[variables('playbookVersion1')]" } }, { - "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", - "apiVersion": "2022-01-01-preview", + "type": "Microsoft.OperationalInsights/workspaces/providers/contentTemplates", + "apiVersion": "2023-04-01-preview", + "name": "[variables('playbookTemplateSpecName2')]", "location": "[parameters('workspace-location')]", + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.OperationalInsights/workspaces', parameters('workspace')), 'Microsoft.SecurityInsights/contentPackages', variables('_solutionId'))]" + ], "properties": { - "version": "2.0.0", - "kind": "Solution", - "contentSchemaVersion": "2.0.0", - "contentId": "[variables('_solutionId')]", - "parentId": "[variables('_solutionId')]", - "source": { - "kind": "Solution", - "name": "Intel471", - "sourceId": "[variables('_solutionId')]" - }, - "author": { - "name": "Intel 471 Inc." - }, - "support": { - "name": "Intel 471", - "email": "support@intel471.com", - "tier": "Partner", - "link": "https://intel471.com/company/contact" - }, - "dependencies": { - "operator": "AND", - "criteria": [ + "description": "Intel471-ImportMalwareIntelligenceToSentinel Playbook with template version 3.0.0", + "mainTemplate": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "[variables('playbookVersion2')]", + "parameters": { + "PlaybookName": { + "defaultValue": "Intel471-ImportMalwareIntelligenceToSentinel", + "type": "String" + }, + "StorageAccountName": { + "type": "String", + "metadata": { + "description": "Name of the pre-existing Storage Account where Logic App can store helper data." + } + }, + "StorageAccountContainerName": { + "type": "String", + "metadata": { + "description": "Name of the pre-existing blob container inside provided Storage Account." + } + }, + "KeyVaultName": { + "type": "String", + "metadata": { + "description": "Name of the pre-existing Key Vault with Titan API credentials. The credentials are expected under following keys: TitanUserNameSentinal nad TitanAPIKeySentinel" + } + }, + "WorkspaceID": { + "type": "String", + "metadata": { + "description": "ID of the Log Analytics workspace to which the indicators will be directed." + } + }, + "LookBackDays": { + "defaultValue": 0, + "type": "int", + "metadata": { + "description": "How many days of history should be pulled on the first run. Leave 0 to start from the current time." + } + } + }, + "variables": { + "MicrosoftSentinelConnectionName": "[[concat('sentinel-', parameters('PlaybookName'))]", + "AzureBlobConnectionName": "[[concat('azureblob-', parameters('PlaybookName'))]", + "StorageAccountName": "[[parameters('StorageAccountName')]", + "StorageAccountContainerName": "[[parameters('StorageAccountContainerName')]", + "AzureKeyVaultName": "[[parameters('KeyVaultName')]", + "AzureKeyVaultConnectionName": "[[concat('keyvault-', parameters('PlaybookName'))]", + "connection-1": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('workspace-location-inline'), '/managedApis/Azuresentinel')]", + "_connection-1": "[[variables('connection-1')]", + "connection-2": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('workspace-location-inline'), '/managedApis/azureblob')]", + "_connection-2": "[[variables('connection-2')]", + "connection-3": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('workspace-location-inline'), '/managedApis/keyvault')]", + "_connection-3": "[[variables('connection-3')]", + "workspace-location-inline": "[concat('[resourceGroup().locatio', 'n]')]", + "workspace-name": "[parameters('workspace')]", + "workspaceResourceId": "[[resourceId('microsoft.OperationalInsights/Workspaces', variables('workspace-name'))]" + }, + "resources": [ { - "kind": "Playbook", - "contentId": "[variables('_Intel471-ImportMalwareIntelligenceToSentinel')]", - "version": "[variables('playbookVersion1')]" + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[[variables('MicrosoftSentinelConnectionName')]", + "location": "[[variables('workspace-location-inline')]", + "kind": "V1", + "properties": { + "displayName": "[[variables('MicrosoftSentinelConnectionName')]", + "parameterValueType": "Alternative", + "api": { + "id": "[[variables('_connection-1')]" + } + } + }, + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[[variables('AzureBlobConnectionName')]", + "location": "[[variables('workspace-location-inline')]", + "properties": { + "displayName": "[[variables('AzureBlobConnectionName')]", + "api": { + "id": "[[variables('_connection-2')]" + }, + "parameterValues": { + "accountName": "[[variables('StorageAccountName')]", + "accessKey": "[[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName')), '2022-09-01').keys[0].value]" + } + } + }, + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[[variables('AzureKeyVaultConnectionName')]", + "location": "[[variables('workspace-location-inline')]", + "kind": "V1", + "properties": { + "displayName": "[[variables('AzureKeyVaultConnectionName')]", + "api": { + "id": "[[variables('_connection-3')]" + }, + "parameterValueType": "Alternative", + "alternativeParameterValues": { + "vaultName": "[[variables('AzureKeyVaultName')]" + } + } + }, + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2017-07-01", + "name": "[[parameters('PlaybookName')]", + "location": "[[variables('workspace-location-inline')]", + "identity": { + "type": "SystemAssigned" + }, + "dependsOn": [ + "[[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]", + "[[resourceId('Microsoft.Web/connections', variables('AzureBlobConnectionName'))]", + "[[resourceId('Microsoft.Web/connections', variables('AzureKeyVaultConnectionName'))]" + ], + "tags": { + "LogicAppsCategory": "security", + "hidden-SentinelWorkspaceId": "[[variables('workspaceResourceId')]" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$connections": { + "type": "Object" + }, + "StorageAccountName": { + "defaultValue": "[variables('blanks')]", + "type": "String" + }, + "StorageAccountContainerName": { + "defaultValue": "[variables('blanks')]", + "type": "String" + }, + "BlobNameCursor": { + "defaultValue": "cursorSentinel.txt", + "type": "String" + }, + "BlobNameFromDate": { + "defaultValue": "fromdateSentinel.txt", + "type": "String" + }, + "LookBackDays": { + "defaultValue": 0, + "type": "int" + }, + "WorkspaceID": { + "type": "String" + } + }, + "triggers": { + "Recurrence": { + "recurrence": { + "frequency": "Hour", + "interval": 1 + }, + "runtimeConfiguration": { + "concurrency": { + "runs": 1 + } + }, + "type": "Recurrence" + } + }, + "actions": { + "CollectAndSubmitIndicators": { + "actions": { + "CursorNotNull": { + "actions": { + "StoreCursor": { + "runAfter": { + "UpdateCursor": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "body": "@string(variables('cursor'))", + "headers": { + "ReadFileMetadataFromServer": true + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "put", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameCursor')))}" + } + }, + "UpdateCursor": { + "type": "SetVariable", + "inputs": { + "name": "cursor", + "value": "@body('Parse_JSON')?['cursorNext']" + } + } + }, + "runAfter": { + "Parse_JSON": [ + "Succeeded" + ] + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@body('Parse_JSON')?['cursorNext']", + "@null" + ] + } + } + ] + }, + "type": "If" + }, + "FilterIndicators": { + "actions": { + "Each_indicator": { + "foreach": "@body('Parse_JSON')?['indicators']", + "actions": { + "Condition": { + "actions": { + "Append_indicator": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "collectedIndicators", + "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', ''), 'composedHashType', '')" + } + } + }, + "expression": { + "and": [ + { + "greater": [ + "@items('Each_indicator')?['data']?['expiration']", + "@div(sub(ticks(utcNow()),ticks('1970-01-01')), 10000)" + ] + } + ] + }, + "type": "If" + } + }, + "type": "Foreach" + } + }, + "runAfter": { + "SetHasResults": [ + "Succeeded" + ] + }, + "expression": { + "and": [ + { + "equals": [ + "@variables('hasResults')", + true + ] + } + ] + }, + "type": "If" + }, + "HTTP": { + "type": "Http", + "inputs": { + "authentication": { + "password": "@body('GetApiKey')?['value']", + "type": "Basic", + "username": "@body('GetUsername')?['value']" + }, + "headers": { + "User-Agent": "Intel 471 - Malware Intelligence Sentinel - Azure Logic App 1.0.0" + }, + "method": "GET", + "queries": "@if(equals(variables('cursor'), 'null'), variables('payload'), setProperty(variables('payload'), 'cursor', variables('cursor')))", + "uri": "https://api.intel471.com/v1/indicators/stream" + } + }, + "IfIndicators": { + "actions": { + "ClearCollectedIndicators": { + "runAfter": { + "Threat_Intelligence_-_Upload_Indicators_of_Compromise_(V2)_(Preview)": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "collectedIndicators", + "value": "[variables('TemplateEmptyArray')]" + } + }, + "MapIndicatorsToTiIndicators": { + "type": "Select", + "inputs": { + "from": "@variables('collectedIndicators')", + "select": { + "confidence": "@if(equals(item()?['data']?['confidence'], 'high'), 85, if(equals(item()?['data']?['confidence'], 'medium'), 50, 15))", + "created": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['activity']['first'], 1000), 'second')}", + "description": "@concat('Intel 471 - ', item()?['data']?['context']?['description'])", + "external_references": [ + { + "external_id": "@item()['data']['threat']['uid']", + "source_name": "intel471", + "url": "@concat('https://titan.intel471.com/malware/', item()['data']['threat']['uid'])" + } + ], + "id": "@concat('indicator--', guid())", + "indicator_types": [ + "malicious-activity" + ], + "kill_chain_phases": [ + { + "kill_chain_name": "lockheed-martin-cyber-kill-chain", + "phase_name": "@if(equals(item()?['data']?['mitre_tactics'], 'command_and_control'), 'C2', if(equals(item()?['data']?['mitre_tactics'], 'stage_capabilities'), 'Installation', if(equals(item()?['data']?['mitre_tactics'], 'initial_access'), 'Exploitation', '')))" + } + ], + "labels": "@createArray(item()['data']['threat']['data']['family'])", + "modified": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['last_updated'], 1000), 'second')}", + "name": "@item()['data']['threat']['data']['family']", + "object_marking_refs": [ + "marking-definition--f88d31f6-486f-44da-b317-01333bde0b82" + ], + "pattern": "@if(equals(item()?['data']?['indicator_type'], 'url'), concat('[url:value = ''', item()?['data']?['indicator_data']?['url'], ''']'), if(equals(item()?['data']?['indicator_type'], 'ipv4'), concat('[ipv4-addr:value = ''', item()?['data']?['indicator_data']?['address'], ''']'), concat('[file:hashes.md5 = ''', item()?['data']?['indicator_data']?['file']?['md5'], ''' OR file:hashes.sha1 = ''', item()?['data']?['indicator_data']?['file']?['sha1'], ''' OR file:hashes.sha256 = ''', item()?['data']?['indicator_data']?['file']?['sha256'], ''']') ))", + "pattern_type": "stix", + "spec_version": "2.1", + "type": "indicator", + "valid_from": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['activity']['first'], 1000), 'second')}", + "valid_until": "@addToTime('1970-01-01T00:00:00Z', div(item()?['data']['expiration'], 1000), 'second')" + } + } + }, + "Threat_Intelligence_-_Upload_Indicators_of_Compromise_(V2)_(Preview)": { + "runAfter": { + "MapIndicatorsToTiIndicators": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "body": { + "indicators": "@body('MapIndicatorsToTiIndicators')", + "sourcesystem": "Intel 471 Titan" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "post", + "path": "/V2/ThreatIntelligence/@{encodeURIComponent(parameters('WorkspaceID'))}/UploadIndicators/" + } + } + }, + "runAfter": { + "FilterIndicators": [ + "Succeeded" + ] + }, + "expression": { + "and": [ + { + "greater": [ + "@length(variables('collectedIndicators'))", + 0 + ] + } + ] + }, + "type": "If" + }, + "Parse_JSON": { + "runAfter": { + "HTTP": [ + "Succeeded" + ] + }, + "type": "ParseJson", + "inputs": { + "content": "@body('HTTP')", + "schema": { + "properties": { + "cursorNext": { + "type": "string" + }, + "indicators": { + "items": { + "properties": { + "activity": { + "properties": { + "first": { + "type": "integer" + }, + "last": { + "type": "integer" + } + }, + "type": "object" + }, + "data": { + "properties": { + "confidence": { + "type": "string" + }, + "context": { + "properties": { + "description": { + "type": "string" + } + }, + "type": "object" + }, + "expiration": { + "type": "integer" + }, + "indicator_data": { + "properties": { + "address": { + "type": "string" + }, + "file": { + "properties": { + "download_url": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "sha1": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "ssdeep": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "geo_ip": { + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "country_code": { + "type": "string" + }, + "isp": { + "properties": { + "autonomous_system": { + "type": "string" + }, + "isp": { + "type": "string" + }, + "network": { + "type": "string" + }, + "organization": { + "type": "string" + } + }, + "type": "object" + }, + "subdivision": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + "indicator_type": { + "type": "string" + }, + "intel_requirements": { + "items": { + "type": "string" + }, + "type": "array" + }, + "mitre_tactics": { + "type": "string" + }, + "source_id": { + "type": "string" + }, + "threat": { + "properties": { + "data": { + "properties": { + "family": { + "type": "string" + }, + "malware_family_profile_uid": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "type": "object" + }, + "uid": { + "type": "string" + } + }, + "type": "object" + }, + "last_updated": { + "type": "integer" + }, + "meta": { + "properties": { + "version": { + "type": "string" + } + }, + "type": "object" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "activity", + "data", + "last_updated", + "meta", + "uid" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + } + } + }, + "SetHasResults": { + "runAfter": { + "CursorNotNull": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "hasResults", + "value": "@contains(body('Parse_JSON'), 'indicators')" + } + } + }, + "runAfter": { + "InitIntel471Payload": [ + "Succeeded" + ] + }, + "expression": "@equals(variables('hasResults'), false)", + "limit": { + "count": 60, + "timeout": "PT1H" + }, + "type": "Until" + }, + "GetApiKey": { + "runAfter": { + "GetUsername": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('TitanAPIKeySentinel')}/value" + } + }, + "GetCursorFromBlob": { + "runAfter": { + "InitCollectedIndicators": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "get", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameCursor')))}/content", + "queries": { + "inferContentType": true + } + } + }, + "GetFromDateFromBlob": { + "runAfter": { + "IfCursorBlobExists": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "get", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameFromDate')))}/content", + "queries": { + "inferContentType": true + } + } + }, + "GetUsername": { + "type": "ApiConnection", + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('TitanUserNameSentinel')}/value" + } + }, + "IfCursorBlobExists": { + "actions": { + "SetCursor": { + "type": "SetVariable", + "inputs": { + "name": "cursor", + "value": "@{body('GetCursorFromBlob')}" + } + } + }, + "runAfter": { + "GetCursorFromBlob": [ + "Succeeded", + "Failed" + ] + }, + "else": { + "actions": { + "CreateBlobForCursor": { + "type": "ApiConnection", + "inputs": { + "body": "null", + "headers": { + "ReadFileMetadataFromServer": true + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "post", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files", + "queries": { + "folderPath": "@parameters('StorageAccountContainerName')", + "name": "@parameters('BlobNameCursor')", + "queryParametersSingleEncoded": true + } + }, + "runtimeConfiguration": { + "contentTransfer": { + "transferMode": "Chunked" + } + } + } + } + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@actions('GetCursorFromBlob').outputs.statusCode", + 404 + ] + } + } + ] + }, + "type": "If" + }, + "IfFromDateBlobExists": { + "actions": { + "SetFromDateFromBlob": { + "type": "SetVariable", + "inputs": { + "name": "fromDate", + "value": "@int(body('GetFromDateFromBlob'))" + } + } + }, + "runAfter": { + "GetFromDateFromBlob": [ + "Succeeded", + "Failed" + ] + }, + "else": { + "actions": { + "CreateBlobForFromDate": { + "runAfter": { + "SetFromDate": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "body": "@variables('fromDate')", + "headers": { + "ReadFileMetadataFromServer": true + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "post", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files", + "queries": { + "folderPath": "@parameters('StorageAccountContainerName')", + "name": "@parameters('BlobNameFromDate')", + "queryParametersSingleEncoded": true + } + }, + "runtimeConfiguration": { + "contentTransfer": { + "transferMode": "Chunked" + } + } + }, + "SetFromDate": { + "type": "SetVariable", + "inputs": { + "name": "fromDate", + "value": "@div(if(equals(parameters('LookBackDays'), 0), sub(ticks(utcNow()),ticks('1970-01-01')), sub(sub(ticks(utcNow()), mul(864000000000, parameters('LookBackDays'))), ticks('1970-01-01'))), 10000)" + } + } + } + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@actions('GetFromDateFromBlob').outputs.statusCode", + 404 + ] + } + } + ] + }, + "type": "If" + }, + "InitCollectedIndicators": { + "runAfter": { + "InitFromDate": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "collectedIndicators", + "type": "array", + "value": "[variables('TemplateEmptyArray')]" + } + ] + } + }, + "InitCursor": { + "runAfter": { + "InitHasResults": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "cursor", + "type": "string", + "value": "null" + } + ] + } + }, + "InitFromDate": { + "runAfter": { + "InitCursor": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "fromDate", + "type": "integer" + } + ] + } + }, + "InitHasResults": { + "runAfter": { + "GetApiKey": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "hasResults", + "type": "boolean", + "value": true + } + ] + } + }, + "InitIntel471Payload": { + "runAfter": { + "IfFromDateBlobExists": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "payload", + "type": "object", + "value": { + "count": 100, + "lastUpdatedFrom": "@variables('fromDate')" + } + } + ] + } + } + } + }, + "parameters": { + "$connections": { + "value": { + "azuresentinel": { + "connectionId": "[[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]", + "connectionName": "[[variables('MicrosoftSentinelConnectionName')]", + "id": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('workspace-location-inline'), '/managedApis/Azuresentinel')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + } + }, + "azureblob": { + "connectionId": "[[resourceId('Microsoft.Web/connections', variables('AzureBlobConnectionName'))]", + "connectionName": "[[variables('AzureBlobConnectionName')]", + "id": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('workspace-location-inline'), '/managedApis/azureblob')]" + }, + "keyvault": { + "connectionId": "[[resourceId('Microsoft.Web/connections', variables('AzureKeyVaultConnectionName'))]", + "connectionName": "[[variables('AzureKeyVaultConnectionName')]", + "id": "[[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', variables('workspace-location-inline'), '/managedApis/keyvault')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + } + } + } + }, + "StorageAccountName": { + "value": "[[variables('StorageAccountName')]" + }, + "StorageAccountContainerName": { + "value": "[[variables('StorageAccountContainerName')]" + }, + "LookBackDays": { + "value": "[[parameters('LookBackDays')]" + }, + "WorkspaceID": { + "value": "[[parameters('WorkspaceID')]" + } + } + } + }, + { + "type": "Microsoft.OperationalInsights/workspaces/providers/metadata", + "apiVersion": "2022-01-01-preview", + "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/',concat('Playbook-', last(split(variables('playbookId2'),'/'))))]", + "properties": { + "parentId": "[variables('playbookId2')]", + "contentId": "[variables('_playbookContentId2')]", + "kind": "Playbook", + "version": "[variables('playbookVersion2')]", + "source": { + "kind": "Solution", + "name": "Intel471", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Intel 471 Inc." + }, + "support": { + "name": "Intel 471", + "email": "support@intel471.com", + "tier": "Partner", + "link": "https://intel471.com/company/contact" + } + } + } + ], + "metadata": { + "title": "Intel 471 Malware Intelligence to Sentinel", + "description": "This playbook ingests malware indicators from Intel 471's Titan API into Microsoft Sentinel as tiIndicator resource type.", + "prerequisites": [ + "1. An active account in Titan platform, which is available as part of Intel 471's subscriptions. For more information, please contact sales@intel471.com.", + "2. Titan API credentials.", + "3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserNameSentinel` and `TitanAPIKeySentinel` keys.", + "4. Pre-existing [Storage account](https://learn.microsoft.com/azure/storage/common/storage-account-overview) with blob container already created for persisting data such as cursor between the API calls.", + "5. Threat Intelligence connector enabled in Sentinel. Go to Sentinel instance → 'Content hub' and install 'Threat Intelligence' solution." + ], + "postDeployment": [ + "1. Go to the Key Vault. Select `Access control (IAM)` → `+ Add` → `Add role assignment`. Choose `Key Vault Secrets User`. On the next screen hit `+ Select members`, search for Intel 471 and select newly created logic app. Select it and proceed with granting access rights.", + "2. Go to the selected Log Analytics workspace and repeat step 1. except grant role `Microsoft Sentinel Contributor`.", + "3. Go to created `Logic App` → `Edit`. Navigate to `CollectAndSubmitIndicators → IfIndicators → True → Threat Intelligence - Upload Indicators of Compromise (V2) (Preview)` block. Inside this block create a new connection. Select `Connect with managed identity`, provide connection name and click `Create`.", + "4. Optionally change the schedule's frequency in `Recurrence` block (the first one)." + ], + "lastUpdateTime": "2023-06-21T00:00:00Z", + "entities": [ + "ip", + "filehash", + "url" + ], + "tags": [ + "ThreatIntelligence" + ], + "releaseNotes": { + "version": "1.0", + "title": "[variables('blanks')]", + "notes": [ + "Initial version" + ] + } + } + }, + "packageKind": "Solution", + "packageVersion": "[variables('_solutionVersion')]", + "packageName": "[variables('_solutionName')]", + "packageId": "[variables('_solutionId')]", + "contentSchemaVersion": "3.0.0", + "contentId": "[variables('_playbookContentId2')]", + "contentKind": "Playbook", + "displayName": "Intel471-ImportMalwareIntelligenceToSentinel", + "contentProductId": "[variables('_playbookcontentProductId2')]", + "id": "[variables('_playbookcontentProductId2')]", + "version": "[variables('playbookVersion2')]" + } + }, + { + "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": "Intel471", + "publisherDisplayName": "Intel 471", + "descriptionHtml": "

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

\n

Intel 471 Threat Intelligence integration ingests malware indicators into Log Analytics workspace.

\n

Playbooks: 2

\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": "Intel471", + "sourceId": "[variables('_solutionId')]" + }, + "author": { + "name": "Intel 471 Inc." + }, + "support": { + "name": "Intel 471", + "email": "support@intel471.com", + "tier": "Partner", + "link": "https://intel471.com/company/contact" + }, + "dependencies": { + "operator": "AND", + "criteria": [ + { + "kind": "Playbook", + "contentId": "[variables('_Intel471-ImportMalwareIntelligenceToGraphSecurity')]", + "version": "[variables('playbookVersion1')]" + }, + { + "kind": "Playbook", + "contentId": "[variables('_Intel471-ImportMalwareIntelligenceToSentinel')]", + "version": "[variables('playbookVersion2')]" } ] }, diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/README.md b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/README.md new file mode 100644 index 00000000000..4b90ebe54b8 --- /dev/null +++ b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/README.md @@ -0,0 +1,162 @@ +# Intel 471 Malware Intelligence import to Microsoft Graph Security + +## Table of contents + +1. [Overview](#overview) +2. [Prerequisites](#prerequisites) +3. [Deployment instructions](#deployment-instructions) +4. [Post-deployment instructions](#post-deployment-instructions) +5. [Querying Intel 471 Malware Intelligence data in Sentinel](#querying-intel-471-malware-intelligence-data-in-sentinel) +6. [Data mapping](#data-mapping) +7. [Script for granting ThreatIndicators.ReadWrite.OwnedBy role](#script-for-granting-threatindicatorsreadwriteownedby-role) + +## Overview + +This playbook fetches malware intelligence indicators from the Intel 471's Titan API and ingests them +as [tiIndicators](https://docs.microsoft.com/graph/api/resources/tiindicator?view=graph-rest-beta) +through [Microsoft Graph Security tiIndicators API](https://learn.microsoft.com/azure/sentinel/connect-threat-intelligence-tip) +to make them available in Microsoft Sentinel and other Microsoft security solutions such as Defender ATP. + +Data connector used in this playbook is on a path for deprecation. For new solutions use the new threat intelligence +upload indicators API data connector, which is used in [Intel471-ImportMalwareIntelligenceToSentinel](..%2FIntel471-ImportMalwareIntelligenceToSentinel) playbook. +For more information, see [Connect your threat intelligence platform to Microsoft Sentinel with the upload indicators API](https://learn.microsoft.com/azure/sentinel/connect-threat-intelligence-upload-api). + +[azuredeploy.json](azuredeploy.json) Azure Resource Manager template (ARM template) is responsible +for building the Logic App along with the necessary connections. The ARM builds following components: + +- **[Logic App](https://docs.microsoft.com/azure/sentinel/create-custom-connector#connect-with-logic-apps)** responsible for fetching the data from Titan API and ingesting them into `ThreatIntelligenceIndicator` table +- Connection objects + - Logic app to Blob storage *(authorized automatically)* + - Logic app to Key Vault *(**requires manual configuration**)* + - Logic app to Microsoft Security Graph *(**requires manual configuration**)* + +![Intel 471 Malware Intelligence Logic App](malware-intelligence-screenshot.png "Intel 471 Malware Intelligence Logic App") + +## Prerequisites + +1. An active account in Titan platform, which is available as part of Intel 471's subscriptions. For more information, please contact sales@intel471.com. +2. Titan API credentials. +3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserNameGraph` and `TitanAPIKeyGraph` keys. +4. Pre-existing [Blob storage](https://docs.microsoft.com/azure/storage/blobs/storage-blobs-introduction) with blob container for persisting data such as cursor between the API calls. +5. Threat Intelligence connector enabled in Sentinel. Go to Sentinel instance → `Content hub` and install `Threat Intelligence` solution. + + +## Deployment instructions + +1. To deploy the Playbook, click the **Deploy to Azure** button. It will launch the ARM Template deployment wizard. +2. Provide following parameters: + * **Playbook Name**: Either leave the default one or change it as needed + * **StorageAccountName**: Name of the Storage account (see prerequisites) + * **StorageAccountContainerName**: Name of the blob container in the Storage account + * **KeyVaultName**: Name of the Key Vault (see prerequisites) + * **Target Product**: Security product to which the indicators will be applied. Allowed values: `Azure Sentinel`, `Microsoft Defender ATP` + * **Action**: The action to apply if the indicator is matched from within the targetProduct security tool. Allowed values: `unknown`, `allow`, `block`, `alert` + * **Look Back Days**: How many days of history should be pulled on the first run. Leave 0 to start from the current time + + [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FSolutions%2FIntel471%2FPlaybooks%2FIntel471-ImportMalwareIntelligenceToGraphSecurity%2Fazuredeploy.json) + [![Deploy to Azure Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FSolutions%2FIntel471%2FPlaybooks%2FIntel471-ImportMalwareIntelligenceToGraphSecurity%2Fazuredeploy.json) + +## Post-deployment instructions + +1. Go to the Key Vault. Select `Access control (IAM)` → `+ Add` → `Add role assignment`. Choose `Key Vault Secrets User`. On the next screen hit `+ Select members`, search for Intel 471 and select newly created logic app. Select it and proceed with granting access rights. +2. Go to created `Logic App` → `Edit`. Navigate to `CollectAndSubmitIndicators → SubmitInBatches → Connections` block. Inside this block create a new connection. Select `Connect with managed identity`, provide connection name and click `Create`. +3. Grant `ThreatIndicators.ReadWrite.OwnedBy` role for the Logic App to allow indicator ingestion through the tiIndicators API. It can't be done in the Azure portal at the moment. You can use the Azure CLI script listed below instead. Consult [Microsoft documentation](https://learn.microsoft.com/azure/app-service/scenario-secure-app-access-microsoft-graph-as-app?tabs=azure-cli#grant-access-to-microsoft-graph) for more details. +4. Optionally change the schedule's frequency in `Recurrence` block (the first one). + +## Querying Intel 471 Malware Intelligence data in Sentinel + +Get first 10 ingested indicators + +``` +ThreatIntelligenceIndicator | where Description contains "Intel 471" | limit 10 +``` + +Look for a specific indicator + +``` +ThreatIntelligenceIndicator | where Description has "Intel 471" | where NetworkIP == "227.151.66.29" +ThreatIntelligenceIndicator | where Description has "Intel 471" | where Url == "tcp://58.68.162.115:16" +ThreatIntelligenceIndicator | where Description has "Intel 471" | where FileHashValue == "B55C257F8004F6A742B1A252EEDDDD655256955A931C1BD0F47299ADD326ED6D" +``` + +Get indicators of a specific type + +``` +ThreatIntelligenceIndicator | where Description has "Intel 471" | where NetworkIP != "" | limit 10 +ThreatIntelligenceIndicator | where Description has "Intel 471" | where Url != "" | limit 10 +ThreatIntelligenceIndicator | where Description has "Intel 471" | where FileHashValue != "" | limit 10 +``` + +Get IP indicators with specific confidence + +``` +ThreatIntelligenceIndicator | where Description has "Intel 471" | where NetworkIP != "" | where ConfidenceScore > 40 | limit 10 +``` + +Get indicators related to a specific malware family + +``` +ThreatIntelligenceIndicator | where Description has "Intel 471" | where MalwareNames contains "njrat" | limit 10 +``` + +## Data mapping + +Data is fetched from `/v1/indicators/stream` Titan API endpoint and is then transformed to conform with `ThreatIntelligenceIndicator` table. +Table below summarizes the mappings and additional transformations that are performed for several fields. + +| Titan API object key | ThreatIntelligenceIndicator record column | Additional transformations | +|-------------------------------|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| - | action | Provided by the user. One of `allow`, `alert`, `block` or `unknown`. | +| data.confidence | confidence | `high` → `85`, `medium` → `50`, `low` → `15` | +| data.context.description | description | - | +| data.expiration | expirationDateTime | - | +| data.uid | externalId | - | +| data.indicator_data.file.* | fileHashType | In Titan API each file indicator object contains all hashes. In `ThreatIntelligenceIndicator` table there's a separate row for each hash. | +| data.indicator_data.file.* | fileHashValue | Example transformation: `{"indicator_data": {"file": {"md5": "abc", "sha1": "bcd"}}}` → `[{"fileHashType": "MD5", "fileHashValue": "abc"}, {"fileHashType": "MD5", "SHA1": "bcd"}]` | +| data.indicator_data.file.size | fileSize | - | +| data.indicator_data.file.type | fileType | - | +| data.mitre_tactics | killChain | `command_and_control` → `C2`, `stage_capabilities` → `Installation`, `initial_access` → `Exploitation` | +| activity.last | lastReportedDateTime | - | +| data.threat.data.family | malwareFamilyNames | - | +| data.indicator_data.address | networkIPv4 | - | +| - | targetProduct | Provided by the user, either `Azure Sentinel` or `Microsoft Defender ATP` | +| - | threatType | Set to `Malware` | +| - | tlpLevel | Set to `amber` | +| data.indicator_data.url | url | - | + +## Script for granting ThreatIndicators.ReadWrite.OwnedBy role + +```bash +#!/bin/bash + +# tested against azure-cli==2.42.0 + +az login + +# Logic App name. IF YOU CHANGED IT DURING THE DEPLOYMENT, CHANGE IT HERE AS WELL +logicAppName="Intel471-ImportMalwareIntelligenceToGraphSecurity" + +# ID of the Microsoft Graph resource. Do not change. +graphAppId="00000003-0000-0000-c000-000000000000" + +# Name of the required role. Do not change. +roleName="ThreatIndicators.ReadWrite.OwnedBy" + +# Get the ID of the Logic App +spId=$(az resource list -n $logicAppName --query '[*].identity.principalId' --out tsv) + +# Get the ID of the Microsoft Graph resource +graphResourceId=$(az ad sp show --id "$graphAppId" --query 'id' --out tsv) + +# Get the ID of the Role +appRoleId=$(az ad sp show --id "$graphAppId" --query "appRoles[?value=='$roleName' && contains(allowedMemberTypes, 'Application')].id" --output tsv) + +# Build a Role assignments request for your app +uri=https://graph.microsoft.com/v1.0/servicePrincipals/$spId/appRoleAssignments +body="{'principalId':'$spId','resourceId':'$graphResourceId','appRoleId':'$appRoleId'}" +echo "URI: "$uri +echo "Body: "$body + +# Call the endpoint to grant the role +az rest --method post --uri $uri --body $body --headers "Content-Type=application/json" +``` \ No newline at end of file diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/azuredeploy.json b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/azuredeploy.json new file mode 100644 index 00000000000..02e75a23cab --- /dev/null +++ b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/azuredeploy.json @@ -0,0 +1,1006 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "title": "Intel 471 Malware Intelligence to Graph Security", + "description": "This playbook ingests malware indicators from Intel 471's Titan API into Microsoft Graph Security as tiIndicator resource type.", + "prerequisites": [ + "1. An active account in Titan platform, which is available as part of Intel 471's subscriptions. For more information, please contact sales@intel471.com.", + "2. Titan API credentials.", + "3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserNameGraph` and `TitanAPIKeyGraph` keys.", + "4. Pre-existing [Storage account](https://learn.microsoft.com/azure/storage/common/storage-account-overview) with blob container already created for persisting data such as cursor between the API calls.", + "5. Threat Intelligence connector enabled in Sentinel. Go to Sentinel instance → 'Content hub' and install 'Threat Intelligence' solution." + ], + "postDeployment": [ + "1. Go to the Key Vault. Select `Access control (IAM)` → `+ Add` → `Add role assignment`. Choose `Key Vault Secrets User`. On the next screen hit `+ Select members`, search for Intel 471 and select newly created logic app. Select it and proceed with granting access rights.", + "2. Go to created `Logic App` → `Edit`. Navigate to `CollectAndSubmitIndicators → SubmitInBatches → Connections` block. Inside this block create a new connection. Select `Connect with managed identity`, provide connection name and click `Create`.", + "3. Grant `ThreatIndicators.ReadWrite.OwnedBy` role for the Logic App to allow indicator ingestion through the tiIndicators API. It can't be done in the Azure portal at the moment. You can use the Azure CLI script listed below instead. Consult [Microsoft documentation](https://learn.microsoft.com/azure/app-service/scenario-secure-app-access-microsoft-graph-as-app?tabs=azure-cli#grant-access-to-microsoft-graph) for more details.", + "4. Optionally change the schedule's frequency in `Recurrence` block (the first one)." + ], + "lastUpdateTime": "2023-06-21T00:00:00.000Z", + "entities": [ + "ip", + "filehash", + "url" + ], + "tags": [ + "ThreatIntelligence" + ], + "author": { + "name": "Marcin Molenda / Intel 471, Inc." + }, + "support": { + "name": "Intel 471", + "email": "support@intel471.com", + "tier": "Partner", + "link": "https://intel471.com/company/contact" + } + }, + "parameters": { + "PlaybookName": { + "defaultValue": "Intel471-ImportMalwareIntelligenceToGraphSecurity", + "type": "String" + }, + "StorageAccountName": { + "type": "String", + "metadata": { + "description": "Name of the pre-existing Storage Account where Logic App can store helper data." + } + }, + "StorageAccountContainerName": { + "type": "String", + "metadata": { + "description": "Name of the pre-existing blob container inside provided Storage Account." + } + }, + "KeyVaultName": { + "type": "String", + "metadata": { + "description": "Name of the pre-existing Key Vault with Titan API credentials. The credentials are expected under following keys: TitanUserNameGraph nad TitanAPIKeyGraph" + } + }, + "TargetProduct": { + "defaultValue": "Azure Sentinel", + "type": "String", + "metadata": { + "description": "Security product to which the indicators will be applied. Allowed values: Azure Sentinel, Microsoft Defender ATP." + } + }, + "Action": { + "defaultValue": "unknown", + "type": "String", + "metadata": { + "description": "The action to apply if the indicator is matched from within the targetProduct security tool. Allowed values: unknown, allow, block, alert." + } + }, + "LookBackDays": { + "defaultValue": 0, + "type": "int", + "metadata": { + "description": "How many days of history should be pulled on the first run. Leave 0 to start from the current time." + } + } + }, + "variables": { + "GraphSecurityConnectionName": "[concat('microsoftgraphsecurity-', parameters('PlaybookName'))]", + "AzureBlobConnectionName": "[concat('azureblob-', parameters('PlaybookName'))]", + "StorageAccountName": "[parameters('StorageAccountName')]", + "StorageAccountContainerName": "[parameters('StorageAccountContainerName')]", + "AzureKeyVaultName": "[parameters('KeyVaultName')]", + "AzureKeyVaultConnectionName": "[concat('keyvault-', parameters('PlaybookName'))]" + }, + "resources": [ + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[variables('GraphSecurityConnectionName')]", + "location": "[resourceGroup().location]", + "properties": { + "displayName": "[variables('GraphSecurityConnectionName')]", + "customParameterValues": {}, + "api": { + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/microsoftgraphsecurity')]" + } + } + }, + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[variables('AzureBlobConnectionName')]", + "location": "[resourceGroup().location]", + "properties": { + "displayName": "[variables('AzureBlobConnectionName')]", + "customParameterValues": {}, + "api": { + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azureblob')]" + }, + "parameterValues": { + "accountName": "[variables('StorageAccountName')]", + "accessKey": "[listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName')), '2022-09-01').keys[0].value]" + } + } + }, + { + "type": "Microsoft.Web/connections", + "apiVersion": "2016-06-01", + "name": "[variables('AzureKeyVaultConnectionName')]", + "location": "[resourceGroup().location]", + "kind": "V1", + "properties": { + "displayName": "[variables('AzureKeyVaultConnectionName')]", + "customParameterValues": {}, + "api": { + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/keyvault')]" + }, + "parameterValueType": "Alternative", + "alternativeParameterValues": { + "vaultName": "[variables('AzureKeyVaultName')]" + } + } + }, + { + "type": "Microsoft.Logic/workflows", + "apiVersion": "2017-07-01", + "name": "[parameters('PlaybookName')]", + "location": "[resourceGroup().location]", + "identity": { + "type": "SystemAssigned" + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/connections', variables('GraphSecurityConnectionName'))]", + "[resourceId('Microsoft.Web/connections', variables('AzureBlobConnectionName'))]", + "[resourceId('Microsoft.Web/connections', variables('AzureKeyVaultConnectionName'))]" + ], + "tags": { + "LogicAppsCategory": "security" + }, + "properties": { + "state": "Enabled", + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "$connections": { + "defaultValue": {}, + "type": "Object" + }, + "StorageAccountName": { + "defaultValue": "", + "type": "String" + }, + "StorageAccountContainerName": { + "defaultValue": "", + "type": "String" + }, + "BlobNameCursor": { + "defaultValue": "cursorGraph.txt", + "type": "String" + }, + "BlobNameFromDate": { + "defaultValue": "fromdateGraph.txt", + "type": "String" + }, + "LookBackDays": { + "defaultValue": 0, + "type": "int" + }, + "TargetProduct": { + "defaultValue": "Azure Sentinel", + "type": "String" + }, + "Action": { + "defaultValue": "unknown", + "type": "String" + } + }, + "triggers": { + "Recurrence": { + "recurrence": { + "frequency": "Hour", + "interval": 1 + }, + "runtimeConfiguration": { + "concurrency": { + "runs": 1 + } + }, + "type": "Recurrence" + } + }, + "actions": { + "CollectAndSubmitIndicators": { + "actions": { + "ClearCollectedIndicators": { + "inputs": { + "name": "collectedIndicators", + "value": [] + }, + "runAfter": { + "SubmitInBatches": [ + "Succeeded" + ] + }, + "type": "SetVariable" + }, + "CursorNotNull": { + "actions": { + "StoreCursor": { + "inputs": { + "body": "@string(variables('cursor'))", + "headers": { + "ReadFileMetadataFromServer": true + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "put", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameCursor')))}" + }, + "runAfter": { + "UpdateCursor": [ + "Succeeded" + ] + }, + "type": "ApiConnection" + }, + "UpdateCursor": { + "inputs": { + "name": "cursor", + "value": "@body('Parse_JSON')?['cursorNext']" + }, + "runAfter": {}, + "type": "SetVariable" + } + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@body('Parse_JSON')?['cursorNext']", + "@null" + ] + } + } + ] + }, + "runAfter": { + "Parse_JSON": [ + "Succeeded" + ] + }, + "type": "If" + }, + "FilterAndSplitFetchedIndicators": { + "actions": { + "Each_indicator": { + "actions": { + "Condition": { + "actions": { + "Switch": { + "cases": { + "Case": { + "actions": { + "Append_md5_indicator": { + "inputs": { + "name": "collectedIndicators", + "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', items('Each_indicator')?['data']['indicator_data']?['file']?['md5']), 'composedHashType', 'md5')" + }, + "runAfter": {}, + "type": "AppendToArrayVariable" + }, + "Append_sha1_indicator": { + "inputs": { + "name": "collectedIndicators", + "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', items('Each_indicator')?['data']['indicator_data']?['file']?['sha1']), 'composedHashType', 'sha1')" + }, + "runAfter": {}, + "type": "AppendToArrayVariable" + }, + "Append_sha256_indicator": { + "inputs": { + "name": "collectedIndicators", + "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', items('Each_indicator')?['data']['indicator_data']?['file']?['sha256']), 'composedHashType', 'sha256')" + }, + "runAfter": {}, + "type": "AppendToArrayVariable" + } + }, + "case": "file" + } + }, + "default": { + "actions": { + "Append_indicator": { + "inputs": { + "name": "collectedIndicators", + "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', ''), 'composedHashType', '')" + }, + "runAfter": {}, + "type": "AppendToArrayVariable" + } + } + }, + "expression": "@items('Each_indicator')?['data']['indicator_type']", + "runAfter": {}, + "type": "Switch" + } + }, + "expression": { + "and": [ + { + "greater": [ + "@items('Each_indicator')?['data']?['expiration']", + "@div(sub(ticks(utcNow()),ticks('1970-01-01')), 10000)" + ] + } + ] + }, + "runAfter": {}, + "type": "If" + } + }, + "foreach": "@body('Parse_JSON')?['indicators']", + "runAfter": {}, + "type": "Foreach" + } + }, + "expression": { + "and": [ + { + "equals": [ + "@variables('hasResults')", + true + ] + } + ] + }, + "runAfter": { + "SetHasResults": [ + "Succeeded" + ] + }, + "type": "If" + }, + "HTTP": { + "inputs": { + "authentication": { + "password": "@body('GetApiKey')?['value']", + "type": "Basic", + "username": "@body('GetUsername')?['value']" + }, + "headers": { + "User-Agent": "Intel 471 - Malware Intelligence Graph - Azure Logic App 1.0.0" + }, + "method": "GET", + "queries": "@if(equals(variables('cursor'), 'null'), variables('payload'), setProperty(variables('payload'), 'cursor', variables('cursor')))", + "uri": "https://api.intel471.com/v1/indicators/stream" + }, + "runAfter": {}, + "type": "Http" + }, + "MapIndicatorsToTiIndicators": { + "inputs": { + "from": "@variables('collectedIndicators')", + "select": { + "action": "@parameters('Action')", + "confidence": "@if(equals(item()?['data']?['confidence'], 'high'), 85, if(equals(item()?['data']?['confidence'], 'medium'), 50, 15))", + "description": "@concat('Intel 471 - ', item()?['data']?['context']?['description'])", + "expirationDateTime": "@addToTime('1970-01-01T00:00:00Z', div(item()?['data']['expiration'], 1000), 'second')", + "externalId": "@{item()?['data']?['uid']}", + "fileHashType": "@{item()?['composedHashType']}", + "fileHashValue": "@{item()?['composedHashValue']}", + "fileSize": "@if(contains(item()?['data']?['indicator_data'], 'file'), item()?['data']?['indicator_data']?['file']?['size'], '')", + "fileType": "@if(contains(item()?['data']?['indicator_data'], 'file'), item()?['data']?['indicator_data']?['file']?['type'], '')", + "killChain": "@if(equals(item()?['data']?['mitre_tactics'], 'command_and_control'), createArray('C2'), if(equals(item()?['data']?['mitre_tactics'], 'stage_capabilities'), createArray('Installation'), if(equals(item()?['data']?['mitre_tactics'], 'initial_access'), createArray('Exploitation'), json('[]'))))", + "lastReportedDateTime": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['activity']['last'], 1000), 'second')}", + "malwareFamilyNames": "@createArray(item()['data']['threat']['data']['family'])", + "networkIPv4": "@{item()?['data']?['indicator_data']?['address']}", + "targetProduct": "@parameters('TargetProduct')", + "threatType": "Malware", + "tlpLevel": "amber", + "url": "@{item()?['data']?['indicator_data']?['url']}" + } + }, + "runAfter": { + "FilterAndSplitFetchedIndicators": [ + "Succeeded" + ] + }, + "type": "Select" + }, + "Parse_JSON": { + "inputs": { + "content": "@body('HTTP')", + "schema": { + "properties": { + "cursorNext": { + "type": "string" + }, + "indicators": { + "items": { + "properties": { + "activity": { + "properties": { + "first": { + "type": "integer" + }, + "last": { + "type": "integer" + } + }, + "type": "object" + }, + "data": { + "properties": { + "confidence": { + "type": "string" + }, + "context": { + "properties": { + "description": { + "type": "string" + } + }, + "type": "object" + }, + "expiration": { + "type": "integer" + }, + "indicator_data": { + "properties": { + "address": { + "type": "string" + }, + "file": { + "properties": { + "download_url": { + "type": "string" + }, + "md5": { + "type": "string" + }, + "sha1": { + "type": "string" + }, + "sha256": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "ssdeep": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "type": "object" + }, + "geo_ip": { + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "country_code": { + "type": "string" + }, + "isp": { + "properties": { + "autonomous_system": { + "type": "string" + }, + "isp": { + "type": "string" + }, + "network": { + "type": "string" + }, + "organization": { + "type": "string" + } + }, + "type": "object" + }, + "subdivision": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "url": { + "type": "string" + } + }, + "type": "object" + }, + "indicator_type": { + "type": "string" + }, + "intel_requirements": { + "items": { + "type": "string" + }, + "type": "array" + }, + "mitre_tactics": { + "type": "string" + }, + "source_id": { + "type": "string" + }, + "threat": { + "properties": { + "data": { + "properties": { + "family": { + "type": "string" + }, + "malware_family_profile_uid": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + } + }, + "type": "object" + }, + "uid": { + "type": "string" + } + }, + "type": "object" + }, + "last_updated": { + "type": "integer" + }, + "meta": { + "properties": { + "version": { + "type": "string" + } + }, + "type": "object" + }, + "uid": { + "type": "string" + } + }, + "required": [ + "activity", + "data", + "last_updated", + "meta", + "uid" + ], + "type": "object" + }, + "type": "array" + } + }, + "type": "object" + } + }, + "runAfter": { + "HTTP": [ + "Succeeded" + ] + }, + "type": "ParseJson" + }, + "SetHasResults": { + "inputs": { + "name": "hasResults", + "value": "@contains(body('Parse_JSON'), 'indicators')" + }, + "runAfter": { + "CursorNotNull": [ + "Succeeded" + ] + }, + "type": "SetVariable" + }, + "SubmitInBatches": { + "actions": { + "Submit_multiple_tiIndicators": { + "inputs": { + "body": { + "value": "@items('SubmitInBatches')" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['microsoftgraphsecurity']['connectionId']" + } + }, + "method": "post", + "path": "/beta/security/tiIndicators/submitTiIndicators" + }, + "runAfter": {}, + "type": "ApiConnection" + } + }, + "foreach": "@chunk(body('MapIndicatorsToTiIndicators'), 100)", + "runAfter": { + "MapIndicatorsToTiIndicators": [ + "Succeeded" + ] + }, + "runtimeConfiguration": { + "concurrency": { + "repetitions": 1 + } + }, + "type": "Foreach" + } + }, + "expression": "@equals(variables('hasResults'), false)", + "limit": { + "count": 60, + "timeout": "PT1H" + }, + "runAfter": { + "InitIntel471Payload": [ + "Succeeded" + ] + }, + "type": "Until" + }, + "GetApiKey": { + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('TitanAPIKeyGraph')}/value" + }, + "runAfter": { + "GetUsername": [ + "Succeeded" + ] + }, + "type": "ApiConnection" + }, + "GetCursorFromBlob": { + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "get", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameCursor')))}/content", + "queries": { + "inferContentType": true + } + }, + "runAfter": { + "InitCollectedIndicators": [ + "Succeeded" + ] + }, + "type": "ApiConnection" + }, + "GetFromDateFromBlob": { + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "get", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameFromDate')))}/content", + "queries": { + "inferContentType": true + } + }, + "runAfter": { + "IfCursorBlobExists": [ + "Succeeded" + ] + }, + "type": "ApiConnection" + }, + "GetUsername": { + "inputs": { + "host": { + "connection": { + "name": "@parameters('$connections')['keyvault']['connectionId']" + } + }, + "method": "get", + "path": "/secrets/@{encodeURIComponent('TitanUserNameGraph')}/value" + }, + "runAfter": {}, + "type": "ApiConnection" + }, + "IfCursorBlobExists": { + "actions": { + "SetCursor": { + "inputs": { + "name": "cursor", + "value": "@{body('GetCursorFromBlob')}" + }, + "runAfter": {}, + "type": "SetVariable" + } + }, + "else": { + "actions": { + "CreateBlobForCursor": { + "inputs": { + "body": "null", + "headers": { + "ReadFileMetadataFromServer": true + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "post", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files", + "queries": { + "folderPath": "@parameters('StorageAccountContainerName')", + "name": "@parameters('BlobNameCursor')", + "queryParametersSingleEncoded": true + } + }, + "runAfter": {}, + "runtimeConfiguration": { + "contentTransfer": { + "transferMode": "Chunked" + } + }, + "type": "ApiConnection" + } + } + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@actions('GetCursorFromBlob').outputs.statusCode", + 404 + ] + } + } + ] + }, + "runAfter": { + "GetCursorFromBlob": [ + "Succeeded", + "Failed" + ] + }, + "type": "If" + }, + "IfFromDateBlobExists": { + "actions": { + "SetFromDateFromBlob": { + "inputs": { + "name": "fromDate", + "value": "@int(body('GetFromDateFromBlob'))" + }, + "runAfter": {}, + "type": "SetVariable" + } + }, + "else": { + "actions": { + "CreateBlobForFromDate": { + "inputs": { + "body": "@variables('fromDate')", + "headers": { + "ReadFileMetadataFromServer": true + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azureblob']['connectionId']" + } + }, + "method": "post", + "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files", + "queries": { + "folderPath": "@parameters('StorageAccountContainerName')", + "name": "@parameters('BlobNameFromDate')", + "queryParametersSingleEncoded": true + } + }, + "runAfter": { + "SetFromDate": [ + "Succeeded" + ] + }, + "runtimeConfiguration": { + "contentTransfer": { + "transferMode": "Chunked" + } + }, + "type": "ApiConnection" + }, + "SetFromDate": { + "inputs": { + "name": "fromDate", + "value": "@div(if(equals(parameters('LookBackDays'), 0), sub(ticks(utcNow()),ticks('1970-01-01')), sub(sub(ticks(utcNow()), mul(864000000000, parameters('LookBackDays'))), ticks('1970-01-01'))), 10000)" + }, + "runAfter": {}, + "type": "SetVariable" + } + } + }, + "expression": { + "and": [ + { + "not": { + "equals": [ + "@actions('GetFromDateFromBlob').outputs.statusCode", + 404 + ] + } + } + ] + }, + "runAfter": { + "GetFromDateFromBlob": [ + "Succeeded", + "Failed" + ] + }, + "type": "If" + }, + "InitCollectedIndicators": { + "inputs": { + "variables": [ + { + "name": "collectedIndicators", + "type": "array", + "value": [] + } + ] + }, + "runAfter": { + "InitFromDate": [ + "Succeeded" + ] + }, + "type": "InitializeVariable" + }, + "InitCursor": { + "inputs": { + "variables": [ + { + "name": "cursor", + "type": "string", + "value": "null" + } + ] + }, + "runAfter": { + "InitHasResults": [ + "Succeeded" + ] + }, + "type": "InitializeVariable" + }, + "InitFromDate": { + "inputs": { + "variables": [ + { + "name": "fromDate", + "type": "integer" + } + ] + }, + "runAfter": { + "InitCursor": [ + "Succeeded" + ] + }, + "type": "InitializeVariable" + }, + "InitHasResults": { + "inputs": { + "variables": [ + { + "name": "hasResults", + "type": "boolean", + "value": true + } + ] + }, + "runAfter": { + "GetApiKey": [ + "Succeeded" + ] + }, + "type": "InitializeVariable" + }, + "InitIntel471Payload": { + "inputs": { + "variables": [ + { + "name": "payload", + "type": "object", + "value": { + "count": 100, + "lastUpdatedFrom": "@variables('fromDate')" + } + } + ] + }, + "runAfter": { + "IfFromDateBlobExists": [ + "Succeeded" + ] + }, + "type": "InitializeVariable" + } + }, + "outputs": {} + }, + "parameters": { + "$connections": { + "value": { + "microsoftgraphsecurity": { + "connectionId": "[resourceId('Microsoft.Web/connections', variables('GraphSecurityConnectionName'))]", + "connectionName": "[variables('GraphSecurityConnectionName')]", + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/microsoftgraphsecurity')]" + }, + "azureblob": { + "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureBlobConnectionName'))]", + "connectionName": "[variables('AzureBlobConnectionName')]", + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/azureblob')]" + }, + "keyvault": { + "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureKeyVaultConnectionName'))]", + "connectionName": "[variables('AzureKeyVaultConnectionName')]", + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/keyvault')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + } + } + } + }, + "StorageAccountName": { + "value": "[variables('StorageAccountName')]" + }, + "StorageAccountContainerName": { + "value": "[variables('StorageAccountContainerName')]" + }, + "LookBackDays": { + "value": "[parameters('LookBackDays')]" + }, + "TargetProduct": { + "value": "[parameters('TargetProduct')]" + }, + "Action": { + "value": "[parameters('Action')]" + } + } + } + } + ] +} \ No newline at end of file diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/images/ImportMalwareIntelligenceToGraphSecurity.png b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/images/ImportMalwareIntelligenceToGraphSecurity.png new file mode 100644 index 00000000000..bba8ca15284 Binary files /dev/null and b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/images/ImportMalwareIntelligenceToGraphSecurity.png differ diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/malware-intelligence-screenshot.png b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/malware-intelligence-screenshot.png new file mode 100644 index 00000000000..d567ef5109f Binary files /dev/null and b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToGraphSecurity/malware-intelligence-screenshot.png differ diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/README.md b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/README.md index f7cd93531b6..08e739ab1ab 100644 --- a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/README.md +++ b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/README.md @@ -1,4 +1,4 @@ -# Intel 471 Malware Intelligence +# Intel 471 Malware Intelligence import to Sentinel ## Table of contents @@ -12,17 +12,17 @@ ## Overview -This playbook fetches malware intelligence indicators from the Intel 471's Titan API and ingests them directly -into `ThreatIntelligenceIndicator` table of a Microsoft Sentinel workspace, from which they can be further processed by Microsoft Sentinel or Microsoft Defender. +This playbook fetches malware intelligence indicators from the Intel 471's Titan API and ingests them +using [Threat Intelligence Upload Indicators API for Microsoft Sentinel](https://learn.microsoft.com/azure/sentinel/connect-threat-intelligence-upload-api). [azuredeploy.json](azuredeploy.json) Azure Resource Manager template (ARM template) is responsible for building the Logic App along with the necessary connections. The ARM builds following components: -- **[Logic App](https://docs.microsoft.com/azure/sentinel/create-custom-connector#connect-with-logic-apps)** responsible for fetching the data from Titan API and ingesting them into `ThreatIntelligenceIndicator` table +- **[Logic App](https://docs.microsoft.com/azure/sentinel/create-custom-connector#connect-with-logic-apps)** responsible for fetching the data from Titan API and ingesting them into `ThreatIntelligenceIndicator` table using Upload Indicators API. - Connection objects - Logic app to Blob storage *(authorized automatically)* - Logic app to Key Vault *(**requires manual configuration**)* - - Logic app to Microsoft Security Graph *(**requires manual configuration**)* + - Logic app to Log Analytics workspace *(authorized automatically)* ![Intel 471 Malware Intelligence Logic App](malware-intelligence-screenshot.png "Intel 471 Malware Intelligence Logic App") @@ -30,8 +30,9 @@ for building the Logic App along with the necessary connections. The ARM builds 1. An active account in Titan platform, which is available as part of Intel 471's subscriptions. For more information, please contact sales@intel471.com. 2. Titan API credentials. -3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserName` and `TitanAPIKey` keys. +3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserNameSentinel` and `TitanAPIKeySentinel` keys. 4. Pre-existing [Blob storage](https://docs.microsoft.com/azure/storage/blobs/storage-blobs-introduction) with blob container for persisting data such as cursor between the API calls. +5. Threat Intelligence connector enabled in Sentinel. Go to Sentinel instance → `Content hub` and install `Threat Intelligence` solution. ## Deployment instructions @@ -42,8 +43,7 @@ for building the Logic App along with the necessary connections. The ARM builds * **StorageAccountName**: Name of the Storage account (see prerequisites) * **StorageAccountContainerName**: Name of the blob container in the Storage account * **KeyVaultName**: Name of the Key Vault (see prerequisites) - * **Target Product**: Security product to which the indicators will be applied. Allowed values: `Azure Sentinel`, `Microsoft Defender ATP` - * **Action**: The action to apply if the indicator is matched from within the targetProduct security tool. Allowed values: `unknown`, `allow`, `block`, `alert` + * **Workspace ID**: ID of the Log Analytics workspace to which the indicators will be directed * **Look Back Days**: How many days of history should be pulled on the first run. Leave 0 to start from the current time [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FSolutions%2FIntel471%2FPlaybooks%2FIntel471-ImportMalwareIntelligenceToSentinel%2Fazuredeploy.json) @@ -52,8 +52,7 @@ for building the Logic App along with the necessary connections. The ARM builds ## Post-deployment instructions 1. Go to the Key Vault. Select `Access control (IAM)` → `+ Add` → `Add role assignment`. Choose `Key Vault Secrets User`. On the next screen hit `+ Select members`, search for Intel 471 and select newly created logic app. Select it and proceed with granting access rights. -2. Go to created `Logic App` → `Edit`. Navigate to `CollectAndSubmitIndicators → SubmitInBatches → Connections` block. Inside this block create a new connection. Select `Connect with managed identity`, provide connection name and click `Create`. -3. Grant `ThreatIndicators.ReadWrite.OwnedBy` role for the Logic App to allow indicator ingestion through the tiIndicators API. It can't be done in the Azure portal at the moment. You can use the Azure CLI script listed below instead. Consult [Microsoft documentation](https://learn.microsoft.com/azure/app-service/scenario-secure-app-access-microsoft-graph-as-app?tabs=azure-cli#grant-access-to-microsoft-graph) for more details. +2. Go to the selected Log Analytics workspace and repeat step 1. except grant role `Microsoft Sentinel Contributor`. 4. Optionally change the schedule's frequency in `Recurrence` block (the first one). ## Querying Intel 471 Malware Intelligence data in Sentinel @@ -94,62 +93,29 @@ ThreatIntelligenceIndicator | where Description has "Intel 471" | where MalwareN ## Data mapping -Data is fetched from `/v1/indicators/stream` Titan API endpoint and is then transformed to conform with `ThreatIntelligenceIndicator` table. -Table below summarizes the mappings and additional transformations that are performed for several fields. - -| Titan API object key | ThreatIntelligenceIndicator record column | Additional transformations | -|-------------------------------|-------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| - | action | Provided by the user. One of `allow`, `alert`, `block` or `unknown`. | -| data.confidence | confidence | `high` → `85`, `medium` → `50`, `low` → `15` | -| data.context.description | description | - | -| data.expiration | expirationDateTime | - | -| data.uid | externalId | - | -| data.indicator_data.file.* | fileHashType | In Titan API each file indicator object contains all hashes. In `ThreatIntelligenceIndicator` table there's a separate row for each hash. | -| data.indicator_data.file.* | fileHashValue | Example transformation: `{"indicator_data": {"file": {"md5": "abc", "sha1": "bcd"}}}` → `[{"fileHashType": "MD5", "fileHashValue": "abc"}, {"fileHashType": "MD5", "SHA1": "bcd"}]` | -| data.indicator_data.file.size | fileSize | - | -| data.indicator_data.file.type | fileType | - | -| data.mitre_tactics | killChain | `command_and_control` → `C2`, `stage_capabilities` → `Installation`, `initial_access` → `Exploitation` | -| activity.last | lastReportedDateTime | - | -| data.threat.data.family | malwareFamilyNames | - | -| data.indicator_data.address | networkIPv4 | - | -| - | targetProduct | Provided by the user, either `Azure Sentinel` or `Microsoft Defender ATP` | -| - | threatType | Set to `Malware` | -| - | tlpLevel | Set to `amber` | -| data.indicator_data.url | url | - | - -## Script for granting ThreatIndicators.ReadWrite.OwnedBy role - -```bash -#!/bin/bash - -# tested against azure-cli==2.42.0 - -az login - -# Logic App name. IF YOU CHANGED IT DURING THE DEPLOYMENT, CHANGE IT HERE AS WELL -logicAppName="Intel471-ImportMalwareIntelligenceToSentinel" - -# ID of the Microsoft Graph resource. Do not change. -graphAppId="00000003-0000-0000-c000-000000000000" - -# Name of the required role. Do not change. -roleName="ThreatIndicators.ReadWrite.OwnedBy" - -# Get the ID of the Logic App -spId=$(az resource list -n $logicAppName --query '[*].identity.principalId' --out tsv) - -# Get the ID of the Microsoft Graph resource -graphResourceId=$(az ad sp show --id "$graphAppId" --query 'id' --out tsv) - -# Get the ID of the Role -appRoleId=$(az ad sp show --id "$graphAppId" --query "appRoles[?value=='$roleName' && contains(allowedMemberTypes, 'Application')].id" --output tsv) - -# Build a Role assignments request for your app -uri=https://graph.microsoft.com/v1.0/servicePrincipals/$spId/appRoleAssignments -body="{'principalId':'$spId','resourceId':'$graphResourceId','appRoleId':'$appRoleId'}" -echo "URI: "$uri -echo "Body: "$body - -# Call the endpoint to grant the role -az rest --method post --uri $uri --body $body --headers "Content-Type=application/json" -``` \ No newline at end of file +Data is fetched from `/v1/indicators/stream` Titan API endpoint and is then transformed into STIX2 format as described in the [Reference](https://learn.microsoft.com/azure/sentinel/upload-indicators-api). +Table below summarizes the mappings and additional transformations that are performed on several fields. + +| Titan API object key | STIX 2.1 indicator format | Additional transformations | +|-----------------------------|------------------------------------------|--------------------------------------------------------------------------------------------------------| +| - | id | Set to `indicator--` | +| - | spec_version | Set to `2.1` | +| - | type | Set to `indicator` | +| activity.first | created | - | +| last_updated | modified | - | +| data.threat.data.family | name | - | +| data.context.description | description | - | +| - | indicator_types | set to `["malicious-activity"]` | +| data.indicator_data.file.* | pattern | file hashes, url and address are transformed according to STIX2 patterning rules | +| data.indicator_data.url | pattern | - +| data.indicator_data.address | pattern | - | +| - | pattern_type | Set to `stix` | +| activity.first | valid_from | - | +| data.expiration | valid_until | - | +| data.mitre_tactics | kill_chain_phases | `command_and_control` → `C2`, `stage_capabilities` → `Installation`, `initial_access` → `Exploitation` | +| data.threat.data.family | labels | - | +| data.confidence | confidence | `high` → `85`, `medium` → `50`, `low` → `15` | +| - | object_marking_refs | Set to `marking-definition--f88d31f6-486f-44da-b317-01333bde0b82` (TLP Amber) | +| data.threat.uid | external_references | - | + + diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/azuredeploy.json b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/azuredeploy.json index 9db27748f7f..4a16e23567d 100644 --- a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/azuredeploy.json +++ b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/azuredeploy.json @@ -2,18 +2,19 @@ "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { - "title": "Intel 471 Malware Intelligence", - "description": "This playbook ingests malware indicators from Intel 471's Titan API into ThreatIntelligenceIndicator table.", + "title": "Intel 471 Malware Intelligence to Sentinel", + "description": "This playbook ingests malware indicators from Intel 471's Titan API into Microsoft Sentinel as tiIndicator resource type.", "prerequisites": [ "1. An active account in Titan platform, which is available as part of Intel 471's subscriptions. For more information, please contact sales@intel471.com.", "2. Titan API credentials.", - "3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserName` and `TitanAPIKey` keys.", - "4. Pre-existing [Blob storage](https://docs.microsoft.com/azure/storage/blobs/storage-blobs-introduction) with blob container for persisting data such as cursor between the API calls." + "3. Pre-existing [Key Vault](https://docs.microsoft.com/azure/key-vault/general/basic-concepts) for securely storing Titan API credentials. Store Titan API credentials as secrets under `TitanUserNameSentinel` and `TitanAPIKeySentinel` keys.", + "4. Pre-existing [Storage account](https://learn.microsoft.com/azure/storage/common/storage-account-overview) with blob container already created for persisting data such as cursor between the API calls.", + "5. Threat Intelligence connector enabled in Sentinel. Go to Sentinel instance → 'Content hub' and install 'Threat Intelligence' solution." ], "postDeployment": [ "1. Go to the Key Vault. Select `Access control (IAM)` → `+ Add` → `Add role assignment`. Choose `Key Vault Secrets User`. On the next screen hit `+ Select members`, search for Intel 471 and select newly created logic app. Select it and proceed with granting access rights.", - "2. Go to created `Logic App` → `Edit`. Navigate to `CollectAndSubmitIndicators → SubmitInBatches → Connections` block. Inside this block create a new connection. Select `Connect with managed identity`, provide connection name and click `Create`.", - "3. Grant `ThreatIndicators.ReadWrite.OwnedBy` role for the Logic App to allow indicator ingestion through the tiIndicators API. It can't be done in the Azure portal at the moment. You can use the Azure CLI script listed below instead. Consult [Microsoft documentation](https://learn.microsoft.com/azure/app-service/scenario-secure-app-access-microsoft-graph-as-app?tabs=azure-cli#grant-access-to-microsoft-graph) for more details.", + "2. Go to the selected Log Analytics workspace and repeat step 1. except grant role `Microsoft Sentinel Contributor`.", + "3. Go to created `Logic App` → `Edit`. Navigate to `CollectAndSubmitIndicators → IfIndicators → True → Threat Intelligence - Upload Indicators of Compromise (V2) (Preview)` block. Inside this block create a new connection. Select `Connect with managed identity`, provide connection name and click `Create`.", "4. Optionally change the schedule's frequency in `Recurrence` block (the first one)." ], "lastUpdateTime": "2023-06-21T00:00:00.000Z", @@ -43,33 +44,25 @@ "StorageAccountName": { "type": "String", "metadata": { - "description": "Pre-existing Storage Account where Logic App can store helper data." + "description": "Name of the pre-existing Storage Account where Logic App can store helper data." } }, "StorageAccountContainerName": { "type": "String", "metadata": { - "description": "Pre-existing Container inside provided Storage Account." + "description": "Name of the pre-existing blob container inside provided Storage Account." } }, "KeyVaultName": { "type": "String", "metadata": { - "description": "Pre-existing Key Vault with Titan API credentials. The credentials are expected under following keys: TitanUserName nad TitanAPIKey" + "description": "Name of the pre-existing Key Vault with Titan API credentials. The credentials are expected under following keys: TitanUserNameSentinal nad TitanAPIKeySentinel" } }, - "TargetProduct": { - "defaultValue": "Azure Sentinel", + "WorkspaceID": { "type": "String", "metadata": { - "description": "Security product to which the indicators will be applied. Allowed values: Azure Sentinel, Microsoft Defender ATP." - } - }, - "Action": { - "defaultValue": "unknown", - "type": "String", - "metadata": { - "description": "The action to apply if the indicator is matched from within the targetProduct security tool. Allowed values: unknown, allow, block, alert." + "description": "ID of the Log Analytics workspace to which the indicators will be directed." } }, "LookBackDays": { @@ -81,7 +74,7 @@ } }, "variables": { - "GraphSecurityConnectionName": "[concat('microsoftgraphsecurity-', parameters('PlaybookName'))]", + "MicrosoftSentinelConnectionName": "[concat('sentinel-', parameters('PlaybookName'))]", "AzureBlobConnectionName": "[concat('azureblob-', parameters('PlaybookName'))]", "StorageAccountName": "[parameters('StorageAccountName')]", "StorageAccountContainerName": "[parameters('StorageAccountContainerName')]", @@ -92,13 +85,15 @@ { "type": "Microsoft.Web/connections", "apiVersion": "2016-06-01", - "name": "[variables('GraphSecurityConnectionName')]", + "name": "[variables('MicrosoftSentinelConnectionName')]", "location": "[resourceGroup().location]", + "kind": "V1", "properties": { - "displayName": "[variables('GraphSecurityConnectionName')]", + "displayName": "[variables('MicrosoftSentinelConnectionName')]", "customParameterValues": {}, + "parameterValueType": "Alternative", "api": { - "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/microsoftgraphsecurity')]" + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]" } } }, @@ -146,7 +141,7 @@ "type": "SystemAssigned" }, "dependsOn": [ - "[resourceId('Microsoft.Web/connections', variables('GraphSecurityConnectionName'))]", + "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]", "[resourceId('Microsoft.Web/connections', variables('AzureBlobConnectionName'))]", "[resourceId('Microsoft.Web/connections', variables('AzureKeyVaultConnectionName'))]" ], @@ -172,23 +167,18 @@ "type": "String" }, "BlobNameCursor": { - "defaultValue": "cursor.txt", + "defaultValue": "cursorSentinel.txt", "type": "String" }, "BlobNameFromDate": { - "defaultValue": "fromdate.txt", + "defaultValue": "fromdateSentinel.txt", "type": "String" }, "LookBackDays": { "defaultValue": 0, "type": "int" }, - "TargetProduct": { - "defaultValue": "Azure Sentinel", - "type": "String" - }, - "Action": { - "defaultValue": "unknown", + "WorkspaceID": { "type": "String" } }, @@ -209,21 +199,15 @@ "actions": { "CollectAndSubmitIndicators": { "actions": { - "ClearCollectedIndicators": { - "inputs": { - "name": "collectedIndicators", - "value": [] - }, - "runAfter": { - "SubmitInBatches": [ - "Succeeded" - ] - }, - "type": "SetVariable" - }, "CursorNotNull": { "actions": { "StoreCursor": { + "runAfter": { + "UpdateCursor": [ + "Succeeded" + ] + }, + "type": "ApiConnection", "inputs": { "body": "@string(variables('cursor'))", "headers": { @@ -236,23 +220,22 @@ }, "method": "put", "path": "/v2/datasets/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountName')))}/files/@{encodeURIComponent(encodeURIComponent(parameters('StorageAccountContainerName'),'/',parameters('BlobNameCursor')))}" - }, - "runAfter": { - "UpdateCursor": [ - "Succeeded" - ] - }, - "type": "ApiConnection" + } }, "UpdateCursor": { + "runAfter": {}, + "type": "SetVariable", "inputs": { "name": "cursor", "value": "@body('Parse_JSON')?['cursorNext']" - }, - "runAfter": {}, - "type": "SetVariable" + } } }, + "runAfter": { + "Parse_JSON": [ + "Succeeded" + ] + }, "expression": { "and": [ { @@ -265,68 +248,25 @@ } ] }, - "runAfter": { - "Parse_JSON": [ - "Succeeded" - ] - }, "type": "If" }, - "FilterAndSplitFetchedIndicators": { + "FilterIndicators": { "actions": { "Each_indicator": { + "foreach": "@body('Parse_JSON')?['indicators']", "actions": { "Condition": { "actions": { - "Switch": { - "cases": { - "Case": { - "actions": { - "Append_md5_indicator": { - "inputs": { - "name": "collectedIndicators", - "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', items('Each_indicator')?['data']['indicator_data']?['file']?['md5']), 'composedHashType', 'md5')" - }, - "runAfter": {}, - "type": "AppendToArrayVariable" - }, - "Append_sha1_indicator": { - "inputs": { - "name": "collectedIndicators", - "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', items('Each_indicator')?['data']['indicator_data']?['file']?['sha1']), 'composedHashType', 'sha1')" - }, - "runAfter": {}, - "type": "AppendToArrayVariable" - }, - "Append_sha256_indicator": { - "inputs": { - "name": "collectedIndicators", - "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', items('Each_indicator')?['data']['indicator_data']?['file']?['sha256']), 'composedHashType', 'sha256')" - }, - "runAfter": {}, - "type": "AppendToArrayVariable" - } - }, - "case": "file" - } - }, - "default": { - "actions": { - "Append_indicator": { - "inputs": { - "name": "collectedIndicators", - "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', ''), 'composedHashType', '')" - }, - "runAfter": {}, - "type": "AppendToArrayVariable" - } - } - }, - "expression": "@items('Each_indicator')?['data']['indicator_type']", + "Append_indicator": { "runAfter": {}, - "type": "Switch" + "type": "AppendToArrayVariable", + "inputs": { + "name": "collectedIndicators", + "value": "@setProperty(setProperty(items('Each_indicator'), 'composedHashValue', ''), 'composedHashType', '')" + } } }, + "runAfter": {}, "expression": { "and": [ { @@ -337,15 +277,18 @@ } ] }, - "runAfter": {}, "type": "If" } }, - "foreach": "@body('Parse_JSON')?['indicators']", "runAfter": {}, "type": "Foreach" } }, + "runAfter": { + "SetHasResults": [ + "Succeeded" + ] + }, "expression": { "and": [ { @@ -356,14 +299,11 @@ } ] }, - "runAfter": { - "SetHasResults": [ - "Succeeded" - ] - }, "type": "If" }, "HTTP": { + "runAfter": {}, + "type": "Http", "inputs": { "authentication": { "password": "@body('GetApiKey')?['value']", @@ -371,46 +311,114 @@ "username": "@body('GetUsername')?['value']" }, "headers": { - "User-Agent": "Intel 471 - Malware Intelligence - Azure Logic App 1.0.0" + "User-Agent": "Intel 471 - Malware Intelligence Sentinel - Azure Logic App 1.0.0" }, "method": "GET", "queries": "@if(equals(variables('cursor'), 'null'), variables('payload'), setProperty(variables('payload'), 'cursor', variables('cursor')))", "uri": "https://api.intel471.com/v1/indicators/stream" - }, - "runAfter": {}, - "type": "Http" + } }, - "MapIndicatorsToTiIndicators": { - "inputs": { - "from": "@variables('collectedIndicators')", - "select": { - "action": "@parameters('Action')", - "confidence": "@if(equals(item()?['data']?['confidence'], 'high'), 85, if(equals(item()?['data']?['confidence'], 'medium'), 50, 15))", - "description": "@concat('Intel 471 - ', item()?['data']?['context']?['description'])", - "expirationDateTime": "@addToTime('1970-01-01T00:00:00Z', div(item()?['data']['expiration'], 1000), 'second')", - "externalId": "@{item()?['data']?['uid']}", - "fileHashType": "@{item()?['composedHashType']}", - "fileHashValue": "@{item()?['composedHashValue']}", - "fileSize": "@if(contains(item()?['data']?['indicator_data'], 'file'), item()?['data']?['indicator_data']?['file']?['size'], '')", - "fileType": "@if(contains(item()?['data']?['indicator_data'], 'file'), item()?['data']?['indicator_data']?['file']?['type'], '')", - "killChain": "@if(equals(item()?['data']?['mitre_tactics'], 'command_and_control'), createArray('C2'), if(equals(item()?['data']?['mitre_tactics'], 'stage_capabilities'), createArray('Installation'), if(equals(item()?['data']?['mitre_tactics'], 'initial_access'), createArray('Exploitation'), json('[]'))))", - "lastReportedDateTime": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['activity']['last'], 1000), 'second')}", - "malwareFamilyNames": "@createArray(item()['data']['threat']['data']['family'])", - "networkIPv4": "@{item()?['data']?['indicator_data']?['address']}", - "targetProduct": "@parameters('TargetProduct')", - "threatType": "Malware", - "tlpLevel": "amber", - "url": "@{item()?['data']?['indicator_data']?['url']}" + "IfIndicators": { + "actions": { + "ClearCollectedIndicators": { + "runAfter": { + "Threat_Intelligence_-_Upload_Indicators_of_Compromise_(V2)_(Preview)": [ + "Succeeded" + ] + }, + "type": "SetVariable", + "inputs": { + "name": "collectedIndicators", + "value": [] + } + }, + "MapIndicatorsToTiIndicators": { + "runAfter": {}, + "type": "Select", + "inputs": { + "from": "@variables('collectedIndicators')", + "select": { + "confidence": "@if(equals(item()?['data']?['confidence'], 'high'), 85, if(equals(item()?['data']?['confidence'], 'medium'), 50, 15))", + "created": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['activity']['first'], 1000), 'second')}", + "description": "@concat('Intel 471 - ', item()?['data']?['context']?['description'])", + "external_references": [ + { + "external_id": "@item()['data']['threat']['uid']", + "source_name": "intel471", + "url": "@concat('https://titan.intel471.com/malware/', item()['data']['threat']['uid'])" + } + ], + "id": "@concat('indicator--', guid())", + "indicator_types": [ + "malicious-activity" + ], + "kill_chain_phases": [ + { + "kill_chain_name": "lockheed-martin-cyber-kill-chain", + "phase_name": "@if(equals(item()?['data']?['mitre_tactics'], 'command_and_control'), 'C2', if(equals(item()?['data']?['mitre_tactics'], 'stage_capabilities'), 'Installation', if(equals(item()?['data']?['mitre_tactics'], 'initial_access'), 'Exploitation', '')))" + } + ], + "labels": "@createArray(item()['data']['threat']['data']['family'])", + "modified": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['last_updated'], 1000), 'second')}", + "name": "@item()['data']['threat']['data']['family']", + "object_marking_refs": [ + "marking-definition--f88d31f6-486f-44da-b317-01333bde0b82" + ], + "pattern": "@if(equals(item()?['data']?['indicator_type'], 'url'), concat('[url:value = ''', item()?['data']?['indicator_data']?['url'], ''']'), if(equals(item()?['data']?['indicator_type'], 'ipv4'), concat('[ipv4-addr:value = ''', item()?['data']?['indicator_data']?['address'], ''']'), concat('[file:hashes.md5 = ''', item()?['data']?['indicator_data']?['file']?['md5'], ''' OR file:hashes.sha1 = ''', item()?['data']?['indicator_data']?['file']?['sha1'], ''' OR file:hashes.sha256 = ''', item()?['data']?['indicator_data']?['file']?['sha256'], ''']') ))", + "pattern_type": "stix", + "spec_version": "2.1", + "type": "indicator", + "valid_from": "@{addToTime('1970-01-01T00:00:00Z', div(item()?['activity']['first'], 1000), 'second')}", + "valid_until": "@addToTime('1970-01-01T00:00:00Z', div(item()?['data']['expiration'], 1000), 'second')" + } + } + }, + "Threat_Intelligence_-_Upload_Indicators_of_Compromise_(V2)_(Preview)": { + "runAfter": { + "MapIndicatorsToTiIndicators": [ + "Succeeded" + ] + }, + "type": "ApiConnection", + "inputs": { + "body": { + "indicators": "@body('MapIndicatorsToTiIndicators')", + "sourcesystem": "Intel 471 Titan" + }, + "host": { + "connection": { + "name": "@parameters('$connections')['azuresentinel']['connectionId']" + } + }, + "method": "post", + "path": "/V2/ThreatIntelligence/@{encodeURIComponent(parameters('WorkspaceID'))}/UploadIndicators/" + } } }, "runAfter": { - "FilterAndSplitFetchedIndicators": [ + "FilterIndicators": [ "Succeeded" ] }, - "type": "Select" + "expression": { + "and": [ + { + "greater": [ + "@length(variables('collectedIndicators'))", + 0 + ] + } + ] + }, + "type": "If" }, "Parse_JSON": { + "runAfter": { + "HTTP": [ + "Succeeded" + ] + }, + "type": "ParseJson", "inputs": { "content": "@body('HTTP')", "schema": { @@ -597,72 +605,40 @@ }, "type": "object" } - }, - "runAfter": { - "HTTP": [ - "Succeeded" - ] - }, - "type": "ParseJson" + } }, "SetHasResults": { - "inputs": { - "name": "hasResults", - "value": "@contains(body('Parse_JSON'), 'indicators')" - }, "runAfter": { "CursorNotNull": [ "Succeeded" ] }, - "type": "SetVariable" - }, - "SubmitInBatches": { - "actions": { - "Submit_multiple_tiIndicators": { - "inputs": { - "body": { - "value": "@items('SubmitInBatches')" - }, - "host": { - "connection": { - "name": "@parameters('$connections')['microsoftgraphsecurity']['connectionId']" - } - }, - "method": "post", - "path": "/beta/security/tiIndicators/submitTiIndicators" - }, - "runAfter": {}, - "type": "ApiConnection" - } - }, - "foreach": "@chunk(body('MapIndicatorsToTiIndicators'), 100)", - "runAfter": { - "MapIndicatorsToTiIndicators": [ - "Succeeded" - ] - }, - "runtimeConfiguration": { - "concurrency": { - "repetitions": 1 - } - }, - "type": "Foreach" + "type": "SetVariable", + "inputs": { + "name": "hasResults", + "value": "@contains(body('Parse_JSON'), 'indicators')" + } } }, + "runAfter": { + "InitIntel471Payload": [ + "Succeeded" + ] + }, "expression": "@equals(variables('hasResults'), false)", "limit": { "count": 60, "timeout": "PT1H" }, + "type": "Until" + }, + "GetApiKey": { "runAfter": { - "InitIntel471Payload": [ + "GetUsername": [ "Succeeded" ] }, - "type": "Until" - }, - "GetApiKey": { + "type": "ApiConnection", "inputs": { "host": { "connection": { @@ -670,16 +646,16 @@ } }, "method": "get", - "path": "/secrets/@{encodeURIComponent('TitanAPIKey')}/value" - }, + "path": "/secrets/@{encodeURIComponent('TitanAPIKeySentinel')}/value" + } + }, + "GetCursorFromBlob": { "runAfter": { - "GetUsername": [ + "InitCollectedIndicators": [ "Succeeded" ] }, - "type": "ApiConnection" - }, - "GetCursorFromBlob": { + "type": "ApiConnection", "inputs": { "host": { "connection": { @@ -691,15 +667,15 @@ "queries": { "inferContentType": true } - }, + } + }, + "GetFromDateFromBlob": { "runAfter": { - "InitCollectedIndicators": [ + "IfCursorBlobExists": [ "Succeeded" ] }, - "type": "ApiConnection" - }, - "GetFromDateFromBlob": { + "type": "ApiConnection", "inputs": { "host": { "connection": { @@ -711,15 +687,11 @@ "queries": { "inferContentType": true } - }, - "runAfter": { - "IfCursorBlobExists": [ - "Succeeded" - ] - }, - "type": "ApiConnection" + } }, "GetUsername": { + "runAfter": {}, + "type": "ApiConnection", "inputs": { "host": { "connection": { @@ -727,25 +699,31 @@ } }, "method": "get", - "path": "/secrets/@{encodeURIComponent('TitanUserName')}/value" - }, - "runAfter": {}, - "type": "ApiConnection" + "path": "/secrets/@{encodeURIComponent('TitanUserNameSentinel')}/value" + } }, "IfCursorBlobExists": { "actions": { "SetCursor": { + "runAfter": {}, + "type": "SetVariable", "inputs": { "name": "cursor", "value": "@{body('GetCursorFromBlob')}" - }, - "runAfter": {}, - "type": "SetVariable" + } } }, + "runAfter": { + "GetCursorFromBlob": [ + "Succeeded", + "Failed" + ] + }, "else": { "actions": { "CreateBlobForCursor": { + "runAfter": {}, + "type": "ApiConnection", "inputs": { "body": "null", "headers": { @@ -764,13 +742,11 @@ "queryParametersSingleEncoded": true } }, - "runAfter": {}, "runtimeConfiguration": { "contentTransfer": { "transferMode": "Chunked" } - }, - "type": "ApiConnection" + } } } }, @@ -786,28 +762,34 @@ } ] }, - "runAfter": { - "GetCursorFromBlob": [ - "Succeeded", - "Failed" - ] - }, "type": "If" }, "IfFromDateBlobExists": { "actions": { "SetFromDateFromBlob": { + "runAfter": {}, + "type": "SetVariable", "inputs": { "name": "fromDate", "value": "@int(body('GetFromDateFromBlob'))" - }, - "runAfter": {}, - "type": "SetVariable" + } } }, + "runAfter": { + "GetFromDateFromBlob": [ + "Succeeded", + "Failed" + ] + }, "else": { "actions": { "CreateBlobForFromDate": { + "runAfter": { + "SetFromDate": [ + "Succeeded" + ] + }, + "type": "ApiConnection", "inputs": { "body": "@variables('fromDate')", "headers": { @@ -826,25 +808,19 @@ "queryParametersSingleEncoded": true } }, - "runAfter": { - "SetFromDate": [ - "Succeeded" - ] - }, "runtimeConfiguration": { "contentTransfer": { "transferMode": "Chunked" } - }, - "type": "ApiConnection" + } }, "SetFromDate": { + "runAfter": {}, + "type": "SetVariable", "inputs": { "name": "fromDate", "value": "@div(if(equals(parameters('LookBackDays'), 0), sub(ticks(utcNow()),ticks('1970-01-01')), sub(sub(ticks(utcNow()), mul(864000000000, parameters('LookBackDays'))), ticks('1970-01-01'))), 10000)" - }, - "runAfter": {}, - "type": "SetVariable" + } } } }, @@ -860,15 +836,15 @@ } ] }, - "runAfter": { - "GetFromDateFromBlob": [ - "Succeeded", - "Failed" - ] - }, "type": "If" }, "InitCollectedIndicators": { + "runAfter": { + "InitFromDate": [ + "Succeeded" + ] + }, + "type": "InitializeVariable", "inputs": { "variables": [ { @@ -877,15 +853,15 @@ "value": [] } ] - }, + } + }, + "InitCursor": { "runAfter": { - "InitFromDate": [ + "InitHasResults": [ "Succeeded" ] }, - "type": "InitializeVariable" - }, - "InitCursor": { + "type": "InitializeVariable", "inputs": { "variables": [ { @@ -894,15 +870,15 @@ "value": "null" } ] - }, + } + }, + "InitFromDate": { "runAfter": { - "InitHasResults": [ + "InitCursor": [ "Succeeded" ] }, - "type": "InitializeVariable" - }, - "InitFromDate": { + "type": "InitializeVariable", "inputs": { "variables": [ { @@ -910,15 +886,15 @@ "type": "integer" } ] - }, + } + }, + "InitHasResults": { "runAfter": { - "InitCursor": [ + "GetApiKey": [ "Succeeded" ] }, - "type": "InitializeVariable" - }, - "InitHasResults": { + "type": "InitializeVariable", "inputs": { "variables": [ { @@ -927,15 +903,15 @@ "value": true } ] - }, + } + }, + "InitIntel471Payload": { "runAfter": { - "GetApiKey": [ + "IfFromDateBlobExists": [ "Succeeded" ] }, - "type": "InitializeVariable" - }, - "InitIntel471Payload": { + "type": "InitializeVariable", "inputs": { "variables": [ { @@ -947,13 +923,7 @@ } } ] - }, - "runAfter": { - "IfFromDateBlobExists": [ - "Succeeded" - ] - }, - "type": "InitializeVariable" + } } }, "outputs": {} @@ -961,10 +931,15 @@ "parameters": { "$connections": { "value": { - "microsoftgraphsecurity": { - "connectionId": "[resourceId('Microsoft.Web/connections', variables('GraphSecurityConnectionName'))]", - "connectionName": "[variables('GraphSecurityConnectionName')]", - "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/microsoftgraphsecurity')]" + "azuresentinel": { + "connectionId": "[resourceId('Microsoft.Web/connections', variables('MicrosoftSentinelConnectionName'))]", + "connectionName": "[variables('MicrosoftSentinelConnectionName')]", + "id": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Web/locations/', resourceGroup().location, '/managedApis/Azuresentinel')]", + "connectionProperties": { + "authentication": { + "type": "ManagedServiceIdentity" + } + } }, "azureblob": { "connectionId": "[resourceId('Microsoft.Web/connections', variables('AzureBlobConnectionName'))]", @@ -992,11 +967,8 @@ "LookBackDays": { "value": "[parameters('LookBackDays')]" }, - "TargetProduct": { - "value": "[parameters('TargetProduct')]" - }, - "Action": { - "value": "[parameters('Action')]" + "WorkspaceID": { + "value": "[parameters('WorkspaceID')]" } } } diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/images/ImportMalwareIntelligenceToSentinel.png b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/images/ImportMalwareIntelligenceToSentinel.png index bba8ca15284..00878936486 100644 Binary files a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/images/ImportMalwareIntelligenceToSentinel.png and b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/images/ImportMalwareIntelligenceToSentinel.png differ diff --git a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/malware-intelligence-screenshot.png b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/malware-intelligence-screenshot.png index d567ef5109f..a832e052264 100644 Binary files a/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/malware-intelligence-screenshot.png and b/Solutions/Intel471/Playbooks/Intel471-ImportMalwareIntelligenceToSentinel/malware-intelligence-screenshot.png differ diff --git a/Solutions/Intel471/ReleaseNotes.md b/Solutions/Intel471/ReleaseNotes.md new file mode 100644 index 00000000000..7e8350de7db --- /dev/null +++ b/Solutions/Intel471/ReleaseNotes.md @@ -0,0 +1,3 @@ +| **Version** | **Date Modified (DD-MM-YYYY)** | **Change History** | +|-------------|--------------------------------|---------------------------------------------| +| 3.0.0 | 27-11-2023 | Added **Playbook** 'Intel 471 Malware Intelligence to Graph Security' using new upload indicators API to Intel 471 Solution. | \ No newline at end of file