Detection rules › Kusto

Kusto rules: 23

Access Token Manipulation - Create Process with Token

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

This query detects the use of the 'runas' command and checks whether the account used to elevate privileges isn't the user's own admin account. Additionally, it will match this event to the logon events - to check whether it has been successful as well as augment the event with the new SID.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 8df80270-b4fa-4a7a-931e-8d17c0b321ae
name: Access Token Manipulation - Create Process with Token
description: |
  This query detects the use of the 'runas' command and checks whether the account used to elevate privileges isn't the user's own admin account. 
  Additionally, it will match this event to the logon events - to check whether it has been successful as well as augment the event with the new SID.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
      - DeviceLogonEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
  - DefenseEvasion
relevantTechniques:
  - T1134.002
query: |
  let RunAsProcess=DeviceProcessEvents
      | where FileName =~ "runas.exe" 
      // You can choose to filter out the local admin account. This is based on convention. Here, we assume that localadmin accounts
      // end with _ladmin (RID 500 / LAPS).
      | where not(AccountName has_any("_ladmin"))
      // De-obfuscate the commandline used. 
      | extend CleanProcessCommandLine=parse_command_line(tostring(ProcessCommandLine), "windows")
      // Exclude a user running something on their system through their admin account.
      | where CleanProcessCommandLine !contains strcat(AccountName, "_adm") // Replace this with your admin account naming convention.
      // Exclude local admin account activities by, for instance, the servicedesk that uses the LAPS provisioned account. This is optional. 
      // Disable the line below if the number of false positives is acceptable. 
      | where not(CleanProcessCommandLine has_any (":_ladmin")) // Replace this with your local RID500/LAPS account.
      // Extract the username for the elevation action.
      | extend ElevatedAccountName=extract("user:([a-zA-Z0-9\\\\]+)",1,tostring(CleanProcessCommandLine))
      // Strip the domain suffix.
      | extend CleanElevatedAccountName= trim("(.*\\\\)",ElevatedAccountName);
  RunAsProcess
  | join kind=leftouter ( 
      DeviceLogonEvents
      | project-rename CleanElevatedAccountName = AccountName
      ) on CleanElevatedAccountName,DeviceId
  | project-rename ElevatedActionType=ActionType1,ElevatedAccountSid=AccountSid1
  | project TimeGenerated,DeviceId,DeviceName,FileName,FolderPath,ProcessCommandLine,SHA256,ProcessIntegrityLevel,AccountDomain,AccountName,AccountSid, LogonId, InitiatingProcessFileName,InitiatingProcessFolderPath,InitiatingProcessCommandLine,CleanProcessCommandLine,ElevatedAccountName,CleanElevatedAccountName,ElevatedActionType,LogonType,ElevatedAccountSid
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
  - entityType: Account
    fieldMappings:
      - identifier: Sid
        columnName: AccountSid 
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountDomain
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: ProcessCommandLine
version: 1.0.0
kind: Scheduled

Stages and Predicates

let RunAsProcess is inlined into the numbered stages below.

Stage 1: source

let RunAsProcess

Stage 2: source

DeviceProcessEvents

Stage 3: where

where FileName =~ "runas.exe"

Stage 4: where

where not (AccountName has "_ladmin")

Stage 5: extend

extend CleanProcessCommandLine

Stage 6: where

where CleanProcessCommandLine !contains strcat(AccountName, "_adm")

Stage 7: where

where not (CleanProcessCommandLine has ":_ladmin")

Stage 8: extend

extend ElevatedAccountName

Stage 9: extend

extend CleanElevatedAccountName

Stage 10: join

join kind=leftouter (DeviceLogonEvents) on CleanElevatedAccountName, DeviceId

Stage 11: project-rename

project-rename

Stage 12: project

project AccountDomain, AccountName, AccountSid, CleanElevatedAccountName, CleanProcessCommandLine, DeviceId, DeviceName, ElevatedAccountName, ElevatedAccountSid, ElevatedActionType, FileName, FolderPath, InitiatingProcessCommandLine, InitiatingProcessFileName, InitiatingProcessFolderPath, LogonId, LogonType, ProcessCommandLine, ProcessIntegrityLevel, SHA256, TimeGenerated

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
AccountNamematch_ladminexcludes:AccountName field:"AccountName" value:"_ladmin"
CleanProcessCommandLinematch:_ladminexcludes:CleanProcessCommandLine field:"CleanProcessCommandLine" value:":_ladmin"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountDomainproject
AccountNameproject
AccountSidproject
CleanElevatedAccountNameproject
CleanProcessCommandLineproject
DeviceIdproject
DeviceNameproject
ElevatedAccountNameproject
ElevatedAccountSidproject
ElevatedActionTypeproject
FileNameproject
FolderPathproject
InitiatingProcessCommandLineproject
InitiatingProcessFileNameproject
InitiatingProcessFolderPathproject
LogonIdproject
LogonTypeproject
ProcessCommandLineproject
ProcessIntegrityLevelproject
SHA256project
TimeGeneratedproject

Azure Key Vault access TimeSeries anomaly

#
Status
available
Severity
low
Time window
14d
Group by
CallerIPAddress, LatestAnomalyTime, Resource, clientInfo_s, id_s
Source
github.com/Azure/Azure-Sentinel

