← ~/DETECTIONS

2024-10-15 // Dylan Evans

New External Teams Chat Recipient Detection

Microsoft Sentinel

Detects newly added external recipients in Microsoft Teams chat threads who were not previously engaged with internal senders.

teamsexternal-recipientslateral-movementemail-sensitivity

New External Teams Chat Recipient Detection

Overview

This detection identifies suspicious new external recipients in Microsoft Teams chat threads who were previously unknown to internal senders, potentially indicating phishing or credential harvesting attempts. The query compares recent chat activity with historical message recipients to flag anomalies where an external sender suddenly engages with a recipient not seen before.

Query

let LookbackPeriod = 7d; //define the period to check for known external senders
let RecentPeriod = 1d; //define the period to check for new external senders
let internalDomains = IdentityInfo
| distinct Domain = tostring(split(AccountUpn, "@")[1]); //define internal user domains
let ExternalRecipientsLastWeek =
MessageEvents
| where Timestamp between (ago(LookbackPeriod + RecentPeriod) .. ago(RecentPeriod))
| mv-expand RecipientDetails
| extend Recipient = parse_json(RecipientDetails)
| extend RecipientSmtpAddress = tostring(Recipient.RecipientSmtpAddress)
| where not(SenderEmailAddress has_any (internalDomains)) //only return check messages where the sender of the message is external
| summarize by RecipientSmtpAddress;
let ExternalRecipientsToday =
MessageEvents
| where Timestamp >= ago(RecentPeriod)
| mv-expand RecipientDetails
| extend Recipient = parse_json(RecipientDetails)
| extend RecipientSmtpAddress = tostring(Recipient.RecipientSmtpAddress)
| where not(RecipientSmtpAddress has_any (internalDomains))
| where SenderEmailAddress has_any (internalDomains)
| where ThreadType == "chat" //only check chat messages, remove this line to check all teams events
| project Timestamp, Subject, ThreadName, SenderEmailAddress, RecipientSmtpAddress;
ExternalRecipientsToday
| join kind=anti (ExternalRecipientsLastWeek) on RecipientSmtpAddress
| project Timestamp, Subject, ThreadName, SenderEmailAddress, NewExternalRecipient = RecipientSmtpAddress;

Logic Explanation

The query works in two phases:

  1. Historical Analysis: It first identifies all external senders (non-internal domains) who sent messages within a 7-day lookback period.
  2. Recent Comparison: It then checks Teams chat messages from the last day, filtering for internal sender emails and external recipients not previously seen. The anti-join operation highlights new external recipients (NewExternalRecipient) in today’s chats that weren’t part of prior external engagement.

Tuning Notes

  • False positives may occur if legitimate new team members join with external email domains (e.g., contractors) but haven’t sent messages yet.

    • Suggestion: Exclude known vendor/partner domains from internalDomains by adding them to a whitelist.
  • ThreadType filtering restricts detection to chats; removing it could flag broader Teams events, including emails or meetings.

    • Suggestion: Keep the filter if only chat-based lateral movement is critical.
  • RecipientSmtpAddress parsing may fail for malformed addresses (e.g., missing @). Adding a where RecipientSmtpAddress !isnull check could improve reliability.

    • Suggestion: Add this to ensure valid addresses are processed.

References