↳ GitHub sourceAnalytics ruleMedium
TI Map URL Entity to SecurityAlert Data
Description
'This query identifies any URL indicators of compromise (IOCs) from threat intelligence (TI) by searching for matches in SecurityAlert data.'
- Rule type
- Scheduled
- Version
- 1.2.11
- Query frequency
- 1h
- Query period
- 14d
- Trigger
- gt 0
Declared MITRE coverage
Declared sources
Metadata from the source file. No dependencies inferred from KQL.
Connectors
Data types
KQL query
Original query, unchanged.
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
| mv-expand Entity = parse_json(Entities)
| extend RawUrl = coalesce(Entity.Url, Entity.Urls)
| mv-expand RawUrl
| extend Url = tostring(RawUrl)
| extend Compromised_Host = tostring(parse_json(ExtendedProperties).["Compromised Host"])
| extend Alert_TimeGenerated = TimeGenerated);
let EventUrls = materialize(SecurityEvents | distinct Url | summarize make_list(Url));
ThreatIntelIndicators
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "url"
| extend Url = ObservableValue
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| where TimeGenerated >= ago(ioc_lookBack)
| where tolower(Url) in (EventUrls)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend Description = tostring(parse_json(Data).description)
| where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
| project-reorder *, Tags, TrafficLightProtocolLevel, Url, Type
// 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 < ValidUntil
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| summarize Alert_TimeGenerated = arg_max(Alert_TimeGenerated, *) by Id, AlertName
| project timestamp = Alert_TimeGenerated, ActivityGroupNames, Id, Type, ValidUntil, Confidence, AlertName, AlertSeverity, Description, Url, Compromised_Host
Declared entities
Related content
Links established from declared identifiers and solution manifests.
Source provenance
GitHubDisplayed values come from files in Azure/Azure-Sentinel. They describe the published template, not your workspace configuration.
- Commit
9800e51↗- Source identifier
3b6bdb38-93c5-452f-ab3a-97a3d1320d16
GSTEP / CATALOG TRACKING
Added to catalog : 16 Sept 2026 · 05:49 UTC
Last change observed : 16 Sept 2026 · 05:49 UTC