Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

TI Map URL Entity to SecurityAlert Data

Back
Idf30a47c1-65fb-42b1-a7f4-00941c12550b
RulenameTI Map URL Entity to SecurityAlert Data
DescriptionThis query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in SecurityAlert data.
SeverityMedium
TacticsImpact
Required data connectorsAzureSecurityCenter
MicrosoftCloudAppSecurity
MicrosoftDefenderThreatIntelligence
ThreatIntelligence
ThreatIntelligenceTaxii
KindScheduled
Query frequency1h
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence/Analytic Rules/URLEntity_SecurityAlerts.yaml
Version1.2.7
Arm templatef30a47c1-65fb-42b1-a7f4-00941c12550b.json
Deploy To Azure
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let URLRegex = "((https?|ftp|ldap|wss?|file):\\/\\/(([\\:\\%\\w\\_\\-]+(\\.|@))*((xn--)?[a-zA-Z0-9\\-]+\\.)+(xn--[a-z0-9]+|[A-Za-z]+)|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{0,3})[.,:\\w@?^=%&\\/~+#-]*[\\w@?^=%&\\/~+#-])";
let SecurityEvents = materialize(SecurityAlert
  | where TimeGenerated >= ago(dt_lookBack)
  | extend MSTI = case(AlertName has "TI map" and VendorName == "Microsoft" and ProductName == 'Azure Sentinel', true, false)
  | where MSTI == false
  // Extract URL from JSON data
  | mv-expand parse_json(Entities)
  | where isnotempty(Entities.Url) or isnotempty(Entities.Urls)
  | extend Url = coalesce(Entities.Url, Entities.Urls)
  | mv-expand Url
  | extend Url = tolower(Url)
  // Extract hostname from JSON data for entity mapping
  | extend Compromised_Host = tostring(parse_json(ExtendedProperties).["Compromised Host"])
  | extend Alert_TimeGenerated = TimeGenerated);
let EventUrls = materialize(SecurityEvents | distinct Url | summarize make_list(Url));
ThreatIntelligenceIndicator
| where isnotempty(Url)
| where TimeGenerated >= ago(ioc_lookBack)
| extend Url = tolower(Url)
| where tolower(Url) in (EventUrls)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;" 
// using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
| join kind=innerunique (SecurityEvents) on Url
| where Alert_TimeGenerated < ExpirationDateTime
| summarize Alert_TimeGenerated = arg_max(Alert_TimeGenerated, *) by IndicatorId, AlertName
| project timestamp = Alert_TimeGenerated, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, AlertName, AlertSeverity, Description, Url, Compromised_Host
triggerOperator: gt
queryFrequency: 1h
description: |
    'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in SecurityAlert data.'
version: 1.2.7
kind: Scheduled
triggerThreshold: 0
requiredDataConnectors:
- connectorId: MicrosoftCloudAppSecurity
  dataTypes:
  - SecurityAlert
- connectorId: AzureSecurityCenter
  dataTypes:
  - SecurityAlert
- connectorId: ThreatIntelligence
  dataTypes:
  - ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
  dataTypes:
  - ThreatIntelligenceIndicator
- connectorId: MicrosoftDefenderThreatIntelligence
  dataTypes:
  - ThreatIntelligenceIndicator
queryPeriod: 14d
name: TI Map URL Entity to SecurityAlert Data
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence/Analytic Rules/URLEntity_SecurityAlerts.yaml
id: f30a47c1-65fb-42b1-a7f4-00941c12550b
tactics:
- Impact
severity: Medium
entityMappings:
- fieldMappings:
  - identifier: HostName
    columnName: Compromised_Host
  entityType: Host
- fieldMappings:
  - identifier: Url
    columnName: Url
  entityType: URL
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let URLRegex = "((https?|ftp|ldap|wss?|file):\\/\\/(([\\:\\%\\w\\_\\-]+(\\.|@))*((xn--)?[a-zA-Z0-9\\-]+\\.)+(xn--[a-z0-9]+|[A-Za-z]+)|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{0,3})[.,:\\w@?^=%&\\/~+#-]*[\\w@?^=%&\\/~+#-])";
  let SecurityEvents = materialize(SecurityAlert
    | where TimeGenerated >= ago(dt_lookBack)
    | extend MSTI = case(AlertName has "TI map" and VendorName == "Microsoft" and ProductName == 'Azure Sentinel', true, false)
    | where MSTI == false
    // Extract URL from JSON data
    | mv-expand parse_json(Entities)
    | where isnotempty(Entities.Url) or isnotempty(Entities.Urls)
    | extend Url = coalesce(Entities.Url, Entities.Urls)
    | mv-expand Url
    | extend Url = tolower(Url)
    // Extract hostname from JSON data for entity mapping
    | extend Compromised_Host = tostring(parse_json(ExtendedProperties).["Compromised Host"])
    | extend Alert_TimeGenerated = TimeGenerated);
  let EventUrls = materialize(SecurityEvents | distinct Url | summarize make_list(Url));
  ThreatIntelligenceIndicator
  | where isnotempty(Url)
  | where TimeGenerated >= ago(ioc_lookBack)
  | extend Url = tolower(Url)
  | where tolower(Url) in (EventUrls)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
  | where Active == true and ExpirationDateTime > now()
  | where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;" 
  // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  | join kind=innerunique (SecurityEvents) on Url
  | where Alert_TimeGenerated < ExpirationDateTime
  | summarize Alert_TimeGenerated = arg_max(Alert_TimeGenerated, *) by IndicatorId, AlertName
  | project timestamp = Alert_TimeGenerated, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, AlertName, AlertSeverity, Description, Url, Compromised_Host  
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2023-02-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/f30a47c1-65fb-42b1-a7f4-00941c12550b')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/f30a47c1-65fb-42b1-a7f4-00941c12550b')]",
      "properties": {
        "alertRuleTemplateName": "f30a47c1-65fb-42b1-a7f4-00941c12550b",
        "customDetails": null,
        "description": "'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in SecurityAlert data.'\n",
        "displayName": "TI Map URL Entity to SecurityAlert Data",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "Compromised_Host",
                "identifier": "HostName"
              }
            ]
          },
          {
            "entityType": "URL",
            "fieldMappings": [
              {
                "columnName": "Url",
                "identifier": "Url"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence/Analytic Rules/URLEntity_SecurityAlerts.yaml",
        "query": "let dt_lookBack = 1h;\nlet ioc_lookBack = 14d;\nlet URLRegex = \"((https?|ftp|ldap|wss?|file):\\\\/\\\\/(([\\\\:\\\\%\\\\w\\\\_\\\\-]+(\\\\.|@))*((xn--)?[a-zA-Z0-9\\\\-]+\\\\.)+(xn--[a-z0-9]+|[A-Za-z]+)|\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{1,3}\\\\.\\\\d{0,3})[.,:\\\\w@?^=%&\\\\/~+#-]*[\\\\w@?^=%&\\\\/~+#-])\";\nlet SecurityEvents = materialize(SecurityAlert\n  | where TimeGenerated >= ago(dt_lookBack)\n  | extend MSTI = case(AlertName has \"TI map\" and VendorName == \"Microsoft\" and ProductName == 'Azure Sentinel', true, false)\n  | where MSTI == false\n  // Extract URL from JSON data\n  | mv-expand parse_json(Entities)\n  | where isnotempty(Entities.Url) or isnotempty(Entities.Urls)\n  | extend Url = coalesce(Entities.Url, Entities.Urls)\n  | mv-expand Url\n  | extend Url = tolower(Url)\n  // Extract hostname from JSON data for entity mapping\n  | extend Compromised_Host = tostring(parse_json(ExtendedProperties).[\"Compromised Host\"])\n  | extend Alert_TimeGenerated = TimeGenerated);\nlet EventUrls = materialize(SecurityEvents | distinct Url | summarize make_list(Url));\nThreatIntelligenceIndicator\n| where isnotempty(Url)\n| where TimeGenerated >= ago(ioc_lookBack)\n| extend Url = tolower(Url)\n| where tolower(Url) in (EventUrls)\n| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId\n| where Active == true and ExpirationDateTime > now()\n| where Description !contains_cs \"State: inactive;\" and Description !contains_cs \"State: falsepos;\" \n// using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated\n| join kind=innerunique (SecurityEvents) on Url\n| where Alert_TimeGenerated < ExpirationDateTime\n| summarize Alert_TimeGenerated = arg_max(Alert_TimeGenerated, *) by IndicatorId, AlertName\n| project timestamp = Alert_TimeGenerated, ActivityGroupNames, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, AlertName, AlertSeverity, Description, Url, Compromised_Host\n",
        "queryFrequency": "PT1H",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Impact"
        ],
        "templateVersion": "1.2.7",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}