Detection rules › Kusto

Detect external user sending suspicious link to multiple users

Group by
AccountObjectId, SenderEmailAddress, SenderObjectId, Url
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

An external sender suddenly sending the same link to multiple internal users, can indicate that external user being compromised and used for BEC Attacks. In these kind of attacks compromised accounts are used to send phishing links or attachments to users in business relationships.

MITRE ATT&CK coverage

TacticTechniques
Initial AccessT1566.002 Phishing: Spearphishing Link

References

Rule body yaml

// External user sending same link to multiple users via Teams
let threshold = 5;
MessageEvents
| where TimeGenerated > ago(1d)
// Focus on chat messsages
| where ThreadType == "chat"
// Only return external users sending messages
| join kind=leftanti (
    IdentityInfo
    | where TimeGenerated > ago(14d)
    | distinct AccountObjectId
) on $left.SenderObjectId == $right.AccountObjectId
// Only flag messages with Teams Links
| join kind=inner MessageUrlInfo on TeamsMessageId
// Exclude teams file thumbnails
| where Url !~ "http://dummy.jpg/"
// Make a set of the chats a user sends a specific URL to
| summarize ChatSet = make_set(ThreadId) by SenderEmailAddress, Url
// Count the amount of chats
| extend ChatCount = array_length(ChatSet)
| where ChatCount > threshold

Stages and Predicates

Parameters

let threshold = 5;

Stage 1: source

MessageEvents

Stage 2: where

| where TimeGenerated > ago(1d)

Stage 3: where

| where ThreadType == "chat"

Stage 4: join (negated)

| join kind=leftanti (
    IdentityInfo
    | where TimeGenerated > ago(14d)
    | distinct AccountObjectId
) on $left.SenderObjectId == $right.AccountObjectId

Stage 5: join

| join kind=inner MessageUrlInfo on TeamsMessageId

Stage 6: where

| where Url !~ "http://dummy.jpg/"

Stage 7: summarize

| summarize ChatSet = make_set(ThreadId) by SenderEmailAddress, Url

Stage 8: extend

| extend ChatCount = array_length(ChatSet)

Stage 9: where

| where ChatCount > threshold

Indicators

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
ChatCountgt
  • 5 transforms: cased
ThreadTypeeq
  • chat transforms: cased
Urlne
  • http://dummy.jpg/

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
ChatSetsummarize
SenderEmailAddresssummarize
Urlsummarize
ChatCountextend