'Identifies a sudden increase in count of Azure Key Vault secret or vault access operations by CallerIPAddress. The query leverages a built-in KQL anomaly detection algorithm to find large deviations from baseline Azure Key Vault access patterns. Any sudden increase in the count of Azure Key Vault accesses can be an indication of adversary dumping credentials via automated methods. If you are seeing any noise, try filtering known source(IP/Account) and user-agent combinations. TimeSeries Reference Blog: https://techcommunity.microsoft.com/t5/azure-sentinel/looking-for-unknown-anomalies-what-is-normal-time-series/ba-p/555052'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 0914adab-90b5-47a3-a79f-7cdcac843aa7
name: Azure Key Vault access TimeSeries anomaly
description: |
  'Identifies a sudden increase in count of Azure Key Vault secret or vault access operations by CallerIPAddress. The query leverages a built-in KQL anomaly detection algorithm to find large deviations from baseline Azure Key Vault access patterns.
  Any sudden increase in the count of Azure Key Vault accesses can be an indication of adversary dumping credentials via automated methods. If you are seeing any noise, try filtering known source(IP/Account) and user-agent combinations.
  TimeSeries Reference Blog: https://techcommunity.microsoft.com/t5/azure-sentinel/looking-for-unknown-anomalies-what-is-normal-time-series/ba-p/555052'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AzureKeyVault
    dataTypes:
      - KeyVaultData
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1003
query: |
  let starttime = 14d;
  let timeframe = 1d;
  let scorethreshold = 3;
  let baselinethreshold = 25;
  // To avoid any False Positives, filtering using AppId is recommended. For example the AppId 509e4652-da8d-478d-a730-e9d4a1996ca4 has been added in the query as it corresponds
  // to Azure Resource Graph performing VaultGet operations for indexing and syncing all tracked resources across Azure.
  let Allowedappid = dynamic(["509e4652-da8d-478d-a730-e9d4a1996ca4"]);
  let OperationList = dynamic(
  ["SecretGet", "KeyGet", "VaultGet"]);
  let TimeSeriesData = AzureDiagnostics
  | where TimeGenerated between (startofday(ago(starttime))..startofday(now()))
  | where not((identity_claim_appid_g in (Allowedappid)) and OperationName == 'VaultGet')
    | where ResourceType =~ "VAULTS" and ResultType =~ "Success"
  | where OperationName in (OperationList)
  | extend ResultType = column_ifexists("ResultType", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None")
  | where ResultType !~ "None" and isnotempty(ResultType)
  | where CallerIPAddress !~ "None" and isnotempty(CallerIPAddress)
  | project TimeGenerated, OperationName, Resource, CallerIPAddress
  | make-series HourlyCount=count() on TimeGenerated from startofday(ago(starttime)) to startofday(now()) step timeframe by CallerIPAddress;
  //Filter anomolies against TimeSeriesData
  let TimeSeriesAlerts = TimeSeriesData
  | extend (anomalies, score, baseline) = series_decompose_anomalies(HourlyCount, scorethreshold, -1, 'linefit')
  | mv-expand HourlyCount to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double),score to typeof(double), baseline to typeof(long)
  | where anomalies > 0 | extend AnomalyHour = TimeGenerated
  | where baseline > baselinethreshold // Filtering low count events per baselinethreshold
  | project CallerIPAddress, AnomalyHour, TimeGenerated, HourlyCount, baseline, anomalies, score;
  let AnomalyHours = TimeSeriesAlerts | where TimeGenerated > ago(2d) | project TimeGenerated;
  // Filter the alerts since specified timeframe
  TimeSeriesAlerts
  | where TimeGenerated > ago(2d)
  // Join against base logs since specified timeframe to retrive records associated with the hour of anomoly
  | join kind = innerunique (
  AzureDiagnostics
  | where TimeGenerated > ago(2d)
  | where not((identity_claim_appid_g in (Allowedappid)) and OperationName == 'VaultGet')
  | where ResourceType =~ "VAULTS" and ResultType =~ "Success"
  | where OperationName in (OperationList)
  | extend DateHour = bin(TimeGenerated, 1h) // create a new column and round to hour
  | where DateHour in ((AnomalyHours)) //filter the dataset to only selected anomaly hours
  | extend ResultType = column_ifexists("ResultType", "NoResultType")
  | extend requestUri_s = column_ifexists("requestUri_s", "None"), identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g = column_ifexists("identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g", "None"),identity_claim_oid_g = column_ifexists("identity_claim_oid_g", ""),
    identity_claim_upn_s = column_ifexists("identity_claim_upn_s", "")
  | extend
      CallerObjectId = iff(isempty(identity_claim_oid_g), identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g, identity_claim_oid_g),
      CallerObjectUPN = iff(isempty(identity_claim_upn_s), identity_claim_http_schemas_xmlsoap_org_ws_2005_05_identity_claims_upn_s, identity_claim_upn_s)
  | extend id_s = column_ifexists("id_s", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None"), clientInfo_s = column_ifexists("clientInfo_s", "None")
  | summarize PerOperationCount=count(), LatestAnomalyTime = arg_max(TimeGenerated,*) by bin(TimeGenerated,1h), Resource, OperationName, id_s, CallerIPAddress, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g, identity_claim_oid_g, requestUri_s, clientInfo_s
  ) on CallerIPAddress
  | extend
      CallerObjectId = iff(isempty(identity_claim_oid_g), identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g, identity_claim_oid_g),
      CallerObjectUPN = iff(isempty(identity_claim_upn_s), identity_claim_http_schemas_xmlsoap_org_ws_2005_05_identity_claims_upn_s, identity_claim_upn_s)
  | summarize EventCount=count(), OperationNameList = make_set(OperationName,1000), RequestURLList = make_set(requestUri_s, 100), AccountList = make_set(CallerObjectId, 100), AccountMax = arg_max(CallerObjectId,*) by Resource, id_s, clientInfo_s, LatestAnomalyTime
  | extend timestamp = LatestAnomalyTime
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountMax
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIPAddress
version: 1.0.6
kind: Scheduled

Stages and Predicates

Parameters

let starttime = 14d;
let timeframe = 1d;
let scorethreshold = 3;
let baselinethreshold = 25;
let Allowedappid = dynamic(["509e4652-da8d-478d-a730-e9d4a1996ca4"]);

let TimeSeriesData and let TimeSeriesAlerts are inlined into the numbered stages below.

Let Binding: OperationList used in Stages 5, 18

let OperationList = dynamic(
["SecretGet", "KeyGet", "VaultGet"]);

Let Binding: AnomalyHours used in Stage 18

let AnomalyHours = TimeSeriesAlerts | where TimeGenerated > ago(2d) | project TimeGenerated;

Stages 1 to 16 define let TimeSeriesAlerts as the rule's main pipeline source. Stages 17 to 21 use it.

Stage 1: source

AzureDiagnostics

Stage 2: where

| where TimeGenerated between (startofday(ago(starttime))..startofday(now()))

Stage 3: where

| where not((identity_claim_appid_g in (Allowedappid)) and OperationName == 'VaultGet')

Stage 4: where

| where ResourceType =~ "VAULTS" and ResultType =~ "Success"

Stage 5: where

| where OperationName in (OperationList)

Stage 6: extend

| extend ResultType = column_ifexists("ResultType", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None")

Stage 7: where

| where ResultType !~ "None" and isnotempty(ResultType)

Stage 8: where

| where CallerIPAddress !~ "None" and isnotempty(CallerIPAddress)

Stage 9: project

| project TimeGenerated, OperationName, Resource, CallerIPAddress

The stages below score time-series anomalies (make-series, series_decompose_anomalies).

Stage 10: make-series

| make-series HourlyCount=count() on TimeGenerated from startofday(ago(starttime)) to startofday(now()) step timeframe by CallerIPAddress

Stage 11: extend

| extend (anomalies, score, baseline) = series_decompose_anomalies(HourlyCount, scorethreshold, -1, 'linefit')

Stage 12: mv-expand

| mv-expand HourlyCount to typeof(double), TimeGenerated to typeof(datetime), anomalies to typeof(double),score to typeof(double), baseline to typeof(long)

Stage 13: where

| where anomalies > 0

Stage 14: extend

| extend AnomalyHour = TimeGenerated

Stage 15: where

| where baseline > baselinethreshold

Stage 16: project

| project CallerIPAddress, AnomalyHour, TimeGenerated, HourlyCount, baseline, anomalies, score

Stage 17: where

TimeSeriesAlerts
| where TimeGenerated > ago(2d)

Stage 18: join

| join kind = innerunique (
AzureDiagnostics
| where TimeGenerated > ago(2d)
| where not((identity_claim_appid_g in (Allowedappid)) and OperationName == 'VaultGet')
| where ResourceType =~ "VAULTS" and ResultType =~ "Success"
| where OperationName in (OperationList)
| extend DateHour = bin(TimeGenerated, 1h)
| where DateHour in ((AnomalyHours))
| extend ResultType = column_ifexists("ResultType", "NoResultType")
| extend requestUri_s = column_ifexists("requestUri_s", "None"), identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g = column_ifexists("identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g", "None"),identity_claim_oid_g = column_ifexists("identity_claim_oid_g", ""),
  identity_claim_upn_s = column_ifexists("identity_claim_upn_s", "")
| extend
    CallerObjectId = iff(isempty(identity_claim_oid_g), identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g, identity_claim_oid_g),
    CallerObjectUPN = iff(isempty(identity_claim_upn_s), identity_claim_http_schemas_xmlsoap_org_ws_2005_05_identity_claims_upn_s, identity_claim_upn_s)
| extend id_s = column_ifexists("id_s", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None"), clientInfo_s = column_ifexists("clientInfo_s", "None")
| summarize PerOperationCount=count(), LatestAnomalyTime = arg_max(TimeGenerated,*) by bin(TimeGenerated,1h), Resource, OperationName, id_s, CallerIPAddress, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g, identity_claim_oid_g, requestUri_s, clientInfo_s
) on CallerIPAddress

Stage 19: extend

extend CallerObjectId, CallerObjectUPN
CallerObjectId =
ifisempty(identity_claim_oid_g)identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g
elseidentity_claim_oid_g
CallerObjectUPN =
ifisempty(identity_claim_upn_s)identity_claim_http_schemas_xmlsoap_org_ws_2005_05_identity_claims_upn_s
elseidentity_claim_upn_s

Stage 20: summarize

summarize AccountList, AccountMax, EventCount, OperationNameList, RequestURLList by Resource, id_s, clientInfo_s, LatestAnomalyTime

Stage 21: extend

extend timestamp

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountListsummarize
AccountMaxsummarize
EventCountsummarize
LatestAnomalyTimesummarize
OperationNameListsummarize
RequestURLListsummarize
Resourcesummarize
clientInfo_ssummarize
id_ssummarize
timestampextend

Azure secure score one admin

#
Status
available
Severity
high
Time window
6h
Source
github.com/Azure/Azure-Sentinel

'This query searches for having 1 Global Administrator reduces the surface area of attack for your Azure tenant, but sets up a single point of failure for the whole tenant. Global Administrators have access to all aspects of Azure'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: F539B2A7-D9E7-4438-AA20-893BC61DF130
name: Azure secure score one admin
description: |
  'This query searches for having 1 Global Administrator reduces the surface area of attack for your Azure tenant, but sets up a single point of failure for the whole tenant. Global Administrators have access to all aspects of Azure'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SenservaPro
    dataTypes:
      - SenservaPro_CL
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1529
query: |
  SenservaPro_CL
  | where ControlName_s == 'AzureSecureScoreOneAdmin'
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: ControlName_s
      - identifier: AadTenantId
        columnName: TenantId
      - identifier: DisplayName
        columnName: TenantDisplayName_s 
  - entityType: SecurityGroup
    fieldMappings:
      - identifier: DistinguishedName
        columnName: Group_s
  - entityType: AzureResource
    fieldMappings:
      - identifier: ResourceId
        columnName: SourceSystem 
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

SenservaPro_CL

Stage 2: where

| where ControlName_s == 'AzureSecureScoreOneAdmin'

Indicators

These rows show field, operator, and value matches.

Bitglass - New risky user

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

'Detects new risky user.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: a123668c-d907-41b9-bf3f-8cb4cd7b163a
name: Bitglass - New risky user
description: |
  'Detects new risky user.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: Bitglass
    dataTypes:
      - Bitglass
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  Bitglass
  | where EventType =~ 'access'
  | where EventResultDetails has_all ('Added', 'Risky Users')
  | extend AccountCustomEntity = User
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

Bitglass

Stage 2: where

| where EventType =~ 'access'

Stage 3: where

| where EventResultDetails has_all ('Added', 'Risky Users')

Stage 4: extend

| extend AccountCustomEntity = User

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventResultDetailsmatch
  • Added transforms: term
  • Risky Users transforms: term
field:"EventResultDetails" kind:match
EventTypeeq
  • access
field:"EventType" kind:eq value:"access"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

Cloudflare - Bad client IP

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

'Detects requests from IP with bad reputation index.'

MITRE ATT&CK Coverage

Rule Body

id: a7ce6135-9d55-4f14-b058-adc2e920a4fb
name: Cloudflare - Bad client IP
description: |
  'Detects requests from IP with bad reputation index.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CloudflareDataConnector
    dataTypes:
      - Cloudflare
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  let ip_reputation = dynamic(['unknown', 'badHost', 'greylist', 'securityScanner', 'scan', 'tor']);
  Cloudflare
  | where ClientIPClass in~ (ip_reputation)
  | extend CompleteUrl = strcat(HttpRequestHeaderHost,ClientRequestPath)
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: CompleteUrl
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let ip_reputation = dynamic(['unknown', 'badHost', 'greylist', 'securityScanner', 'scan', 'tor']);

Stage 1: source

Cloudflare

Stage 2: where

| where ClientIPClass in~ (ip_reputation)

Stage 3: extend

| extend CompleteUrl = strcat(HttpRequestHeaderHost,ClientRequestPath)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ClientIPClassin
  • badHost
  • greylist
  • scan
  • securityScanner
  • tor
  • unknown
field:"ClientIPClass" kind:in

Output Fields

These fields are emitted when the rule matches.

FieldSource
CompleteUrlextend

Drop attempts stateful anomaly on database

#
Status
available
Severity
medium
Time window
14d
Group by
ApplicationName, ClientIp, Database, HostName, PrincipalName, ResourceId, timeSlice
Source
github.com/Azure/Azure-Sentinel

'This query detects batches of distinct SQL queries that execute (or attempt to) commands that could indicate potential security issues - such as attempts to drop tables or databases (e.g. for data vandalism).'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 237c3855-138c-4588-a68f-b870abd3bfc9
name: Drop attempts stateful anomaly on database
description: |
  'This query detects batches of distinct SQL queries that execute (or attempt to) commands that could indicate potential security issues - such as attempts to drop tables or databases (e.g. for data vandalism).'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureSql
    dataTypes:
      - AzureDiagnostics
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Impact
relevantTechniques:
  - T1485
tags:
  - SQL
query: |
    let monitoredStatementsThreshold = 1;           // Minimal number of monitored statements in the slice to trigger an anomaly.
    let trainingSlicesThreshold = 5;                // The maximal amount of slices with monitored statements in the training window before anomaly detection is throttled.
    let timeSliceSize = 1h;                         // The size of the single timeSlice for individual aggregation.
    let detectionWindow = 1h;                       // The size of the recent detection window for detecting anomalies.  
    let trainingWindow = detectionWindow + 14d;     // The size of the training window before the detection window for learning the normal state.
    let hotwords = pack_array('drop table', 'drop database'); // List of monitored hot words.
    let processedData = materialize (
        AzureDiagnostics
        | where TimeGenerated >= ago(trainingWindow)
        | where Category == 'SQLSecurityAuditEvents' and action_id_s has_any ("RCM", "BCM") // Keep only SQL affected rows
        | project TimeGenerated, PrincipalName = server_principal_name_s, ClientIp = client_ip_s, HostName = host_name_s, ResourceId,
                  ApplicationName = application_name_s, ActionName = action_name_s, Database = strcat(LogicalServerName_s, '/', database_name_s),
                  IsSuccess = succeeded_s, AffectedRows = affected_rows_d,
                  ResponseRows = response_rows_d, Statement = statement_s,
                  Error = case( additional_information_s has 'error_code', toint(extract("<error_code>([0-9.]+)", 1, additional_information_s))
                        , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+)", 1, additional_information_s))
                        , 0),
                  State = case( additional_information_s has 'error_state', toint(extract("<error_state>([0-9.]+)", 1, additional_information_s))
                        , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+), Level ([0-9.]+)", 2, additional_information_s))
                        , 0),
                  AdditionalInfo = additional_information_s, timeSlice = floor(TimeGenerated, timeSliceSize)
        | extend hasHotword = iff(Statement has_any (hotwords), 1, 0)
        | summarize countEvents = count(), countStatements = dcount(Statement)
            , countStatementsWithHotwords = dcountif(Statement, hasHotword == 1)
            , countFailedStatementsWithHotwords = dcountif(Statement, (hasHotword == 1) and (Error > 0))
            , countSuccessfulStatementsWithHotwords = dcountif(Statement, ((hasHotword == 1)) and (Error == 0))
            , anyMonitoredStatement = anyif(Statement, (hasHotword == 1))
            , anySuccessfulMonitoredStatement = anyif(Statement, (hasHotword == 1) and (Error == 0))
            , anyInfo = anyif(AdditionalInfo, hasHotword == 1)
            , hotWord = anyif(extract(strcat_array(hotwords, '|'), 0, tolower(Statement)), hasHotword == 1)
            by Database, ClientIp, ApplicationName, PrincipalName, timeSlice,HostName,ResourceId   
        | extend WindowType = case( timeSlice >= ago(detectionWindow), 'detection',
                                               (ago(trainingWindow) <= timeSlice and timeSlice < ago(detectionWindow)), 'training', 'other')
        | where WindowType in ('detection', 'training'));
    let trainingSet =
        processedData
        | where WindowType == 'training'
        | summarize countSlicesWithHotwords = dcountif(timeSlice, countStatementsWithHotwords >= monitoredStatementsThreshold)
            by Database;
    processedData
    | where WindowType == 'detection' 
    | join kind = inner (trainingSet) on Database
    | extend IsHotwordAnomalyOnStatement = iff(((countStatementsWithHotwords >= monitoredStatementsThreshold) and (countSlicesWithHotwords <= trainingSlicesThreshold)), true, false)
        , anomalyScore = round(countStatementsWithHotwords/monitoredStatementsThreshold, 0)
    | where IsHotwordAnomalyOnStatement == 'true'
    | project TimeGenerated = timeSlice, Database, ClientIp, ApplicationName, PrincipalName, HostName, ResourceId, countEvents, countStatements, countStatementsWithHotwords, anyMonitoredStatement, anyInfo, anomalyScore, hotWord
    | extend Name = tostring(split(PrincipalName,'@',0)[0]), UPNSuffix = tostring(split(PrincipalName,'@',1)[0])    
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ApplicationName
  - entityType: AzureResource
    fieldMappings:
      - identifier: ResourceId
        columnName: ResourceId
customDetails:
  HotWords: hotWord
alertDetailsOverride:
  alertDisplayNameFormat: 'Drop attempts stateful anomaly on database {{Database}}'
  alertDescriptionFormat: 'An anomaly was detected on database {{Database}} with {{countStatementsWithHotwords}} statements with monitored hot words (drop table, drop database etc.) in the last hour. Investigate the database activity for potential malicious attempts to drop tables or databases.'
version: 1.1.2
kind: Scheduled

Stages and Predicates

Parameters

let monitoredStatementsThreshold = 1;
let trainingSlicesThreshold = 5;
let timeSliceSize = 1h;
let detectionWindow = 1h;
let trainingWindow = detectionWindow + 14d;
let hotwords = pack_array('drop table', 'drop database');

let processedData is inlined into the numbered stages below.

Let Binding: trainingSet used in Stage 10

let trainingSet = processedData
    | where WindowType == 'training'
    | summarize countSlicesWithHotwords = dcountif(timeSlice, countStatementsWithHotwords >= monitoredStatementsThreshold)
        by Database;

Stages 1 to 8 define let processedData as the rule's main pipeline source. Stages 9 to 14 use it.

Stage 1: source

AzureDiagnostics

Stage 2: where

| where TimeGenerated >= ago(trainingWindow)

Stage 3: where

| where Category == 'SQLSecurityAuditEvents' and action_id_s has_any ("RCM", "BCM")

Stage 4: project

| project TimeGenerated, PrincipalName = server_principal_name_s, ClientIp = client_ip_s, HostName = host_name_s, ResourceId,
              ApplicationName = application_name_s, ActionName = action_name_s, Database = strcat(LogicalServerName_s, '/', database_name_s),
              IsSuccess = succeeded_s, AffectedRows = affected_rows_d,
              ResponseRows = response_rows_d, Statement = statement_s,
              Error = case( additional_information_s has 'error_code', toint(extract("<error_code>([0-9.]+)", 1, additional_information_s))
                    , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+)", 1, additional_information_s))
                    , 0),
              State = case( additional_information_s has 'error_state', toint(extract("<error_state>([0-9.]+)", 1, additional_information_s))
                    , additional_information_s has 'failure_reason', toint(extract("<failure_reason>Err ([0-9.]+), Level ([0-9.]+)", 2, additional_information_s))
                    , 0),
              AdditionalInfo = additional_information_s, timeSlice = floor(TimeGenerated, timeSliceSize)

