Detection rules › Kusto

Kusto rules: 04

App Gateway WAF - XSS Detection

#
Status
available
Severity
high
Time window
6h
Group by
Action, ClientIp, TransactionId, Uri
Source
github.com/Azure/Azure-Sentinel

'Identifies a match for an XSS attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/www-community/attacks/xss/'

MITRE ATT&CK Coverage

Rule Body

id: 1c7ff502-2ad4-4970-9d29-9210c6753138
name: App Gateway WAF - XSS Detection
description: | 
  'Identifies a match for an XSS attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements.
   References: https://owasp.org/www-community/attacks/xss/'
severity: High
status: Available
requiredDataConnectors: 
  - connectorId: WAF 
    dataTypes: 
      - AzureDiagnostics
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - InitialAccess
  - Execution
relevantTechniques:
  - T1189
  - T1203
  - T0853
tags:
  - Cross Site Scripting
query:  |  
 let Threshold = 3;  
 AGWFirewallLogs
 | where Action == "Matched"
 | where Message has "XSS"
 | project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message
 | join kind = inner(
 AGWFirewallLogs
 | where Action == "Blocked"
 | extend transactionId_g = tostring(TransactionId)) on TransactionId
 | extend Uri = strcat(Hostname,RequestUri)
 | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(transactionId_g,100), Message = make_set(Message,100), Total_TransactionId = dcount(transactionId_g) by ClientIp, Uri, Action
 | where Total_TransactionId >= Threshold
# The Threshold value above can be changed as per your infrastructure's requirement 
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Uri
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let Threshold = 3;

Stage 1: source

AGWFirewallLogs

Stage 2: where

| where Action == "Matched"

Stage 3: where

| where Message has "XSS"

Stage 4: project

| project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message

Stage 5: join

| join kind = inner(
AGWFirewallLogs
| where Action == "Blocked"
| extend transactionId_g = tostring(TransactionId)) on TransactionId

Stage 6: extend

extend Uri

Stage 7: summarize

summarize EndTime, Message, StartTime, Total_TransactionId, TransactionID by ClientIp, Uri, Action

Stage 8: where

where Total_TransactionId >= 3

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionsummarize
ClientIpsummarize
EndTimesummarize
Messagesummarize
StartTimesummarize
Total_TransactionIdsummarize
TransactionIDsummarize
Urisummarize

Brute Force Attack against GitHub Account

#
Status
available
Severity
medium
Time window
7d
Source
github.com/Azure/Azure-Sentinel

Attackers who are trying to guess your users' passwords or use brute-force methods to get in. If your organization is using SSO with Microsoft Entra ID, authentication logs to GitHub.com will be generated. Using the following query can help you identify a sudden increase in failed logon attempt of users.

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

Rule Body

id: 97ad74c4-fdd9-4a3f-b6bf-5e28f4f71e06
name: Brute Force Attack against GitHub Account
description: |
  'Attackers who are trying to guess your users' passwords or use brute-force methods to get in. If your organization is using SSO with Microsoft Entra ID, authentication logs to GitHub.com will be generated. Using the following query can help you identify a sudden increase in failed logon attempt of users.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let LearningPeriod = 7d;
  let BinTime = 1h;
  let RunTime = 1h;
  let StartTime = 1h;  
  let sensitivity = 2.5;
  let EndRunTime = StartTime - RunTime;
  let EndLearningTime = StartTime + LearningPeriod;
  let aadFunc = (tableName:string){
  table(tableName)  
  | where TimeGenerated between (ago(EndLearningTime) .. ago(EndRunTime))
  | where AppDisplayName =~ "GitHub.com"
  | where ResultType != 0
  | make-series FailedLogins = count() on TimeGenerated from ago(LearningPeriod) to ago(EndRunTime) step BinTime by UserPrincipalName, Type
  | extend (Anomalies, Score, Baseline) = series_decompose_anomalies(FailedLogins, sensitivity, -1, 'linefit')
  | mv-expand FailedLogins to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double), Score to typeof(double), Baseline to typeof(long)  
  | where TimeGenerated >= ago(RunTime)
  | where Anomalies > 0 and Baseline > 0
  | join kind=inner (
            table(tableName)  
            | where TimeGenerated between (ago(StartTime) .. ago(EndRunTime))
            | where AppDisplayName =~ "GitHub.com"
            | where ResultType != 0
            | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), IPAddresses = make_set(IPAddress,100), Locations = make_set(LocationDetails,20), Devices = make_set(DeviceDetail,20) by UserPrincipalName, UserId, AppDisplayName
        ) on UserPrincipalName
  | project-away UserPrincipalName1
  | extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])
  | extend IPAddressFirst = tostring(IPAddresses[0])
  };
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  union isfuzzy=true aadSignin, aadNonInt
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: UserId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddressFirst
version: 2.0.2
kind: Scheduled

Stages and Predicates

Parameters

let LearningPeriod = 7d;
let BinTime = 1h;
let RunTime = 1h;
let StartTime = 1h;
let sensitivity = 2.5;
let EndRunTime = StartTime - RunTime;
let EndLearningTime = StartTime + LearningPeriod;
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");

Let Binding: aadFunc

let aadFunc = (tableName:string){
table(tableName)  
| where TimeGenerated between (ago(EndLearningTime) .. ago(EndRunTime))
| where AppDisplayName =~ "GitHub.com"
| where ResultType != 0
| make-series FailedLogins = count() on TimeGenerated from ago(LearningPeriod) to ago(EndRunTime) step BinTime by UserPrincipalName, Type
| extend (Anomalies, Score, Baseline) = series_decompose_anomalies(FailedLogins, sensitivity, -1, 'linefit')
| mv-expand FailedLogins to typeof(double), TimeGenerated to typeof(datetime), Anomalies to typeof(double), Score to typeof(double), Baseline to typeof(long)  
| where TimeGenerated >= ago(RunTime)
| where Anomalies > 0 and Baseline > 0
| join kind=inner (
          table(tableName)  
          | where TimeGenerated between (ago(StartTime) .. ago(EndRunTime))
          | where AppDisplayName =~ "GitHub.com"
          | where ResultType != 0
          | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), IPAddresses = make_set(IPAddress,100), Locations = make_set(LocationDetails,20), Devices = make_set(DeviceDetail,20) by UserPrincipalName, UserId, AppDisplayName
      ) on UserPrincipalName
| project-away UserPrincipalName1
| extend Name = tostring(split(UserPrincipalName,'@',0)[0]), UPNSuffix = tostring(split(UserPrincipalName,'@',1)[0])
| extend IPAddressFirst = tostring(IPAddresses[0])
};

