Detection rules › Kusto

Kusto rules: 0y

AIShield - Image classification AI Model extraction high suspicious vulnerability detection

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

'This alert creates an incident when Image classification AI Model extraction high suspicious, high severity vulnerability detected from the AIShield.'

Rule Body

id: b4cc5396-2a34-45f5-a726-860e476edf15
name: AIShield - Image classification AI Model extraction high suspicious vulnerability detection
description: |
  'This alert creates an incident when Image classification AI Model extraction high suspicious, high severity vulnerability detected from the AIShield.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: BoschAIShield
    dataTypes:
      - AIShield
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
  AIShield
  | where Message has 'Image Classification AI Model Extraction Attack Identified'
  | where Severity =~ 'High'
  | where SuspiciousLevel =~ 'high suspicious attack'
  | extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceName
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertDisplayNameFormat: AIShield - Image Classification AI Model extraction high suspicious vulnerability detected.
  alertDescriptionFormat: |
    This query detects Image Classification AI Model extraction high suspicious, high severity alert from AIShield generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
  alertTacticsColumnName: null
  alertSeverityColumnName: Severity
version: 1.0.4
kind: Scheduled

Stages and Predicates

Stage 1: source

AIShield

Stage 2: where

| where Message has 'Image Classification AI Model Extraction Attack Identified'

Stage 3: where

| where Severity =~ 'High'

Stage 4: where

| where SuspiciousLevel =~ 'high suspicious attack'

Stage 5: extend

| extend NTDomain = tostring(split(Computer, '\\')[0]), HostName = tostring(split(Computer, '.')[0])

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostNameextend
NTDomainextend

AWSCloudTrail - Successful brute force attack on S3 Bucket

#
Status
available
Severity
high
Time window
1h
Group by
AccountName, AccountUPNSuffix, RecipientAccountId, SourceIpAddress, UserIdentityAccountId, bucketName
Source
github.com/Azure/Azure-Sentinel

Detects repeated failed GetObject attempts against an S3 bucket followed by a successful access from the same source, which can indicate brute-force-style object discovery or access attempts.

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 31b9e94b-0df6-4a3d-a297-3457b53c5d86
name: AWSCloudTrail - Successful brute force attack on S3 Bucket
description: |
  Detects repeated failed GetObject attempts against an S3 bucket followed by a successful access from the
  same source, which can indicate brute-force-style object discovery or access attempts.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
    let timeframe = 1h;
    let failed_attempts = AWSCloudTrail
    | where TimeGenerated >= ago(timeframe)
    | where EventName == "GetObject" and isnotempty(ErrorMessage) and isnotempty(ErrorCode)
    | where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
    | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
    | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
    | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
    | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
      AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
    | extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
    | summarize time_min_failed=arg_min(TimeGenerated, *), failed_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
    | where failed_keys > 20;
    let success_attempts = AWSCloudTrail
    | where TimeGenerated >= ago(timeframe)
    | where EventName == "GetObject" and isempty(ErrorMessage) and isempty(ErrorCode)
    | where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
    | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
    | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
    | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
    | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
      AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
    | extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
    | summarize time_min_success=arg_min(TimeGenerated, *), success_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
    | where success_keys >= 1;
    failed_attempts
    | join kind=inner success_attempts on SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, bucketName
    | where time_min_success > time_min_failed
    | project-away keyName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  bucketName: bucketName
  failed_keys: failed_keys
  success_keys: success_keys
  RecipientAccountId: RecipientAccountId
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS S3 brute force pattern detected from {{SourceIpAddress}}'
  alertDescriptionFormat: 'Detected repeated failed and then successful S3 GetObject access for bucket {{bucketName}} in account {{RecipientAccountId}}.'
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 1h;

let failed_attempts is inlined into the numbered stages below.

Let Binding: success_attempts used in Stage 8

let success_attempts = AWSCloudTrail
| where TimeGenerated >= ago(timeframe)
| where EventName == "GetObject" and isempty(ErrorMessage) and isempty(ErrorCode)
| where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
  AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)
| summarize time_min_success=arg_min(TimeGenerated, *), success_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName
| where success_keys >= 1;

Stages 1 to 7 define let failed_attempts as the rule's main pipeline source. Stages 8 to 10 use it.

Stage 1: source

AWSCloudTrail

Stage 2: where

| where TimeGenerated >= ago(timeframe)

Stage 3: where

| where EventName == "GetObject" and isnotempty(ErrorMessage) and isnotempty(ErrorCode)

Stage 4: where

| where UserIdentityAccountId == "ANONYMOUS_PRINCIPAL" or UserIdentityAccessKeyId <> RecipientAccountId

Stage 5: extend (5 consecutive steps)

| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
  AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| extend bucketName = tostring(parse_json(RequestParameters).bucketName), keyName = tostring(parse_json(RequestParameters).key)

Stage 6: summarize

| summarize time_min_failed=arg_min(TimeGenerated, *), failed_keys = dcount(keyName) by RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, SourceIpAddress, bucketName

Stage 7: where

| where failed_keys > 20

Stage 8: join

failed_attempts
| join kind=inner success_attempts on SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityAccountId, bucketName

Stage 9: where

where time_min_success > time_min_failed

Stage 10: project-away

project-away keyName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNamesummarize
AccountUPNSuffixsummarize
RecipientAccountIdsummarize
SourceIpAddresssummarize
UserIdentityAccountIdsummarize
bucketNamesummarize
failed_keyssummarize
time_min_failedsummarize

AWSCloudTrail - Suspicious overly permissive KMS key policy created

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

Detects creation or update of KMS key policies that grant broad encryption permissions to all principals. Overly permissive key policies can be abused for malicious encryption operations and indicate potential account compromise or risky misconfiguration.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 60dfc193-0f73-4279-b43c-110ade02b201
name: AWSCloudTrail - Suspicious overly permissive KMS key policy created
description: |
  Detects creation or update of KMS key policies that grant broad encryption permissions to all principals.
  Overly permissive key policies can be abused for malicious encryption operations and indicate potential account
  compromise or risky misconfiguration.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1486
query: |
    let kmsActions = dynamic(["kms:Encrypt", "kms:*"]); //Add other overly permissive APIs to this list.
    AWSCloudTrail
    | where EventName in ("CreateKey","PutKeyPolicy") and isempty(ErrorCode) and isempty(ErrorMessage)
    | extend Statement = parse_json(tostring((parse_json(RequestParameters).policy))).Statement
    | mvexpand Statement
    | extend Action = tostring(parse_json(Statement).Action), Effect = tostring(parse_json(Statement).Effect), Principal = iff(isnotempty(tostring(parse_json(Statement).Principal.AWS)),tostring(parse_json(Statement).Principal.AWS), tostring(parse_json(Statement).Principal))
    | where Effect =~ "Allow" and Action has_any (kmsActions) and Principal == "*" 
    | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
    | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
    | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
    | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
      AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  EventName: EventName
  EventSource: EventSource
  AWSRegion: AWSRegion
  UserIdentityArn: UserIdentityArn
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS overly permissive KMS key policy change by {{AccountName}}'
  alertDescriptionFormat: 'Detected {{EventName}} from {{SourceIpAddress}} creating a broadly permissive KMS policy in account {{RecipientAccountId}}.'
version: 1.0.5
kind: Scheduled

Stages and Predicates

Parameters

let kmsActions = dynamic(["kms:Encrypt", "kms:*"]);

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName in ("CreateKey","PutKeyPolicy") and isempty(ErrorCode) and isempty(ErrorMessage)

Stage 3: extend

| extend Statement = parse_json(tostring((parse_json(RequestParameters).policy))).Statement

Stage 4: mv-expand

| mvexpand Statement

Stage 5: extend

| extend Action = tostring(parse_json(Statement).Action), Effect = tostring(parse_json(Statement).Effect), Principal = iff(isnotempty(tostring(parse_json(Statement).Principal.AWS)),tostring(parse_json(Statement).Principal.AWS), tostring(parse_json(Statement).Principal))
Principal =
ifisnotempty(tostring(Statement.Principal.AWS))tostring(parse_json(Statement).Principal.AWS)
elsetostring(parse_json(Statement).Principal)

Stage 6: where

| where Effect =~ "Allow" and Action has_any (kmsActions) and Principal == "*"

Stage 7: extend (4 consecutive steps)

| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
  AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actionmatch
  • kms:* transforms: term
  • kms:Encrypt transforms: term
field:"Action" kind:match
Effecteq
  • Allow
field:"Effect" kind:eq value:"Allow"
ErrorCodeis_null
  • (no value, null check)
field:"aws::errorCode" kind:is_null
ErrorMessageis_null
  • (no value, null check)
field:"aws::errorMessage" kind:is_null
EventNamein
  • CreateKey
  • PutKeyPolicy
field:"aws::eventName" kind:in
Principaleq
  • *
field:"Principal" kind:eq value:"*"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Statementextend
Actionextend
Effectextend
Principalextend
UserIdentityArnextend
UserNameextend
AccountNameextend
AccountUPNSuffixextend

BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups

#

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
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups

Rule Body

id: 000d4f86-fc35-4451-9b2b-2a660a95f715
name: BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups
description: BloodHound Attack Path Finding - Large Default Groups in SQL Admins Groups
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsSQLAdmin"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "LargeDefaultGroupsSQLAdmin"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsSQLAdmin
field:"Finding" kind:eq value:"LargeDefaultGroupsSQLAdmin"

BloodHound Attack Path Finding - Large Default Groups With Read LAPS Password Privileges

#

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
Source
github.com/Azure/Azure-Sentinel

BloodHound Attack Path Finding - Large Default Groups With Read LAPS Password Privileges

Rule Body

id: d6450ee2-2161-4d3c-96c9-674c3c44a791
name: BloodHound Attack Path Finding - Large Default Groups With Read LAPS Password
  Privileges
description: BloodHound Attack Path Finding - Large Default Groups With Read LAPS
  Password Privileges
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsReadLAPS"
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: domain_name
customDetails:
  Description: ShortDescription
  Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

BHEAttackPathsData_CL

Stage 2: where

| where Finding == "LargeDefaultGroupsReadLAPS"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Findingeq
  • LargeDefaultGroupsReadLAPS
field:"Finding" kind:eq value:"LargeDefaultGroupsReadLAPS"

Box - Forbidden file type downloaded

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

'Detects when new user downloads forbidden file types.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 8889e69c-2161-412a-94a6-76c1b2d9daa7
name: Box - Forbidden file type downloaded
description: |
  'Detects when new user downloads forbidden file types.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BoxDataConnector
    dataTypes:
      - BoxEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1189
query: |
  let forbidden_files = dynamic(['ps1', 'bat', 'scr', 'sh']);
  BoxEvents
  | where EventType =~ 'DOWNLOAD'
  | extend file_type = extract(@'\.(\w+)$', 1, SourceItemName)
  | where file_type in (forbidden_files)
  | extend AccountCustomEntity = SrcUserName
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let forbidden_files = dynamic(['ps1', 'bat', 'scr', 'sh']);

Stage 1: source

BoxEvents

Stage 2: where

| where EventType =~ 'DOWNLOAD'

Stage 3: extend

| extend file_type = extract(@'\.(\w+)$', 1, SourceItemName)

Stage 4: where

| where file_type in (forbidden_files)

Stage 5: extend

| extend AccountCustomEntity = SrcUserName

Stage 6: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
file_typeextend
AccountCustomEntityextend
IPCustomEntityextend

CiscoISE - Command executed with the highest privileges from new IP

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

'Detects command execution with PrivilegeLevel - 15 from new source.'

MITRE ATT&CK Coverage

Rule Body

id: 1fa0da3e-ec99-484f-aadb-93f59764e158
name: CiscoISE -  Command executed with the highest privileges from new IP
description: |
  'Detects command execution with PrivilegeLevel - 15 from new source.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Persistence
  - PrivilegeEscalation
  - DefenseEvasion
  - Execution
relevantTechniques:
  - T1133
  - T1548
  - T1059
query: |
  let lbperiod = 14d;
  let lbtime = 15m;
  let knownAdminIpList =
  CiscoISEEvent
  | where TimeGenerated between (ago(lbperiod) .. ago(lbtime))
  | where PrivilegeLevel == '15'
  | summarize makelist(SrcIpAddr)
  ;
  CiscoISEEvent
  | where TimeGenerated > ago(lbtime)
  | where PrivilegeLevel == '15'
  | where SrcIpAddr !in (knownAdminIpList)
  | project TimeGenerated, SrcIpAddr, DstIpAddr, DstUserName, CmdSet
  | extend AccountCustomEntity = DstUserName
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.4
kind: Scheduled

Stages and Predicates

Parameters

let lbperiod = 14d;
let lbtime = 15m;

Let Binding: knownAdminIpList used in Stage 4

let knownAdminIpList = CiscoISEEvent
| where TimeGenerated between (ago(lbperiod) .. ago(lbtime))
| where PrivilegeLevel == '15'
| summarize makelist(SrcIpAddr);

Stage 1: source

CiscoISEEvent

Stage 2: where

| where TimeGenerated > ago(lbtime)

Stage 3: where

| where PrivilegeLevel == '15'

Stage 4: where

| where SrcIpAddr !in (knownAdminIpList)

Stage 5: project

| project TimeGenerated, SrcIpAddr, DstIpAddr, DstUserName, CmdSet

Stage 6: extend

| extend AccountCustomEntity = DstUserName

Stage 7: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
SrcIpAddreqknownAdminIpListexcludes:SrcIpAddr field:"SrcIpAddr" value:"knownAdminIpList"

Output Fields

These fields are emitted when the rule matches.

FieldSource
CmdSetproject
DstIpAddrproject
DstUserNameproject
SrcIpAddrproject
TimeGeneratedproject
AccountCustomEntityextend
IPCustomEntityextend

Claroty - Critical baseline deviation

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

Detects Claroty events where EventOriginalType or EventType indicates a Baseline Deviation and EventSeverity is 5, which represents a critical deviation from baseline that may indicate disruptive or unauthorized activity.

MITRE ATT&CK Coverage

Rule Body

id: 9a8b4321-e2be-449b-8227-a78227441b2a
name: Claroty - Critical baseline deviation
description: Detects Claroty events where EventOriginalType or EventType indicates a Baseline Deviation and EventSeverity
  is 5, which represents a critical deviation from baseline that may indicate disruptive or unauthorized activity.
severity: High
status: Available
requiredDataConnectors:
- connectorId: CefAma
  dataTypes:
  - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1529
- T1565.001
query: |-
  ClarotyEvent
  | where EventOriginalType has 'Baseline Deviation' or EventType has 'Baseline Deviation'
  | where EventSeverity == '5'
  | project TimeGenerated, DstIpAddr
  | extend IPCustomEntity = DstIpAddr
entityMappings:
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IPCustomEntity
alertDetailsOverride:
  alertDisplayNameFormat: Claroty critical baseline deviation on {{IPCustomEntity}}
  alertDescriptionFormat: Claroty reported a critical baseline deviation for {{IPCustomEntity}}.
version: 1.0.4
kind: Scheduled

Stages and Predicates

Stage 1: source

ClarotyEvent

Stage 2: where

| where EventOriginalType has 'Baseline Deviation' or EventType has 'Baseline Deviation'

Stage 3: where

| where EventSeverity == '5'

Stage 4: project

| project TimeGenerated, DstIpAddr

Stage 5: extend

| extend IPCustomEntity = DstIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstIpAddrproject
TimeGeneratedproject
IPCustomEntityextend

CybleVision Alerts Mobile Apps

#

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
low
Time window
30m
Source
github.com/Azure/Azure-Sentinel

'Detects suspicious, unauthorized or impersonating mobile applications from 3rd-party marketplaces using CybleVision data. Extracts metadata, screenshots, developer, package name, and detailed app attributes.'

MITRE ATT&CK Coverage

TacticTechniques
Reconnaissance
Resource Development
Initial Access

Rule Body

id: 6d55fefc-b334-4b79-b11c-667746b5bdde
name: CybleVision Alerts Mobile Apps
description: |
  'Detects suspicious, unauthorized or impersonating mobile applications from 3rd-party marketplaces using CybleVision data. Extracts metadata, screenshots, developer, package name, and detailed app attributes.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: CybleVisionAlerts
  dataTypes:
  - CybleVisionAlerts_CL
enabled: true
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: PT5H
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics:
- Reconnaissance
- ResourceDevelopment
- InitialAccess
relevantTechniques:
- T1595
- T1608
- T1195
subTechniques: []
query: |
  Alerts_mobile_apps 
  | where Service == "mobile_apps" 
  | extend MappedSeverity = Severity
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: CybleVision Suspicious Mobile App {{MA_Title}}
  alertDescriptionFormat: |
    A potentially suspicious mobile application was detected. Developer {{MA_Developer}} Market {{MA_MarketSource}} Deep Link {{MA_DeepLink}}\n
  alertDynamicProperties: []
customDetails:
  MappedSeverity: Severity
  Status: Status
  AlertID: AlertID
  Service: Service
  AppName: AppName
  Developer: MA_Developer
  MarketSource: MA_MarketSource
  MarketURL: MA_MarketURL
  PackageName: MA_PackageName
  Description: MA_Description
  Downloads: MA_Downloads
  MarketStatus: MA_MarketStatus
  MarketUpdated: MA_MarketUpdated
  CreatedOn: MA_Created
  AppEmail: MA_Email
  IconURL: MA_MarketURL
  Website: MA_Website
  Version: MA_Version
  Screenshots: MA_Screenshots
  Tags: MA_Tags
entityMappings:
- entityType: URL
  fieldMappings:
  - identifier: Url
    columnName: MA_DeepLink
- entityType: DNS
  fieldMappings:
  - identifier: DomainName
    columnName: Domain
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

Alerts_mobile_apps

Stage 2: where

| where Service == "mobile_apps"

Stage 3: extend

| extend MappedSeverity = Severity

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Serviceeq
  • mobile_apps
field:"Service" kind:eq value:"mobile_apps"

Output Fields

These fields are emitted when the rule matches.

FieldSource
MappedSeverityextend

CYFIRMA - Data Breach and Web Monitoring - Ransomware Exposure Detected Rule