Stage 5: extend

| extend hasHotword = iff(Statement has_any (hotwords), 1, 0)
hasHotword =
ifStatement has "hotwords"1
else0

Stage 6: summarize

| summarize countEvents = count(), countStatements = dcount(Statement)
        , countStatementsWithHotwords = dcountif(Statement, hasHotword == 1)
        , countFailedStatementsWithHotwords = dcountif(Statement, (hasHotword == 1) and (Error > 0))
        , countSuccessfulStatementsWithHotwords = dcountif(Statement, ((hasHotword == 1)) and (Error == 0))
        , anyMonitoredStatement = anyif(Statement, (hasHotword == 1))
        , anySuccessfulMonitoredStatement = anyif(Statement, (hasHotword == 1) and (Error == 0))
        , anyInfo = anyif(AdditionalInfo, hasHotword == 1)
        , hotWord = anyif(extract(strcat_array(hotwords, '|'), 0, tolower(Statement)), hasHotword == 1)
        by Database, ClientIp, ApplicationName, PrincipalName, timeSlice,HostName,ResourceId

Stage 7: extend

| extend WindowType = case( timeSlice >= ago(detectionWindow), 'detection',
                                           (ago(trainingWindow) <= timeSlice and timeSlice < ago(detectionWindow)), 'training', 'other')
WindowType =
if/* macro: (timeSlice >= ago(detectionWindow)) */'detection'
elif/* macro: (ago(trainingWindow) <= timeSlice) */'training'
else'other'

Stage 8: where

| where WindowType in ('detection', 'training')

Stage 9: where

processedData
| where WindowType == 'detection'

Stage 10: join

| join kind = inner (trainingSet) on Database

Stage 11: extend

extend IsHotwordAnomalyOnStatement, anomalyScore
IsHotwordAnomalyOnStatement =
ifcountStatementsWithHotwords >= monitoredStatementsThreshold and countSlicesWithHotwords <= trainingSlicesThresholdtrue
elsefalse

Stage 12: where

where IsHotwordAnomalyOnStatement == true

Stage 13: project

project ApplicationName, ClientIp, Database, HostName, PrincipalName, ResourceId, TimeGenerated, anomalyScore, anyInfo, anyMonitoredStatement, countEvents, countStatements, countStatementsWithHotwords, hotWord

Stage 14: extend

extend Name, UPNSuffix

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ApplicationNameproject
ClientIpproject
Databaseproject
HostNameproject
PrincipalNameproject
ResourceIdproject
TimeGeneratedproject
anomalyScoreproject
anyInfoproject
anyMonitoredStatementproject
countEventsproject
countStatementsproject
countStatementsWithHotwordsproject
hotWordproject
Nameextend
UPNSuffixextend

