Adversaries may be using compromised domains to send malicious Teams messages as part of a spear-phishing campaign. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential lateral movement and exfiltration attempts early.
KQL Query
// This query provides insights of top outbound recipient domains of outbound Teams messages by volume and shows total number of inbound Teams messages with Threats from the same domains (as inbound senders) indicating potential partner user or organization compromise.
MessageEvents
| where IsExternalThread==1 and IsOwnedThread==1
| mv-expand Recipients = RecipientDetails
| extend RecipientEmailAddress = Recipients.RecipientSmtpAddress
| extend RecipientDomain = tostring(split(RecipientEmailAddress, "@")[1])
//| where RecipientDomain !="contoso.com"
| summarize count() by RecipientDomain
| project OutboundCount=count_, RecipientDomain, SenderFromDomain=RecipientDomain
| join (MessageEvents |mv-expand Recipients = RecipientDetails|extend RecipientEmailAddress = Recipients.RecipientSmtpAddress|extend SenderFromDomain = tostring(split(RecipientEmailAddress, "@")[1])| where IsExternalThread==1 and IsOwnedThread==0 and isempty(ThreatTypes)==false) on SenderFromDomain
| summarize max(OutboundCount),count() by SenderFromDomain
//| extend Bad_Traffic_Percentage = todouble(round(IncomingTeamsMessagesWithThreats=count_ / todouble(OutboundTeamsMessages=max_OutboundCount), 2))
| project SenderFromDomain, OutboundTeamsMessages=max_OutboundCount, IncomingTeamsMessagesWithThreats=count_//, Bad_Traffic_Percentage
| sort by OutboundTeamsMessages
id: af2b5dbd-4b66-47b4-9936-abfecb246ba1
name: Top domains outbound sending Malicious Teams messages inbound
description: |
This query looking for potential partner compromise via comparing outbound Teams message traffic per target domain and looking for malicious Teams messages from the same domains as inbound.
description-detailed: |
This query looking for potential partner compromise via comparing outbound Teams message traffic per target domain and looking for malicious Teams messages from the same domains as inbound.
Replace contoso.com with your own recipient domain and remove comment marker if you want to see Bad_Traffic_Percentage
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- MessageEvents
tactics:
- InitialAccess
relevantTechniques:
- T1566
query: |
// This query provides insights of top outbound recipient domains of outbound Teams messages by volume and shows total number of inbound Teams messages with Threats from the same domains (as inbound senders) indicating potential partner user or organization compromise.
MessageEvents
| where IsExternalThread==1 and IsOwnedThread==1
| mv-expand Recipients = RecipientDetails
| extend RecipientEmailAddress = Recipients.RecipientSmtpAddress
| extend RecipientDomain = tostring(split(RecipientEmailAddress, "@")[1])
//| where RecipientDomain !="contoso.com"
| summarize count() by RecipientDomain
| project OutboundCount=count_, RecipientDomain, SenderFromDomain=RecipientDomain
| join (MessageEvents |mv-expand Recipients = RecipientDetails|extend RecipientEmailAddress = Recipients.RecipientSmtpAddress|extend SenderFromDomain = tostring(split(RecipientEmailAddress, "@")[1])| where IsExternalThread==1 and IsOwnedThread==0 and isempty(ThreatTypes)==false) on SenderFromDomain
| summarize max(OutboundCount),count() by SenderFromDomain
//| extend Bad_Traffic_Percentage = todouble(round(IncomingTeamsMessagesWithThreats=count_ / todouble(OutboundTeamsMessages=max_OutboundCount), 2))
| project SenderFromDomain, OutboundTeamsMessages=max_OutboundCount, IncomingTeamsMessagesWithThreats=count_//, Bad_Traffic_Percentage
| sort by OutboundTeamsMessages
version: 1.0.0
Scenario: Scheduled Backup Job Sending Teams Messages to Internal Domains
Description: A scheduled backup tool (e.g., Veeam, Commvault) sends status updates or logs to an internal Teams channel as part of its reporting process.
Filter/Exclusion: Exclude traffic from known backup tools by checking the process_name or command_line fields, or use a custom list of backup-related domains.
Scenario: Admin Task Using Microsoft Teams for Internal Communication
Description: An admin task (e.g., PowerShell script, Azure DevOps pipeline) sends automated messages to a Teams channel for internal updates or notifications.
Filter/Exclusion: Filter messages sent from known admin tools or scripts by checking the user_agent, process_name, or source_ip against a list of trusted admin systems.
Scenario: Integration with Microsoft Power Automate Sending Teams Messages
Description: A Power Automate flow (Microsoft Flow) sends automated messages to a Teams channel as part of a workflow (e.g., alerting on file changes, system status).
Filter/Exclusion: Exclude messages originating from Power Automate by checking the client_issuer or user_agent fields, or by using a custom list of Power Automate-related domains.
Scenario: Internal Monitoring Tool Sending Alerts to Teams
Description: An internal monitoring tool (e.g., Splunk, Datadog, or custom SIEM) sends alerts to a Teams channel for incident response or system health.
Filter/Exclusion: Exclude messages from known monitoring tools by checking the process_name, source_ip, or user_agent fields, or by using a custom list of monitoring tool domains.
Scenario: User-Driven Teams Bot or Integration Sending Messages
Description: A user has configured a bot or integration (e.g., ServiceNow,