#

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
5m
Source
github.com/Azure/Azure-Sentinel

"This analytics rule detects high-severity ransomware threats targeting the organization, as reported by CYFIRMA's Dark Web and Data Breach Intelligence feeds. The alert is generated when threat actors post, claim, or associate ransomware activity with corporate domains, brands, or subsidiaries, indicating a potential data breach, extortion attempt, or unauthorized access."

MITRE ATT&CK Coverage

Rule Body

id: d5f9a6fe-7fd2-488c-8690-0ca24fba43dc
name: CYFIRMA - Data Breach and Web Monitoring - Ransomware Exposure Detected Rule
description: |
  "This analytics rule detects high-severity ransomware threats targeting the organization, as reported by CYFIRMA's Dark Web and Data Breach Intelligence feeds. 
  The alert is generated when threat actors post, claim, or associate ransomware activity with corporate domains, brands, or subsidiaries, indicating a potential data breach, extortion attempt, or unauthorized access."
version: 1.0.1
kind: Scheduled
severity: Medium
requiredDataConnectors:
  - connectorId: CyfirmaDigitalRiskAlertsConnector
    dataTypes:
      - CyfirmaDBWMRansomwareAlerts_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - InitialAccess
  - Exfiltration
relevantTechniques:
  - T1566.001
  - T1566.002
  - T1566.003
query: |
  // Medium severity - Data Breach and Web Monitoring - Ransomware Exposure Detected
  let timeFrame = 5m;
  CyfirmaDBWMRansomwareAlerts_CL
  | where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())
  | extend
      Description=description,
      FirstSeen=first_seen,
      LastSeen=last_seen,
      RiskScore=risk_score,
      AlertUID=alert_uid,
      UID=uid,
      AssetType=asset_type,
      AssetValue=signature,
      Source=source,
      Impact=impact,
      Recommendation='',
      ProviderName='CYFIRMA',
      ProductName='DeCYFIR/DeTCT',
      AlertTitle=Alert_title,
      ThreatActors= threat_actors
  | project
      TimeGenerated,
      Description,
      RiskScore,
      FirstSeen,
      LastSeen,
      AlertUID,
      UID,
      AssetType,
      AssetValue,
      Source,
      Impact,
      Recommendation,
      ProductName,
      ProviderName,
      AlertTitle,
      ThreatActors
customDetails:
  AssetType: AssetType
  AssetValue: AssetValue
  ThreatActors: ThreatActors
  RiskScore: RiskScore
  Source: Source
  Impact: Impact
  Description: Description
  Recommendation: Recommendation
  FirstSeen: FirstSeen
  LastSeen: LastSeen
  AlertUID: AlertUID
  UID: UID
  TimeGenerated: TimeGenerated
alertDetailsOverride:
  alertDisplayNameFormat: "CYFIRMA - Medium Severity Alert: Ransomware Exposure Detected - {{AlertTitle}} "
  alertDescriptionFormat: "{{Description}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaDBWMRansomwareAlerts_CL

Stage 2: where

| where severity == 'High' and TimeGenerated between (ago(timeFrame) .. now())

Stage 3: extend

| extend
    Description=description,
    FirstSeen=first_seen,
    LastSeen=last_seen,
    RiskScore=risk_score,
    AlertUID=alert_uid,
    UID=uid,
    AssetType=asset_type,
    AssetValue=signature,
    Source=source,
    Impact=impact,
    Recommendation='',
    ProviderName='CYFIRMA',
    ProductName='DeCYFIR/DeTCT',
    AlertTitle=Alert_title,
    ThreatActors= threat_actors

Stage 4: project

| project
    TimeGenerated,
    Description,
    RiskScore,
    FirstSeen,
    LastSeen,
    AlertUID,
    UID,
    AssetType,
    AssetValue,
    Source,
    Impact,
    Recommendation,
    ProductName,
    ProviderName,
    AlertTitle,
    ThreatActors

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertTitleproject
AlertUIDproject
AssetTypeproject
AssetValueproject
Descriptionproject
FirstSeenproject
Impactproject
LastSeenproject
ProductNameproject
ProviderNameproject
Recommendationproject
RiskScoreproject
Sourceproject
ThreatActorsproject
TimeGeneratedproject
UIDproject

Digital Guardian - Exfiltration to private email

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

'Detects exfiltration to private email.'

MITRE ATT&CK Coverage

Rule Body

id: edead9b5-243a-466b-ae78-2dae32ab1117
name: Digital Guardian - Exfiltration to private email
description: |
  'Detects exfiltration to private email.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
relevantTechniques:
  - T1048
query: |
  DigitalGuardianDLPEvent
  | where NetworkApplicationProtocol =~ 'SMTP'
  | where isnotempty(inspected_document)
  | extend s_user = substring(SrcUserName, 0, indexof(SrcUserName, '@'))
  | extend d_user = substring(DstUserName, 0, indexof(DstUserName, '@'))
  | extend s_domain = extract(@'@(.*)', 1, SrcUserName)
  | extend d_domain = extract(@'@(.*)', 1, DstUserName)
  | where s_domain != d_domain
  | where s_user == d_user
  | extend AccountCustomEntity = SrcUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

DigitalGuardianDLPEvent

Stage 2: where

| where NetworkApplicationProtocol =~ 'SMTP'

Stage 3: where

| where isnotempty(inspected_document)

Stage 4: extend (4 consecutive steps)

| extend s_user = substring(SrcUserName, 0, indexof(SrcUserName, '@'))
| extend d_user = substring(DstUserName, 0, indexof(DstUserName, '@'))
| extend s_domain = extract(@'@(.*)', 1, SrcUserName)
| extend d_domain = extract(@'@(.*)', 1, DstUserName)

Stage 5: where

| where s_domain != d_domain

Stage 6: where

| where s_user == d_user

Stage 7: extend

| extend AccountCustomEntity = SrcUserName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
s_userextend
d_userextend
s_domainextend
d_domainextend
AccountCustomEntityextend

Expired access credentials being used in Azure

#
Status
available
Severity
medium
Time window
1d
Group by
$left.FailedIp, $right.IPAddress, Day, UserPrincipalName
Source
github.com/Azure/Azure-Sentinel

This query searches for logins with an expired access credential (for example an expired cookie). It then matches the IP address from which the expired credential access occurred with the IP addresses of successful logins. If there are logins with expired credentials, but no successful logins from an IP, this might indicate an attacker has copied the authentication cookie and is re-using it on another machine.

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 433c3b0a-7278-4d74-b137-963ac6f9a7e7
name: Expired access credentials being used in Azure
description: |
  This query searches for logins with an expired access credential (for example an expired cookie). It then matches the IP address from which the expired credential access occurred with the IP addresses of successful logins.
  If there are logins with expired credentials, but no successful logins from an IP, this might indicate an attacker has copied the authentication cookie and is re-using it on another machine.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1528
query: |
  // Timeframe to search for failed logins.
  let timeframe=1d;
  // Timeframe to look back for successful logins from the same user by IP.
  let lookback=7d;
  let SuspiciousSignings=(
      SigninLogs
      | where TimeGenerated >= ago(timeframe)
      | where ResourceDisplayName contains "Windows Azure Active Directory"
      // 50132 = SsoArtifactInvalidOrExpired - The session is not valid due to password expiration or recent password change.
      // 50173 = FreshTokenNeeded - The provided grant has expired due to it being revoked, and a fresh auth token is needed. 
      // 70008 = ExpiredOrRevokedGrant - The refresh token has expired due to inactivity. The token was issued on XXX and was inactive for a certain period of time.
      // 81010 = DesktopSsoAuthTokenInvalid - Seamless SSO failed because the user's Kerberos ticket has expired or is invalid.
      | where ResultType in (50173, 50132, 70008, 81010)
      | summarize FailedCountPerDay=count(),FailedUserAgents=make_set(UserAgent), FailedCountries=make_set(LocationDetails.countryOrRegion),FailedIps=make_set(IPAddress) by UserPrincipalName, Day=bin(TimeGenerated, 1d)
      | where FailedCountPerDay >= 1
  );
  let SuccessLogins=(
      SigninLogs
      | where TimeGenerated >= ago(lookback)
      | where UserPrincipalName in ((SuspiciousSignings | project UserPrincipalName))
      | where ResultType == 0
      | summarize count() by UserPrincipalName, IPAddress
  );
  SuspiciousSignings
  | mv-expand FailedIp=FailedIps
  | extend FailedIp=tostring(FailedIp)
  | join kind=leftanti SuccessLogins on $left.FailedIp==$right.IPAddress, UserPrincipalName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: FailedIp
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 1d;
let lookback = 7d;

let SuspiciousSignings is inlined into the numbered stages below.

Let Binding: SuccessLogins used in Stage 9

let SuccessLogins = (
    SigninLogs
    | where TimeGenerated >= ago(lookback)
    | where UserPrincipalName in ((SuspiciousSignings | project UserPrincipalName))
    | where ResultType == 0
    | summarize count() by UserPrincipalName, IPAddress
);

Stages 1 to 6 define let SuspiciousSignings as the rule's main pipeline source. Stages 7 to 9 use it.

Stage 1: source

SigninLogs

Stage 2: where

| where TimeGenerated >= ago(timeframe)

Stage 3: where

| where ResourceDisplayName contains "Windows Azure Active Directory"

Stage 4: where

| where ResultType in (50173, 50132, 70008, 81010)

Stage 5: summarize

| summarize FailedCountPerDay=count(),FailedUserAgents=make_set(UserAgent), FailedCountries=make_set(LocationDetails.countryOrRegion),FailedIps=make_set(IPAddress) by UserPrincipalName, Day=bin(TimeGenerated, 1d)

Stage 6: where

| where FailedCountPerDay >= 1

Stage 7: mv-expand

SuspiciousSignings
| mv-expand FailedIp=FailedIps

Stage 8: extend

| extend FailedIp=tostring(FailedIp)

Stage 9: join (negated)

join kind=leftanti (SuccessLogins) on $left.FailedIp, $right.IPAddress, UserPrincipalName

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ResultTypeeq0excludes:ResultType field:"ResultType" value:"0"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Daysummarize
FailedCountPerDaysummarize
FailedCountriessummarize
FailedIpssummarize
FailedUserAgentssummarize
UserPrincipalNamesummarize
FailedIpextend

Front Door Premium WAF - SQLi Detection

#
Status
available
Severity
high
Time window
6h
Group by
action_s, clientIP_s, host_s, trackingReference_s
Source
github.com/Azure/Azure-Sentinel

'Identifies a match for a SQL Injection attack in the Front Door Premium WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/Top10/A03_2021-Injection/'

MITRE ATT&CK Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 16da3a2a-af29-48a0-8606-d467c180fe18
name: Front Door Premium WAF - SQLi Detection  
description: | 
  'Identifies a match for a SQL Injection attack in the Front Door Premium WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements.
  References: https://owasp.org/Top10/A03_2021-Injection/'
severity: High
status: Available
requiredDataConnectors: 
  - connectorId: WAF
    dataTypes: 
      - AzureDiagnostics
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - DefenseEvasion
  - Execution
  - InitialAccess
  - PrivilegeEscalation 
relevantTechniques:
  - T1211
  - T1059
  - T1190
  - T0890
tags:
  - SQL Injection
query:  | 
  let Threshold = 1;
  AzureDiagnostics
  | where Category =~ "FrontDoorWebApplicationFirewallLog"
  | where action_s =~ "AnomalyScoring"
  | where details_msg_s has "SQL Injection"
  | parse details_data_s with MessageText "Matched Data:" MatchedData "AND " * "table_name FROM " TableName " " *
  | project trackingReference_s, host_s, requestUri_s, TimeGenerated, clientIP_s, details_matches_s, details_msg_s, details_data_s, TableName, MatchedData
  | join kind = inner(
  AzureDiagnostics
  | where Category =~ "FrontDoorWebApplicationFirewallLog"
  | where action_s =~ "Block") on trackingReference_s
  | summarize URI_s = make_set(requestUri_s,100), Table = make_set(TableName,100), StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TrackingReference = make_set(trackingReference_s,100), Matched_Data = make_set(MatchedData,100), Detail_Data = make_set(details_data_s,100), Detail_Message = make_set(details_msg_s,100), Total_TrackingReference = dcount(trackingReference_s) by clientIP_s, host_s, action_s
  | where Total_TrackingReference >= Threshold
# The Threshold value above can be changed as per your infrastructure's requirement
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URI_s
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: clientIP_s 
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let Threshold = 1;

Stage 1: source

AzureDiagnostics

Stage 2: where

| where Category =~ "FrontDoorWebApplicationFirewallLog"

Stage 3: where

| where action_s =~ "AnomalyScoring"

Stage 4: where

| where details_msg_s has "SQL Injection"

Stage 5: parse

| parse details_data_s with MessageText "Matched Data:" MatchedData "AND " * "table_name FROM " TableName " " *

Stage 6: project

| project trackingReference_s, host_s, requestUri_s, TimeGenerated, clientIP_s, details_matches_s, details_msg_s, details_data_s, TableName, MatchedData

Stage 7: join

| join kind = inner(
AzureDiagnostics
| where Category =~ "FrontDoorWebApplicationFirewallLog"
| where action_s =~ "Block") on trackingReference_s

Stage 8: summarize

summarize Detail_Data, Detail_Message, EndTime, Matched_Data, StartTime, Table, Total_TrackingReference, TrackingReference, URI_s by clientIP_s, host_s, action_s

Stage 9: where

where Total_TrackingReference >= 1

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Detail_Datasummarize
Detail_Messagesummarize
EndTimesummarize
Matched_Datasummarize
StartTimesummarize
Tablesummarize
Total_TrackingReferencesummarize
TrackingReferencesummarize
URI_ssummarize
action_ssummarize
clientIP_ssummarize
host_ssummarize

GitHub - User visibility Was changed

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

'Detect activities when a user visibility Was changed. This query runs every day and its severity is Medium.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 0b85a077-8ba5-4cb5-90f7-1e882afe20c9
name: GitHub - User visibility Was changed
description: |
  'Detect activities when a user visibility Was changed. This query runs every day and its severity is Medium.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
    GitHubAuditData
    | where Visibility != PreviousVisibility
    | project Actor, PreviousVisibility, Visibility
    | extend Name = iif(Actor contains "@", split(Actor, "@")[0], Actor)
    | extend UPNSuffix = iif(Actor contains "@", split(Actor, "@")[1], "")
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Actor
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

GitHubAuditData

Stage 2: where

| where Visibility != PreviousVisibility

Stage 3: project

| project Actor, PreviousVisibility, Visibility

Stage 4: extend

| extend Name = iif(Actor contains "@", split(Actor, "@")[0], Actor)

Stage 5: extend

| extend UPNSuffix = iif(Actor contains "@", split(Actor, "@")[1], "")

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actorproject
PreviousVisibilityproject
Visibilityproject
Nameextend
UPNSuffixextend

Google DNS - CVE-2020-1350 (SIGRED) exploitation pattern

#
Severity
high
Time window
1m
Group by
SrcIpAddr
Source
github.com/Azure/Azure-Sentinel

'Detects exploitation pattern of CVE-2020-1350 (SIGRED) vulnerability.'

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation

Rule Body

id: 1267d53d-f5fd-418b-b8da-34453a5994c2
name: Google DNS - CVE-2020-1350 (SIGRED) exploitation pattern
description: |
  'Detects exploitation pattern of CVE-2020-1350 (SIGRED) vulnerability.'
severity: High
requiredDataConnectors:
  - connectorId: GCPDNSDataConnector
    dataTypes:
      - GCPCloudDNS
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - PrivilegeEscalation
relevantTechniques:
  - T1068
query: |
  let threshold = 15;
  GCPCloudDNS
  | where QueryTypeName in~ ('SIG', 'RRSIG')
  | where NetworkProtocol =~ 'TCP'
  | summarize count() by SrcIpAddr, bin(TimeGenerated, 1m)
  | where count_ < threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 15;

Stage 1: source

GCPCloudDNS

Stage 2: where

| where QueryTypeName in~ ('SIG', 'RRSIG')

Stage 3: where

| where NetworkProtocol =~ 'TCP'

Stage 4: summarize

| summarize count() by SrcIpAddr, bin(TimeGenerated, 1m)

Stage 5: where

| where count_ < threshold

Stage 6: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SrcIpAddrsummarize
IPCustomEntityextend

GTI - Critical CVSS Score Vulnerability Detected

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

Detects vulnerabilities ingested from Google Threat Intelligence with a CVSS v3 base score at or above the critical threshold (9.0). Uses explicit Cvssv3xBaseScore and Cvssv3xTranslatedBaseScore fields from the GTIVulnerabilities parser. Fires when a matching record is found, indicating a critically rated vulnerability that warrants immediate analyst review.

MITRE ATT&CK Coverage

Rule Body

id: 3794141b-9bd3-41ae-8303-79f97692998b
name: GTI - Critical CVSS Score Vulnerability Detected
description: |
  Detects vulnerabilities ingested from Google Threat Intelligence with a CVSS v3 base score at or
  above the critical threshold (9.0). Uses explicit Cvssv3xBaseScore and Cvssv3xTranslatedBaseScore
  fields from the GTIVulnerabilities parser. Fires when a matching record is found, indicating a
  critically rated vulnerability that warrants immediate analyst review.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GTIVulnerabilitiesConnector
    dataTypes:
      - GTI_Vulnerabilities_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Execution
  - PrivilegeEscalation
  - Impact
relevantTechniques:
  - T1190
  - T1068
  - T1203
query: |
  GTIVulnerabilities
  | extend Cvssv3Score = coalesce(Cvssv3xBaseScore, Cvssv3xTranslatedBaseScore)
  | where isnotempty(Cvssv3Score) and Cvssv3Score >= 9.0
  | extend
      Vendor       = tostring(Cpes[0].start_cpe.vendor),
      Product      = tostring(Cpes[0].start_cpe.product),
      CpeVersion   = tostring(Cpes[0].start_cpe.version),
      VendorFixUrl = coalesce(tostring(VendorFixReferences[0].url), tostring(VendorFixReferences[0]))
  | mv-apply _c = Cpes on (
      where isnotnull(_c.start_cpe)
      | summarize AffectedVendors  = make_set(tostring(_c.start_cpe.vendor)),
                  AffectedProducts = make_set(tostring(_c.start_cpe.product))
    )
  | project
      TimeGenerated,
      CveId,
      VulnName,
      Cvssv3Score,
      Cvssv4xScore,
      EpssScore,
      EpssPercentile,
      RiskRating,
      Priority,
      ExploitationState,
      ExploitationConsequence,
      Cvssv4xThreatExploitMaturity,
      Vendor,
      Product,
      CpeVersion,
      AffectedVendors,
      AffectedProducts,
      Description,
      VendorFixUrl,
      VendorFixReferences,
      Tags,
      Type
alertDetailsOverride:
  alertDisplayNameFormat: 'GTI Critical CVSS: {{CveId}} (Score: {{Cvssv3Score}})'
  alertDescriptionFormat: 'CVE: {{CveId}} | CVSS v3: {{Cvssv3Score}} | Risk: {{RiskRating}}'
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: VendorFixUrl
customDetails:
  CveId: CveId
  Cvssv3Score: Cvssv3Score
  Cvssv4Score: Cvssv4xScore
  EpssScore: EpssScore
  EpssPercentile: EpssPercentile
  RiskRating: RiskRating
  Priority: Priority
  ExploitState: ExploitationState
  ExploitMaturity: Cvssv4xThreatExploitMaturity
  AffectedVendor: Vendor
  AffectedProduct: Product
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P1D
    matchingMethod: Selected
    groupByCustomDetails:
      - CveId
eventGroupingSettings:
  aggregationKind: AlertPerResult
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

GTIVulnerabilities

Stage 2: extend

| extend Cvssv3Score = coalesce(Cvssv3xBaseScore, Cvssv3xTranslatedBaseScore)

Stage 3: where

| where isnotempty(Cvssv3Score) and Cvssv3Score >= 9.0

Stage 4: extend

| extend
    Vendor       = tostring(Cpes[0].start_cpe.vendor),
    Product      = tostring(Cpes[0].start_cpe.product),
    CpeVersion   = tostring(Cpes[0].start_cpe.version),
    VendorFixUrl = coalesce(tostring(VendorFixReferences[0].url), tostring(VendorFixReferences[0]))

Stage 5: kusto:mv-apply

| mv-apply _c = Cpes on (
    where isnotnull(_c.start_cpe)
    | summarize AffectedVendors  = make_set(tostring(_c.start_cpe.vendor)),
                AffectedProducts = make_set(tostring(_c.start_cpe.product))
  )

Stage 6: project

| project
    TimeGenerated,
    CveId,
    VulnName,
    Cvssv3Score,
    Cvssv4xScore,
    EpssScore,
    EpssPercentile,
    RiskRating,
    Priority,
    ExploitationState,
    ExploitationConsequence,
    Cvssv4xThreatExploitMaturity,
    Vendor,
    Product,
    CpeVersion,
    AffectedVendors,
    AffectedProducts,
    Description,
    VendorFixUrl,
    VendorFixReferences,
    Tags,
    Type

Stage 7: summarize aggregation inside the mv-apply branch

summarize

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Cvssv3Scorege
  • 9.0 transforms: number
field:"Cvssv3Score" kind:ge value:"9.0"
Cvssv3Scoreis_not_null
  • (no value, null check)
field:"Cvssv3Score" kind:is_not_null
_c.start_cpeis_not_null
  • (no value, null check)
field:"_c.start_cpe" kind:is_not_null

High count of connections by client IP on many ports

#
Severity
medium
Time window
10m
Group by
Computer, cIP, sIP
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

'Identifies when 30 or more ports are used for a given client IP in 10 minutes occurring on the IIS server. This could be indicative of attempted port scanning or exploit attempt at internet facing web applications. This could also simply indicate a misconfigured service or device. References: IIS status code mapping - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0 Win32 Status code mapping - https://msdn.microsoft.com/library/cc231199.aspx'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 44a555d8-ecee-4a25-95ce-055879b4b14b
name: High count of connections by client IP on many ports
description: |
  'Identifies when 30 or more ports are used for a given client IP in 10 minutes occurring on the IIS server.
  This could be indicative of attempted port scanning or exploit attempt at internet facing web applications.
  This could also simply indicate a misconfigured service or device.
  References:
  IIS status code mapping - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0
  Win32 Status code mapping - https://msdn.microsoft.com/library/cc231199.aspx'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureMonitor(IIS)
    dataTypes:
      - W3CIISLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
query: |
  let timeBin = 10m;
  let portThreshold = 30;
  W3CIISLog
  | extend scStatusFull = strcat(scStatus, ".",scSubStatus)
  // Map common IIS codes
  | extend scStatusFull_Friendly = case(
  scStatusFull == "401.0", "Access denied.",
  scStatusFull == "401.1", "Logon failed.",
  scStatusFull == "401.2", "Logon failed due to server configuration.",
  scStatusFull == "401.3", "Unauthorized due to ACL on resource.",
  scStatusFull == "401.4", "Authorization failed by filter.",
  scStatusFull == "401.5", "Authorization failed by ISAPI/CGI application.",
  scStatusFull == "403.0", "Forbidden.",
  scStatusFull == "403.4", "SSL required.",
  "See - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0")
  // Mapping to Hex so can be mapped using website in comments above
  | extend scWin32Status_Hex = tohex(tolong(scWin32Status))
  // Map common win32 codes
  | extend scWin32Status_Friendly = case(
  scWin32Status_Hex =~ "775", "The referenced account is currently locked out and cannot be logged on to.",
  scWin32Status_Hex =~ "52e", "Logon failure: Unknown user name or bad password.",
  scWin32Status_Hex =~ "532", "Logon failure: The specified account password has expired.",
  scWin32Status_Hex =~ "533", "Logon failure: Account currently disabled.",
  scWin32Status_Hex =~ "2ee2", "The request has timed out.",
  scWin32Status_Hex =~ "0", "The operation completed successfully.",
  scWin32Status_Hex =~ "1", "Incorrect function.",
  scWin32Status_Hex =~ "2", "The system cannot find the file specified.",
  scWin32Status_Hex =~ "3", "The system cannot find the path specified.",
  scWin32Status_Hex =~ "4", "The system cannot open the file.",
  scWin32Status_Hex =~ "5", "Access is denied.",
  scWin32Status_Hex =~ "8009030e", "SEC_E_NO_CREDENTIALS",
  scWin32Status_Hex =~ "8009030C", "SEC_E_LOGON_DENIED",
  "See - https://msdn.microsoft.com/library/cc231199.aspx")
  // decode URI when available
  | extend decodedUriQuery = url_decode(csUriQuery)
  // Count of attempts by client IP on many ports
  | summarize makeset(sPort), makeset(decodedUriQuery), makeset(csUserName), makeset(sSiteName), makeset(sPort), makeset(csUserAgent), makeset(csMethod), makeset(csUriQuery), makeset(scStatusFull), makeset(scStatusFull_Friendly), makeset(scWin32Status_Hex), makeset(scWin32Status_Friendly), ConnectionsCount = count() by bin(TimeGenerated, timeBin), cIP, Computer, sIP
  | extend portCount = arraylength(set_sPort)
  | where portCount >= portThreshold
  | project TimeGenerated, cIP, set_sPort, set_csUserName, set_decodedUriQuery, Computer, set_sSiteName, sIP, set_csUserAgent, set_csMethod, set_scStatusFull, set_scStatusFull_Friendly, set_scWin32Status_Hex, set_scWin32Status_Friendly, ConnectionsCount, portCount
  | order by portCount
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: cIP
version: 1.0.3
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Network" ]

Stages and Predicates

Parameters

let timeBin = 10m;
let portThreshold = 30;

Stage 1: source

W3CIISLog

Stage 2: extend (5 consecutive steps)

| extend scStatusFull = strcat(scStatus, ".",scSubStatus)
| extend scStatusFull_Friendly = case(
scStatusFull == "401.0", "Access denied.",
scStatusFull == "401.1", "Logon failed.",
scStatusFull == "401.2", "Logon failed due to server configuration.",
scStatusFull == "401.3", "Unauthorized due to ACL on resource.",
scStatusFull == "401.4", "Authorization failed by filter.",
scStatusFull == "401.5", "Authorization failed by ISAPI/CGI application.",
scStatusFull == "403.0", "Forbidden.",
scStatusFull == "403.4", "SSL required.",
"See - https://support.microsoft.com/help/943891/the-http-status-code-in-iis-7-0-iis-7-5-and-iis-8-0")
| extend scWin32Status_Hex = tohex(tolong(scWin32Status))
| extend scWin32Status_Friendly = case(
scWin32Status_Hex =~ "775", "The referenced account is currently locked out and cannot be logged on to.",
scWin32Status_Hex =~ "52e", "Logon failure: Unknown user name or bad password.",
scWin32Status_Hex =~ "532", "Logon failure: The specified account password has expired.",
scWin32Status_Hex =~ "533", "Logon failure: Account currently disabled.",
scWin32Status_Hex =~ "2ee2", "The request has timed out.",
scWin32Status_Hex =~ "0", "The operation completed successfully.",
scWin32Status_Hex =~ "1", "Incorrect function.",
scWin32Status_Hex =~ "2", "The system cannot find the file specified.",
scWin32Status_Hex =~ "3", "The system cannot find the path specified.",
scWin32Status_Hex =~ "4", "The system cannot open the file.",
scWin32Status_Hex =~ "5", "Access is denied.",
scWin32Status_Hex =~ "8009030e", "SEC_E_NO_CREDENTIALS",
scWin32Status_Hex =~ "8009030C", "SEC_E_LOGON_DENIED",
"See - https://msdn.microsoft.com/library/cc231199.aspx")
| extend decodedUriQuery = url_decode(csUriQuery)

Stage 3: summarize

| summarize makeset(sPort), makeset(decodedUriQuery), makeset(csUserName), makeset(sSiteName), makeset(sPort), makeset(csUserAgent), makeset(csMethod), makeset(csUriQuery), makeset(scStatusFull), makeset(scStatusFull_Friendly), makeset(scWin32Status_Hex), makeset(scWin32Status_Friendly), ConnectionsCount = count() by bin(TimeGenerated, timeBin), cIP, Computer, sIP

Stage 4: extend

| extend portCount = arraylength(set_sPort)

Stage 5: where

| where portCount >= portThreshold

Stage 6: project

| project TimeGenerated, cIP, set_sPort, set_csUserName, set_decodedUriQuery, Computer, set_sSiteName, sIP, set_csUserAgent, set_csMethod, set_scStatusFull, set_scStatusFull_Friendly, set_scWin32Status_Hex, set_scWin32Status_Friendly, ConnectionsCount, portCount

Stage 7: sort

| order by portCount

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
portCountge
  • 30 transforms: number
field:"portCount" kind:ge value:"30"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Computerproject
ConnectionsCountproject
TimeGeneratedproject
cIPproject
portCountproject
sIPproject
set_csMethodproject
set_csUserAgentproject
set_csUserNameproject
set_decodedUriQueryproject
set_sPortproject
set_sSiteNameproject
set_scStatusFullproject
set_scStatusFull_Friendlyproject
set_scWin32Status_Friendlyproject
set_scWin32Status_Hexproject

iboss - Command-and-Control Detected

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

Identifies web traffic where the iboss platform flagged command-and-control (C2) activity. A populated CNCDetected flag indicates the gateway observed communication to a known or suspected C2 destination. Surfaces the user, destination URL, and host to support triage of potentially compromised endpoints.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 40cf9670-d4be-4149-9082-5809a2b12ca1
name: iboss - Command-and-Control Detected
description: |
  Identifies web traffic where the iboss platform flagged command-and-control (C2) activity. A populated CNCDetected flag indicates the gateway observed communication to a known or suspected C2 destination. Surfaces the user, destination URL, and host to support triage of potentially compromised endpoints.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: ibossAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  ibossUrlEvent
  | where CNCDetected == 1
  | project EventTime, SrcUsername, Url, Domain, SrcIpAddr, DstIpAddr, DstPortNumber, DvcAction, UrlCategory, HttpUserAgent
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SrcUsername
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Url
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

ibossUrlEvent

Stage 2: where

| where CNCDetected == 1

Stage 3: project

| project EventTime, SrcUsername, Url, Domain, SrcIpAddr, DstIpAddr, DstPortNumber, DvcAction, UrlCategory, HttpUserAgent

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Domainproject
DstIpAddrproject
DstPortNumberproject
DvcActionproject
EventTimeproject
HttpUserAgentproject
SrcIpAddrproject
SrcUsernameproject
Urlproject
UrlCategoryproject

Object Marked as Clean

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

Detects when an object is marked as clean.

Rule Body