GitHub Security Vulnerability in Repository

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

'This alerts when there is a new security vulnerability in a GitHub repository.'

MITRE ATT&CK Coverage

Rule Body

id: 5436f471-b03d-41cb-b333-65891f887c43
name: GitHub Security Vulnerability in Repository
description: |
  'This alerts when there is a new security vulnerability in a GitHub repository.'
severity: Informational
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Execution
  - PrivilegeEscalation
  - DefenseEvasion
  - CredentialAccess
  - LateralMovement
relevantTechniques:
  - T1190
  - T1203
  - T1068
  - T1211
  - T1212
  - T1210
query: |
  GitHubRepo
  | where Action == "vulnerabilityAlert"
  | project TimeGenerated, DismmisedAt, Reason, vulnerableManifestFilename, Description, Link, PublishedAt, Severity, Summary
entityMappings: 
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Link
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

GitHubRepo

Stage 2: where

| where Action == "vulnerabilityAlert"

Stage 3: project

| project TimeGenerated, DismmisedAt, Reason, vulnerableManifestFilename, Description, Link, PublishedAt, Severity, Summary

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actioneq
  • vulnerabilityAlert
field:"Action" kind:eq value:"vulnerabilityAlert"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Descriptionproject
DismmisedAtproject
Linkproject
PublishedAtproject
Reasonproject
Severityproject
Summaryproject
TimeGeneratedproject
vulnerableManifestFilenameproject

Netskope - Heavy Personal Cloud Storage Usage (Shadow IT)

#
Status
available
Severity
medium
Time window
1h
Group by
CsUsername, XCCountry, XCDevice, XCsApp, XCsAppCategory, XCsAppInstanceName, XCsAppTags
Source
github.com/Azure/Azure-Sentinel

Detects heavy usage of personal cloud storage applications like personal Dropbox, Google Drive, OneDrive personal, etc. Indicates potential Shadow IT or data leakage risk.

MITRE ATT&CK Coverage

Rule Body

id: 272f9bca-5fd0-4413-b494-03b2d9f0bb9b
name: Netskope - Heavy Personal Cloud Storage Usage (Shadow IT)
description: |
  Detects heavy usage of personal cloud storage applications like personal Dropbox, Google Drive, OneDrive personal, etc. Indicates potential Shadow IT or data leakage risk.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: NetskopeWebTxConnector
    dataTypes:
      - NetskopeWebTransactions_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
  - Collection
relevantTechniques:
  - T1567
  - T1530
query: |
  let heavyUsageThresholdMB = 500;
  NetskopeWebTransactions_CL
  | where TimeGenerated > ago(1h)
  | where isnotempty(CsUsername) and isnotempty(XCsApp)
  | where XCsApp has_any ('Dropbox', 'Google Drive', 'OneDrive', 'Box', 'iCloud', 'pCloud', 'MEGA', 'MediaFire', 'WeTransfer')
  | where XCsAppInstanceTag contains 'Personal' 
      or XCsAppInstanceName contains 'Personal'
      or XCsAppTags contains 'Unsanctioned'
      or not(XCsAppTags contains 'Enterprise')
  | summarize 
      TotalBytes = sum(Bytes),
      UploadBytes = sum(CsBytes),
      DownloadBytes = sum(ScBytes),
      FileCount = dcount(XCsAppObjectName),
      Files = make_set(XCsAppObjectName, 10),
      Activities = make_set(XCsAppActivity),
      EventCount = count()
      by CsUsername, XCsApp, XCsAppCategory, XCsAppInstanceName, XCsAppTags, XCDevice, XCCountry
  | extend 
      TotalMB = round(TotalBytes / 1048576.0, 2),
      UploadMB = round(UploadBytes / 1048576.0, 2),
      DownloadMB = round(DownloadBytes / 1048576.0, 2)
  | where TotalMB > heavyUsageThresholdMB or FileCount > 50
  | project 
      TimeGenerated = now(),
      User = CsUsername,
      CloudApplication = XCsApp,
      AppCategory = XCsAppCategory,
      AppInstance = XCsAppInstanceName,
      AppTags = XCsAppTags,
      TotalDataMB = TotalMB,
      UploadMB,
      DownloadMB,
      FileCount,
      Files,
      Activities,
      Device = XCDevice,
      Country = XCCountry,
      EventCount
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: User
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: CloudApplication
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let heavyUsageThresholdMB = 500;

Stage 1: source

NetskopeWebTransactions_CL

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: where

| where isnotempty(CsUsername) and isnotempty(XCsApp)

Stage 4: where

| where XCsApp has_any ('Dropbox', 'Google Drive', 'OneDrive', 'Box', 'iCloud', 'pCloud', 'MEGA', 'MediaFire', 'WeTransfer')

Stage 5: where

| where XCsAppInstanceTag contains 'Personal' 
    or XCsAppInstanceName contains 'Personal'
    or XCsAppTags contains 'Unsanctioned'
    or not(XCsAppTags contains 'Enterprise')

Stage 6: summarize

| summarize 
    TotalBytes = sum(Bytes),
    UploadBytes = sum(CsBytes),
    DownloadBytes = sum(ScBytes),
    FileCount = dcount(XCsAppObjectName),
    Files = make_set(XCsAppObjectName, 10),
    Activities = make_set(XCsAppActivity),
    EventCount = count()
    by CsUsername, XCsApp, XCsAppCategory, XCsAppInstanceName, XCsAppTags, XCDevice, XCCountry

Stage 7: extend

| extend 
    TotalMB = round(TotalBytes / 1048576.0, 2),
    UploadMB = round(UploadBytes / 1048576.0, 2),
    DownloadMB = round(DownloadBytes / 1048576.0, 2)

Stage 8: where

| where TotalMB > heavyUsageThresholdMB or FileCount > 50

Stage 9: project

| project 
    TimeGenerated = now(),
    User = CsUsername,
    CloudApplication = XCsApp,
    AppCategory = XCsAppCategory,
    AppInstance = XCsAppInstanceName,
    AppTags = XCsAppTags,
    TotalDataMB = TotalMB,
    UploadMB,
    DownloadMB,
    FileCount,
    Files,
    Activities,
    Device = XCDevice,
    Country = XCCountry,
    EventCount

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CsUsernameis_not_null
  • (no value, null check)
field:"CsUsername" kind:is_not_null
FileCountgt
  • 50 transforms: number
field:"FileCount" kind:gt value:"50"
TotalMBgt
  • 500 transforms: number
field:"TotalMB" kind:gt value:"500"
XCsAppis_not_null
  • (no value, null check)
field:"XCsApp" kind:is_not_null
XCsAppmatch
  • Box transforms: term
  • Dropbox transforms: term
  • Google Drive transforms: term
  • MEGA transforms: term
  • MediaFire transforms: term
  • OneDrive transforms: term
  • WeTransfer transforms: term
  • iCloud transforms: term
  • pCloud transforms: term
field:"XCsApp" kind:match
XCsAppInstanceNamecontains
  • Personal
field:"XCsAppInstanceName" kind:contains value:"Personal"
XCsAppInstanceTagcontains
  • Personal
field:"XCsAppInstanceTag" kind:contains value:"Personal"
XCsAppTagscontains
  • Unsanctioned
