Adversaries may use malicious domains or URLs in Teams messages to exfiltrate data or deploy malware, leveraging known threat intelligence indicators to evade basic detection. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential command and control channels or initial compromise vectors early.
KQL Query
//This Query uses MessageUrlInfo, MessageEvents and UrlClickEvents to find external Teams messages with low reputation URL doamins (.xyz) and identify the top 10 users clicking on them.
// Extract IOC details from ThreatIntelIndicators export
let IOC = ThreatIntelIndicators
| where SourceSystem == "Microsoft Defender Threat Intelligence"
| extend IOCType = case(
ObservableKey has "ipv4" or ObservableKey has "network-traffic", "IP Address",
ObservableKey has "domain", "Domain",
ObservableKey has "url", "URL",
ObservableKey has "file", "File Hash",
ObservableKey has "email", "Email Address",
"Other")
| extend IOCValue = ObservableValue
| extend Pattern = tostring(split(Pattern, "=")[1]) // Extract value from STIX pattern if needed
| extend Description = tostring(parse_json(Data).description)
| extend IndicatorTypes = tostring(parse_json(Data).indicator_types)
| extend ValidFrom = todatetime(parse_json(Data).valid_from)
| extend ValidUntil = todatetime(parse_json(Data).valid_until)
| project TimeGenerated, IOCType, IOCValue, Pattern, Description, IndicatorTypes, ValidFrom, ValidUntil, Confidence
| order by TimeGenerated desc;
let IOCDomain = IOC
| where IOCType == "Domain";
let IOCUrl = IOC
| where IOCType == "URL";
let URLHits = MessageUrlInfo
| join IOCUrl on $left.Url == $right.IOCValue;
let DomainHits = MessageUrlInfo
| join IOCDomain on $left.UrlDomain == $right.IOCValue;
URLHits
| union DomainHits
| join kind=inner MessageEvents on TeamsMessageId
id: 2e7cda70-c3cd-4173-945e-6b5c14b05817
name: Teams Threat Intelligence Indicator Hit for Domain or URL
description: |
This rule detects and alerts on known threats in Teams messages when a contained domain or URL matches a Microsoft Defender Threat Intelligence indicator (of type 'Domain' or 'URL')
description-detailed: |
This rule detects when a domain or URL observed in Teams Messages matches a known threat intelligence indicator from Microsoft Defender Threat Intelligence. It specifically looks for hits against 'Domain' and 'URL' type indicators.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- MessageUrlInfo
- MessageEvents
- ThreatIntelIndicators
tactics:
- InitialAccess
relevantTechniques:
- T1566
query: |
//This Query uses MessageUrlInfo, MessageEvents and UrlClickEvents to find external Teams messages with low reputation URL doamins (.xyz) and identify the top 10 users clicking on them.
// Extract IOC details from ThreatIntelIndicators export
let IOC = ThreatIntelIndicators
| where SourceSystem == "Microsoft Defender Threat Intelligence"
| extend IOCType = case(
ObservableKey has "ipv4" or ObservableKey has "network-traffic", "IP Address",
ObservableKey has "domain", "Domain",
ObservableKey has "url", "URL",
ObservableKey has "file", "File Hash",
ObservableKey has "email", "Email Address",
"Other")
| extend IOCValue = ObservableValue
| extend Pattern = tostring(split(Pattern, "=")[1]) // Extract value from STIX pattern if needed
| extend Description = tostring(parse_json(Data).description)
| extend IndicatorTypes = tostring(parse_json(Data).indicator_types)
| extend ValidFrom = todatetime(parse_json(Data).valid_from)
| extend ValidUntil = todatetime(parse_json(Data).valid_until)
| project TimeGenerated, IOCType, IOCValue, Pattern, Description, IndicatorTypes, ValidFrom, ValidUntil, Confidence
| order by TimeGenerated desc;
let IOCDomain = IOC
| where IOCType == "Domain";
let IOCUrl = IOC
| where IOCType == "URL";
let URLHits = MessageUrlInfo
| join IOCUrl on $left.Url == $right.IOCValue;
let DomainHits = MessageUrlInfo
| join IOCDomain on $left.UrlDomain == $right.IOCValue;
URLHits
| union DomainHits
| join kind=inner MessageEvents on TeamsMessageId
| Sentinel Table | Notes |
|---|---|
UrlClickEvents | Ensure this data connector is enabled |
Scenario: Legitimate URL in a Shared Link
Description: A user shares a legitimate document or file via a URL in a Teams message, which coincidentally matches a known threat indicator in the Threat Intelligence database.
Filter/Exclusion: Exclude URLs that are part of shared files or links from internal file servers (e.g., \\fileserver\shared\* or https://internal.sharepoint.com/*). Use a custom filter like:
(url contains "internal.sharepoint.com" or url contains "fileserver")
Scenario: Scheduled Job or Automation Task
Description: A scheduled job or automation script (e.g., using PowerShell or Azure DevOps) sends a Teams message containing a domain or URL that is flagged as a threat.
Filter/Exclusion: Exclude messages from known automation tools or scheduled tasks. Use a filter like:
(message contains "ScheduledJob" or message contains "Azure DevOps" or message contains "PowerShell")
Scenario: Internal Threat Intelligence Feed Match
Description: A domain or URL used internally by the company is mistakenly included in the Microsoft Defender Threat Intelligence feed, causing false positives.
Filter/Exclusion: Exclude domains that are known internal assets. Use a filter like:
(domain contains "internaldomain.com" or domain contains "intranet" or domain contains "local")
Scenario: User-Generated Content with Known Safe URLs
Description: A user pastes a URL from a trusted source (e.g., a company blog or internal knowledge base) into a Teams message, which is flagged due to a match in the threat intelligence database.
Filter/Exclusion: Exclude URLs from known internal or trusted external domains. Use a filter like: