entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: EmailFrom
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIp
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: RepresentativeUrlDomain
tactics:
- InitialAccess
suppressionEnabled: false
suppressionDuration: PT1H
requiredDataConnectors:
- dataTypes:
- RedSiftEmailForensics_CL
connectorId: RedSiftPush
alertDetailsOverride:
alertDisplayNameFormat: RedSift - URL to new domain in email from {{EmailFrom}}
alertDescriptionFormat: 'Email from {{EmailFrom}} contains {{NewDomainCount}} previously unseen URL domain(s): {{NewUrlDomainList}}.'
incidentConfiguration:
groupingConfiguration:
reopenClosedIncident: false
lookbackDuration: P1D
groupByEntities:
- Account
- DNS
groupByCustomDetails:
- EmailSubject
enabled: true
matchingMethod: Selected
createIncident: true
id: 8972b513-12a2-4b46-8263-3f091d88a8bc
severity: Medium
eventGroupingSettings:
aggregationKind: AlertPerResult
status: Available
customDetails:
EmailSubject: EmailSubject
CorrelationUid: CorrelationUid
NewUrlList: NewUrlList
NewDomainCount: NewDomainCount
ReturnPath: EmailReturnPath
NewUrlDomainList: NewUrlDomainList
query: |
let lookback = 14d;
let recentWindow = 1h;
let historicalDomains = RedSiftEmailForensics_CL
| extend EmailUrls = todynamic(EmailUrls)
| where TimeGenerated between (ago(lookback) .. ago(recentWindow))
| where isnotempty(EmailUrls) and array_length(EmailUrls) > 0
| mv-expand Url = EmailUrls
| extend UrlString = tostring(Url.url_string)
| where isnotempty(UrlString)
| extend UrlDomain = tostring(parse_url(UrlString).Host)
| where isnotempty(UrlDomain)
| summarize by UrlDomain;
RedSiftEmailForensics_CL
| extend
EmailFrom = tostring(column_ifexists("EmailFrom", "")),
EmailSubject = tostring(column_ifexists("EmailSubject", "")),
EmailReturnPath = tostring(column_ifexists("EmailReturnPath", "")),
EmailMessageUid = tostring(column_ifexists("EmailMessageUid", "")),
SrcIp = tostring(column_ifexists("SrcIp", "")),
DstHostname = tostring(column_ifexists("DstHostname", "")),
Severity = tostring(column_ifexists("Severity", "")),
Message = tostring(column_ifexists("Message", "")),
CorrelationUid = tostring(column_ifexists("CorrelationUid", "")),
EmailUrls = todynamic(EmailUrls)
| where TimeGenerated >= ago(recentWindow)
| where isnotempty(EmailUrls) and array_length(EmailUrls) > 0
| mv-expand Url = EmailUrls
| extend UrlString = tostring(Url.url_string)
| where isnotempty(UrlString)
| extend UrlDomain = tostring(parse_url(UrlString).Host)
| where isnotempty(UrlDomain)
| join kind=leftanti (historicalDomains) on UrlDomain
| summarize
NewUrlDomains = make_set(UrlDomain, 50),
NewUrls = make_set(UrlString, 50),
NewDomainCount = dcount(UrlDomain),
UrlCount = dcount(UrlString),
RepresentativeUrlDomain = take_any(UrlDomain)
by TimeGenerated,
EmailFrom,
EmailSubject,
EmailReturnPath,
EmailMessageUid,
SrcIp,
DstHostname,
Severity,
Message,
CorrelationUid
| extend
NewUrlDomainList = strcat_array(NewUrlDomains, ", "),
NewUrlList = strcat_array(NewUrls, ", ")
| project
TimeGenerated,
EmailFrom,
EmailSubject,
EmailReturnPath,
EmailMessageUid,
SrcIp,
DstHostname,
RepresentativeUrlDomain,
NewDomainCount,
UrlCount,
NewUrlDomainList,
NewUrlList,
Severity,
Message,
CorrelationUid
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Red Sift/Analytic Rules/RedSiftEmailUrlWithNewDomain.yaml
kind: Scheduled
queryPeriod: 14d
version: 1.0.0
name: Red Sift - Email with URL to previously unseen domain
queryFrequency: 1h
triggerThreshold: 0
relevantTechniques:
- T1566
description: |
'Detects email forensics events containing one or more URLs whose domain has not been seen in the previous 14 days, which may indicate newly observed phishing infrastructure or suspicious delivery patterns.'
triggerOperator: gt