Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

TI map Email entity to SecurityEvent

Back
Id0a59051d-aed4-4fb6-bf84-bc80534482b2
RulenameTI map Email entity to SecurityEvent
DescriptionIdentifies a match in SecurityEvent table from any Email IOC from TI
SeverityMedium
TacticsInitialAccess
TechniquesT1566
Required data connectorsMicrosoftDefenderThreatIntelligence
SecurityEvents
ThreatIntelligence
ThreatIntelligenceTaxii
WindowsForwardedEvents
WindowsSecurityEvents
KindScheduled
Query frequency1h
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence (NEW)/Analytic Rules/EmailEntity_SecurityEvent.yaml
Version1.3.10
Arm template0a59051d-aed4-4fb6-bf84-bc80534482b2.json
Deploy To Azure
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let emailregex = @'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$';
ThreatIntelIndicators
//Filtering the table for Email related IOCs
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| where IndicatorType == "email-addr"
| extend EmailSenderAddress = ObservableValue
| extend EmailSourceDomain = substring(EmailSenderAddress, indexof(EmailSenderAddress, "@") + 1, strlen(EmailSenderAddress) - indexof(EmailSenderAddress, "@") - 1)
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend IndicatorId = tostring(split(Id, "--")[2])
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
  | project-reorder *, TrafficLightProtocolLevel, EmailSenderAddress, EmailSourceDomain, Type
// using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
| join kind=innerunique (
(union isfuzzy=true
(SecurityEvent
| where TimeGenerated >= ago(dt_lookBack) and isnotempty(TargetUserName)
//Normalizing the column to lower case for exact match with EmailSenderAddress column
| extend TargetUserName = tolower(TargetUserName)
// renaming timestamp column so it is clear the log this came from SecurityEvent table
| extend SecurityEvent_TimeGenerated = TimeGenerated
),
(WindowsEvent
| where TimeGenerated >= ago(dt_lookBack)
| extend TargetUserName = tostring(EventData.TargetUserName)
| where isnotempty(TargetUserName)
//Normalizing the column to lower case for exact match with EmailSenderAddress column
| extend TargetUserName = tolower(TargetUserName)
// renaming timestamp column so it is clear the log this came from SecurityEvent table
| extend SecurityEvent_TimeGenerated = TimeGenerated
))
)
on $left.EmailSenderAddress == $right.TargetUserName
| where SecurityEvent_TimeGenerated < ValidUntil
| summarize SecurityEvent_TimeGenerated = arg_max(SecurityEvent_TimeGenerated, *) by Id, TargetUserName
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project SecurityEvent_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence,
EmailSenderAddress, Computer, EventID, TargetUserName, Activity, IpAddress, AccountType,
LogonTypeName, LogonProcessName, Status, SubStatus, Url
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
| extend timestamp = SecurityEvent_TimeGenerated
triggerThreshold: 0
queryFrequency: 1h
id: 0a59051d-aed4-4fb6-bf84-bc80534482b2
kind: Scheduled
description: |
    'Identifies a match in SecurityEvent table from any Email IOC from TI'
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: Name
    columnName: TargetUserName
- entityType: Host
  fieldMappings:
  - identifier: HostName
    columnName: HostName
  - identifier: DnsDomain
    columnName: DnsDomain
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IpAddress
- entityType: URL
  fieldMappings:
  - identifier: Url
    columnName: Url
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Threat Intelligence (NEW)/Analytic Rules/EmailEntity_SecurityEvent.yaml
queryPeriod: 14d
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let emailregex = @'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$';
  ThreatIntelIndicators
  //Filtering the table for Email related IOCs
  | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
  | where IndicatorType == "email-addr"
  | extend EmailSenderAddress = ObservableValue
  | extend EmailSourceDomain = substring(EmailSenderAddress, indexof(EmailSenderAddress, "@") + 1, strlen(EmailSenderAddress) - indexof(EmailSenderAddress, "@") - 1)
  | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
  | extend IndicatorId = tostring(split(Id, "--")[2])
  | extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
  | where IsActive and (ValidUntil > now() or isempty(ValidUntil))
    | project-reorder *, TrafficLightProtocolLevel, EmailSenderAddress, EmailSourceDomain, Type
  // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  | join kind=innerunique (
  (union isfuzzy=true
  (SecurityEvent
  | where TimeGenerated >= ago(dt_lookBack) and isnotempty(TargetUserName)
  //Normalizing the column to lower case for exact match with EmailSenderAddress column
  | extend TargetUserName = tolower(TargetUserName)
  // renaming timestamp column so it is clear the log this came from SecurityEvent table
  | extend SecurityEvent_TimeGenerated = TimeGenerated
  ),
  (WindowsEvent
  | where TimeGenerated >= ago(dt_lookBack)
  | extend TargetUserName = tostring(EventData.TargetUserName)
  | where isnotempty(TargetUserName)
  //Normalizing the column to lower case for exact match with EmailSenderAddress column
  | extend TargetUserName = tolower(TargetUserName)
  // renaming timestamp column so it is clear the log this came from SecurityEvent table
  | extend SecurityEvent_TimeGenerated = TimeGenerated
  ))
  )
  on $left.EmailSenderAddress == $right.TargetUserName
  | where SecurityEvent_TimeGenerated < ValidUntil
  | summarize SecurityEvent_TimeGenerated = arg_max(SecurityEvent_TimeGenerated, *) by Id, TargetUserName
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | project SecurityEvent_TimeGenerated, Description, ActivityGroupNames, Id, ValidUntil, Confidence,
  EmailSenderAddress, Computer, EventID, TargetUserName, Activity, IpAddress, AccountType,
  LogonTypeName, LogonProcessName, Status, SubStatus, Url
  | extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
  | extend timestamp = SecurityEvent_TimeGenerated  
requiredDataConnectors:
- connectorId: ThreatIntelligence
  dataTypes:
  - ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
  dataTypes:
  - ThreatIntelIndicators
- connectorId: SecurityEvents
  dataTypes:
  - SecurityEvent
- connectorId: WindowsSecurityEvents
  dataTypes:
  - SecurityEvents
- connectorId: WindowsForwardedEvents
  dataTypes:
  - WindowsEvent
- connectorId: MicrosoftDefenderThreatIntelligence
  dataTypes:
  - ThreatIntelIndicators
severity: Medium
name: TI map Email entity to SecurityEvent
tactics:
- InitialAccess
version: 1.3.10
triggerOperator: gt
relevantTechniques:
- T1566