field:"XCsAppTags" kind:contains value:"Unsanctioned"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Activitiesproject
AppCategoryproject
AppInstanceproject
AppTagsproject
CloudApplicationproject
Countryproject
Deviceproject
DownloadMBproject
EventCountproject
FileCountproject
Filesproject
TimeGeneratedproject
TotalDataMBproject
UploadMBproject
Userproject

SAP BTP - Cloud Integration artifact deployment

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

Identifies deployment and undeployment of integration artifacts in SAP Cloud Integration. Integration flows are executable code that can process, transform, and route data between systems. Unauthorized artifact deployment could indicate: - Attacker deploying malicious integration flows for data exfiltration - Deployment of rogue code for persistent access - Undeployment of critical integrations causing denial of service

MITRE ATT&CK Coverage

Rule Body

id: a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d
kind: Scheduled
name: SAP BTP - Cloud Integration artifact deployment
description: |
  Identifies deployment and undeployment of integration artifacts in SAP Cloud Integration.
  Integration flows are executable code that can process, transform, and route data between
  systems.
  
  Unauthorized artifact deployment could indicate:
  - Attacker deploying malicious integration flows for data exfiltration
  - Deployment of rogue code for persistent access
  - Undeployment of critical integrations causing denial of service
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SAPBTPAuditEvents
    dataTypes:
      - SAPBTPAuditLog_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - Persistence
relevantTechniques:
  - T1059
  - T1546
query: |
  SAPBTPAuditLog_CL
  | where Category == "audit.configuration"
  | extend objectType = tostring(Message.object.type)
  | where objectType in ("Deployment", "Undeployment")
  | extend attributes = todynamic(Message.attributes)
  | mv-apply attr = attributes on (
      summarize
          SymbolicName = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "symbolicName"),
          ArtifactId = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "id"),
          ArtifactVersion = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "version"),
          DeployedBy = take_anyif(tostring(attr.["new"]), tostring(attr.name) == "deployedBy"),
          UndeployedBy = take_anyif(tostring(attr.["new"]), tostring(attr.name) == "undeployedBy"),
          Creator = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "creator"),
          TenantName = take_anyif(tostring(attr.["new"]), tostring(attr.name) == "tenantName"),
          RuntimeLocationId = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "runtimeLocationId")
  )
  | extend Actor = case(
      isnotempty(DeployedBy), DeployedBy,
      isnotempty(UndeployedBy), UndeployedBy,
      isnotempty(Creator), Creator,
      ""
  )
  | extend ActionCategory = iff(objectType == "Deployment", "Deploy", "Undeploy"),
           normalizedAction = iff(objectType == "Deployment", "deployed", "undeployed")
  | extend MessageText = strcat("Integration artifact '", SymbolicName, "' (version ", ArtifactVersion, ") was ", normalizedAction, " in tenant ", TenantName)
  | extend AccountName = iff(Actor has "@", tostring(split(Actor, "@")[0]), ""),
           UPNSuffix   = iff(Actor has "@", tostring(split(Actor, "@")[1]), "")
  | project
      UpdatedOn,
      Actor,
      AccountName,
      UPNSuffix,
      MessageText,
      ArtifactName = SymbolicName,
      ArtifactId,
      ArtifactVersion,
      ActionCategory,
      ObjectType = objectType,
      TenantName,
      RuntimeLocationId,
      Tenant,
      CloudApp = "SAP Cloud Integration"
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: CloudApp
alertDetailsOverride:
  alertDisplayNameFormat: 'SAP Cloud Integration: {{MessageText}}'
  alertDescriptionFormat: |
    {{MessageText}} by {{Actor}}.
    
    This could indicate:
    - Legitimate integration artifact deployment or maintenance
    - Unauthorized deployment of malicious integration code
    - Attacker undeploying security-relevant integrations
customDetails:
  ArtifactName: ArtifactName
  ArtifactId: ArtifactId
  ArtifactVersion: ArtifactVersion
  ActionCategory: ActionCategory
  TenantName: TenantName
  RuntimeLocationId: RuntimeLocationId
version: 1.1.1

Stages and Predicates

Stage 1: source

SAPBTPAuditLog_CL

Stage 2: where

| where Category == "audit.configuration"

Stage 3: extend

| extend objectType = tostring(Message.object.type)

Stage 4: where

| where objectType in ("Deployment", "Undeployment")

Stage 5: extend

| extend attributes = todynamic(Message.attributes)

Stage 6: kusto:mv-apply

| mv-apply attr = attributes on (
    summarize
        SymbolicName = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "symbolicName"),
        ArtifactId = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "id"),
        ArtifactVersion = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "version"),
        DeployedBy = take_anyif(tostring(attr.["new"]), tostring(attr.name) == "deployedBy"),
        UndeployedBy = take_anyif(tostring(attr.["new"]), tostring(attr.name) == "undeployedBy"),
        Creator = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "creator"),
        TenantName = take_anyif(tostring(attr.["new"]), tostring(attr.name) == "tenantName"),
        RuntimeLocationId = take_anyif(tostring(coalesce(attr.["new"], attr.["old"])), tostring(attr.name) == "runtimeLocationId")
)

Stage 7: extend (4 consecutive steps)

| extend Actor = case(
    isnotempty(DeployedBy), DeployedBy,
    isnotempty(UndeployedBy), UndeployedBy,
    isnotempty(Creator), Creator,
    ""
)
| extend ActionCategory = iff(objectType == "Deployment", "Deploy", "Undeploy"),
         normalizedAction = iff(objectType == "Deployment", "deployed", "undeployed")
| extend MessageText = strcat("Integration artifact '", SymbolicName, "' (version ", ArtifactVersion, ") was ", normalizedAction, " in tenant ", TenantName)
| extend AccountName = iff(Actor has "@", tostring(split(Actor, "@")[0]), ""),
         UPNSuffix   = iff(Actor has "@", tostring(split(Actor, "@")[1]), "")
Actor =
ifisnotempty(DeployedBy)DeployedBy
elifisnotempty(UndeployedBy)UndeployedBy
elifisnotempty(Creator)Creator
else""

Stage 8: project

| project
    UpdatedOn,
    Actor,
    AccountName,
    UPNSuffix,
    MessageText,
    ArtifactName = SymbolicName,
    ArtifactId,
    ArtifactVersion,
    ActionCategory,
    ObjectType = objectType,
    TenantName,
    RuntimeLocationId,
    Tenant,
    CloudApp = "SAP Cloud Integration"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Categoryeq
  • audit.configuration
field:"Category" kind:eq value:"audit.configuration"
objectTypein
  • Deployment
  • Undeployment
field:"objectType" kind:in

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNameproject
ActionCategoryproject
Actorproject
ArtifactIdproject
ArtifactNameproject
ArtifactVersionproject
CloudAppproject
MessageTextproject
ObjectTypeproject
RuntimeLocationIdproject
Tenantproject
TenantNameproject
UPNSuffixproject
UpdatedOnproject

Tomcat - Multiple empty requests from same IP

#
Status
available
Severity
medium
Time window
5m
Group by
SrcIpAddr
Source
github.com/Azure/Azure-Sentinel

'Detects multiple empty requests from same IP'

MITRE ATT&CK Coverage

Rule Body

id: 7c9a1026-4872-11ec-81d3-0242ac130003
name: Tomcat - Multiple empty requests from same IP
description: |
  'Detects multiple empty requests from same IP'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - Tomcat_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Impact
relevantTechniques:
  - T1190
  - T1133
  - T1499