Union isfuzzy=true (2 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: aadSignin, aadNonInt

Leg 1: aadSignin

Leg 2: aadNonInt

Cisco WSA - Unexpected file type

#
Status
available
Severity
medium
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'Detects unexpected file type.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 8e9d1f70-d529-4598-9d3e-5dd5164d1d02
name: Cisco WSA - Unexpected file type
description: |
  'Detects unexpected file type.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1189
query: |
  CiscoWSAEvent
  | where isnotempty(AmpFileName)
  | where isempty(AmpThreatName)
  | where ResponseBodyMimeType =~ 'application/octet-stream'
  | where AmpFileName !endswith '.exe'
  | extend IPCustomEntity = SrcIpAddr, AccountCustomEntity = SrcUserName
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

CiscoWSAEvent

Stage 2: where

| where isnotempty(AmpFileName)

Stage 3: where

| where isempty(AmpThreatName)

Stage 4: where

| where ResponseBodyMimeType =~ 'application/octet-stream'

Stage 5: where

| where AmpFileName !endswith '.exe'

Stage 6: extend

| extend IPCustomEntity = SrcIpAddr, AccountCustomEntity = SrcUserName

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
AmpFileNameends_with.exeexcludes:AmpFileName field:"AmpFileName" value:".exe"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend
IPCustomEntityextend

CyberArkEPM - Renamed Windows binary

#
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'Detects renamed windows binaries.'

MITRE ATT&CK Coverage

TacticTechniques
Execution
Stealth

Rule Body

id: 9281b7cc-8f05-45a9-bf10-17fb29492a84
name: CyberArkEPM - Renamed Windows binary
description: |
  'Detects renamed windows binaries.'
severity: High
requiredDataConnectors:
  - connectorId: CyberArkEPM
    dataTypes:
      - CyberArkEPM
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1204
  - T1036
query: | 
  CyberArkEPM
  | where EventSubType != 'AttackAttempt'
  | where ActingProcessName has @'\'
  | where ActingProcessName !has ActingProcessFileInternalName
  | project EventEndTime, EventMessage, ActorUsername, ActingProcessFileInternalName
  | extend AccountCustomEntity = ActorUsername
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

CyberArkEPM

Stage 2: where

| where EventSubType != 'AttackAttempt'

Stage 3: where

| where ActingProcessName has @'\'

Stage 4: where

| where ActingProcessName !has ActingProcessFileInternalName

Stage 5: project

| project EventEndTime, EventMessage, ActorUsername, ActingProcessFileInternalName

Stage 6: extend

| extend AccountCustomEntity = ActorUsername

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActingProcessFileInternalNameproject
ActorUsernameproject
EventEndTimeproject
EventMessageproject
AccountCustomEntityextend

Dataverse - New user agent type that was not used before

#
Status
available
Severity
low
Time window
14d
Group by
InstanceUrl, UserAgent, UserId
Source
github.com/Azure/Azure-Sentinel

Identifies users accessing Dataverse from a User Agent that has not been seen in any Dataverse instance in the last 14 days.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 34a5d79b-8f9a-420c-aa64-7f4d262ac29a
kind: Scheduled
name: Dataverse - New user agent type that was not used before
description: Identifies users accessing Dataverse from a User Agent that has not been
  seen in any Dataverse instance in the last 14 days.
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: Dataverse
    dataTypes:
      - DataverseActivity
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - DefenseEvasion
relevantTechniques:
  - T1078
  - T0866
  - T0819
  - T1036
query: |
  let query_lookback = 14d;
  let query_frequency = 1h;
  let known_useragents = dynamic([
      // Enter known user agents to exclude.
      // example:
      // "Agent1", "Agent2", "Agent3"
      ]);
  DataverseActivity
  | where TimeGenerated between(ago(query_lookback) .. ago(query_frequency))
  | where isnotempty(UserAgent)
  | summarize by UserAgent
  | join kind = rightanti (DataverseActivity
      | where TimeGenerated > ago(query_frequency)
      | where not (UserId has_any ("@onmicrosoft.com", "@microsoft.com", "Unknown"))
      | where isnotempty(UserAgent)
      | where UserAgent !in~ (known_useragents)
      | where UserAgent !hasprefix "azure-logic-apps" and UserAgent !hasprefix "PowerApps")
      on UserAgent
  // Exclude user agents with a render agent to reduce noise.
  | join kind = leftanti(
      DataverseActivity
      | where TimeGenerated > ago(query_frequency)
      | where UserAgent has_any ("Gecko", "WebKit", "Presto", "Trident", "EdgeHTML", "Blink"))
      on UserAgent
  | summarize
      FirstSeen = min(TimeGenerated),
      LatestIP = arg_max(ClientIp, TimeGenerated)
      by UserAgent, UserId, InstanceUrl
  | extend
      AccountName = tostring(split(UserId, '@')[0]),
      UPNSuffix = tostring(split(UserId, '@')[1]),
      CloudAppId = int(32780)
  | project
      FirstSeen,
      UserId,
      UserAgent,
      LatestIP,
      InstanceUrl,
      CloudAppId,
      AccountName,
      UPNSuffix
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: CloudAppId
      - identifier: InstanceName
        columnName: InstanceUrl
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: LatestIP
alertDetailsOverride:
  alertDisplayNameFormat: 'Dataverse - new user agent detected in {{{InstanceUrl}} '
  alertDescriptionFormat: |
    {{UserId}} with new agent not seen previously in the Dataverse activity log.
    Agent: {{UserAgent}}
    Latest IP: {{LatestIP}}
version: 3.2.0

Stages and Predicates

Parameters

let query_lookback = 14d;
let query_frequency = 1h;

Let Binding: known_useragents used in Stage 5

let known_useragents = dynamic([
    ]);

Stage 1: source

DataverseActivity

Stage 2: where

where TimeGenerated between (ago(1209600s) .. ago(3600s))

Stage 3: where

where isnotempty(UserAgent)

Stage 4: summarize

summarize by UserAgent

Stage 5: join

| join kind = rightanti (DataverseActivity
    | where TimeGenerated > ago(query_frequency)
    | where not (UserId has_any ("@onmicrosoft.com", "@microsoft.com", "Unknown"))
    | where isnotempty(UserAgent)
    | where UserAgent !in~ (known_useragents)
    | where UserAgent !hasprefix "azure-logic-apps" and UserAgent !hasprefix "PowerApps")
    on UserAgent

Stage 6: join (negated)

join kind=leftanti (DataverseActivity) on UserAgent

Stage 7: summarize

summarize FirstSeen, LatestIP by UserAgent, UserId, InstanceUrl

Stage 8: extend

extend AccountName, CloudAppId, UPNSuffix

Stage 9: project

project AccountName, CloudAppId, FirstSeen, InstanceUrl, LatestIP, UPNSuffix, UserAgent, UserId

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
UserAgentis_not_null
  • (no value, null check)
field:"UserAgent" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
UserAgenteq[]excludes:UserAgent field:"UserAgent" value:"[]"
UserAgentstarts_withPowerAppsexcludes:UserAgent field:"UserAgent" value:"PowerApps"
UserAgentstarts_withazure-logic-appsexcludes:UserAgent field:"UserAgent" value:"azure-logic-apps"
UserIdmatch@onmicrosoft.com, @microsoft.com, Unknownexcludes:UserId field:"UserId" value:"@onmicrosoft.com" field:"UserId" value:"@microsoft.com" field:"UserId" value:"Unknown"
UserAgentmatchGecko, WebKit, Presto, Trident, EdgeHTML, Blinkexcludes:UserAgent

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
CloudAppIdproject
FirstSeenproject
InstanceUrlproject
LatestIPproject
UPNSuffixproject
UserAgentproject
UserIdproject

Failed AzureAD logons but success logon to host

#
Severity
medium
Time window
1d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

Identifies a list of IP addresses with a minimum number (default of 5) of failed logon attempts to Microsoft Entra ID. Uses that list to identify any successful remote logons to hosts from these IPs within the same timeframe.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Credential Access

Telemetry Coverage

Rule Body

id:  8ee967a2-a645-4832-85f4-72b635bcb3a6
name: Failed AzureAD logons but success logon to host
description: |
  'Identifies a list of IP addresses with a minimum number (default of 5) of failed logon attempts to Microsoft Entra ID.
  Uses that list to identify any successful remote logons to hosts from these IPs within the same timeframe.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
     - SigninLogs
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: Syslog
    dataTypes:
      - Syslog
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvents
  - connectorId: WindowsForwardedEvents
    dataTypes:
      - WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CredentialAccess
relevantTechniques:
  - T1078
  - T1110
query: |
  //Adjust this threshold to fit the environment
  let signin_threshold = 5;
  //Make a list of all IPs with failed signins to AAD above our threshold
  let aadFunc = (tableName:string){
  let suspicious_signins =
  table(tableName)
  | where ResultType !in ("0", "50125", "50140")
  | where IPAddress !in ('127.0.0.1', '::1', '')
  | summarize count() by IPAddress
  | where count_ > signin_threshold
  | summarize make_set(IPAddress);
  //See if any of these IPs have sucessfully logged into *nix hosts
  let linux_logons =
  Syslog
  | where Facility contains "auth" and ProcessName != "sudo"
  | where SyslogMessage has "Accepted"
  | extend SourceIP = extract("(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.(([0-9]{1,3})))",1,SyslogMessage)
  | where SourceIP in (suspicious_signins)
  | extend Reason = "Multiple failed AAD logins from IP address"
  | project TimeGenerated, Computer, HostIP, IpAddress = SourceIP, SyslogMessage, Facility, ProcessName, Reason;
  //See if any of these IPs have sucessfully logged into Windows hosts
  let win_logons = (union isfuzzy=true
  (SecurityEvent
  | where EventID == 4624
  | where LogonType in (10, 7, 3)
  | where IpAddress != "-"
  | where IpAddress in (suspicious_signins)
  | extend Reason = "Multiple failed AAD logins from IP address"
  | project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, LogonTypeName, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason
  ),
  (WindowsEvent
  | where EventID == 4624 and has_any_ipv4(EventData, toscalar(suspicious_signins))
  | extend LogonType = tostring(EventData.LogonType)
  | where LogonType in (10, 7, 3)
  | extend  IpAddress = tostring(EventData.IpAddress)
  | where IpAddress != "-"
  | where IpAddress in (suspicious_signins)
  | extend Reason = "Multiple failed AAD logins from IP address"
  | extend Activity = "4624 - An account was successfully logged on."
  | extend TargetUserName = tostring(EventData.TargetUserName), TargetDomainName = tostring(EventData.TargetDomainName)
  | extend Account =  strcat(TargetDomainName,"\\", TargetUserName)
  | extend TargetUserSid = tostring(EventData.TargetUserSid)
  | extend TargetAccount = strcat(EventData.TargetDomainName,"\\", EventData.TargetUserName)
  | extend AccountType =case(Account endswith "$" or TargetUserSid in ("S-1-5-18", "S-1-5-19", "S-1-5-20"), "Machine", isempty(TargetUserSid), "", "User")
  | extend LogonProcessName = tostring(EventData.LogonProcessName)
  | project TimeGenerated, Account, AccountType, Computer, Activity, EventID, LogonProcessName, IpAddress, TargetUserSid, TargetUserName, TargetDomainName, _ResourceId, Reason
  )
  );
  union isfuzzy=true linux_logons,win_logons
  | extend timestamp = TimeGenerated
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex+1), Computer)
  };
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  union isfuzzy=true aadSignin, aadNonInt
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: TargetUserName
      - identifier: NTDomain
        columnName: TargetDomainName
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
  - entityType: Host
    fieldMappings:
      - identifier: AzureID
        columnName: _ResourceId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IpAddress
version: 1.3.2
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Identity" ]

Stages and Predicates

Parameters

let signin_threshold = 5;
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");

Let Binding: aadFunc