id: 56d70592-077e-4e7b-9d8a-b5d8b0a6a52b
name: Object Marked as Clean
description: Detects when an object is marked as clean.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 41610\n| project\n    Date\
  \ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n\
  \    EventId = instanceId,\n    [\"User Name\"] = user,\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 == 41610

Stage 3: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    ["User Name"] = user,
  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
Severityproject
User Nameproject

Objects Deleted from Malware Detection Exclusions

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

Detects when an object is deleted from malware detection exclusions.

Rule Body

id: f4cad1aa-a1e2-46dc-bf09-6cf8dc67f0ca
name: Objects Deleted from Malware Detection Exclusions
description: Detects when an object is deleted from malware detection exclusions.
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 42270\n| extend RemovedObjectNames\
  \ = extract(\"RemovedObjectNames=\\\"([^\\\"]*)\\\"\", 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 Names\"] = RemovedObjectNames,\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 == 42270

Stage 3: extend

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

Stage 4: project

| project
    Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
    DataSource = original_host,
    EventId = instanceId,
    ["User Name"] = user,
    ["Object Names"] = RemovedObjectNames,
    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 Namesproject
Severityproject
User Nameproject

OCI - Multiple rejects on rare ports

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

'Detects multiple rejects on rare ports.'

MITRE ATT&CK Coverage

TacticTechniques
Reconnaissance

Rule Body

id: 482c24b9-a700-4b2a-85d3-1c42110ba78c
name: OCI - Multiple rejects on rare ports
description: |
  'Detects multiple rejects on rare ports.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: OracleCloudInfrastructureLogsConnector
    dataTypes:
      - OCILogs
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Reconnaissance
relevantTechniques:
  - T1595
query: |
  let threshold = 50;
  let r_dports = 
  OCILogs
  | where EventType contains 'vcn.flowlogs'
  | where ipv4_is_private(DstIpAddr)
  | where ipv4_is_private(SrcIpAddr) == False
  | summarize count() by DstPortNumber
  | top 10 by count_ asc
  | summarize dports = makeset(DstPortNumber)
  | extend k = 1;
  OCILogs
  | where EventType contains 'vcn.flowlogs'
  | where data_action_s =~ 'REJECT'
  | where ipv4_is_private(DstIpAddr)
  | where ipv4_is_private(SrcIpAddr) == False
  | extend k = 1
  | join kind=innerunique (r_dports) on k
  | where dports has tostring(DstPortNumber)
  | summarize count() by SrcIpAddr, bin(TimeGenerated, 5m)
  | where count_ > threshold
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 50;

Let Binding: r_dports used in Stage 7

let r_dports = OCILogs
| where EventType contains 'vcn.flowlogs'
| where ipv4_is_private(DstIpAddr)
| where ipv4_is_private(SrcIpAddr) == False
| summarize count() by DstPortNumber
| top 10 by count_ asc
| summarize dports = makeset(DstPortNumber)
| extend k = 1;

Stage 1: source

OCILogs

Stage 2: where

| where EventType contains 'vcn.flowlogs'

Stage 3: where

| where data_action_s =~ 'REJECT'

Stage 4: where

| where ipv4_is_private(DstIpAddr)

Stage 5: where

| where ipv4_is_private(SrcIpAddr) == False

Stage 6: extend

| extend k = 1

Stage 7: join

| join kind=innerunique (r_dports) on k

Stage 8: where

where dports =~ <unknown:tostring(, DstPortNumber)>

Stage 9: summarize

summarize by SrcIpAddr

Stage 10: where

where count_ > 50

Stage 11: extend

extend IPCustomEntity

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:"DstIpAddr" kind:cidr_match
EventTypecontains
  • vcn.flowlogs
field:"EventType" kind:contains value:"vcn.flowlogs"
count_gt
  • 50 transforms: number
field:"count_" kind:gt value:"50"
data_action_seq
  • REJECT
field:"data_action_s" kind:eq value:"REJECT"

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
SrcIpAddrsummarize
IPCustomEntityextend

ProofpointPOD - Suspicious attachment

#
Status
available
Severity
medium
Time window
10m
Source
github.com/Azure/Azure-Sentinel

'Detects when email contains suspicious attachment (file type).'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: f6a51e2c-2d6a-4f92-a090-cfb002ca611f
name: ProofpointPOD - Suspicious attachment
description: |
  'Detects when email contains suspicious attachment (file type).'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: ProofpointPOD
    dataTypes:
      - ProofpointPOD_message_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1566
query: |
  let lbtime = 10m;
  let disallowed_ext = dynamic(['ps1', 'exe', 'vbs', 'js', 'scr']);
  ProofpointPOD
  | where TimeGenerated > ago(lbtime)
  | where EventType == 'message'
  | where NetworkDirection == 'inbound'
  | where FilterDisposition !in ('reject', 'discard')
  | extend attachedExt = todynamic(MsgParts)[0]['detectedExt']
  | where tolower(attachedExt) in (disallowed_ext)
  | project SrcUserUpn, AccountCustomEntity = parse_json(DstUserUpn)[0], attachedExt
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let lbtime = 10m;
let disallowed_ext = dynamic(['ps1', 'exe', 'vbs', 'js', 'scr']);

Stage 1: source

ProofpointPOD

Stage 2: where

| where TimeGenerated > ago(lbtime)

Stage 3: where

| where EventType == 'message'

Stage 4: where

| where NetworkDirection == 'inbound'

Stage 5: where

| where FilterDisposition !in ('reject', 'discard')

Stage 6: extend

| extend attachedExt = todynamic(MsgParts)[0]['detectedExt']

Stage 7: where

| where tolower(attachedExt) in (disallowed_ext)

Stage 8: project

| project SrcUserUpn, AccountCustomEntity = parse_json(DstUserUpn)[0], attachedExt

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventTypeeq
  • message
field:"EventType" kind:eq value:"message"
NetworkDirectioneq
  • inbound
field:"NetworkDirection" kind:eq value:"inbound"
attachedExtin
  • exe transforms: tolower
  • js transforms: tolower
  • ps1 transforms: tolower
  • scr transforms: tolower
  • vbs transforms: tolower
field:"attachedExt" kind:in

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityproject
SrcUserUpnproject
attachedExtproject

Samsung Knox - Security Log Full 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 the Knox Security Log is full on a device.

Rule Body

id: bf9be360-7f08-48b2-8e9d-ca240c48b404
name: Samsung Knox - Security Log Full Events
version: 1.0.2
kind: NRT
description: When the Knox Security Log is full on a device.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: SamsungDCDefinition
    dataTypes:
      - Samsung_Knox_Audit_CL
tactics: []
relevantTechniques: []
query: |
  Samsung_Knox_Audit_CL
  | where Name == "LOG_IS_FULL" 
  and MitreTtp has "KNOX.1"
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_Audit_CL

Stage 2: where

| where Name == "LOG_IS_FULL" 
and MitreTtp has "KNOX.1"

Indicators

These rows show field, operator, and value matches.

SharePointFileOperation via devices with previously unseen user agents

#
Status
available
Severity
medium
Time window
14d
Group by
Operation, Site_Url, UserId
Source
github.com/Azure/Azure-Sentinel

Identifies anomalies if the number of documents uploaded or downloaded from device(s) associated with a previously unseen user agent exceeds a threshold (default is 5) and deviation (default is 25).

MITRE ATT&CK Coverage

TacticTechniques
Exfiltration

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 5dd76a87-9f87-4576-bab3-268b0e2b338b
name: SharePointFileOperation via devices with previously unseen user agents
description: |
  'Identifies anomalies if the number of documents uploaded or downloaded from device(s) associated with a previously unseen user agent exceeds a threshold (default is 5) and deviation (default is 25).'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
      - OfficeActivity
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
relevantTechniques:
  - T1030
query: |
  // Set threshold for the number of downloads/uploads from a new user agent
  let threshold = 5;
  // Define constants for SharePoint file operations
  let szSharePointFileOperation = "SharePointFileOperation";
  let szOperations = dynamic(["FileDownloaded", "FileUploaded"]);
  // Define the historical activity for analysis
  let starttime = 14d; // Define the start time for historical data (14 days ago)
  let endtime = 1d;   // Define the end time for historical data (1 day ago)
  // Extract the base events for analysis
  let Baseevents =
    OfficeActivity
    | where TimeGenerated between (ago(starttime) .. ago(endtime))
    | where RecordType =~ szSharePointFileOperation
    | where Operation in~ (szOperations)
    | where isnotempty(UserAgent);
  // Identify frequently occurring user agents
  let FrequentUA = Baseevents
    | summarize FUACount = count() by UserAgent, RecordType, Operation
    | where FUACount >= threshold
    | distinct UserAgent;
  // Calculate a user baseline for further analysis
  let UserBaseLine = Baseevents
    | summarize Count = count() by UserId, Operation, Site_Url
    | summarize AvgCount = avg(Count) by UserId, Operation, Site_Url;
  // Extract recent activity for analysis
  let RecentActivity = OfficeActivity
    | where TimeGenerated > ago(endtime)
    | where RecordType =~ szSharePointFileOperation
    | where Operation in~ (szOperations)
    | where isnotempty(UserAgent)
    | where UserAgent in~ (FrequentUA)
    | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), OfficeObjectIdCount = dcount(OfficeObjectId), OfficeObjectIdList = make_set(OfficeObjectId), UserAgentSeenCount = count() 
    by RecordType, Operation, UserAgent, UserType, UserId, ClientIP, OfficeWorkload, Site_Url;
  // Analyze user behavior based on baseline and recent activity
  let UserBehaviorAnalysis = UserBaseLine
    | join kind=inner (RecentActivity) on UserId, Operation, Site_Url
    | extend Deviation = abs(UserAgentSeenCount - AvgCount) / AvgCount;
  // Filter and format results for specific user behavior analysis
  UserBehaviorAnalysis
    | where Deviation > 25
    | extend UserIdName = tostring(split(UserId, '@')[0]), UserIdUPNSuffix = tostring(split(UserId, '@')[1])
    | project-reorder StartTime, EndTime, UserAgent, UserAgentSeenCount, UserId, ClientIP, Site_Url
    | project-away Site_Url1, UserId1, Operation1
    | order by UserAgentSeenCount desc, UserAgent asc, UserId asc, Site_Url asc
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserId
      - identifier: Name
        columnName: UserIdName
      - identifier: UPNSuffix
        columnName: UserIdUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Site_Url
version: 2.2.4
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 5;
let szSharePointFileOperation = "SharePointFileOperation";
let szOperations = dynamic(["FileDownloaded", "FileUploaded"]);
let starttime = 14d;
let endtime = 1d;

let Baseevents, let UserBaseLine and let UserBehaviorAnalysis are inlined into the numbered stages below.

Let Binding: FrequentUA

let FrequentUA = Baseevents
  | summarize FUACount = count() by UserAgent, RecordType, Operation
  | where FUACount >= threshold
  | distinct UserAgent;

Let Binding: RecentActivity used in Stage 8

let RecentActivity = OfficeActivity
  | where TimeGenerated > ago(endtime)
  | where RecordType =~ szSharePointFileOperation
  | where Operation in~ (szOperations)
  | where isnotempty(UserAgent)
  | where UserAgent in~ (FrequentUA)
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), OfficeObjectIdCount = dcount(OfficeObjectId), OfficeObjectIdList = make_set(OfficeObjectId), UserAgentSeenCount = count() 
  by RecordType, Operation, UserAgent, UserType, UserId, ClientIP, OfficeWorkload, Site_Url;

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

Stage 1: source

OfficeActivity

Stage 2: where

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

Stage 3: where

where RecordType =~ "SharePointFileOperation"

Stage 4: where

where Operation in~ ("FileDownloaded", "FileUploaded")

Stage 5: where

where isnotempty(UserAgent)

Stage 6: summarize

summarize Count by UserId, Operation, Site_Url

Stage 7: summarize

summarize AvgCount by UserId, Operation, Site_Url

Stage 8: join

| join kind=inner (RecentActivity) on UserId, Operation, Site_Url

Stage 9: extend

extend Deviation

Stage 10: where

where Deviation > 25

Stage 11: extend

extend UserIdName, UserIdUPNSuffix

Stage 12: project-reorder

project-reorder

Stage 13: project-away

project-away Operation1, Site_Url1, UserId1

Stage 14: sort

sort by Site_Url, UserAgent, UserAgentSeenCount, UserId

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Deviationgt
  • 25 transforms: number
field:"Deviation" kind:gt value:"25"
Operationin
  • FileDownloaded corpus 2 (kusto 2)
  • FileUploaded corpus 2 (kusto 2)
field:"Operation" kind:in
RecordTypeeq
  • SharePointFileOperation corpus 2 (kusto 2)
field:"RecordType" kind:eq value:"SharePointFileOperation"
UserAgentin
  • FrequentUA
field:"UserAgent" kind:in value:"FrequentUA"
UserAgentis_not_null
  • (no value, null check)
field:"UserAgent" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
AvgCountsummarize
Operationsummarize
Site_Urlsummarize
UserIdsummarize
Deviationextend
UserIdNameextend
UserIdUPNSuffixextend

Suspicious Entra ID Joined Device Update

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

This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance. This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys. Ref: https://dirkjanm.io/assets/raw/Insomnihack Breaking and fixing Azure AD device identity security.pdf

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

PlatformRecord / event type
AzureUpdate device

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 3a3c6835-0086-40ca-b033-a93bf26d878f
name: Suspicious Entra ID Joined Device Update
description: |
  'This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
  This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
  Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
relevantTechniques:
  - T1528
