Skip to content

Commit baeb25a

Browse files
committed
Add Red Sift solution (Push connector via Codeless Connector Framework)
New Microsoft Sentinel solution that ingests Red Sift events via a push CCF connector. Includes: - connector definition, DCR, and table schemas for RedSiftAuth_CL and RedSiftEmailForensics_CL - five analytic rules for new IP logins, MFA disabled events, and suspicious email URL activity - solution data and metadata for the initial Red Sift release
1 parent 2b09372 commit baeb25a

18 files changed

+2891
-0
lines changed

Logos/redsift_logo.svg

Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
id: 6e0b70d4-0ab8-480e-9707-8ad45fc21a65
2+
name: Red Sift - New Email with URL from Previously Unseen Sender
3+
description: |
4+
'Detects email forensics events that contain one or more URLs where the sender in the from field has not been seen in the previous 14 days, which may indicate phishing activity or a newly observed sender.'
5+
severity: Medium
6+
status: Available
7+
requiredDataConnectors:
8+
- connectorId: RedSiftPush
9+
dataTypes:
10+
- RedSiftEmailForensics_CL
11+
queryFrequency: 1h
12+
queryPeriod: 14d
13+
triggerOperator: gt
14+
triggerThreshold: 0
15+
tactics:
16+
- InitialAccess
17+
relevantTechniques:
18+
- T1566
19+
query: |
20+
let lookback = 14d;
21+
let recentWindow = 1h;
22+
let historicalSenders = RedSiftEmailForensics_CL
23+
| extend
24+
EmailFrom = tostring(column_ifexists("EmailFrom", ""))
25+
| where TimeGenerated between (ago(lookback) .. ago(recentWindow))
26+
| where isnotempty(EmailFrom)
27+
| summarize by EmailFrom;
28+
RedSiftEmailForensics_CL
29+
| extend
30+
EmailFrom = tostring(column_ifexists("EmailFrom", "")),
31+
EmailSubject = tostring(column_ifexists("EmailSubject", "")),
32+
EmailReturnPath = tostring(column_ifexists("EmailReturnPath", "")),
33+
EmailMessageUid = tostring(column_ifexists("EmailMessageUid", "")),
34+
SrcIp = tostring(column_ifexists("SrcIp", "")),
35+
DstHostname = tostring(column_ifexists("DstHostname", "")),
36+
Severity = tostring(column_ifexists("Severity", "")),
37+
Message = tostring(column_ifexists("Message", "")),
38+
CorrelationUid = tostring(column_ifexists("CorrelationUid", "")),
39+
EmailUrls = todynamic(column_ifexists("EmailUrls", dynamic([])))
40+
| where TimeGenerated >= ago(recentWindow)
41+
| where isnotempty(EmailFrom)
42+
| extend UrlCount = array_length(EmailUrls)
43+
| where UrlCount > 0
44+
| mv-apply Url = EmailUrls on (
45+
summarize UrlSet = make_set(tostring(Url.url_string), 50)
46+
)
47+
| extend UrlList = strcat_array(UrlSet, ", ")
48+
| join kind=leftanti (historicalSenders) on EmailFrom
49+
| project
50+
TimeGenerated,
51+
EmailFrom,
52+
EmailSubject,
53+
EmailReturnPath,
54+
EmailMessageUid,
55+
SrcIp,
56+
DstHostname,
57+
UrlCount,
58+
UrlList,
59+
Severity,
60+
Message,
61+
CorrelationUid
62+
entityMappings:
63+
- entityType: Account
64+
fieldMappings:
65+
- identifier: FullName
66+
columnName: EmailFrom
67+
- entityType: IP
68+
fieldMappings:
69+
- identifier: Address
70+
columnName: SrcIp
71+
- entityType: DNS
72+
fieldMappings:
73+
- identifier: DomainName
74+
columnName: DstHostname
75+
customDetails:
76+
EmailSubject: EmailSubject
77+
ReturnPath: EmailReturnPath
78+
UrlCount: UrlCount
79+
UrlList: UrlList
80+
CorrelationUid: CorrelationUid
81+
alertDetailsOverride:
82+
alertDisplayNameFormat: "RedSift - New URL-bearing sender {{EmailFrom}}"
83+
alertDescriptionFormat: "Email from previously unseen sender {{EmailFrom}} contains {{UrlCount}} URL(s)."
84+
incidentConfiguration:
85+
createIncident: true
86+
groupingConfiguration:
87+
enabled: true
88+
reopenClosedIncident: false
89+
lookbackDuration: P1D
90+
matchingMethod: Selected
91+
groupByEntities:
92+
- Account
93+
groupByAlertDetails: []
94+
groupByCustomDetails:
95+
- EmailSubject
96+
eventGroupingSettings:
97+
aggregationKind: AlertPerResult
98+
suppressionEnabled: false
99+
suppressionDuration: PT1H
100+
version: 1.0.0
101+
kind: Scheduled
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
id: 6084dfd8-830b-4839-9a9c-5f08cc984729
2+
name: Red Sift - New Email with URL from Previously Unseen Source
3+
description: |
4+
'Detects email forensics events that contain one or more URLs where the sender is using a source IP address not seen in the previous 14 days, which may indicate suspicious infrastructure changes or phishing activity.'
5+
severity: Medium
6+
status: Available
7+
requiredDataConnectors:
8+
- connectorId: RedSiftPush
9+
dataTypes:
10+
- RedSiftEmailForensics_CL
11+
queryFrequency: 1h
12+
queryPeriod: 14d
13+
triggerOperator: gt
14+
triggerThreshold: 0
15+
tactics:
16+
- InitialAccess
17+
relevantTechniques:
18+
- T1566
19+
query: |
20+
let lookback = 14d;
21+
let recentWindow = 1h;
22+
let historicalSources = RedSiftEmailForensics_CL
23+
| extend
24+
EmailFrom = tostring(column_ifexists("EmailFrom", "")),
25+
SrcIp = tostring(column_ifexists("SrcIp", ""))
26+
| where TimeGenerated between (ago(lookback) .. ago(recentWindow))
27+
| where isnotempty(EmailFrom) and isnotempty(SrcIp)
28+
| summarize by EmailFrom, SrcIp;
29+
RedSiftEmailForensics_CL
30+
| extend
31+
EmailFrom = tostring(column_ifexists("EmailFrom", "")),
32+
EmailSubject = tostring(column_ifexists("EmailSubject", "")),
33+
EmailReturnPath = tostring(column_ifexists("EmailReturnPath", "")),
34+
EmailMessageUid = tostring(column_ifexists("EmailMessageUid", "")),
35+
SrcIp = tostring(column_ifexists("SrcIp", "")),
36+
DstHostname = tostring(column_ifexists("DstHostname", "")),
37+
Severity = tostring(column_ifexists("Severity", "")),
38+
Message = tostring(column_ifexists("Message", "")),
39+
CorrelationUid = tostring(column_ifexists("CorrelationUid", "")),
40+
EmailUrls = todynamic(column_ifexists("EmailUrls", dynamic([])))
41+
| where TimeGenerated >= ago(recentWindow)
42+
| where isnotempty(EmailFrom) and isnotempty(SrcIp)
43+
| extend UrlCount = array_length(EmailUrls)
44+
| where UrlCount > 0
45+
| mv-apply Url = EmailUrls on (
46+
summarize UrlSet = make_set(tostring(Url.url_string), 50)
47+
)
48+
| extend UrlList = strcat_array(UrlSet, ", ")
49+
| join kind=leftanti (historicalSources) on EmailFrom, SrcIp
50+
| project
51+
TimeGenerated,
52+
EmailFrom,
53+
EmailSubject,
54+
EmailReturnPath,
55+
EmailMessageUid,
56+
SrcIp,
57+
DstHostname,
58+
UrlCount,
59+
UrlList,
60+
Severity,
61+
Message,
62+
CorrelationUid
63+
entityMappings:
64+
- entityType: Account
65+
fieldMappings:
66+
- identifier: FullName
67+
columnName: EmailFrom
68+
- entityType: IP
69+
fieldMappings:
70+
- identifier: Address
71+
columnName: SrcIp
72+
- entityType: DNS
73+
fieldMappings:
74+
- identifier: DomainName
75+
columnName: DstHostname
76+
customDetails:
77+
EmailSubject: EmailSubject
78+
ReturnPath: EmailReturnPath
79+
UrlCount: UrlCount
80+
UrlList: UrlList
81+
CorrelationUid: CorrelationUid
82+
alertDetailsOverride:
83+
alertDisplayNameFormat: "RedSift - New URL-bearing email source for {{EmailFrom}}"
84+
alertDescriptionFormat: "Email from {{EmailFrom}} contains {{UrlCount}} URL(s) and originated from previously unseen source IP {{SrcIp}}."
85+
incidentConfiguration:
86+
createIncident: true
87+
groupingConfiguration:
88+
enabled: true
89+
reopenClosedIncident: false
90+
lookbackDuration: P1D
91+
matchingMethod: Selected
92+
groupByEntities:
93+
- Account
94+
- IP
95+
groupByAlertDetails: []
96+
groupByCustomDetails:
97+
- EmailSubject
98+
eventGroupingSettings:
99+
aggregationKind: AlertPerResult
100+
suppressionEnabled: false
101+
suppressionDuration: PT1H
102+
version: 1.0.0
103+
kind: Scheduled
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
id: 8972b513-12a2-4b46-8263-3f091d88a8bc
2+
name: Red Sift - Email with URL to Previously Unseen Domain
3+
description: |
4+
'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.'
5+
severity: Medium
6+
status: Available
7+
requiredDataConnectors:
8+
- connectorId: RedSiftPush
9+
dataTypes:
10+
- RedSiftEmailForensics_CL
11+
queryFrequency: 1h
12+
queryPeriod: 14d
13+
triggerOperator: gt
14+
triggerThreshold: 0
15+
tactics:
16+
- InitialAccess
17+
relevantTechniques:
18+
- T1566
19+
query: |
20+
let lookback = 14d;
21+
let recentWindow = 1h;
22+
let historicalDomains = RedSiftEmailForensics_CL
23+
| extend EmailUrls = todynamic(EmailUrls)
24+
| where TimeGenerated between (ago(lookback) .. ago(recentWindow))
25+
| where isnotempty(EmailUrls) and array_length(EmailUrls) > 0
26+
| mv-expand Url = EmailUrls
27+
| extend UrlString = tostring(Url.url_string)
28+
| where isnotempty(UrlString)
29+
| extend UrlDomain = tostring(parse_url(UrlString).Host)
30+
| where isnotempty(UrlDomain)
31+
| summarize by UrlDomain;
32+
RedSiftEmailForensics_CL
33+
| extend
34+
EmailFrom = tostring(column_ifexists("EmailFrom", "")),
35+
EmailSubject = tostring(column_ifexists("EmailSubject", "")),
36+
EmailReturnPath = tostring(column_ifexists("EmailReturnPath", "")),
37+
EmailMessageUid = tostring(column_ifexists("EmailMessageUid", "")),
38+
SrcIp = tostring(column_ifexists("SrcIp", "")),
39+
DstHostname = tostring(column_ifexists("DstHostname", "")),
40+
Severity = tostring(column_ifexists("Severity", "")),
41+
Message = tostring(column_ifexists("Message", "")),
42+
CorrelationUid = tostring(column_ifexists("CorrelationUid", "")),
43+
EmailUrls = todynamic(EmailUrls)
44+
| where TimeGenerated >= ago(recentWindow)
45+
| where isnotempty(EmailUrls) and array_length(EmailUrls) > 0
46+
| mv-expand Url = EmailUrls
47+
| extend UrlString = tostring(Url.url_string)
48+
| where isnotempty(UrlString)
49+
| extend UrlDomain = tostring(parse_url(UrlString).Host)
50+
| where isnotempty(UrlDomain)
51+
| join kind=leftanti (historicalDomains) on UrlDomain
52+
| summarize
53+
NewUrlDomains = make_set(UrlDomain, 50),
54+
NewUrls = make_set(UrlString, 50),
55+
NewDomainCount = dcount(UrlDomain),
56+
UrlCount = dcount(UrlString),
57+
RepresentativeUrlDomain = take_any(UrlDomain)
58+
by TimeGenerated,
59+
EmailFrom,
60+
EmailSubject,
61+
EmailReturnPath,
62+
EmailMessageUid,
63+
SrcIp,
64+
DstHostname,
65+
Severity,
66+
Message,
67+
CorrelationUid
68+
| extend
69+
NewUrlDomainList = strcat_array(NewUrlDomains, ", "),
70+
NewUrlList = strcat_array(NewUrls, ", ")
71+
| project
72+
TimeGenerated,
73+
EmailFrom,
74+
EmailSubject,
75+
EmailReturnPath,
76+
EmailMessageUid,
77+
SrcIp,
78+
DstHostname,
79+
RepresentativeUrlDomain,
80+
NewDomainCount,
81+
UrlCount,
82+
NewUrlDomainList,
83+
NewUrlList,
84+
Severity,
85+
Message,
86+
CorrelationUid
87+
entityMappings:
88+
- entityType: Account
89+
fieldMappings:
90+
- identifier: FullName
91+
columnName: EmailFrom
92+
- entityType: IP
93+
fieldMappings:
94+
- identifier: Address
95+
columnName: SrcIp
96+
- entityType: DNS
97+
fieldMappings:
98+
- identifier: DomainName
99+
columnName: RepresentativeUrlDomain
100+
customDetails:
101+
EmailSubject: EmailSubject
102+
ReturnPath: EmailReturnPath
103+
NewDomainCount: NewDomainCount
104+
NewUrlDomainList: NewUrlDomainList
105+
NewUrlList: NewUrlList
106+
CorrelationUid: CorrelationUid
107+
alertDetailsOverride:
108+
alertDisplayNameFormat: "RedSift - URL to new domain in email from {{EmailFrom}}"
109+
alertDescriptionFormat: "Email from {{EmailFrom}} contains {{NewDomainCount}} previously unseen URL domain(s): {{NewUrlDomainList}}."
110+
incidentConfiguration:
111+
createIncident: true
112+
groupingConfiguration:
113+
enabled: true
114+
reopenClosedIncident: false
115+
lookbackDuration: P1D
116+
matchingMethod: Selected
117+
groupByEntities:
118+
- Account
119+
- DNS
120+
groupByAlertDetails: []
121+
groupByCustomDetails:
122+
- EmailSubject
123+
eventGroupingSettings:
124+
aggregationKind: AlertPerResult
125+
suppressionEnabled: false
126+
suppressionDuration: PT1H
127+
version: 1.0.0
128+
kind: Scheduled

0 commit comments

Comments
 (0)