let aadFunc = (tableName:string){
let suspicious_signins =
table(tableName)
| where ResultType !in ("0", "50125", "50140")
| where IPAddress !in ('127.0.0.1', '::1', '')
| summarize count() by IPAddress
| where count_ > signin_threshold
| summarize make_set(IPAddress);

Union isfuzzy=true (2 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: linux_logons, win_logons

Leg 1: linux_logons

Leg 2: win_logons

Applied to the Combined Result

| extend timestamp = TimeGenerated | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.')) | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex+1), Computer)
};
union isfuzzy=true aadSignin, aadNonInt

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountproject
AccountTypeproject
Activityproject
Computerproject
EventIDproject
IpAddressproject
LogonProcessNameproject
Reasonproject
TargetDomainNameproject
TargetUserNameproject
TargetUserSidproject
TimeGeneratedproject
_ResourceIdproject
timestampextend
DomainIndexextend
HostNameextend
HostNameDomainextend

Google Threat Intelligence - Threat Hunting Domain

#
Severity
medium
Time window
1d
Group by
Id, lowerDomain
Source
github.com/Azure/Azure-Sentinel

Google Threat Intelligence domain correlation.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Telemetry Coverage

ProviderRecord / event type
SysmonEvent ID 22: DNSEvent (DNS query)

Rule Body

id: "d9e1646c-dc17-4150-ac85-581f5c9cb41f"
name: Google Threat Intelligence - Threat Hunting Domain
description: |
  'Google Threat Intelligence domain correlation.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 30m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: 'Google Threat Intelligence Match'
  alertDescriptionFormat: 'Correlation found from the {{Type}} table.'
query: |
  let ioc_lookBack = 1d;
  _Im_Dns
  | where isnotempty(DnsQuery)
  | extend lowerDomain=tolower(DnsQuery)
  | join kind=inner (
  ThreatIntelIndicators
  | where ObservableKey == 'domain-name:value'
  | where isnotempty(ObservableValue)
  | where SourceSystem == "Google Threat Intelligence"
  | where TimeGenerated >= ago(ioc_lookBack)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
  | where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
  | extend lowerDomain=tolower(ObservableValue)
  ) on lowerDomain
  | project Domain=ObservableValue, Description=Data.description, Type, TimeGenerated
entityMappings:
  - entityType: DNS
    fieldMappings:
      - identifier: DomainName
        columnName: Domain
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let ioc_lookBack = 1d;

Stage 1: source

_Im_Dns

Stage 2: where

| where isnotempty(DnsQuery)

Stage 3: extend

| extend lowerDomain=tolower(DnsQuery)

Stage 4: join

| join kind=inner (
ThreatIntelIndicators
| where ObservableKey == 'domain-name:value'
| where isnotempty(ObservableValue)
| where SourceSystem == "Google Threat Intelligence"
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id
| where IsActive == true and (isnull(ValidUntil) or ValidUntil > now())
| extend lowerDomain=tolower(ObservableValue)
) on lowerDomain

Stage 5: project

project Description, Domain, TimeGenerated, Type

Stage 6: summarize aggregation inside the join branch

summarize by Id

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Idsummarize

Guardian- Language Detection Policy Violation Detection

#
Status
available
Severity
informational
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'This alert creates an incident when Language Detection Policy Violation detected from the Guardian.'

Rule Body

id: e65938c4-1379-4f7a-bd22-78f670a239c3
name: Guardian- Language Detection Policy Violation Detection
description: |
  'This alert creates an incident when Language Detection Policy Violation detected from the Guardian.'
severity: Informational
status: Available
requiredDataConnectors:
  - connectorId: BoschAIShield
    dataTypes:
      - Guardian
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
  Guardian
  | where PolicyViolatedControlFeature =~ 'Language Detection'
  | where Severity =~ 'Informational'
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertDisplayNameFormat: Guardian- Language Detection Policy Violation detection
  alertDescriptionFormat: |
    This query detects Language Detection Policy Violation detected from the Guardian generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
  alertTacticsColumnName: null
  alertSeverityColumnName: Severity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Guardian

Stage 2: where

| where PolicyViolatedControlFeature =~ 'Language Detection'

Stage 3: where

| where Severity =~ 'Informational'

Indicators

These rows show field, operator, and value matches.

Identify Mango Sandstorm powershell commands

#
Severity
high
Time window
1d
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

The query below identifies powershell commands used by the threat actor Mango Sandstorm. Reference: https://www.microsoft.com/security/blog/2022/08/25/mercury-leveraging-log4j-2-vulnerabilities-in-unpatched-systems-to-target-israeli-organizations/

MITRE ATT&CK Coverage

TacticTechniques
Lateral Movement

Telemetry Coverage

Rule Body

id: ce74dc9a-cb3c-4081-8c2f-7d39f6b7bae1
name: Identify Mango Sandstorm powershell commands
description: |
  'The query below identifies powershell commands used by the threat actor Mango Sandstorm.
  Reference:  https://www.microsoft.com/security/blog/2022/08/25/mercury-leveraging-log4j-2-vulnerabilities-in-unpatched-systems-to-target-israeli-organizations/'
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - LateralMovement
relevantTechniques:
  - T1570
tags:
  - Mango Sandstorm
  - Schema: ASIMFileEvent
    SchemaVersion: 0.1.0
query: |
  (union isfuzzy=true
  (SecurityEvent
  | where EventID == 4688
  | where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
  | where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
  | extend DvcHostname = Computer, ProcessId = tostring(ProcessId), ActorUsername = Account
  ),
  (DeviceProcessEvents
  | where FileName =~ "powershell.exe" and ProcessCommandLine has_cs "-exec bypass -w 1 -enc"
  | where ProcessCommandLine contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
  | extend DvcHostname = DeviceName, ProcessId = tostring(InitiatingProcessId), ActorUsername = strcat(AccountDomain, @"\", AccountName)
  ),
  (imProcessCreate
  | where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
  | where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
  | extend ProcessId = tostring(TargetProcessId)
  )
  )
  | extend AccountName = tostring(split(ActorUsername, "\\")[0]), AccountNTDomain = tostring(split(ActorUsername, "\\")[1])
  | extend HostName = tostring(split(DvcHostname, ".")[0]), DomainIndex = toint(indexof(DvcHostname, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(DvcHostname, DomainIndex + 1), DvcHostname)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorUsername
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DvcHostname
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: ProcessId
version: 1.0.5
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Intelligence" ]

Stages and Predicates

Union isfuzzy=true (3 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, DeviceProcessEvents, imProcessCreate

Leg 1: SecurityEvent

SecurityEvent
| where EventID == 4688
| where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
| where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
| extend DvcHostname = Computer, ProcessId = tostring(ProcessId), ActorUsername = Account

Leg 2: DeviceProcessEvents

DeviceProcessEvents
| where FileName =~ "powershell.exe" and ProcessCommandLine has_cs "-exec bypass -w 1 -enc"
| where ProcessCommandLine contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
| extend DvcHostname = DeviceName, ProcessId = tostring(InitiatingProcessId), ActorUsername = strcat(AccountDomain, @"\", AccountName)

Leg 3: imProcessCreate

imProcessCreate
| where Process has_any ("powershell.exe","powershell_ise.exe","pwsh.exe") and CommandLine has_cs "-exec bypass -w 1 -enc"
| where CommandLine  contains_cs "UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
| extend ProcessId = tostring(TargetProcessId)

Applied to the Combined Result

| extend AccountName = tostring(split(ActorUsername, "\\")[0]), AccountNTDomain = tostring(split(ActorUsername, "\\")[1])
| extend HostName = tostring(split(DvcHostname, ".")[0]), DomainIndex = toint(indexof(DvcHostname, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(DvcHostname, DomainIndex + 1), DvcHostname)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinecontains
  • UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA
field:"CommandLine" kind:contains value:"UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
CommandLinematch
  • -exec bypass -w 1 -enc transforms: term
field:"CommandLine" kind:match value:"-exec bypass -w 1 -enc"
EventIDeq
  • 4688 corpus 23 (kusto 23)
field:"EventID" kind:eq value:"4688"
FileNameeq
  • powershell.exe corpus 187 (elastic 140, splunk 44, kusto 3)
field:"process_basename" kind:eq value:"powershell.exe"
Processmatch
  • powershell.exe transforms: term corpus 2 (kusto 2)
  • powershell_ise.exe transforms: term
  • pwsh.exe transforms: term
field:"Process" kind:match
ProcessCommandLinecontains
  • UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA
field:"CommandLine" kind:contains value:"UwB0AGEAcgB0AC0ASgBvAGIAIAAtAFMAYwByAGkAcAB0AEIAbABvAGMAawAgAHsAKABzAGEAcABzACAAKAAiAHAA"
ProcessCommandLinematch
  • -exec bypass -w 1 -enc transforms: term
field:"CommandLine" kind:match value:"-exec bypass -w 1 -enc"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorUsernameextend
DvcHostnameextend
ProcessIdextend
AccountNTDomainextend
AccountNameextend
DomainIndexextend
HostNameextend
HostNameDomainextend

Log4j vulnerability exploit aka Log4Shell IP IOC

#
Status
available
Severity
high
Time window
1h
Group by
IPAddress, Type
Source
github.com/Azure/Azure-Sentinel

Identifies a match across various data feeds for IP IOCs related to the Log4j vulnerability exploit aka Log4Shell described in CVE-2021-44228. References: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2021-44228

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 6e575295-a7e6-464c-8192-3e1d8fd6a990
name: Log4j vulnerability exploit aka Log4Shell IP IOC
description: |
  'Identifies a match across various data feeds for IP IOCs related to the Log4j vulnerability exploit aka Log4Shell described in CVE-2021-44228.
   References: https://cve.mitre.org/cgi-bin/cvename.cgi?name=2021-44228'
severity: High
status: Available
tags:
  - Log4j
  - Log4Shell
  - CVE-2021-44228
  - Schema: ASIMDns
    SchemaVersion: 0.1.1
  - Schema: ASIMNetworkSession
    SchemaVersion: 0.2.0
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
     - OfficeActivity
  - connectorId: DNS
    dataTypes:
      - DnsEvents
  - connectorId: AzureMonitor(VMInsights)
    dataTypes:
      - VMConnection
  - connectorId: CiscoASA
    dataTypes:
      - CommonSecurityLog
  - connectorId: CiscoAsaAma
    dataTypes:
      - CommonSecurityLog
  - connectorId: PaloAltoNetworks
    dataTypes:
      - CommonSecurityLog
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
  - connectorId: AzureMonitor(WireData)
    dataTypes:
      - WireData
  - connectorId: AzureMonitor(IIS)
    dataTypes:
      - W3CIISLog
  - connectorId: AzureActivity
    dataTypes:
      - AzureActivity
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceNetworkEvents
  - connectorId: AzureFirewall
    dataTypes:
      - AzureDiagnostics
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query:  |
  let IPList = externaldata(IPAddress:string)[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/Log4j_IOC_List.csv"] with (format="csv", ignoreFirstRecord=True);
  let IPRegex = '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}';
  //Network logs
  let CSlogSourceIP = CommonSecurityLog | summarize by IPAddress = SourceIP, Type;
  let CSlogDestIP = CommonSecurityLog | summarize by IPAddress = DestinationIP, Type;
  let CSlogMsgIP = CommonSecurityLog | extend MessageIP = extract(IPRegex, 0, Message) | summarize by IPAddress = MessageIP, Type;
  let DnsIP = DnsEvents | summarize by IPAddress = IPAddresses, Type;
  // If you have enabled the _Im_Dns and/or imNetworkSession normalization in your workspace, you can uncomment one or both below.  Reference - https://docs.microsoft.com/azure/sentinel/normalization
  //let imDnsIP = _Im_Dns (response_has_any_prefix=IPList) | summarize by IPAddress = ResponseName, Type;
  //let imNetSessIP = imNetworkSession (dstipaddr_has_any_prefix=IPList) | summarize by IPAddress = DstIpAddr, Type;
  //Cloud service logs
  let officeIP = OfficeActivity | summarize by IPAddress = ClientIP, Type;
  let signinIP = SigninLogs | summarize by IPAddress, Type;
  let nonintSigninIP = AADNonInteractiveUserSignInLogs | summarize by IPAddress, Type;
  let azureActIP = AzureActivity | summarize by IPAddress = CallerIpAddress, Type;
  let awsCtIP = AWSCloudTrail | summarize by IPAddress = SourceIpAddress, Type;
  //Device logs
  let vmConnSourceIP = VMConnection | summarize by IPAddress = SourceIp, Type;
  let vmConnDestIP = VMConnection | summarize by IPAddress = DestinationIp, Type;
  let iisLogIP = W3CIISLog | summarize by IPAddress = cIP, Type;
  let devNetIP = DeviceNetworkEvents | summarize by IPAddress = RemoteIP, Type;
  //need to parse to get IP
  let azureDiagIP = AzureDiagnostics | where ResourceType == "AZUREFIREWALLS" | where Category in ("AzureFirewallApplicationRule", "AzureFirewallNetworkRule")
  | where msg_s has_any (IPList) | parse msg_s with Protocol 'request from ' SourceHost ':' SourcePort 'to ' DestinationHost ':' DestinationPort '. Action:' Action | summarize by IPAddress = DestinationHost, Type;
  let sysEvtIP = Event | where Source == "Microsoft-Windows-Sysmon" | where EventID == 3 | where EventData has_any (IPList) | extend EvData = parse_xml(EventData)
  | extend EventDetail = EvData.DataItem.EventData.Data
  | extend SourceIP = tostring(EventDetail.[9].["#text"]), DestinationIP = tostring(EventDetail.[14].["#text"])
  | where SourceIP in (IPList) or DestinationIP in (IPList) | extend IPAddress = iff(SourceIP in (IPList), SourceIP, DestinationIP) | summarize by IPAddress, Type;
  // If you have enabled the _Im_DNS and/or imNetworkSession normalization in your workdspace, you can uncomment below and include. Reference - https://docs.microsoft.com/azure/sentinel/normalization
  //let ipsort = union isfuzzy=true CSlogDestIP, CSlogMsgIP, CSlogSourceIP, DnsIP, officeIP, signinIP, nonintSigninIP, azureActIP, awsCtIP, vmConnDestIP, vmConnSourceIP, azureDiagIP, sysEvtIP, imDnsIP, imNetSessIP
  // If you uncomment above, then comment out the line below
  let ipsort = union isfuzzy=true CSlogDestIP, CSlogMsgIP, CSlogSourceIP, DnsIP, officeIP, signinIP, nonintSigninIP, azureActIP, awsCtIP, vmConnDestIP, vmConnSourceIP, azureDiagIP, sysEvtIP
  | summarize by IPAddress
  | where isnotempty(IPAddress) | where not(ipv4_is_private(IPAddress)) and IPAddress !in ('0.0.0.0','127.0.0.1');
  let ipMatch = ipsort | where IPAddress in (IPList);
  (union isfuzzy=true
  (CommonSecurityLog
  | where SourceIP in (ipMatch) or DestinationIP in (ipMatch) or Message has_any (ipMatch)
  | project TimeGenerated, SourceIP, DestinationIP, Message, SourceUserID, RequestURL, Type
  | extend MessageIP = extract(IPRegex, 0, Message)
  | extend IPMatch = case(SourceIP in (ipMatch), "SourceIP", DestinationIP in (ipMatch), "DestinationIP", MessageIP in (ipMatch), "Message", "No Match")
  | extend timestamp = TimeGenerated, IPEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, IPMatch == "Message", MessageIP, "No Match")
  ),
  (OfficeActivity
  | where  ClientIP in (ipMatch)
  | project TimeGenerated, UserAgent, Operation, RecordType, UserId, ClientIP, Type
  | extend SourceIPAddress = ClientIP, Account = UserId
  | extend timestamp = TimeGenerated , IPEntity = SourceIPAddress , AccountEntity = Account
  ),
  (DnsEvents
  | where  IPAddresses has_any (ipMatch)
  | project TimeGenerated, Computer, IPAddresses, Name, ClientIP, Type
  | extend DestinationIPAddress = IPAddresses,  Host = Computer
  | extend timestamp = TimeGenerated, IPEntity = DestinationIPAddress, HostEntity = Host
  ),
  (VMConnection
  | where SourceIp in (ipMatch) or DestinationIp in (ipMatch)
  | project TimeGenerated, Computer, SourceIp, DestinationIp, Type
  | extend IPMatch = case( SourceIp in (ipMatch), "SourceIP", DestinationIp in (ipMatch), "DestinationIP", "None")
  | extend timestamp = TimeGenerated , IPEntity = case(IPMatch == "SourceIP", SourceIp, IPMatch == "DestinationIP", DestinationIp, "None"), Host = Computer
  ),
  (Event
  | where Source =~ "Microsoft-Windows-Sysmon"
  | where EventID == 3
  | where EventData has_any (ipMatch)
  | project TimeGenerated, EventData, UserName, Computer, Type
  | extend EvData = parse_xml(EventData)
  | extend EventDetail = EvData.DataItem.EventData.Data
  | extend SourceIP = tostring(EventDetail.[9].["#text"]), DestinationIP = tostring(EventDetail.[14].["#text"])
  | where SourceIP in (ipMatch) or DestinationIP in (ipMatch)
  | extend IPMatch = case( SourceIP in (ipMatch), "SourceIP", DestinationIP in (ipMatch), "DestinationIP", "None")
  | extend timestamp = TimeGenerated, AccountEntity = UserName, HostEntity = Computer , IPEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, "None")
  ),
  (SigninLogs
  | where IPAddress in (ipMatch)
  | project TimeGenerated, UserPrincipalName, IPAddress, Type
  | extend timestamp = TimeGenerated, AccountEntity = UserPrincipalName, IPEntity = IPAddress
  ),
  (AADNonInteractiveUserSignInLogs
  | where IPAddress in (ipMatch)
  | project TimeGenerated, UserPrincipalName, IPAddress, Type
  | extend timestamp = TimeGenerated, AccountEntity = UserPrincipalName, IPEntity = IPAddress
  ),
  (W3CIISLog
  | where cIP in (ipMatch)
  | project TimeGenerated, Computer, cIP, csUserName, Type
  | extend timestamp = TimeGenerated, IPEntity = cIP, HostEntity = Computer, AccountEntity = csUserName
  ),
  (AzureActivity
  | where CallerIpAddress in (ipMatch)
  | project TimeGenerated, CallerIpAddress, Caller, Type
  | extend timestamp = TimeGenerated, IPEntity = CallerIpAddress, AccountEntity = Caller
  ),
  (
  AWSCloudTrail
  | where SourceIpAddress in (ipMatch)
  | project TimeGenerated, SourceIpAddress, UserIdentityUserName, Type
  | extend timestamp = TimeGenerated, IPEntity = SourceIpAddress, AccountEntity = UserIdentityUserName
  ),
  (
  DeviceNetworkEvents
  | where RemoteIP in (ipMatch)
  | where ActionType =~ "InboundConnectionAccepted"
  | project TimeGenerated, RemoteIP, DeviceName, Type
  | extend timestamp = TimeGenerated, IPEntity = RemoteIP, HostEntity = DeviceName
  ),
  (
  AzureDiagnostics
  | where ResourceType =~ "AZUREFIREWALLS"
  | where Category in ("AzureFirewallApplicationRule", "AzureFirewallNetworkRule")
  | where msg_s has_any (ipMatch)
  | project TimeGenerated, msg_s, Type
  | parse msg_s with Protocol 'request from ' SourceIP ':' SourcePort 'to ' DestinationIP ':' DestinationPort '. Action:' Action
  | where DestinationIP has_any (ipMatch)
  | extend timestamp = TimeGenerated, IPEntity = DestinationIP
  )
  // If you have enabled the _Im_Dns and/or imNetworkSession normalization in your workdspace, you can uncomment below and include. Reference - https://docs.microsoft.com/azure/sentinel/normalization
  //,
  //(_Im_Dns (response_has_any_prefix=IPList)
  //| project TimeGenerated, ResponseName, SrcIpAddr, Type
  //| extend DestinationIPAddress = ResponseName,  Host = SrcIpAddr
  //| extend timestamp = TimeGenerated, IPEntity = DestinationIPAddress, HostEntity = Host
  //),
  //(imNetworkSession (dstipaddr_has_any_prefix=IPList)
  //| project TimeGenerated, DstIpAddr, SrcIpAddr, Type
  //| extend timestamp = TimeGenerated, IPEntity = DstIpAddr, HostEntity = SrcIpAddr
  //)
  )
  | extend Name = tostring(split(AccountEntity, '@', 0)[0]), UPNSuffix = tostring(split(AccountEntity, '@', 1)[0])
  | extend HostName = tostring(split(HostEntity, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(HostEntity, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPEntity
version: 2.0.6
kind: Scheduled

Stages and Predicates

Parameters

let IPRegex = '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}';

Let Binding: IPList

let IPList = externaldata(IPAddress:string)[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/Log4j_IOC_List.csv"] with (format="csv", ignoreFirstRecord=True);

Let Binding: CSlogSourceIP

let CSlogSourceIP = CommonSecurityLog | summarize by IPAddress = SourceIP, Type;

Let Binding: CSlogDestIP

let CSlogDestIP = CommonSecurityLog | summarize by IPAddress = DestinationIP, Type;

Let Binding: CSlogMsgIP

let CSlogMsgIP = CommonSecurityLog | extend MessageIP = extract(IPRegex, 0, Message) | summarize by IPAddress = MessageIP, Type;

Let Binding: DnsIP

let DnsIP = DnsEvents | summarize by IPAddress = IPAddresses, Type;

Let Binding: officeIP

let officeIP = OfficeActivity | summarize by IPAddress = ClientIP, Type;

Let Binding: signinIP

let signinIP = SigninLogs | summarize by IPAddress, Type;

Let Binding: nonintSigninIP

let nonintSigninIP = AADNonInteractiveUserSignInLogs | summarize by IPAddress, Type;

Let Binding: azureActIP

let azureActIP = AzureActivity | summarize by IPAddress = CallerIpAddress, Type;

Let Binding: awsCtIP

let awsCtIP = AWSCloudTrail | summarize by IPAddress = SourceIpAddress, Type;

Let Binding: vmConnSourceIP

let vmConnSourceIP = VMConnection | summarize by IPAddress = SourceIp, Type;

Let Binding: vmConnDestIP

let vmConnDestIP = VMConnection | summarize by IPAddress = DestinationIp, Type;

Let Binding: iisLogIP

let iisLogIP = W3CIISLog | summarize by IPAddress = cIP, Type;

Let Binding: devNetIP

let devNetIP = DeviceNetworkEvents | summarize by IPAddress = RemoteIP, Type;

Let Binding: azureDiagIP

let azureDiagIP = AzureDiagnostics | where ResourceType == "AZUREFIREWALLS" | where Category in ("AzureFirewallApplicationRule", "AzureFirewallNetworkRule")
| where msg_s has_any (IPList) | parse msg_s with Protocol 'request from ' SourceHost ':' SourcePort 'to ' DestinationHost ':' DestinationPort '. Action:' Action | summarize by IPAddress = DestinationHost, Type;

Let Binding: sysEvtIP

let sysEvtIP = Event | where Source == "Microsoft-Windows-Sysmon" | where EventID == 3 | where EventData has_any (IPList) | extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend SourceIP = tostring(EventDetail.[9].["#text"]), DestinationIP = tostring(EventDetail.[14].["#text"])
| where SourceIP in (IPList) or DestinationIP in (IPList) | extend IPAddress = iff(SourceIP in (IPList), SourceIP, DestinationIP) | summarize by IPAddress, Type;

Let Binding: ipsort

let ipsort = union isfuzzy=true CSlogDestIP, CSlogMsgIP, CSlogSourceIP, DnsIP, officeIP, signinIP, nonintSigninIP, azureActIP, awsCtIP, vmConnDestIP, vmConnSourceIP, azureDiagIP, sysEvtIP
| summarize by IPAddress
| where isnotempty(IPAddress) | where not(ipv4_is_private(IPAddress)) and IPAddress !in ('0.0.0.0','127.0.0.1');

Let Binding: ipMatch

let ipMatch = ipsort | where IPAddress in (IPList);

Union isfuzzy=true (12 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: CommonSecurityLog, OfficeActivity, DnsEvents, VMConnection, Event, SigninLogs, AADNonInteractiveUserSignInLogs, W3CIISLog, AzureActivity, AWSCloudTrail, DeviceNetworkEvents, AzureDiagnostics

Leg 1: CommonSecurityLog

CommonSecurityLog
| where SourceIP in (ipMatch) or DestinationIP in (ipMatch) or Message has_any (ipMatch)
| project TimeGenerated, SourceIP, DestinationIP, Message, SourceUserID, RequestURL, Type
| extend MessageIP = extract(IPRegex, 0, Message)
| extend IPMatch = case(SourceIP in (ipMatch), "SourceIP", DestinationIP in (ipMatch), "DestinationIP", MessageIP in (ipMatch), "Message", "No Match")
| extend timestamp = TimeGenerated, IPEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, IPMatch == "Message", MessageIP, "No Match")

Leg 2: OfficeActivity

OfficeActivity
| where  ClientIP in (ipMatch)
| project TimeGenerated, UserAgent, Operation, RecordType, UserId, ClientIP, Type
| extend SourceIPAddress = ClientIP, Account = UserId
| extend timestamp = TimeGenerated , IPEntity = SourceIPAddress , AccountEntity = Account

Leg 3: DnsEvents

DnsEvents
| where  IPAddresses has_any (ipMatch)
| project TimeGenerated, Computer, IPAddresses, Name, ClientIP, Type
| extend DestinationIPAddress = IPAddresses,  Host = Computer
| extend timestamp = TimeGenerated, IPEntity = DestinationIPAddress, HostEntity = Host

Leg 4: VMConnection

VMConnection
| where SourceIp in (ipMatch) or DestinationIp in (ipMatch)
| project TimeGenerated, Computer, SourceIp, DestinationIp, Type
| extend IPMatch = case( SourceIp in (ipMatch), "SourceIP", DestinationIp in (ipMatch), "DestinationIP", "None")
| extend timestamp = TimeGenerated , IPEntity = case(IPMatch == "SourceIP", SourceIp, IPMatch == "DestinationIP", DestinationIp, "None"), Host = Computer

Leg 5: Event

Event
| where Source =~ "Microsoft-Windows-Sysmon"
| where EventID == 3
| where EventData has_any (ipMatch)
| project TimeGenerated, EventData, UserName, Computer, Type
| extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend SourceIP = tostring(EventDetail.[9].["#text"]), DestinationIP = tostring(EventDetail.[14].["#text"])
| where SourceIP in (ipMatch) or DestinationIP in (ipMatch)
| extend IPMatch = case( SourceIP in (ipMatch), "SourceIP", DestinationIP in (ipMatch), "DestinationIP", "None")
| extend timestamp = TimeGenerated, AccountEntity = UserName, HostEntity = Computer , IPEntity = case(IPMatch == "SourceIP", SourceIP, IPMatch == "DestinationIP", DestinationIP, "None")

Leg 6: SigninLogs

SigninLogs
| where IPAddress in (ipMatch)
| project TimeGenerated, UserPrincipalName, IPAddress, Type
| extend timestamp = TimeGenerated, AccountEntity = UserPrincipalName, IPEntity = IPAddress

Leg 7: AADNonInteractiveUserSignInLogs

AADNonInteractiveUserSignInLogs
| where IPAddress in (ipMatch)
| project TimeGenerated, UserPrincipalName, IPAddress, Type
| extend timestamp = TimeGenerated, AccountEntity = UserPrincipalName, IPEntity = IPAddress

Leg 8: W3CIISLog

W3CIISLog
| where cIP in (ipMatch)
| project TimeGenerated, Computer, cIP, csUserName, Type
| extend timestamp = TimeGenerated, IPEntity = cIP, HostEntity = Computer, AccountEntity = csUserName

Leg 9: AzureActivity

AzureActivity
| where CallerIpAddress in (ipMatch)
| project TimeGenerated, CallerIpAddress, Caller, Type
| extend timestamp = TimeGenerated, IPEntity = CallerIpAddress, AccountEntity = Caller

Leg 10: AWSCloudTrail

AWSCloudTrail
| where SourceIpAddress in (ipMatch)
| project TimeGenerated, SourceIpAddress, UserIdentityUserName, Type
| extend timestamp = TimeGenerated, IPEntity = SourceIpAddress, AccountEntity = UserIdentityUserName

Leg 11: DeviceNetworkEvents

DeviceNetworkEvents
| where RemoteIP in (ipMatch)
| where ActionType =~ "InboundConnectionAccepted"
| project TimeGenerated, RemoteIP, DeviceName, Type
| extend timestamp = TimeGenerated, IPEntity = RemoteIP, HostEntity = DeviceName

Leg 12: AzureDiagnostics

AzureDiagnostics
| where ResourceType =~ "AZUREFIREWALLS"
| where Category in ("AzureFirewallApplicationRule", "AzureFirewallNetworkRule")
| where msg_s has_any (ipMatch)
| project TimeGenerated, msg_s, Type
| parse msg_s with Protocol 'request from ' SourceIP ':' SourcePort 'to ' DestinationIP ':' DestinationPort '. Action:' Action
| where DestinationIP has_any (ipMatch)
| extend timestamp = TimeGenerated, IPEntity = DestinationIP

Applied to the Combined Result

| extend Name = tostring(split(AccountEntity, '@', 0)[0]), UPNSuffix = tostring(split(AccountEntity, '@', 1)[0])
| extend HostName = tostring(split(HostEntity, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(HostEntity, '.'), 1, -1), '.'))

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TimeGeneratedproject
Typeproject
msg_sproject
IPEntityextend
timestampextend
Nameextend
UPNSuffixextend
DnsDomainextend
HostNameextend

Microsoft Entra ID Hybrid Health AD FS New Server

#
Status
available
Severity
medium
Time window
1d
Source
github.com/Azure/Azure-Sentinel

This detection uses AzureActivity logs (Administrative category) to identify the creation or update of a server instance in an Microsoft Entra ID Hybrid Health AD FS service. A threat actor can create a new AD Health ADFS service and create a fake server instance to spoof AD FS signing logs. There is no need to compromise an on-premises AD FS server. This can be done programmatically via HTTP requests to Azure. More information in this blog: https://o365blog.com/post/hybridhealthagent/

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 88f453ff-7b9e-45bb-8c12-4058ca5e44ee
name: Microsoft Entra ID Hybrid Health AD FS New Server
description: |
  'This detection uses AzureActivity logs (Administrative category) to identify the creation or update of a server instance in an Microsoft Entra ID Hybrid Health AD FS service.
  A threat actor can create a new AD Health ADFS service and create a fake server instance to spoof AD FS signing logs. There is no need to compromise an on-premises AD FS server.
  This can be done programmatically via HTTP requests to Azure. More information in this blog: https://o365blog.com/post/hybridhealthagent/'
severity: Medium
status: Available 
requiredDataConnectors:
  - connectorId: AzureActivity
    dataTypes:
      - AzureActivity
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1578
tags:
  - SimuLand
query: |
  AzureActivity
  | where CategoryValue =~ 'Administrative'
  | where ResourceProviderValue =~ 'Microsoft.ADHybridHealthService'
  | where _ResourceId has 'AdFederationService'
  | where OperationNameValue =~ 'Microsoft.ADHybridHealthService/services/servicemembers/action'
  | extend claimsJson = parse_json(Claims)
  | extend AppId = tostring(claimsJson.appid), AccountName = tostring(claimsJson.name), Name = tostring(split(Caller,'@',0)[0]), UPNSuffix = tostring(split(Caller,'@',1)[0])
  | project-away claimsJson
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Caller
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIpAddress
version: 2.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

AzureActivity

Stage 2: where

| where CategoryValue =~ 'Administrative'

Stage 3: where

| where ResourceProviderValue =~ 'Microsoft.ADHybridHealthService'

Stage 4: where

| where _ResourceId has 'AdFederationService'

Stage 5: where

| where OperationNameValue =~ 'Microsoft.ADHybridHealthService/services/servicemembers/action'

Stage 6: extend

| extend claimsJson = parse_json(Claims)

Stage 7: extend

| extend AppId = tostring(claimsJson.appid), AccountName = tostring(claimsJson.name), Name = tostring(split(Caller,'@',0)[0]), UPNSuffix = tostring(split(Caller,'@',1)[0])

Stage 8: project-away

| project-away claimsJson

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CategoryValueeq
  • Administrative corpus 6 (kusto 4, sigma 2)
field:"CategoryValue" kind:eq value:"Administrative"
OperationNameValueeq
  • Microsoft.ADHybridHealthService/services/servicemembers/action corpus 3 (kusto 2, sigma 1)
field:"azure_ad::operation_name_value" kind:eq value:"Microsoft.ADHybridHealthService/services/servicemembers/action"
ResourceProviderValueeq
  • Microsoft.ADHybridHealthService corpus 6 (kusto 4, sigma 2)
field:"ResourceProviderValue" kind:eq value:"Microsoft.ADHybridHealthService"
_ResourceIdmatch
  • AdFederationService transforms: term corpus 4 (kusto 4)
field:"_ResourceId" kind:match value:"AdFederationService"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameextend
AppIdextend
Nameextend
UPNSuffixextend

Netskope - Anomalous User Behavior (High Volume from Unmanaged Device)

#
Status
available
Severity
medium
Time window
1h
Group by
CsUsername, XCDevice, XCsAccessMethod
Source
github.com/Azure/Azure-Sentinel

Detects anomalous user behavior including high data volume transfers from unmanaged devices, unusual access patterns, and suspicious application usage.

MITRE ATT&CK Coverage

TacticTechniques
Collection
Exfiltration

Rule Body

id: fa4c4f1c-3c5f-4c3a-a13f-924c30db56e9
name: Netskope - Anomalous User Behavior (High Volume from Unmanaged Device)
description: |
  Detects anomalous user behavior including high data volume transfers from unmanaged devices, unusual access patterns, and suspicious application usage.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: NetskopeWebTxConnector
    dataTypes:
      - NetskopeWebTransactions_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
  - Collection
relevantTechniques:
  - T1567
  - T1074
query: |
  let highVolumeThresholdGB = 1;
  NetskopeWebTransactions_CL
  | where TimeGenerated > ago(1h)
  | where isnotempty(CsUsername)
  | summarize 
      TotalBytes = sum(Bytes),
      UploadBytes = sum(CsBytes),
      DownloadBytes = sum(ScBytes),
      UniqueApps = dcount(XCsApp),
      Apps = make_set(XCsApp, 20),
      UniqueHosts = dcount(CsHost),
      Activities = make_set(XCsAppActivity),
      Countries = make_set(XCCountry),
      Devices = make_set(XCDevice),
      AccessMethods = make_set(XCsAccessMethod),
      EventCount = count()
      by CsUsername, XCDevice, XCsAccessMethod
  | extend 
      TotalGB = round(TotalBytes / 1073741824.0, 3),
      UploadGB = round(UploadBytes / 1073741824.0, 3),
      DownloadGB = round(DownloadBytes / 1073741824.0, 3)
  | where TotalGB > highVolumeThresholdGB
  | extend IsUnmanagedDevice = XCDevice =~ 'unmanaged' or XCDevice =~ 'BYOD' or XCDevice =~ 'Personal' or XCDevice =~ 'Unknown' or XCsAccessMethod != 'Client'
  | where IsUnmanagedDevice or TotalGB > 5 or UniqueApps > 20
  | extend RiskIndicators = strcat_array(array_concat(
      iff(IsUnmanagedDevice, dynamic(['Unmanaged Device']), dynamic([])),
      iff(TotalGB > 5, dynamic(['High Data Volume']), dynamic([])),
      iff(UniqueApps > 20, dynamic(['Many Apps Accessed']), dynamic([])),
      iff(array_length(Countries) > 1, dynamic(['Multiple Countries']), dynamic([]))
  ), ', ')
  | project 
      TimeGenerated = now(),
      User = CsUsername,
      Device = XCDevice,
      AccessMethod = XCsAccessMethod,
      TotalDataGB = TotalGB,
      UploadGB,
      DownloadGB,
      UniqueApplications = UniqueApps,
      Applications = Apps,
      UniqueHosts,
      Activities,
      Countries,
      EventCount,
      IsUnmanagedDevice,
      RiskIndicators
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: User
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Device
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let highVolumeThresholdGB = 1;

Stage 1: source

NetskopeWebTransactions_CL

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: where

| where isnotempty(CsUsername)

Stage 4: summarize

| summarize 
    TotalBytes = sum(Bytes),
    UploadBytes = sum(CsBytes),
    DownloadBytes = sum(ScBytes),
    UniqueApps = dcount(XCsApp),
    Apps = make_set(XCsApp, 20),
    UniqueHosts = dcount(CsHost),
    Activities = make_set(XCsAppActivity),
    Countries = make_set(XCCountry),
    Devices = make_set(XCDevice),
    AccessMethods = make_set(XCsAccessMethod),
    EventCount = count()
    by CsUsername, XCDevice, XCsAccessMethod

Stage 5: extend

| extend 
    TotalGB = round(TotalBytes / 1073741824.0, 3),
    UploadGB = round(UploadBytes / 1073741824.0, 3),
    DownloadGB = round(DownloadBytes / 1073741824.0, 3)

Stage 6: where

| where TotalGB > highVolumeThresholdGB

Stage 7: extend

| extend IsUnmanagedDevice = XCDevice =~ 'unmanaged' or XCDevice =~ 'BYOD' or XCDevice =~ 'Personal' or XCDevice =~ 'Unknown' or XCsAccessMethod != 'Client'

Stage 8: where

| where IsUnmanagedDevice or TotalGB > 5 or UniqueApps > 20

Stage 9: extend

| extend RiskIndicators = strcat_array(array_concat(
    iff(IsUnmanagedDevice, dynamic(['Unmanaged Device']), dynamic([])),
    iff(TotalGB > 5, dynamic(['High Data Volume']), dynamic([])),
    iff(UniqueApps > 20, dynamic(['Many Apps Accessed']), dynamic([])),
    iff(array_length(Countries) > 1, dynamic(['Multiple Countries']), dynamic([]))
), ', ')

Stage 10: project

| project 
    TimeGenerated = now(),
    User = CsUsername,
    Device = XCDevice,
    AccessMethod = XCsAccessMethod,
    TotalDataGB = TotalGB,
    UploadGB,
    DownloadGB,
    UniqueApplications = UniqueApps,
    Applications = Apps,
    UniqueHosts,
    Activities,
    Countries,
    EventCount,
    IsUnmanagedDevice,
    RiskIndicators

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CsUsernameis_not_null
  • (no value, null check)
field:"CsUsername" kind:is_not_null
IsUnmanagedDeviceeq
  • true transforms: boolean
field:"IsUnmanagedDevice" kind:eq value:"true"
TotalGBgt
  • 1 transforms: number
  • 5 transforms: number
field:"TotalGB" kind:gt
UniqueAppsgt
  • 20 transforms: number
field:"UniqueApps" kind:gt value:"20"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccessMethodproject
Activitiesproject
Applicationsproject
Countriesproject
Deviceproject
DownloadGBproject
EventCountproject
IsUnmanagedDeviceproject
RiskIndicatorsproject
TimeGeneratedproject
TotalDataGBproject
UniqueApplicationsproject
UniqueHostsproject
UploadGBproject
Userproject

NordPass - Vault export

#
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

This will alert you if the vault has been exported, allowing you to review and evaluate the incident to mitigate potential risks. NOTE: The organization can control whether it allows its members to export the vault, although we recommend that it always be disabled.

MITRE ATT&CK Coverage

TacticTechniques
Exfiltration

Rule Body

id: c7f14b43-7625-4516-b137-30b7fda65bcf
name: NordPass - Vault export
kind: Scheduled
version: 1.0.0
displayName: Vault export
description: |
  This will alert you if the vault has been exported, allowing you to review and evaluate the incident to mitigate potential risks.
  NOTE: The organization can control whether it allows its members to export the vault, although we recommend that it always be disabled.
severity: High
query: |
  NordPassEventLogs_CL
  | where event_type == "item_access"
  | where action == "vault_exported"
  | extend TargetEmail = user_email
requiredDataConnectors:
  - connectorId: NordPass
    dataTypes:
      - NordPassEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
relevantTechniques:
  - T1020
incidentConfiguration:
  createIncident: false
entityMappings:
- entityType: Mailbox
  fieldMappings:
  - identifier: MailboxPrimaryAddress
    columnName: TargetEmail

Stages and Predicates

Stage 1: source

NordPassEventLogs_CL

Stage 2: where

| where event_type == "item_access"

Stage 3: where

| where action == "vault_exported"

Stage 4: extend

| extend TargetEmail = user_email

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TargetEmailextend

Samsung Knox - Application Privilege Escalation or Change Events

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Source
github.com/Azure/Azure-Sentinel

When a Knox mobile app has transitioned from an acceptable uid/esuid/fsuid to a different, non-App id.

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation

Rule Body

id: 215e89ca-cdbc-4661-b8b2-7041f6ecc7fb
name: Samsung Knox - Application Privilege Escalation or Change Events
version: 1.0.2
kind: NRT
description: When a Knox mobile app has transitioned from an acceptable uid/esuid/fsuid to a different, non-App id.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SamsungDCDefinition
    dataTypes:
      - Samsung_Knox_Process_CL
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1548
query: |
  Samsung_Knox_Process_CL 
  | where Name == "PROCESS_PRIVILEGE_ESCALATION"
  | where MitreTtp has "T1548"
suppressionEnabled: false
suppressionDuration: PT5H
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: SingleAlert

Stages and Predicates

Stage 1: source

Samsung_Knox_Process_CL

Stage 2: where

| where Name == "PROCESS_PRIVILEGE_ESCALATION"

Stage 3: where

| where MitreTtp has "T1548"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
MitreTtpmatch
  • T1548 transforms: term
field:"MitreTtp" kind:match value:"T1548"
Nameeq
  • PROCESS_PRIVILEGE_ESCALATION
field:"Name" kind:eq value:"PROCESS_PRIVILEGE_ESCALATION"

Storage Deleted

#
Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

Detects when storage is deleted from Veeam Backup & Replication. This might indicate unauthorized removal of critical components.

Rule Body

id: 7cf8c19b-6cd2-4d0c-814b-45d34e68568b
name: Storage Deleted
description: Detects when storage is deleted from Veeam Backup & Replication. This
  might indicate unauthorized removal of critical components.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 41402\n| extend Name = extract(\"\
  Name=\\\"([^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n    Date = format_datetime(TimeGenerated,\
  \ 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n    EventId = instanceId,\n\
  \    [\"User Name\"] = user,\n    [\"Object Name\"] = Name,\n    MessageDetails\
  \ = Description,\n   Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 41402

Stage 3: extend

| extend Name = extract("Name=\"([^\"]*)\"", 1, SyslogMessage)

Stage 4: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    ["User Name"] = user,
    ["Object Name"] = Name,
    MessageDetails = Description,
   Severity = SeverityDescription

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Object Nameproject
Severityproject
User Nameproject

TI Map IP entity to Network Session Events (ASIM Network Session schema)

#
Status
available
Severity
medium
Time window
14d
Group by
$left.TI_ipEntity, $right.IoCIP, Id, ObservableValue
Source
github.com/Azure/Azure-Sentinel

This rule identifies a match Network Sessions for which the source or destination IP address is a known IoC. This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM NetworkSession schema

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Telemetry Coverage

Rule Body

id: 54f4ceb4-fd83-4633-b5b0-c0de9feb8890
name: TI Map IP entity to Network Session Events (ASIM Network Session schema)
description: |
  'This rule identifies a match Network Sessions for which the source or destination IP address is a known IoC. This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM NetworkSession schema'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AWSS3
    dataTypes:
      - AWSVPCFlow
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceNetworkEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsForwardedEvents
    dataTypes:
      - WindowsEvent
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - connectorId: MicrosoftSysmonForLinux
    dataTypes:
      - Syslog
  - connectorId: PaloAltoNetworks
    dataTypes:
      - CommonSecurityLog
  - connectorId: AzureMonitor(VMInsights)
    dataTypes:
      - VMConnection
  - connectorId: AzureFirewall
    dataTypes:
      - AzureDiagnostics
  - connectorId: AzureNSG
    dataTypes:
      - AzureDiagnostics
  - connectorId: CiscoASA
    dataTypes:
      - CommonSecurityLog
  - connectorId: Corelight
    dataTypes:
      - Corelight_CL
  - connectorId: AIVectraStream
    dataTypes:
      - VectraStream
  - connectorId: CheckPoint
    dataTypes:
      - CommonSecurityLog
  - connectorId: Fortinet
    dataTypes:
      - CommonSecurityLog
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: CiscoMeraki
    dataTypes:
      - Syslog
      - CiscoMerakiNativePoller
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators

queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let IP_TI = materialize (
    ThreatIntelIndicators
    //extract key part of kv pair
       | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
       | where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
       | extend NetworkSourceIP = toupper(ObservableValue)
       | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
    | where TimeGenerated >= ago(ioc_lookBack)
    | extend TI_ipEntity = NetworkSourceIP
    | where TI_ipEntity != "NO_IP"
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
    | where IsActive and (ValidUntil > now() or isempty(ValidUntil))
  );
  IP_TI
     | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
    // 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 
   (
     _Im_NetworkSession (starttime=ago(dt_lookBack))
     | where isnotempty(SrcIpAddr)
     | summarize imNWS_mintime=min(TimeGenerated), imNWS_maxtime=max(TimeGenerated) by SrcIpAddr, DstIpAddr, Dvc, EventProduct, EventVendor 
     | lookup (IP_TI | project TI_ipEntity, IsActive) on $left.SrcIpAddr == $right.TI_ipEntity
     | project-rename SrcMatch = IsActive
     | lookup (IP_TI | project TI_ipEntity, IsActive) on $left.DstIpAddr == $right.TI_ipEntity
     | project-rename DstMatch = IsActive
     | where SrcMatch or DstMatch
     | extend 
         IoCIP = iff(SrcMatch, SrcIpAddr, DstIpAddr),
         IoCDirection = iff(SrcMatch, "Source", "Destination")
   )on $left.TI_ipEntity == $right.IoCIP
   | where imNWS_mintime < ValidUntil
   | extend Description = tostring(parse_json(Data).description)
   | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
   | project imNWS_mintime, imNWS_maxtime, Description, ActivityGroupNames, Id, Type, ValidUntil, Confidence, SrcIpAddr, DstIpAddr, IoCDirection, IoCIP, Dvc, EventVendor, EventProduct
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IoCIP
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
customDetails:
  EventStartTime: imNWS_mintime
  EventEndTime: imNWS_maxtime
  IoCDescription: Description
  ActivityGroupNames: ActivityGroupNames
  IndicatorId: Id
  ThreatType: Type
  IoCExpirationTime: ValidUntil
  IoCConfidenceScore: Confidence
  IoCIPDirection: IoCDirection

alertDetailsOverride:
  alertDisplayNameFormat: A network session {{IoCDirection}} address {{IoCIP}} matched an IoC. 
  alertDescriptionFormat: The {{IoCDirection}} address {{IoCIP}} of a network session  matched a known indicator of compromise of {{Type}}. Consult the threat intelligence blead for more information on the indicator.

tags:
  - Schema: ASIMNetworkSession
    SchemaVersion: 0.2.4

version: 1.2.10
kind: Scheduled

Stages and Predicates

Parameters

let dt_lookBack = 1h;
let ioc_lookBack = 14d;

let IP_TI is inlined into the numbered stages below.

Stages 1 to 10 define let IP_TI as the rule's main pipeline source. Stages 11 to 16 use it.

Stage 1: source

ThreatIntelIndicators

Stage 2: extend

| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))

Stage 3: where

| where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")

Stage 4: extend

| extend NetworkSourceIP = toupper(ObservableValue)

Stage 5: extend

| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)

Stage 6: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 7: extend

| extend TI_ipEntity = NetworkSourceIP

Stage 8: where

| where TI_ipEntity != "NO_IP"

Stage 9: summarize

| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue

Stage 10: where

| where IsActive and (ValidUntil > now() or isempty(ValidUntil))

Stage 11: project-reorder

IP_TI
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity

Stage 12: join

join kind=innerunique (_Im_NetworkSession) on $left.TI_ipEntity, $right.IoCIP

Stage 13: where

where imNWS_mintime < ValidUntil

Stage 14: extend

extend Description

Stage 15: extend

extend ActivityGroupNames

Stage 16: project

project ActivityGroupNames, Confidence, Description, DstIpAddr, Dvc, EventProduct, EventVendor, Id, IoCDirection, IoCIP, SrcIpAddr, Type, ValidUntil, imNWS_maxtime, imNWS_mintime

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
Confidenceproject
Descriptionproject
DstIpAddrproject
Dvcproject
EventProductproject
EventVendorproject
Idproject
IoCDirectionproject
IoCIPproject
SrcIpAddrproject
Typeproject
ValidUntilproject
imNWS_maxtimeproject
imNWS_mintimeproject

User joining Zoom meeting from suspicious timezone

#
Severity
low
Time window
14d
Group by
MeetingId
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

'The alert shows users that join a Zoom meeting from a time zone other than the one the meeting was created in. You can also whitelist known good time zones in the tz_whitelist value using the tz database name format https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Privilege Escalation

Rule Body

id: 58fc0170-0877-4ea8-a9ff-d805e361cfae
name: User joining Zoom meeting from suspicious timezone
description: |
  'The alert shows users that join a Zoom meeting from a time zone other than the one the meeting was created in.
  You can also whitelist known good time zones in the tz_whitelist value using the tz database name format https://en.wikipedia.org/wiki/List_of_tz_database_time_zones'
severity: Low
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - PrivilegeEscalation
relevantTechniques:
  - T1078
query: |
  let schedule_lookback = 14d;
  let join_lookback = 1d;
  // If you want to whitelist specific timezones include them in a list here
  let tz_whitelist = dynamic([]);
  let meetings = (
  ZoomLogs
  | where TimeGenerated >= ago(schedule_lookback)
  | where Event =~ "meeting.created"
  | extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)
  | extend SchedTimezone = tostring(parse_json(MeetingEvents).Timezone));
  ZoomLogs
  | where TimeGenerated >= ago(join_lookback)
  | where Event =~ "meeting.participant_joined"
  | extend JoinedTimeZone = tostring(parse_json(MeetingEvents).Timezone)
  | extend MeetingName = tostring(parse_json(MeetingEvents).MeetingName)
  | extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)
  | where JoinedTimeZone !in (tz_whitelist)
  | join (meetings) on MeetingId
  | where SchedTimezone != JoinedTimeZone
  | project TimeGenerated, MeetingName, JoiningUser=payload_object_participant_user_name_s, JoinedTimeZone, SchedTimezone, MeetingScheduler=User1
  | extend AccountName = tostring(split(JoiningUser, "@")[0]), AccountUPNSuffix = tostring(split(JoiningUser, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: JoiningUser
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
version: 1.0.4
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Parameters

let schedule_lookback = 14d;
let join_lookback = 1d;
let tz_whitelist = dynamic([]);

Let Binding: meetings used in Stage 6

let meetings = (
ZoomLogs
| where TimeGenerated >= ago(schedule_lookback)
| where Event =~ "meeting.created"
| extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)
| extend SchedTimezone = tostring(parse_json(MeetingEvents).Timezone));

Stage 1: source

ZoomLogs

Stage 2: where

| where TimeGenerated >= ago(join_lookback)

Stage 3: where

| where Event =~ "meeting.participant_joined"

Stage 4: extend (3 consecutive steps)

| extend JoinedTimeZone = tostring(parse_json(MeetingEvents).Timezone)
| extend MeetingName = tostring(parse_json(MeetingEvents).MeetingName)
| extend MeetingId = tostring(parse_json(MeetingEvents).MeetingId)

Stage 5: where

| where JoinedTimeZone !in (tz_whitelist)

Stage 6: join

| join (meetings) on MeetingId

Stage 7: where

where SchedTimezone != JoinedTimeZone

Stage 8: project

project JoinedTimeZone, JoiningUser, MeetingName, MeetingScheduler, SchedTimezone, TimeGenerated

Stage 9: extend

extend AccountName, AccountUPNSuffix

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Eventeq
  • meeting.created
  • meeting.participant_joined
field:"Event" kind:eq

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
JoinedTimeZoneeq[]excludes:JoinedTimeZone field:"JoinedTimeZone" value:"[]"

Output Fields

These fields are emitted when the rule matches.

FieldSource
JoinedTimeZoneproject
JoiningUserproject
MeetingNameproject
MeetingSchedulerproject
SchedTimezoneproject
TimeGeneratedproject
AccountNameextend
AccountUPNSuffixextend

Valimail Enforce - High-Value User Management Event

#
Status
available
Severity
high
Time window
1h
Group by
EventCategory, User
Source
github.com/Azure/Azure-Sentinel

This query searches for high-severity user management events such as user deletion or deactivation in Valimail Enforce, which may indicate unauthorized access or insider threat.

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation
Impact

Rule Body

id: e960f5b0-cd80-474a-996a-013ff3989772
name: Valimail Enforce - High-Value User Management Event
description: |
  This query searches for high-severity user management events such as user deletion or deactivation
  in Valimail Enforce, which may indicate unauthorized access or insider threat.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: ValimailEnforce
    dataTypes:
      - ValimailEnforceEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
  - PrivilegeEscalation
relevantTechniques:
  - T1531
  - T1078
query: |
  ValimailEnforceEvents_CL
  | where EventCategory == "UserManagement"
  | where EventSeverity == "High"
  | where IsHighValueEvent == true
  | summarize
      EventCount = count(),
      FirstSeen = min(PerformedAt),
      LastSeen = max(PerformedAt),
      AffectedUsers = make_set(Subject),
      Actions = make_set(EventType)
    by User, EventCategory
  | extend
      AccountName = tostring(split(User, "@")[0]),
      AccountDomain = tostring(split(User, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountDomain
alertDetailsOverride:
  alertDisplayNameFormat: "High-value user management action by {{User}}"
  alertDescriptionFormat: |
    User '{{User}}' performed {{EventCount}} high-value user management
    action(s) in Valimail Enforce. Actions: {{Actions}}
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1d
    matchingMethod: Selected
    groupByEntities:
      - Account
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

ValimailEnforceEvents_CL

Stage 2: where

| where EventCategory == "UserManagement"

Stage 3: where

| where EventSeverity == "High"

Stage 4: where

| where IsHighValueEvent == true

Stage 5: summarize

| summarize
    EventCount = count(),
    FirstSeen = min(PerformedAt),
    LastSeen = max(PerformedAt),
    AffectedUsers = make_set(Subject),
    Actions = make_set(EventType)
  by User, EventCategory

Stage 6: extend

| extend
    AccountName = tostring(split(User, "@")[0]),
    AccountDomain = tostring(split(User, "@")[1])

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionssummarize
AffectedUserssummarize
EventCategorysummarize
EventCountsummarize
FirstSeensummarize
LastSeensummarize
Usersummarize
AccountDomainextend
AccountNameextend

Veeam ONE Malware Detection Change Tracking

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

Detects changes in Veeam ONE malware detection tracking.

Rule Body

id: 2a860019-0eda-4b49-bc62-8f683aed4929
name: Veeam ONE Malware Detection Change Tracking
description: Detects changes in Veeam ONE malware detection tracking.
severity: High
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
  dataTypes:
  - VeeamOneTriggeredAlarms_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 403
version: 1.0.1
kind: Scheduled
customDetails:
  PredefinedAlarmId: PredefinedAlarmId
  TriggeredAlarmId: TriggeredAlarmId
  Description: Description
  Name: Name
  VoneHostName: VoneHostName
  TriggeredTime: TriggeredTime
  Status: Status
  Comment: Comment
  ObjectId: ObjectId
  ObjectName: ObjectName
  ObjectType: ObjectType

Stages and Predicates

Stage 1: source

VeeamOneTriggeredAlarms_CL

Stage 2: where

| where PredefinedAlarmId == 403

Indicators

These rows show field, operator, and value matches.

Veeam ONE VM with No Replica

#

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
high
Time window
5m
Source
github.com/Azure/Azure-Sentinel

Detects Veeam ONE VMs with no replica configuration.

Rule Body

id: 128ea2a4-1047-4f28-ad55-b1b88357d172
name: Veeam ONE VM with No Replica
description: Detects Veeam ONE VMs with no replica configuration.
severity: High
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
  dataTypes:
  - VeeamOneTriggeredAlarms_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 331
version: 1.0.1
kind: Scheduled
customDetails:
  PredefinedAlarmId: PredefinedAlarmId
  TriggeredAlarmId: TriggeredAlarmId
  Description: Description
  Name: Name
  VoneHostName: VoneHostName
  TriggeredTime: TriggeredTime
  Status: Status
  Comment: Comment
  ObjectId: ObjectId
  ObjectName: ObjectName
  ObjectType: ObjectType

Stages and Predicates

Stage 1: source

VeeamOneTriggeredAlarms_CL

Stage 2: where

| where PredefinedAlarmId == 331

Indicators

These rows show field, operator, and value matches.

Back to all Kusto rules