query: |
  let threshold = 50;
  TomcatEvent
  | where HttpResponseBodyBytes == 0
  | summarize MultipleClientErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where MultipleClientErrors > threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 50;

Stage 1: source

TomcatEvent

Stage 2: where

| where HttpResponseBodyBytes == 0

Stage 3: summarize

| summarize MultipleClientErrors = count() by SrcIpAddr, bin(TimeGenerated, 5m)

Stage 4: where

| where MultipleClientErrors > threshold

Stage 5: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
MultipleClientErrorssummarize
SrcIpAddrsummarize
IPCustomEntityextend

Ubiquiti - SSH from external source

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

'Detects remote to local (R2L) SSH connection to internal host.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 0998a19d-8451-4cdd-8493-fc342816a197
name: Ubiquiti - SSH from external source
description: |
  'Detects remote to local (R2L) SSH connection to internal host.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    dataTypes:
      - Ubiquiti_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1133
query: |
  UbiquitiAuditEvent
  | where EventCategory =~ 'firewall'
  | where ipv4_is_private(SrcIpAddr) == 'False'
  | where ipv4_is_private(DstIpAddr)
  | where DstPortNumber == '22'
  | extend IPCustomEntity = DstIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

UbiquitiAuditEvent

Stage 2: where

| where EventCategory =~ 'firewall'

Stage 3: where

| where ipv4_is_private(SrcIpAddr) == 'False'

Stage 4: where

| where ipv4_is_private(DstIpAddr)

Stage 5: where

| where DstPortNumber == '22'

Stage 6: extend

| extend IPCustomEntity = DstIpAddr

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
DstIpAddrcidr_match
  • 10.0.0.0/8
  • 127.0.0.0/8
  • 169.254.0.0/16
  • 172.16.0.0/12
  • 192.168.0.0/16
field:"dest_ip" kind:cidr_match
DstPortNumbereq
  • 22
field:"DstPortNumber" kind:eq value:"22"
EventCategoryeq
  • firewall
field:"EventCategory" kind:eq value:"firewall"

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
SrcIpAddrcidr_match10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.0.0/16, 127.0.0.0/8excludes:SrcIpAddr

Output Fields

These fields are emitted when the rule matches.

FieldSource
IPCustomEntityextend

Vaikora - Behavioral anomaly detected

#

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
medium
Time window
1h
Group by
AgentId, Severity
Source
github.com/Azure/Azure-Sentinel

Identifies AI agent behavioral anomalies flagged by Vaikora with an anomaly score of 0.7 or above, indicating significant deviation from the agent's established behavioral baseline.

MITRE ATT&CK Coverage

Rule Body

id: e61258ec-1a7f-454c-95b5-458a6edb1ea4
name: Vaikora - Behavioral anomaly detected
description: |
  Identifies AI agent behavioral anomalies flagged by Vaikora with an anomaly score of 0.7 or above, indicating significant deviation from the agent's established behavioral baseline.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: VaikoraSentinel
    dataTypes:
      - Vaikora_AgentSignals_CL
queryFrequency: 30m
queryPeriod: 1h
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
  - DefenseEvasion
  - Execution
relevantTechniques:
  - T1059
  - T1027
query: |
  Vaikora_AgentSignals_CL
  | where TimeGenerated > ago(1h)
  | where is_anomaly_b == true
  | where anomaly_score_d >= 0.7
  | summarize
      AnomalyCount = count(),
      MaxAnomalyScore = max(anomaly_score_d),
      AvgAnomalyScore = avg(anomaly_score_d),
      AnomalyReasons = make_set(anomaly_reason_s),
      ActionTypes = make_set(action_type_s)
    by AgentId = agent_id_s, Severity = severity_s
  | extend
      ReasonList = strcat_array(AnomalyReasons, "; "),
      ActionList = strcat_array(ActionTypes, ", ")
suppressionDuration: 30m
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: Selected
    groupByEntities:
      - Account
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  MaxAnomalyScore: MaxAnomalyScore
  AvgAnomalyScore: AvgAnomalyScore
  AnomalyCount: AnomalyCount
  AnomalyReasons: ReasonList
  ActionTypes: ActionList
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AgentId
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Vaikora_AgentSignals_CL

Stage 2: where

| where TimeGenerated > ago(1h)

Stage 3: where

| where is_anomaly_b == true

Stage 4: where

| where anomaly_score_d >= 0.7

Stage 5: summarize

| summarize
    AnomalyCount = count(),
    MaxAnomalyScore = max(anomaly_score_d),
    AvgAnomalyScore = avg(anomaly_score_d),
    AnomalyReasons = make_set(anomaly_reason_s),
    ActionTypes = make_set(action_type_s)
  by AgentId = agent_id_s, Severity = severity_s

Stage 6: extend

| extend
    ReasonList = strcat_array(AnomalyReasons, "; "),
    ActionList = strcat_array(ActionTypes, ", ")

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActionTypessummarize
AgentIdsummarize
AnomalyCountsummarize
AnomalyReasonssummarize
AvgAnomalyScoresummarize
MaxAnomalyScoresummarize
Severitysummarize
ActionListextend
ReasonListextend

Veeam ONE Immutability State

#

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
medium
Time window
3h
Source
github.com/Azure/Azure-Sentinel

Detects changes in the immutability state of Veeam Backup & Replication repositories. This might indicate configuration changes that require review.

Rule Body

id: b5785a66-3722-4bdc-9d12-bb40f2cf4824
name: Veeam ONE Immutability State
description: Detects changes in the immutability state of Veeam Backup & Replication
  repositories. This might indicate configuration changes that require review.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: VeeamCustomTablesDataConnector
  dataTypes:
  - VeeamOneTriggeredAlarms_CL
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: VeeamOneTriggeredAlarms_CL | where PredefinedAlarmId == 376
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 == 376

Indicators

These rows show field, operator, and value matches.

Zinc Actor IOCs files - October 2022

#
Status
available
Severity
high
Time window
6h
Source
github.com/Azure/Azure-Sentinel

Identifies a match across filename and commandline IOC's related to an actor tracked by Microsoft as Zinc. Reference: https://www.microsoft.com/security/blog/2022/09/29/zinc-weaponizing-open-source-software/

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Telemetry Coverage

Rule Body

id: 9a7f6651-801b-491c-a548-8b454b356eaa
name: Zinc Actor IOCs files - October 2022
description: | 
  'Identifies a match across filename and commandline IOC's related to an actor tracked by Microsoft as Zinc.
  Reference: https://www.microsoft.com/security/blog/2022/09/29/zinc-weaponizing-open-source-software/'
severity: High 
status: Available
requiredDataConnectors:                
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceNetworkEvents
      - DeviceFileEvents
      - DeviceEvents
      - DeviceProcessEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes: 
      - SecurityEvents          
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - Persistence
relevantTechniques:
  - T1546
tags:
  - Zinc