query: |
  AuditLogs
  | where OperationName =~ "Update device"
  | mv-apply TargetResource=TargetResources on (
      where TargetResource.type =~ "Device"
      | extend ModifiedProperties = TargetResource.modifiedProperties
      | extend DeviceId = TargetResource.id)
  | mv-apply Prop=ModifiedProperties on ( 
      where Prop.displayName =~ "CloudDisplayName"
      | extend OldName = Prop.oldValue 
      | extend NewName = Prop.newValue)
  | mv-apply Prop=ModifiedProperties on ( 
      where Prop.displayName =~ "IsCompliant"
      | extend OldComplianceState = Prop.oldValue  
      | extend NewComplianceState = Prop.newValue)
  | mv-apply Prop=ModifiedProperties on ( 
      where Prop.displayName =~ "TargetId.DeviceTrustType"
      | extend OldTrustType = Prop.oldValue  
      | extend NewTrustType = Prop.newValue)
  | mv-apply Prop=ModifiedProperties on ( 
      where Prop.displayName =~ "Included Updated Properties" 
      | extend UpdatedProperties = Prop.newValue)
  | extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
  | extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
  | extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
  | extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
  | extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
  | extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
  | extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
  | extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
  | extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
  | extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))
  | where OldDeviceName != NewDeviceName
  | where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'
  // Most common is transferring from AAD Registered to AAD Joined - we just want AAD Joined devices
  | where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'
  // We can modify this value to tune FPs - more properties changed about the device beyond its name the more suspicious it could be
  | where UpdatedPropertiesCount > 1
  | project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount
  | extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: NewDeviceName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: OldDeviceName
  - entityType: Host
    fieldMappings:
      - identifier: AzureID
        columnName: DeviceId
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: InitiatingUserPrincipalName    
      - identifier: Name
        columnName: InitiatedByName      
      - identifier: UPNSuffix
        columnName: InitiatedByUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAadUserId
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAppServicePrincipalId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: InitiatingIpAddress
alertDetailsOverride:
  alertDisplayNameFormat: Suspicious Entra ID Joined Device Update {{OldDeviceName}} renamed to {{NewDeviceName}} and {{UpdatedPropertiesCount}} properties changed
  alertDescriptionFormat: |
    This query looks for suspicious updates to an Microsoft Entra ID joined device where the device name is changed and the device falls out of compliance.
    In this case {{OldDeviceName}} was renamed to {{NewDeviceName}} and {{UpdatedPropertiesCount}} properties were changed.
    This could occur when a threat actor updates the details of an Autopilot provisioned device using a stolen device ticket, in order to access certificates and keys.
    Ref: https://dirkjanm.io/assets/raw/Insomnihack%20Breaking%20and%20fixing%20Azure%20AD%20device%20identity%20security.pdf
version: 1.0.4
kind: Scheduled

Stages and Predicates

Stage 1: source

AuditLogs

Stage 2: where

| where OperationName =~ "Update device"

Stage 3: kusto:mv-apply

| mv-apply TargetResource=TargetResources on (
    where TargetResource.type =~ "Device"
    | extend ModifiedProperties = TargetResource.modifiedProperties
    | extend DeviceId = TargetResource.id)

Stage 4: kusto:mv-apply

| mv-apply Prop=ModifiedProperties on ( 
    where Prop.displayName =~ "CloudDisplayName"
    | extend OldName = Prop.oldValue 
    | extend NewName = Prop.newValue)

Stage 5: kusto:mv-apply

| mv-apply Prop=ModifiedProperties on ( 
    where Prop.displayName =~ "IsCompliant"
    | extend OldComplianceState = Prop.oldValue  
    | extend NewComplianceState = Prop.newValue)

Stage 6: kusto:mv-apply

| mv-apply Prop=ModifiedProperties on ( 
    where Prop.displayName =~ "TargetId.DeviceTrustType"
    | extend OldTrustType = Prop.oldValue  
    | extend NewTrustType = Prop.newValue)

Stage 7: kusto:mv-apply

| mv-apply Prop=ModifiedProperties on ( 
    where Prop.displayName =~ "Included Updated Properties" 
    | extend UpdatedProperties = Prop.newValue)

Stage 8: extend (10 consecutive steps)

| extend OldDeviceName = tostring(parse_json(tostring(OldName))[0])
| extend NewDeviceName = tostring(parse_json(tostring(NewName))[0])
| extend OldComplianceState = tostring(parse_json(tostring(OldComplianceState))[0])
| extend NewComplianceState = tostring(parse_json(tostring(NewComplianceState))[0])
| extend InitiatingAppName = tostring(InitiatedBy.app.displayName)
| extend InitiatingAppServicePrincipalId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatingUserPrincipalName = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatingAadUserId = tostring(InitiatedBy.user.id)
| extend InitiatingIpAddress = tostring(iff(isnotempty(InitiatedBy.user.ipAddress), InitiatedBy.user.ipAddress, InitiatedBy.app.ipAddress))
| extend UpdatedPropertiesCount = array_length(split(UpdatedProperties, ','))

Stage 9: where

| where OldDeviceName != NewDeviceName

Stage 10: where

| where OldComplianceState =~ 'true' and NewComplianceState =~ 'false'

Stage 11: where

| where NewTrustType == '"AzureAd"' and OldTrustType != '"Workplace"'

Stage 12: where

| where UpdatedPropertiesCount > 1

Stage 13: project-reorder

| project-reorder TimeGenerated, DeviceId, NewDeviceName, OldDeviceName, NewComplianceState, InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingAppName, InitiatingAppServicePrincipalId, InitiatingIpAddress, AADOperationType, OldTrustType, NewTrustType, UpdatedProperties, UpdatedPropertiesCount

Stage 14: extend

| extend InitiatedByName = tostring(split(InitiatingUserPrincipalName,'@',0)[0]), InitiatedByUPNSuffix = tostring(split(InitiatingUserPrincipalName,'@',1)[0])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
NewComplianceStateeq
  • false
field:"NewComplianceState" kind:eq value:"false"
NewTrustTypeeq
  • "AzureAd"
field:"NewTrustType" kind:eq
OldComplianceStateeq
  • true
field:"OldComplianceState" kind:eq value:"true"
OldTrustTypene
  • "Workplace"
field:"OldTrustType" kind:ne
OperationNameeq
  • Update device
field:"OperationName" kind:eq value:"Update device"
Prop.displayNameeq
  • CloudDisplayName transforms: kusto_dynamic_path
  • Included Updated Properties transforms: kusto_dynamic_path
  • IsCompliant transforms: kusto_dynamic_path
  • TargetId.DeviceTrustType transforms: kusto_dynamic_path
field:"Prop.displayName" kind:eq
TargetResource.typeeq
  • Device transforms: kusto_dynamic_path
field:"TargetResource.type" kind:eq value:"Device"
UpdatedPropertiesCountgt
  • 1 transforms: number
field:"UpdatedPropertiesCount" kind:gt value:"1"

Output Fields

These fields are emitted when the rule matches.

FieldSource
OldDeviceNameextend
NewDeviceNameextend
OldComplianceStateextend
NewComplianceStateextend
InitiatingAppNameextend
InitiatingAppServicePrincipalIdextend
InitiatingUserPrincipalNameextend
InitiatingAadUserIdextend
InitiatingIpAddressextend
UpdatedPropertiesCountextend
InitiatedByNameextend
InitiatedByUPNSuffixextend

Zero Networks Segment - Rare JIT Rule Creation

#
Status
available
Severity
medium
Time window
14d
Group by
DestinationEntityName, PerformedByName
Source
github.com/Azure/Azure-Sentinel

'Identifies when a JIT Rule connection is new or rare by a given account today based on comparison with the previous 14 days. JIT Rule creations are indicated by the Activity Type Id 20'

MITRE ATT&CK Coverage

TacticTechniques
Lateral Movement

Rule Body

id: 58688058-68b2-4b39-8009-ac6dc4d81ea1
name: Zero Networks Segment - Rare JIT Rule Creation
description: |
  'Identifies when a JIT Rule connection is new or rare by a given account today based on comparison with the previous 14 days.
  JIT Rule creations are indicated by the Activity Type Id 20'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: ZeroNetworksSegmentAuditFunction
    dataTypes:
      - ZNSegmentAudit_CL
  - connectorId: ZeroNetworksSegmentAuditNativePoller
    dataTypes:
      - ZNSegmentAuditNativePoller_CL
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - LateralMovement
relevantTechniques:
  - T1021
query: |
  let starttime = 14d;
  let endtime = 1d;
  ZNSegmentAudit
  | where TimeGenerated >= ago(endtime)
  | where AuditTypeId == 20
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ConnectionCount = count()
  by PerformedByName, tostring(DestinationEntityName)
    // use left anti to exclude anything from the previous 14 days that is not rare
  | join kind=leftanti (
  ZNSegmentAudit
  | where TimeGenerated between (ago(starttime) .. ago(endtime))
  | where AuditTypeId == 20
  | summarize by tostring(DestinationEntityName)
  ) on DestinationEntityName
  | summarize StartTime = min(StartTime), EndTime = max(EndTime), ConnectionCount = sum(ConnectionCount)
  by PerformedByName, DestinationEntityName
  | extend TimeGenerated = StartTime
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: PerformedByName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DestinationEntityName
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let starttime = 14d;
let endtime = 1d;

Stage 1: source

ZNSegmentAudit

Stage 2: where

| where TimeGenerated >= ago(endtime)

Stage 3: where

| where AuditTypeId == 20

Stage 4: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), ConnectionCount = count()
by PerformedByName, tostring(DestinationEntityName)

Stage 5: join (negated)

join kind=leftanti (ZNSegmentAudit) on DestinationEntityName

Stage 6: summarize

summarize ConnectionCount, EndTime, StartTime by PerformedByName, DestinationEntityName

Stage 7: extend

extend TimeGenerated

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
AuditTypeIdeq20excludes:AuditTypeId field:"AuditTypeId" value:"20"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ConnectionCountsummarize
DestinationEntityNamesummarize
EndTimesummarize
PerformedByNamesummarize
StartTimesummarize
TimeGeneratedextend

Back to all Kusto rules