query:  |
  let iocs = externaldata(DateAdded:string,IoC:string,Type:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/ZincOctober2022IOCs.csv"] with (format="csv", ignoreFirstRecord=True);
  let file_path = (iocs | where Type =~ "filepath" | project IoC);
  let commandline = (iocs | where Type =~ "commandline" | project IoC);
  (union isfuzzy=true 
  (DeviceNetworkEvents
  | where  InitiatingProcessFolderPath has_any (file_path) or InitiatingProcessCommandLine has_any (commandline)
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RemoteIP, RemoteUrl, LocalIP, Type
  | extend timestamp = TimeGenerated, HostEntity = DeviceName
  ),
  (Event
  | where Source =~ "Microsoft-Windows-Sysmon"
  | where EventID == 1
  | extend EvData = parse_xml(EventData)
  | extend EventDetail = EvData.DataItem.EventData.Data
  | extend Image = EventDetail.[4].["#text"],  CommandLine = EventDetail.[10].["#text"]
  | where Image has_any (file_path)  or   CommandLine has_any (commandline)
  | project TimeGenerated, EventDetail, UserName, Computer, Type, Source, CommandLine, Image
  | extend Type = strcat(Type, ": ", Source)
  | extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = UserName, ProcessEntity = tostring(split(Image, '\\', -1)[-1])
  ),  
  (DeviceProcessEvents
  | where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline))  or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, InitiatingProcessSHA256, FolderPath, Type
  | extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName
  ),
  (DeviceFileEvents
  | where  (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath  has_any (commandline)) or ( InitiatingProcessCommandLine has_any (commandline)) or ( InitiatingProcessCommandLine has_any (file_path))
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RequestAccountName, RequestSourceIP, InitiatingProcessSHA256, FolderPath, Type
  | extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = RequestAccountName, ProcessEntity = InitiatingProcessFileName, AlgorithmEntity = "SHA256", FileHashEntity = InitiatingProcessSHA256
  ),
  (DeviceEvents
  | where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline)) or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))
  | project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, FolderPath, Type
  | extend CommandLine = InitiatingProcessCommandLine
  | extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName
  ),
  (SecurityEvent
  | where EventID == 4688
  | where ( CommandLine has_any (file_path)) or ( CommandLine has_any (commandline))  or (NewProcessName has_any (file_path)) or (NewProcessName has_any (commandline)) or (ParentProcessName has_any (file_path)) or (ParentProcessName has_any (commandline))
  | project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type
  | extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = Account, ProcessEntity = NewProcessName
  )
  )
  | extend HostName = tostring(split(HostEntity, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(HostEntity, '.'), 1, -1), '.'))
  | extend Name = tostring(split(AccountEntity, '@', 0)[0]), UPNSuffix = tostring(split(AccountEntity, '@', 1)[0])
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: RemoteIP 
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: ProcessEntity
  - entityType: FileHash
    fieldMappings:
      - identifier: Algorithm
        columnName: AlgorithmEntity
      - identifier: Value
        columnName: FileHashEntity     
version: 1.0.3
kind: Scheduled

Stages and Predicates

Let Binding: iocs

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

Let Binding: file_path

let file_path = (iocs | where Type =~ "filepath" | project IoC);

Let Binding: commandline

let commandline = (iocs | where Type =~ "commandline" | project IoC);

Union isfuzzy=true (6 Sources)

Each leg below queries one source; the rule matches if any leg does. Sources: DeviceNetworkEvents, Event, DeviceProcessEvents, DeviceFileEvents, DeviceEvents, SecurityEvent

Leg 1: DeviceNetworkEvents

DeviceNetworkEvents
| where  InitiatingProcessFolderPath has_any (file_path) or InitiatingProcessCommandLine has_any (commandline)
| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RemoteIP, RemoteUrl, LocalIP, Type
| extend timestamp = TimeGenerated, HostEntity = DeviceName

Leg 2: Event

Event
| where Source =~ "Microsoft-Windows-Sysmon"
| where EventID == 1
| extend EvData = parse_xml(EventData)
| extend EventDetail = EvData.DataItem.EventData.Data
| extend Image = EventDetail.[4].["#text"],  CommandLine = EventDetail.[10].["#text"]
| where Image has_any (file_path)  or   CommandLine has_any (commandline)
| project TimeGenerated, EventDetail, UserName, Computer, Type, Source, CommandLine, Image
| extend Type = strcat(Type, ": ", Source)
| extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = UserName, ProcessEntity = tostring(split(Image, '\\', -1)[-1])

Leg 3: DeviceProcessEvents

DeviceProcessEvents
| where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline))  or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))
| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, InitiatingProcessSHA256, FolderPath, Type
| extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName

Leg 4: DeviceFileEvents

DeviceFileEvents
| where  (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath  has_any (commandline)) or ( InitiatingProcessCommandLine has_any (commandline)) or ( InitiatingProcessCommandLine has_any (file_path))
| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, RequestAccountName, RequestSourceIP, InitiatingProcessSHA256, FolderPath, Type
| extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = RequestAccountName, ProcessEntity = InitiatingProcessFileName, AlgorithmEntity = "SHA256", FileHashEntity = InitiatingProcessSHA256

Leg 5: DeviceEvents

DeviceEvents
| where  ( InitiatingProcessCommandLine has_any (file_path)) or ( InitiatingProcessCommandLine has_any (commandline)) or (InitiatingProcessFolderPath has_any (file_path)) or (InitiatingProcessFolderPath has_any (commandline)) or (FolderPath  has_any (file_path)) or (FolderPath has_any (commandline))
| project TimeGenerated, ActionType, DeviceId, DeviceName, InitiatingProcessAccountDomain, InitiatingProcessAccountName, InitiatingProcessCommandLine, InitiatingProcessFolderPath, InitiatingProcessId, InitiatingProcessParentFileName, InitiatingProcessFileName, FolderPath, Type
| extend CommandLine = InitiatingProcessCommandLine
| extend timestamp = TimeGenerated, HostEntity = DeviceName , AccountEntity = InitiatingProcessAccountName, ProcessEntity = InitiatingProcessFileName

Leg 6: SecurityEvent

SecurityEvent
| where EventID == 4688
| where ( CommandLine has_any (file_path)) or ( CommandLine has_any (commandline))  or (NewProcessName has_any (file_path)) or (NewProcessName has_any (commandline)) or (ParentProcessName has_any (file_path)) or (ParentProcessName has_any (commandline))
| project TimeGenerated, Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type
| extend timestamp = TimeGenerated, HostEntity = Computer , AccountEntity = Account, ProcessEntity = NewProcessName

Applied to the Combined Result

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinematch
  • commandline transforms: term
  • file_path transforms: term
field:"CommandLine" kind:match
EventIDeq
  • 1 corpus 12 (kusto 11, elastic 1)
  • 4688 corpus 23 (kusto 23)
field:"EventID" kind:eq
FolderPathmatch
  • commandline transforms: term
  • commandline transforms: term
  • file_path transforms: term
  • file_path transforms: term
field:"Image" kind:match
Imagematch
  • file_path transforms: term
field:"Image" kind:match value:"file_path"
InitiatingProcessCommandLinematch
  • commandline transforms: term
  • commandline transforms: term
  • file_path transforms: term
  • file_path transforms: term
field:"CommandLine" kind:match
InitiatingProcessFolderPathmatch
  • commandline transforms: term
  • commandline transforms: term
  • file_path transforms: term
  • file_path transforms: term
field:"ParentImage" kind:match
NewProcessNamematch
  • commandline transforms: term
  • file_path transforms: term
field:"Image" kind:match
ParentProcessNamematch
  • commandline transforms: term
  • file_path transforms: term
field:"ParentImage" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountproject
Computerproject
NewProcessIdproject
NewProcessNameproject
ParentProcessNameproject
TimeGeneratedproject
Typeproject
AccountEntityextend
HostEntityextend
ProcessEntityextend
timestampextend
DnsDomainextend
HostNameextend
Nameextend
UPNSuffixextend

Back to all Kusto rules