Detection rules › Kusto

Kusto rules: 3c

1Password - Vault export post account creation

#
Severity
medium
Time window
14d
Group by
$left.userUuid, $right.userUuid
Source
github.com/Azure/Azure-Sentinel

This will alert when a successful vault export has occurred within 14 days of a new account being created within 1Password. Ref: https://1password.com/ Ref: https://github.com/securehats/

MITRE ATT&CK Coverage

TacticTechniques
Persistence
Credential Access

Telemetry Coverage

PlatformRecord / event type
1Passwordvault-export: Export Vault

Rule Body

id: 969e2e5c-9cc6-423c-a3de-514f7ad75fe7
name: 1Password - Vault export post account creation
version: 1.0.0
kind: Scheduled
description: |-
  This will alert when a successful vault export has occurred within 14 days of a new account being created within 1Password.

  Ref: https://1password.com/
  Ref: https://github.com/securehats/
severity: Medium
requiredDataConnectors:
  - connectorId: 1Password
    dataTypes:
      - OnePasswordEventLogs_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- Persistence
relevantTechniques:
- T1555
- T1136
query: |-
  let ruleFrequency = 1h;
  let lookback = 14d;
  let onePasswordUserCreationCheck =
      OnePasswordEventLogs_CL
      | where TimeGenerated between (ago(lookback) .. ago(now() - ago(ruleFrequency)))
      | extend creationActivityFound = iff(action == "activate" and object_type == "user", bool(1), bool(0))
  ;
  OnePasswordEventLogs_CL
  | where action == "export" and object_type == "vault"
  | extend userUuid = tostring(actor_details.uuid)
  | join (
      onePasswordUserCreationCheck
      | where creationActivityFound == true
      | extend userUuid = tostring(object_details.uuid)
      )
      on $left.userUuid == $right.userUuid
  | extend
      TargetUsername = object_details1.email
      , SrcIpAddr = session1.ip_address
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: TargetUsername
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
  aggregationKind: SingleAlert

Stages and Predicates

Parameters

let ruleFrequency = 1h;
let lookback = 14d;

Let Binding: onePasswordUserCreationCheck used in Stage 4

let onePasswordUserCreationCheck = OnePasswordEventLogs_CL
    | where TimeGenerated between (ago(lookback) .. ago(now() - ago(ruleFrequency)))
    | extend creationActivityFound = iff(action == "activate" and object_type == "user", bool(1), bool(0));

Stage 1: source

OnePasswordEventLogs_CL

Stage 2: where

| where action == "export" and object_type == "vault"

Stage 3: extend

| extend userUuid = tostring(actor_details.uuid)

Stage 4: join

| join (
    onePasswordUserCreationCheck
    | where creationActivityFound == true
    | extend userUuid = tostring(object_details.uuid)
    )
    on $left.userUuid == $right.userUuid

Stage 5: extend

extend SrcIpAddr, TargetUsername

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
userUuidextend
SrcIpAddrextend
TargetUsernameextend

[Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions

#
Status
available
Severity
high
Time window
2h
Group by
AppServicePrincipalId
Source
github.com/Azure/Azure-Sentinel

Detects an application receiving administrator permissions and then using them to assign a role. This pattern can indicate rapid privilege escalation through app consent abuse.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 90bacdf4-e35b-47cb-92d1-29d8397eb4a0
name: "[Entra ID] Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions"
version: 1.0.0
kind: Scheduled
description: |
  Detects an application receiving administrator permissions and then using them to assign a role. This pattern can indicate rapid privilege escalation through app consent abuse.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 1h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - PrivilegeEscalation
  - DefenseEvasion
  - InitialAccess
relevantTechniques:
  - T1078.004
  - T1098.003
query: |
  let query_frequency = 1h;
  let query_period = 2h;
  AuditLogs
  | where TimeGenerated > ago(query_period)
  | where Category =~ "ApplicationManagement" and LoggedByService =~ "Core Directory"
  | where OperationName =~ "Add app role assignment to service principal"
  | mv-expand TargetResource = TargetResources
  | mv-expand modifiedProperty = TargetResource["modifiedProperties"]
  | where tostring(modifiedProperty["displayName"]) == "AppRole.Value"
  | extend PermissionGrant = tostring(modifiedProperty["newValue"])
  | where PermissionGrant has "RoleManagement.ReadWrite.Directory"
  | mv-apply modifiedProperty = TargetResource["modifiedProperties"] on (
      summarize modifiedProperties = make_bag(
                                     bag_pack(
      tostring(modifiedProperty["displayName"]),
      bag_pack(
      "oldValue",
      trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])),
      "newValue",
      trim(@'[\"\s]+', tostring(modifiedProperty["newValue"]))
  )
  ),
                                     100
                                 )
      )
  | project
      PermissionGrant_TimeGenerated = TimeGenerated,
      PermissionGrant_OperationName = OperationName,
      PermissionGrant_Result = Result,
      PermissionGrant,
      AppDisplayName = tostring(modifiedProperties["ServicePrincipal.DisplayName"]["newValue"]),
      AppServicePrincipalId = tostring(modifiedProperties["ServicePrincipal.ObjectID"]["newValue"]),
      PermissionGrant_InitiatedBy = InitiatedBy,
      PermissionGrant_TargetResources = TargetResources,
      PermissionGrant_AdditionalDetails = AdditionalDetails,
      PermissionGrant_CorrelationId = CorrelationId
  | join kind=inner (
      AuditLogs
      | where TimeGenerated > ago(query_frequency)
      | where Category =~ "RoleManagement"
          and LoggedByService =~ "Core Directory"
          and AADOperationType =~ "Assign"
      | where isnotempty(InitiatedBy["app"])
      | mv-expand TargetResource = TargetResources
      | mv-expand modifiedProperty = TargetResource["modifiedProperties"]
      | where tostring(modifiedProperty["displayName"]) in ("Role.DisplayName", "RoleDefinition.DisplayName")
      | extend RoleAssignment = tostring(modifiedProperty["newValue"])
      | where RoleAssignment contains "Admin"
      | project
          RoleAssignment_TimeGenerated = TimeGenerated,
          RoleAssignment_OperationName = OperationName,
          RoleAssignment_Result = Result,
          RoleAssignment,
          TargetType = tostring(TargetResources[0]["type"]),
          Target = iff(isnotempty(TargetResources[0]["displayName"]), tostring(TargetResources[0]["displayName"]), tolower(TargetResources[0]["userPrincipalName"])),
          TargetId = tostring(TargetResources[0]["id"]),
          RoleAssignment_InitiatedBy = InitiatedBy,
          RoleAssignment_TargetResources = TargetResources,
          RoleAssignment_AdditionalDetails = AdditionalDetails,
          RoleAssignment_CorrelationId = CorrelationId,
          AppServicePrincipalId = tostring(InitiatedBy["app"]["servicePrincipalId"])
      )
      on AppServicePrincipalId
  | where PermissionGrant_TimeGenerated < RoleAssignment_TimeGenerated
  | extend
      TargetName = tostring(split(Target, "@")[0]),
      TargetUPNSuffix = tostring(split(Target, "@")[1])
  | project
          Alert_Category_en = "Entra ID",
      Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
      Alert_Name_en = "Application Assigned Administrator Permissions Immediately After Obtaining Role Management Permissions",
      Alert_Description_en = strcat(
                             "At Taiwan time",
                             format_datetime(datetime_utc_to_local(RoleAssignment_TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"),
                             ". ",
                             "EntraID:",
                             "",
                             ". ",
                             "detected initiator: ",
                             tostring(PermissionGrant_InitiatedBy.user.userPrincipalName),
                             " granted application: ",
                             AppDisplayName,
                             " permission: ",
                             PermissionGrant,
                             "afterward, the application immediately assigned a role to target: ",
                             Target,
                             "role assignment: ",
                             RoleAssignment
                         ),
      Alert_TriageStep_en = strcat(
                            "1. Confirm initiator: ",
                            tostring(PermissionGrant_InitiatedBy.user.userPrincipalName),
                            "IP:",
                            tostring(PermissionGrant_InitiatedBy.ipAddress),
                            ", and review the authorized application: ",
                            AppDisplayName,
                            ", obtained permission: ",
                            PermissionGrant,
                            ", to determine whether it is expected behavior/automation. ",
                            "2. Review application: ",
                            AppDisplayName,
                            " for target: ",
                            Target,
                            " role assignment content: ",
                            RoleAssignment,
                            ", to determine whether it is expected behavior/automation. "
                        ),
      Alert_Containment_en = strcat(
                             "1. If determined to be unauthorized behavior, immediately disable initiator: ",
                             tostring(PermissionGrant_InitiatedBy.user.userPrincipalName),
                             " and application: ",
                             AppDisplayName,
                             ", and revoke its Directory/RoleManagement-related permissions. ",
                             "2. Immediately revert/remove the improper administrator role assignment: ",
                             Target,
                             ", and review whether other role/group/application assignments were changed."
                         ),
      Alert_Remediation_en =
      "1. Strengthen Conditional Access and account governance: enforce MFA/managed device and named location restrictions for administrator accounts, and block abnormal IPs. "
      "2. Implement least privilege and PIM: use Just-In-Time (JIT) activation with approval for administrator roles to reduce the risk of immediate abuse after account compromise. "
      "3. Strengthen detection and automation: establish automated response SOAR (notification/revoke authorization/disable application/revert role/block source).",
              Alert_Time_TW = datetime_utc_to_local(RoleAssignment_TimeGenerated, "Asia/Taipei"),
      Alert_Time_UTC0 = RoleAssignment_TimeGenerated,
      Event_Action = RoleAssignment_OperationName,
      Event_Status = RoleAssignment_Result,
      Event_Description = strcat("initiator: ", tostring(PermissionGrant_InitiatedBy.user.userPrincipalName), " to application: ", AppDisplayName, ", ", PermissionGrant_OperationName, ". Subsequently, application: ", AppDisplayName, " for target ", Target, ", ", RoleAssignment_OperationName),
      Source_Identity_FullName = AppDisplayName,
      Source_Identity_ID = AppServicePrincipalId,
      Source_Identity_Type = "Service",
      Target_Identity_FullName = Target,
      Target_Identity_ID = TargetId,
      Target_Identity_Name = TargetName,
      Target_Identity_Type = iff(TargetType has "User", "User", "Service"),
      Target_Resource_ID = "",
      Target_Resource_Name = "Microsoft Entra ID",
      Target_Resource_Type = "Microsoft Entra ID"
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Source_Identity_FullName
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: Target_Identity_FullName
alertDetailsOverride:
  alertDynamicProperties: []

Stages and Predicates

Parameters

let query_frequency = 1h;
let query_period = 2h;

Stage 1: source

AuditLogs

Stage 2: where

| where TimeGenerated > ago(query_period)

Stage 3: where

| where Category =~ "ApplicationManagement" and LoggedByService =~ "Core Directory"

Stage 4: where

| where OperationName =~ "Add app role assignment to service principal"

Stage 5: mv-expand

| mv-expand TargetResource = TargetResources

Stage 6: mv-expand

| mv-expand modifiedProperty = TargetResource["modifiedProperties"]

Stage 7: where

| where tostring(modifiedProperty["displayName"]) == "AppRole.Value"

Stage 8: extend

| extend PermissionGrant = tostring(modifiedProperty["newValue"])

Stage 9: where

| where PermissionGrant has "RoleManagement.ReadWrite.Directory"

Stage 10: kusto:mv-apply

| mv-apply modifiedProperty = TargetResource["modifiedProperties"] on (
    summarize modifiedProperties = make_bag(
                                   bag_pack(
    tostring(modifiedProperty["displayName"]),
    bag_pack(
    "oldValue",
    trim(@'[\"\s]+', tostring(modifiedProperty["oldValue"])),
    "newValue",
    trim(@'[\"\s]+', tostring(modifiedProperty["newValue"]))
)
),
                                   100
                               )
    )

Stage 11: project

| project
    PermissionGrant_TimeGenerated = TimeGenerated,
    PermissionGrant_OperationName = OperationName,
    PermissionGrant_Result = Result,
    PermissionGrant,
    AppDisplayName = tostring(modifiedProperties["ServicePrincipal.DisplayName"]["newValue"]),
    AppServicePrincipalId = tostring(modifiedProperties["ServicePrincipal.ObjectID"]["newValue"]),
    PermissionGrant_InitiatedBy = InitiatedBy,
    PermissionGrant_TargetResources = TargetResources,
    PermissionGrant_AdditionalDetails = AdditionalDetails,
    PermissionGrant_CorrelationId = CorrelationId

Stage 12: join

| join kind=inner (
    AuditLogs
    | where TimeGenerated > ago(query_frequency)
    | where Category =~ "RoleManagement"
        and LoggedByService =~ "Core Directory"
        and AADOperationType =~ "Assign"
    | where isnotempty(InitiatedBy["app"])
    | mv-expand TargetResource = TargetResources
    | mv-expand modifiedProperty = TargetResource["modifiedProperties"]
    | where tostring(modifiedProperty["displayName"]) in ("Role.DisplayName", "RoleDefinition.DisplayName")
    | extend RoleAssignment = tostring(modifiedProperty["newValue"])
    | where RoleAssignment contains "Admin"
    | project
        RoleAssignment_TimeGenerated = TimeGenerated,
        RoleAssignment_OperationName = OperationName,
        RoleAssignment_Result = Result,
        RoleAssignment,
        TargetType = tostring(TargetResources[0]["type"]),
        Target = iff(isnotempty(TargetResources[0]["displayName"]), tostring(TargetResources[0]["displayName"]), tolower(TargetResources[0]["userPrincipalName"])),
        TargetId = tostring(TargetResources[0]["id"]),
        RoleAssignment_InitiatedBy = InitiatedBy,
        RoleAssignment_TargetResources = TargetResources,
        RoleAssignment_AdditionalDetails = AdditionalDetails,
        RoleAssignment_CorrelationId = CorrelationId,
        AppServicePrincipalId = tostring(InitiatedBy["app"]["servicePrincipalId"])
    )
    on AppServicePrincipalId

Stage 13: where

where PermissionGrant_TimeGenerated < RoleAssignment_TimeGenerated

Stage 14: extend

extend TargetName, TargetUPNSuffix

Stage 15: project

project Alert_Category_en, Alert_Containment_en, Alert_Description_en, Alert_Name_en, Alert_Remediation_en, Alert_SubCategory_en, Alert_Time_TW, Alert_Time_UTC0, Alert_TriageStep_en, Event_Action, Event_Description, Event_Status, Source_Identity_FullName, Source_Identity_ID, Source_Identity_Type, Target_Identity_FullName, Target_Identity_ID, Target_Identity_Name, Target_Identity_Type, Target_Resource_ID, Target_Resource_Name, Target_Resource_Type

Stage 16: summarize aggregation inside the mv-apply branch

summarize

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AADOperationTypeeq
  • Assign
field:"AADOperationType" kind:eq value:"Assign"
Categoryeq
  • ApplicationManagement corpus 13 (kusto 13)
  • RoleManagement
field:"Category" kind:eq
InitiatedBy["app"]is_not_null
  • (no value, null check)
LoggedByServiceeq
  • Core Directory corpus 11 (kusto 10, sigma 1)
  • Core Directory
field:"azure_ad::logged_by_service" kind:eq
OperationNameeq
  • Add app role assignment to service principal corpus 3 (kusto 3)
field:"OperationName" kind:eq value:"Add app role assignment to service principal"
PermissionGrantmatch
  • RoleManagement.ReadWrite.Directory transforms: term
field:"PermissionGrant" kind:match value:"RoleManagement.ReadWrite.Directory"
RoleAssignmentcontains
  • Admin
field:"RoleAssignment" kind:contains value:"Admin"
modifiedProperty["displayName"]eq
  • AppRole.Value transforms: tostring, kusto_dynamic_path
modifiedProperty["displayName"]in
  • Role.DisplayName transforms: tostring, kusto_dynamic_path
  • RoleDefinition.DisplayName transforms: tostring, kusto_dynamic_path

BitSight - diligence risk category detected

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

'Rule helps to detect whenever there is a diligence risk category found in BitSight.'

MITRE ATT&CK Coverage

Rule Body

id: 161ed3ac-b242-4b13-8c6b-58716e5e9972
name: BitSight - diligence risk category detected
description: |
  'Rule helps to detect whenever there is a diligence risk category found in BitSight.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BitSight
    dataTypes:
      - BitSightFindingsData
queryFrequency: 1d
queryPeriod: 24h
triggerOperator: GreaterThan
triggerThreshold: 0
tactics:
  - Execution
  - Reconnaissance
relevantTechniques:
  - T1203
  - T1595.002
query: |
    let timeframe = 24h;
    BitSightFindingsData
    | where ingestion_time() > ago(timeframe)
    | where RiskCategory == "Diligence"
    | extend Severity = toreal(Severity)
    | extend Severity = case( Severity <= 6.9 and Severity >= 4.0, "Low",
                              Severity <= 8.9 and Severity >= 7.0, "Medium",
                              Severity <= 10.0 and Severity >= 9.0, "High",
                              "Informational")
    | project FirstSeen, CompanyName, Severity, RiskCategory, TemporaryId, RiskVector
incidentConfiguration:
    createIncident: false
eventGroupingSettings:
    aggregationKind: AlertPerResult
alertDetailsOverride:
    alertDisplayNameFormat: 'BitSight: Alert for {{RiskVector}} in {{CompanyName}} from BitSight'
    alertDescriptionFormat: 'Alert is generated for {{CompanyName}}.\n\nRisk Vector: {{RiskVector}}\nTemporaryId: {{TemporaryId}}\nRisk Category: Diligence'
    alertSeverityColumnName: Severity
entityMappings:
  - entityType: Malware
    fieldMappings:
      - identifier: Name
        columnName: RiskVector
      - identifier: Category
        columnName: RiskCategory
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let timeframe = 24h;

Stage 1: source

BitSightFindingsData

Stage 2: where

| where ingestion_time() > ago(timeframe)

Stage 3: where

| where RiskCategory == "Diligence"

Stage 4: extend

| extend Severity = toreal(Severity)

Stage 5: extend

| extend Severity = case( Severity <= 6.9 and Severity >= 4.0, "Low",
                          Severity <= 8.9 and Severity >= 7.0, "Medium",
                          Severity <= 10.0 and Severity >= 9.0, "High",
                          "Informational")
Severity =
ifSeverity <= 6.9 and Severity >= 4.0"Low"
elifSeverity <= 8.9 and Severity >= 7.0"Medium"
elifSeverity <= 10.0 and Severity >= 9.0"High"
else"Informational"

Stage 6: project

| project FirstSeen, CompanyName, Severity, RiskCategory, TemporaryId, RiskVector

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
CompanyNameproject
FirstSeenproject
RiskCategoryproject
RiskVectorproject
Severityproject
TemporaryIdproject

Cisco WSA - Internet access from public IP

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

'Detects internet access from public IP.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 4250b050-e1c6-4926-af04-9484bbd7e94f
name: Cisco WSA - Internet access from public IP
description: |
  'Detects internet access from public IP.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1189
query: |
  let ip_except = dynamic(['127.0.0.2']);    //Add exceptions to this list
  CiscoWSAEvent
  | where ipv4_is_private(SrcIpAddr) == false
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let ip_except = dynamic(['127.0.0.2']);

Stage 1: source

CiscoWSAEvent

Stage 2: where

| where ipv4_is_private(SrcIpAddr) == false

Stage 3: extend

| extend IPCustomEntity = SrcIpAddr

Exclusions

The rule actively suppresses these predicates.

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

Output Fields

These fields are emitted when the rule matches.

FieldSource
IPCustomEntityextend

Cognni Incidents for Low Sensitivity Legal Information

#

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

'Display incidents in which low sensitivity legal information was placed at risk by user sharing.'

MITRE ATT&CK Coverage

TacticTechniques
Collection

Rule Body

id: 8374ec0f-d857-4c17-b1e7-93d11800f8fb
name: Cognni Incidents for Low Sensitivity Legal Information
description: |
  'Display incidents in which low sensitivity legal information was placed at risk by user sharing.'
severity: Low
requiredDataConnectors:
  - connectorId: CognniSentinelDataConnector
    dataTypes:
      - CognniIncidents_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Collection
relevantTechniques:
  - T1530
query: |
  let lowRisk = 1;
  let legal = 'Legal Information';
  CognniIncidents_CL 
  | where Severity == lowRisk
  | where informationType_s == legal
  | where TimeGenerated >= ago(5h)
  | extend AccountCustomEntity = userId_s
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let lowRisk = 1;
let legal = 'Legal Information';

Stage 1: source

CognniIncidents_CL

Stage 2: where

| where Severity == lowRisk

Stage 3: where

| where informationType_s == legal

Stage 4: where

| where TimeGenerated >= ago(5h)

Stage 5: extend

| extend AccountCustomEntity = userId_s

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

Mimecast Secure Email Gateway - Attachment Protect

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

'Detect threat for mail attachment under the targeted threat protection.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Collection
Initial Access

Rule Body

id: 72bd7b0c-493c-4fa5-8a95-7f6376b6cfb2
name: Mimecast Secure Email Gateway - Attachment Protect
description: |
  'Detect threat for mail attachment under the targeted threat protection.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: MimecastSEGAPI
    dataTypes:
      - MimecastCG
enabled: true
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
- Exfiltration
- Discovery
- InitialAccess
- Execution
relevantTechniques:
- T1114
- T1566
- T0865
query: |
  MimecastCG
  | where Type == "email_ttp_ap"
  | extend  SenderEnvelope = ['Sender Envelope']  , SenderIp = ['Sender IP']
suppressionDuration: 5h
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: P7D
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
- entityType: MailMessage
  fieldMappings:
  - identifier: Sender
    columnName: SenderEnvelope
  - identifier: Recipient
    columnName: Recipients
  - identifier: Subject
    columnName: Subject
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SenderIp
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

MimecastCG

Stage 2: where

| where Type == "email_ttp_ap"

Stage 3: extend

| extend  SenderEnvelope = ['Sender Envelope']  , SenderIp = ['Sender IP']

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Typeeq
  • email_ttp_ap
field:"Type" kind:eq value:"email_ttp_ap"

Output Fields

These fields are emitted when the rule matches.

FieldSource
SenderEnvelopeextend
SenderIpextend

Mimecast Secure Email Gateway - Attachment Protect

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

Detect threat for mail attachment under the targeted threat protection

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Collection
Initial Access

Rule Body

id: 72264f4f-61fb-4f4f-96c4-635571a376c2
name: Mimecast Secure Email Gateway - Attachment Protect
description: Detect threat for mail attachment under the targeted threat protection
severity: High
requiredDataConnectors:
  - connectorId: MimecastSIEMAPI
    dataTypes:
      - MimecastSIEM_CL
enabled: true
query: MimecastSIEM_CL| where mimecastEventId_s=="mail_ttp_attachment"
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Collection
- Exfiltration
- Discovery
- InitialAccess
- Execution
relevantTechniques:
- T1114
- T1566
- T0865
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1d
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: SingleAlert
customDetails:
  sha256: sha256_s
  fileName: fileName_s
  MsgId_s: MsgId_s
entityMappings:
- entityType: MailMessage
  fieldMappings:
  - identifier: Sender
    columnName: Sender_s
  - identifier: Recipient
    columnName: Recipient_s
  - identifier: Subject
    columnName: Subject_s
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IP_s
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

MimecastSIEM_CL

Stage 2: where

| where mimecastEventId_s=="mail_ttp_attachment"

Indicators

These rows show field, operator, and value matches.

Multiple Sources Affected by the Same TI Destination

#
Status
available
Severity
medium
Time window
1d
Group by
Fqdn, ThreatDescription
Source
github.com/Azure/Azure-Sentinel

'Identifies multiple machines trying to reach out to the same destination blocked by TI in Azure Firewall. This can indicate attack on the organization by the same attack group. Configurable Parameters: - Minimum affected threshold - alert only if more than this number of hosts affected. Default is set to 5. - Recommendation is to use the new resource specific logs. If you are using both, the TiTraffic Count will be duplicated.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control
Exfiltration

Rule Body

id: 4644baf7-3464-45dd-bd9d-e07687e25f81
name: Multiple Sources Affected by the Same TI Destination
description: |
  'Identifies multiple machines trying to reach out to the same destination blocked by TI in Azure Firewall. This can indicate attack on the organization by the same attack group.
  
  Configurable Parameters:
  
  - Minimum affected threshold - alert only if more than this number of hosts affected. Default is set to 5.
  - Recommendation is to use the new resource specific logs. If you are using both, the TiTraffic Count will be duplicated.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AzureFirewall
    dataTypes: 
      - AzureDiagnostics
      - AZFWThreatIntel
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
  - CommandAndControl
relevantTechniques:
  - T1041
  - T1071
query: |
  let Timeframe = 1d;
  let StartTime = ago(Timeframe);
  let EndTime = now(); 
  let MinAffectedThreshold = 5;
  union isfuzzy=true
  (AzureDiagnostics 
  | where TimeGenerated  between (StartTime .. EndTime)
  | where OperationName == "AzureFirewallThreatIntelLog"
  | parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int  "." * "Action: Deny. " ThreatDescription),
  (AZFWThreatIntel
  | where TimeGenerated between (StartTime .. EndTime))
  | extend Fqdn = DestinationIp
  | summarize TiTrafficCount = count(), dCountSourceIps = dcount(SourceIp), AffectedIps = make_set(SourceIp, 10000) by Fqdn, ThreatDescription
  | where array_length(AffectedIps) > MinAffectedThreshold
  | mv-expand SourceIp = AffectedIps
  | order by TiTrafficCount desc, Fqdn asc, parse_ipv4(tostring(SourceIp)) asc
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIp
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Fqdn
customDetails:
  ThreatDescription: ThreatDescription
version: 1.1.5
kind: Scheduled

Stages and Predicates

Parameters

let Timeframe = 1d;
let StartTime = ago(Timeframe);
let EndTime = now();
let MinAffectedThreshold = 5;

Union isfuzzy=true (2 Sources)

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

Leg 1: AzureDiagnostics

AzureDiagnostics 
| where TimeGenerated  between (StartTime .. EndTime)
| where OperationName == "AzureFirewallThreatIntelLog"
| parse msg_s with * "from " SourceIp ":" SourcePort:int " to " Fqdn ":" DestinationPort:int  "." * "Action: Deny. " ThreatDescription

Leg 2: AZFWThreatIntel

AZFWThreatIntel
| where TimeGenerated between (StartTime .. EndTime)

Applied to the Combined Result

| extend Fqdn = DestinationIp
| summarize TiTrafficCount = count(), dCountSourceIps = dcount(SourceIp), AffectedIps = make_set(SourceIp, 10000) by Fqdn, ThreatDescription
| where array_length(AffectedIps) > MinAffectedThreshold
| mv-expand SourceIp = AffectedIps
| order by TiTrafficCount desc, Fqdn asc, parse_ipv4(tostring(SourceIp)) asc

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedIpssummarize
Fqdnsummarize
ThreatDescriptionsummarize
TiTrafficCountsummarize
dCountSourceIpssummarize

Network endpoint to host executable correlation

#
Status
available
Severity
medium
Time window
1d
Group by
$left.suspectExeName, $right.shortFileName
Source
github.com/Azure/Azure-Sentinel

'Correlates blocked URLs hosting [malicious] executables with host endpoint data to identify potential instances of executables of the same name having been recently run.

MITRE ATT&CK Coverage

TacticTechniques
Execution

Telemetry Coverage

Rule Body

id: 01f64465-b1ef-41ea-a7f5-31553a11ad43
name: Network endpoint to host executable correlation
description: |
  'Correlates blocked URLs hosting [malicious] executables with host endpoint data to identify potential instances of executables of the same name having been recently run.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: TrendMicro
    dataTypes:
      - CommonSecurityLog
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes: 
      - SecurityEvents 
  - connectorId: WindowsForwardedEvents
    dataTypes: 
      - WindowsEvent 
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204
query: |
  let endpointData = 
  (union isfuzzy=true
  (SecurityEvent
    | where EventID == 4688
    | extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
    ),
    (WindowsEvent
    | where EventID == 4688
    | extend  NewProcessName = tostring(EventData.NewProcessName)
    | extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
    | extend TargetUserName = tostring(EventData.TargetUserName)
    ));
  // Correlate suspect executables seen in TrendMicro rule updates with similar activity on endpoints
  CommonSecurityLog
  | where DeviceVendor =~ "Trend Micro"
  | where Activity =~ "Deny List updated" 
  | where RequestURL endswith ".exe"
  | project TimeGenerated, Activity , RequestURL , SourceIP, DestinationIP
  | extend suspectExeName = tolower(tostring(split(RequestURL, '/')[-1]))
  | join kind=innerunique (endpointData) on $left.suspectExeName == $right.shortFileName 
  | extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: TargetUserName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DestinationIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: RequestURL
version: 1.1.6
kind: Scheduled

Stages and Predicates

Let Binding: endpointData used in Stages 1, 8

let endpointData = (union isfuzzy=true
(SecurityEvent
  | where EventID == 4688
  | extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
  ),
  (WindowsEvent
  | where EventID == 4688
  | extend  NewProcessName = tostring(EventData.NewProcessName)
  | extend shortFileName = tolower(tostring(split(NewProcessName, '\\')[-1]))
  | extend TargetUserName = tostring(EventData.TargetUserName)
  ));

Stage 1: source

let endpointData

Stage 2: source

CommonSecurityLog

Stage 3: where

| where DeviceVendor =~ "Trend Micro"

Stage 4: where

| where Activity =~ "Deny List updated"

Stage 5: where

| where RequestURL endswith ".exe"

Stage 6: project

| project TimeGenerated, Activity , RequestURL , SourceIP, DestinationIP

Stage 7: extend

| extend suspectExeName = tolower(tostring(split(RequestURL, '/')[-1]))

Stage 8: join

join kind=innerunique (endpointData) on $left.suspectExeName, $right.shortFileName

Stage 9: extend

extend DnsDomain, HostName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Activityproject
DestinationIPproject
RequestURLproject
SourceIPproject
TimeGeneratedproject
suspectExeNameextend
DnsDomainextend
HostNameextend

Process Injection Initiated By MMC

#
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

This query searches for suspicious behavior initiated by MMC. This is done by looking at a number of actions that are commonly associated with process injection.

Known False Positives

  • Some legitimate MMC snap-ins may perform actions that are similar to process injection. These will require additional filtering.

MITRE ATT&CK Coverage

References

Telemetry Coverage

Rule Body

let timeframe = 2*1h;
DeviceEvents
| where ingestion_time() >= ago(timeframe)
| where InitiatingProcessFileName =~ "mmc.exe"
// Look for actions associated with process injection.
| where ActionType in~ ("CreateRemoteThreadApiCall", "MemoryRemoteProtect", "NtAllocateVirtualMemoryRemoteApiCall", "NtMapViewOfSectionRemoteApiCall","ReadProcessMemoryApiCall", "SetThreadContextRemoteApiCall", "QueueUserApcRemoteApiCall")
| extend ParsedCommandLine=parse_command_line(InitiatingProcessCommandLine, "windows")
// When a .msc file is opened in MMC, the file path is passed as an argument to MMC.
// Based on testing this is the first argument in the command line. In some cases a command-line switch /32 is passed as the first argument
// and the file path is the second argument. This is handled by the iif statement below.
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)
// Begin environment-specific filter.
// End environment-specific filter.

Stages and Predicates

Parameters

let timeframe = 2*1h;

Stage 1: source

DeviceEvents

Stage 2: where

| where ingestion_time() >= ago(timeframe)

Stage 3: where

| where InitiatingProcessFileName =~ "mmc.exe"

Stage 4: where

| where ActionType in~ ("CreateRemoteThreadApiCall", "MemoryRemoteProtect", "NtAllocateVirtualMemoryRemoteApiCall", "NtMapViewOfSectionRemoteApiCall","ReadProcessMemoryApiCall", "SetThreadContextRemoteApiCall", "QueueUserApcRemoteApiCall")

Stage 5: extend (3 consecutive steps)

| extend ParsedCommandLine=parse_command_line(InitiatingProcessCommandLine, "windows")
| extend MscFile=ParsedCommandLine[1]
| extend MscFile=iif(MscFile startswith "/", ParsedCommandLine[2], MscFile)

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypein
  • CreateRemoteThreadApiCall
  • MemoryRemoteProtect
  • NtAllocateVirtualMemoryRemoteApiCall
  • NtMapViewOfSectionRemoteApiCall
  • QueueUserApcRemoteApiCall
  • ReadProcessMemoryApiCall
  • SetThreadContextRemoteApiCall
field:"ActionType" kind:in
InitiatingProcessFileNameeq
  • mmc.exe
field:"InitiatingProcessFileName" kind:eq value:"mmc.exe"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ParsedCommandLineextend
MscFileextend

ProofpointPOD - Weak ciphers

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

'Detects when weak TLS ciphers are used.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 56b0a0cd-894e-4b38-a0a1-c41d9f96649a
name: ProofpointPOD - Weak ciphers
description: |
  'Detects when weak TLS ciphers are used.'
severity: Low
status: Available 
requiredDataConnectors:
  - connectorId: ProofpointPOD
    dataTypes:
      - ProofpointPOD_message_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1573
query: |
  let lbtime = 1h;
  let tls_ciphers = dynamic(['RC4-SHA', 'DES-CBC3-SHA']);
  ProofpointPOD
  | where EventType == 'message'
  | where TlsCipher in (tls_ciphers)
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let lbtime = 1h;
let tls_ciphers = dynamic(['RC4-SHA', 'DES-CBC3-SHA']);

Stage 1: source

ProofpointPOD

Stage 2: where

| where EventType == 'message'

Stage 3: where

| where TlsCipher in (tls_ciphers)

Stage 4: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
IPCustomEntityextend

Subnets Alerts for Prancer

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

'High severity subnet alerts found by Prancer.'

MITRE ATT&CK Coverage

TacticTechniques
Reconnaissance

Rule Body

id: 10be8f37-d83c-4b7e-81c2-1271c51ac09f
name: Subnets Alerts for Prancer
description: |
  'High severity subnet alerts found by Prancer.'
severity: High
requiredDataConnectors:
- connectorId: PrancerLogData
  dataTypes:
    - prancer_CL
queryFrequency: 5h
queryPeriod: 5h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: SingleAlert
status: Available
tactics:
  - Reconnaissance
relevantTechniques:
  - T1595
query: |
  union prancer_CL
  | where deviceProduct_s == 'azure'
  | where parse_json(data_data_snapshots_s)[0].type == 'Microsoft.Network/virtualNetworks/subnets'
  | where data_data_severity_s == 'High' and data_data_result_s == 'failed'
  | extend snapshot = parse_json(data_data_snapshots_s)
  | mv-expand snapshot 
  | extend
      id = tostring(snapshot.id),
      structure = tostring(snapshot.structure),
      reference = tostring(snapshot.reference),
      source = tostring(snapshot.source),
      collection = tostring(snapshot.collection),
      type = tostring(snapshot.type),
      region = tostring(snapshot.region),
      resourceTypes = tostring(snapshot.resourceTypes),
      path = tostring(snapshot.path)

customDetails:
entityMappings:
  - entityType: AzureResource
    fieldMappings:
      - identifier: ResourceId
        columnName: path
alertDetailsOverride:
  alertDisplayNameFormat: "{{data_data_message_s}}"  
  alertDescriptionFormat: "{{data_data_description_s}}"
  alertSeverityColumnName: "{{data_data_severity_s}}"
  alertDynamicProperties:
    - alertProperty: RemediationSteps
      value: data_data_remediation_description_s
version: 1.0.2
kind: Scheduled


Stages and Predicates

Stage 1: union

union of 1 branches

Stage 2: source

prancer_CL

Stage 3: where

where deviceProduct_s == "azure"

Stage 4: where

where parse_json(data_data_snapshots_s)[0].type == "Microsoft.Network/virtualNetworks/subnets"

Stage 5: where

where data_data_result_s == "failed" and data_data_severity_s == "High"

Stage 6: extend

extend snapshot

Stage 7: mv-expand

mv-expand snapshot

Stage 8: extend

extend collection, id, path, reference, region, resourceTypes, source, structure, type

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
data_data_result_seq
  • failed
field:"data_data_result_s" kind:eq value:"failed"
data_data_severity_seq
  • High
field:"data_data_severity_s" kind:eq value:"High"
data_data_snapshots_s[0].typeeq
  • Microsoft.Network/virtualNetworks/subnets transforms: kusto_accessor_expr:{"expression":"parse_json(data_data_snapshots_s)[0].type","field":"data_data_snapshots_s[0].type"}, kusto_dynamic_path
field:"data_data_snapshots_s[0].type" kind:eq value:"Microsoft.Network/virtualNetworks/subnets"
deviceProduct_seq
  • azure
field:"deviceProduct_s" kind:eq value:"azure"

Output Fields

These fields are emitted when the rule matches.

FieldSource
snapshotextend
collectionextend
idextend
pathextend
referenceextend
regionextend
resourceTypesextend
sourceextend
structureextend
typeextend

Subscription moved to another tenant

#
Severity
low
Time window
20m
Source
github.com/Azure/Azure-Sentinel

'This detection uses AzureActivity logs (Security category) to identify when a subscription is moved to another tenant. A threat actor may move a subscription into their own tenant to circumvent local resource deployment and logging policies. Once moved, threat actors may deploy resources and perform malicious activities such as crypto mining. This is a technique known as "subscription hijacking". More information can be found here: https://techcommunity.microsoft.com/t5/microsoft-365-defender-blog/hunt-for-compromised-azure-subscriptions-using-microsoft/ba-p/3607121'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: 48c026d8-7f36-4a95-9568-6f1420d66e37
kind: Scheduled
name: Subscription moved to another tenant
description: |
  'This detection uses AzureActivity logs (Security category) to identify when a subscription is moved to another tenant.
  A threat actor may move a subscription into their own tenant to circumvent local resource deployment and logging policies.
  Once moved, threat actors may deploy resources and perform malicious activities such as crypto mining.
  This is a technique known as "subscription hijacking". More information can be found here: https://techcommunity.microsoft.com/t5/microsoft-365-defender-blog/hunt-for-compromised-azure-subscriptions-using-microsoft/ba-p/3607121'
severity: Low
requiredDataConnectors:
  - connectorId: AzureActivity
    dataTypes:
      - AzureActivity
queryPeriod: 20m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1496
query: |
  let queryFrequency = 5m;
  let eventCapture = "moved from tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}) to tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})";
  AzureActivity
  | where ingestion_time() > ago(queryFrequency)
  | where CategoryValue =~ "Security"
  | where OperationNameValue =~ "Microsoft.Subscription/updateTenant/action"
  | extend Properties_d = coalesce(parse_json(Properties), Properties_d)
  | where isnotempty(Properties_d)
  | extend Summary = tostring(Properties_d.message)
  | extend EventCapture = extract_all(eventCapture, Summary)
  | extend SourceTenantId = iff(isnotempty(EventCapture), EventCapture[0][0], "")
  | extend DestinationTenantId = iff(isnotempty(EventCapture), EventCapture[0][1], "")
  | extend 
      Name = split(Caller, "@", 0)[0],
      UPNSuffix = split(Caller, "@", 1)[0]
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: AzureResource
    fieldMappings: 
      - identifier: ResourceId
        columnName: _ResourceId                 
  - entityType: Account
    fieldMappings: 
      - identifier: FullName
        columnName: Caller
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
customDetails:
  DestinationTenantId: DestinationTenantId
  SourceTenantId: SourceTenantId
alertDetailsOverride:
  alertDescriptionFormat: |
    The user {{Caller}} moved a subscription:
    
    {{Summary}}
    
    If this was not expected, it may indicate a subscription hijacking event.
  alertDisplayNameFormat: |
    Subscription {{SubscriptionId}} changed tenants
version: 1.0.1

Stages and Predicates

Parameters

let queryFrequency = 5m;

Let Binding: eventCapture used in Stage 7

let eventCapture = "moved from tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}) to tenant ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})";

Stage 1: source

AzureActivity

Stage 2: where

| where ingestion_time() > ago(queryFrequency)

Stage 3: where

| where CategoryValue =~ "Security"

Stage 4: where

| where OperationNameValue =~ "Microsoft.Subscription/updateTenant/action"

Stage 5: extend

| extend Properties_d = coalesce(parse_json(Properties), Properties_d)

Stage 6: where

| where isnotempty(Properties_d)

Stage 7: extend (5 consecutive steps)

| extend Summary = tostring(Properties_d.message)
| extend EventCapture = extract_all(eventCapture, Summary)
| extend SourceTenantId = iff(isnotempty(EventCapture), EventCapture[0][0], "")
| extend DestinationTenantId = iff(isnotempty(EventCapture), EventCapture[0][1], "")
| extend 
    Name = split(Caller, "@", 0)[0],
    UPNSuffix = split(Caller, "@", 1)[0]

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Properties_dextend
Summaryextend
EventCaptureextend
SourceTenantIdextend
DestinationTenantIdextend
Nameextend
UPNSuffixextend

Theom - Critical data in API headers or body

#

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

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

"Creates Sentinel incidents for critical/high Theom risks, associated with ruleId's TRIS0007 to TRIS0010 and TRIS0014"

MITRE ATT&CK Coverage

TacticTechniques
Collection

Rule Body

id: 2ef36aaa-ec4a-473a-9734-f364ce8868f8
name: Theom - Critical data in API headers or body
description: |
   "Creates Sentinel incidents for critical/high Theom risks, associated with ruleId's TRIS0007 to TRIS0010 and TRIS0014"
severity: High
status : Available
requiredDataConnectors: 
  - connectorId: Theom
    dataTypes:
      - TheomAlerts_CL
queryPeriod: 5m
queryFrequency: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
relevantTechniques:
  - T1119
query: |
   TheomAlerts_CL
     | where ( customProps_RuleId_s == "TRIS0007" or customProps_RuleId_s == "TRIS0008" or
       customProps_RuleId_s == "TRIS0009" or customProps_RuleId_s == "TRIS0010" or customProps_RuleId_s == "TRIS0014" )  
       and (priority_s == "P1" or priority_s == "P2")
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: "Theom Alert ID: {{id_s}} "
  alertDescriptionFormat: "\nSummary: {{summary_s}}  \nAdditional info: {{details_s}}\nPlease investigate further on Theom UI at {{deepLink_s}}\n"
entityMappings:
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: customProps_AssetName_s
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: deepLink_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

TheomAlerts_CL

Stage 2: where

| where ( customProps_RuleId_s == "TRIS0007" or customProps_RuleId_s == "TRIS0008" or
    customProps_RuleId_s == "TRIS0009" or customProps_RuleId_s == "TRIS0010" or customProps_RuleId_s == "TRIS0014" )  
    and (priority_s == "P1" or priority_s == "P2")

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
customProps_RuleId_seq
  • TRIS0007
  • TRIS0008
  • TRIS0009
  • TRIS0010
  • TRIS0014
field:"customProps_RuleId_s" kind:eq
priority_seq
  • P1
  • P2
field:"priority_s" kind:eq

TI Map File Hash to DeviceFileEvents Event

#
Severity
medium
Time window
14d
Group by
$left.FileHashValue, $right.FileHashValue, DeviceId, Id, IndicatorId, ObservableValue
Source
github.com/Azure/Azure-Sentinel

Identifies a match in DeviceFileEvents Event data from any FileHash IOC from TI

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Telemetry Coverage

Rule Body

id: d6f04915-4471-4cb3-b163-a8b72997cf72
name: TI Map File Hash to DeviceFileEvents Event
description: |
  'Identifies a match in DeviceFileEvents Event data from any FileHash IOC from TI'
severity: Medium
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceFileEvents
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let DeviceFileEvents_ = (union
  (DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA1) | extend FileHashValue = SHA1),
  (DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA256) | extend FileHashValue = SHA256));
  let Hashes = DeviceFileEvents_ | distinct FileHashValue;
  ThreatIntelIndicators
  //extract key part of kv pair
        | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
        | where IndicatorType == "file"
        | extend FileHashType = replace("'", "", substring(ObservableKey, indexof(ObservableKey, "hashes.") + 7, strlen(ObservableKey) - indexof(ObservableKey, "hashes.") - 7))
        | extend FileHashValue = ObservableValue
        | extend IndicatorId = tostring(split(Id, "--")[2])
  | where isnotempty(FileHashValue)
  | where TimeGenerated > ago(ioc_lookBack)
  //  | where FileHashValue in (Hashes)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
  | where IsActive and (ValidUntil > now() or isempty(ValidUntil))
  | extend Description = tostring(parse_json(Data).description)
  | where Description !contains_cs "State: inactive;" and Description !contains_cs "State: falsepos;"
    | project-reorder *, FileHashType, FileHashValue, Type
  | join kind=innerunique (DeviceFileEvents_) on $left.FileHashValue == $right.FileHashValue
  | where TimeGenerated < ValidUntil
  | summarize TimeGenerated = arg_max(TimeGenerated, *) by  IndicatorId, DeviceId
  | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | project TimeGenerated, TrafficLightProtocolLevel, Description, ActivityGroupNames, IndicatorId, FileHashValue, FileHashType, ValidUntil, Confidence, ActionType, DeviceId, DeviceName, FolderPath, RequestAccountDomain, RequestAccountName, RequestAccountSid, MachineGroup
  | extend timestamp = TimeGenerated
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: RequestAccountName
      - identifier: Sid
        columnName: RequestAccountSid
      - identifier: NTDomain
        columnName: RequestAccountDomain
  - entityType: FileHash
    fieldMappings:
      - identifier: Value
        columnName: FileHashValue
      - identifier: Algorithm
        columnName: FileHashType
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
version: 1.0.4
kind: Scheduled

Stages and Predicates

Parameters

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

Let Binding: DeviceFileEvents_ used in Stages 1, 13

let DeviceFileEvents_ = (union
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA1) | extend FileHashValue = SHA1),
(DeviceFileEvents | where TimeGenerated > ago(dt_lookBack) | where isnotempty(SHA256) | extend FileHashValue = SHA256));

Let Binding: Hashes

let Hashes = DeviceFileEvents_ | distinct FileHashValue;

Stage 1: source

let DeviceFileEvents_

Stage 2: source

ThreatIntelIndicators

Stage 3: extend

extend IndicatorType

Stage 4: where

where IndicatorType == "file"

Stage 5: extend (3 consecutive steps)

extend FileHashType, FileHashValue, IndicatorId

Stage 6: where

where isnotempty(FileHashValue)

Stage 7: where

where TimeGenerated > ago(1209600s)

Stage 8: summarize

summarize LatestIndicatorTime by Id, ObservableValue

Stage 9: where

where (isempty(ValidUntil) or ValidUntil > now()) and IsActive == true

Stage 10: extend

extend Description

Stage 11: where

where not (Description contains_cs "State: falsepos;") and not (Description contains_cs "State: inactive;")

Stage 12: project-reorder

project-reorder

Stage 13: join

join kind=innerunique (DeviceFileEvents_) on $left.FileHashValue, $right.FileHashValue

Stage 14: where

where TimeGenerated < ValidUntil

Stage 15: summarize

summarize TimeGenerated by IndicatorId, DeviceId

Stage 16: extend (3 consecutive steps)

extend ActivityGroupNames, Description, TrafficLightProtocolLevel

Stage 17: project

project ActionType, ActivityGroupNames, Confidence, Description, DeviceId, DeviceName, FileHashType, FileHashValue, FolderPath, IndicatorId, MachineGroup, RequestAccountDomain, RequestAccountName, RequestAccountSid, TimeGenerated, TrafficLightProtocolLevel, ValidUntil

Stage 18: extend

extend timestamp

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
FileHashValueis_not_null
  • (no value, null check)
field:"FileHashValue" kind:is_not_null
IndicatorTypeeq
  • file
field:"IndicatorType" kind:eq value:"file"
IsActiveeq
  • true transforms: boolean
field:"IsActive" kind:eq value:"true"
SHA1is_not_null
  • (no value, null check)
field:"SHA1" kind:is_not_null
SHA256is_not_null
  • (no value, null check)
field:"SHA256" kind:is_not_null
ValidUntilis_null
  • (no value, null check)
field:"ValidUntil" kind:is_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
DescriptioncontainsState: falsepos;excludes:Description field:"Description" value:"State: falsepos;"
DescriptioncontainsState: inactive;excludes:Description field:"Description" value:"State: inactive;"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActionTypeproject
ActivityGroupNamesproject
Confidenceproject
Descriptionproject
DeviceIdproject
DeviceNameproject
FileHashTypeproject
FileHashValueproject
FolderPathproject
IndicatorIdproject
MachineGroupproject
RequestAccountDomainproject
RequestAccountNameproject
RequestAccountSidproject
TimeGeneratedproject
TrafficLightProtocolLevelproject
ValidUntilproject
timestampextend

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

#
Severity
medium
Time window
14d
Group by
$left.TI_ipEntity, $right.SrcIpAddr, DstIpAddr, IndicatorId, ObservableValue
Source
github.com/Azure/Azure-Sentinel

This rule identifies Web Sessions for which the source IP address is a known IoC. This rule uses the Advanced Security Information Model (ASIM) and supports any web session source that complies with ASIM.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 0548be6c-135e-4eb6-b9ff-14a09df62c77
name: TI Map IP entity to Web Session Events (ASIM Web Session schema)
description: |
  This rule identifies Web Sessions for which the source IP address is a known IoC. This rule uses the [Advanced Security Information Model (ASIM)](https://aka.ms/AboutASIM) and supports any web session source that complies with ASIM.
severity: Medium
requiredDataConnectors:
  - connectorId: SquidProxy
    dataTypes:
      - SquidProxy_CL
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let HAS_ANY_MAX = 10000;
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  let IP_TI = ThreatIntelIndicators
  //extract key part of kv pair
       | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
       | where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
       | extend NetworkSourceIP = toupper(ObservableValue)
       | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
    | where TimeGenerated >= ago(ioc_lookBack)
    // As there is potentially more than 1 indicator type for matching IP, taking NetworkIP first, then others if that is empty.
    // Taking the first non-empty value based on potential IOC match availability
    | extend TI_ipEntity = NetworkSourceIP
    // Picking up only IOC's that contain the entities we want
    | where TI_ipEntity != "NO_IP"
    // Exclude local addresses, using the ipv4_is_private operator
    | where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."
    | extend ThreatType = tostring(Data.indicator_types[0])
    | extend IndicatorId = tostring(split(Id, "--")[2])
    | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId, ObservableValue
    | where IsActive and (ValidUntil > now() or isempty(ValidUntil));
  let IP_TI_list = toscalar(IP_TI
    | summarize NIoCs = dcount(TI_ipEntity), IoCs = make_set(TI_ipEntity)
    | project IoCs = iff(NIoCs > HAS_ANY_MAX, dynamic([]), IoCs));
  IP_TI
     | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
  // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  | join kind = innerunique (
      _Im_WebSession (starttime = ago(dt_lookBack), srcipaddr_has_any_prefix = IP_TI_list)
      | where isnotempty(SrcIpAddr)
      // renaming time column so it is clear the log this came from
      | extend imNWS_TimeGenerated = TimeGenerated
    )
    on $left.TI_ipEntity == $right.SrcIpAddr
  | where imNWS_TimeGenerated < ValidUntil
  | summarize imNWS_TimeGenerated = arg_max(imNWS_TimeGenerated, *) by IndicatorId, DstIpAddr
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | project imNWS_TimeGenerated, Description, ActivityGroupNames, IndicatorId, ThreatType, ValidUntil, Confidence, TI_ipEntity, Dvc, SrcIpAddr, DstIpAddr, Url
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: DstIpAddr
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
customDetails:
  EventTime: imNWS_TimeGenerated
  IoCDescription: Description
  ActivityGroupNames: ActivityGroupNames
  IndicatorId: IndicatorId
  ThreatType: ThreatType
  IoCExpirationTime: ValidUntil
  IoCConfidenceScore: Confidence
alertDetailsOverride:
  alertDisplayNameFormat: The IP {{SrcIpAddr}} of the web request matches an IP IoC
  alertDescriptionFormat: The source address {{SrcIpAddr}} of the web request for the URL {{Url}} matches a known indicator of compromise of {{ThreatType}}. Consult the threat intelligence feed for more information about the indicator.
version: 1.2.11
kind: Scheduled

Stages and Predicates

Parameters

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

let IP_TI is inlined into the numbered stages below.

Let Binding: IP_TI_list used in Stage 15

let IP_TI_list = toscalar(IP_TI
  | summarize NIoCs = dcount(TI_ipEntity), IoCs = make_set(TI_ipEntity)
  | project IoCs = iff(NIoCs > HAS_ANY_MAX, dynamic([]), IoCs));

Stages 1 to 13 define let IP_TI as the rule's main pipeline source. Stages 14 to 20 use it.

Stage 1: source

ThreatIntelIndicators

Stage 2: extend

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

Stage 3: where

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

Stage 4: extend

| extend NetworkSourceIP = toupper(ObservableValue)

Stage 5: extend

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

Stage 6: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 7: extend

| extend TI_ipEntity = NetworkSourceIP

Stage 8: where

| where TI_ipEntity != "NO_IP"

Stage 9: where

| where ipv4_is_private(TI_ipEntity) == false and TI_ipEntity !startswith "fe80" and TI_ipEntity !startswith "::" and TI_ipEntity !startswith "127."

Stage 10: extend

| extend ThreatType = tostring(Data.indicator_types[0])

Stage 11: extend

| extend IndicatorId = tostring(split(Id, "--")[2])

Stage 12: summarize

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

Stage 13: where

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

Stage 14: project-reorder

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

Stage 15: join

| join kind = innerunique (
    _Im_WebSession (starttime = ago(dt_lookBack), srcipaddr_has_any_prefix = IP_TI_list)
    | where isnotempty(SrcIpAddr)
    | extend imNWS_TimeGenerated = TimeGenerated
  )
  on $left.TI_ipEntity == $right.SrcIpAddr

Stage 16: where

where imNWS_TimeGenerated < ValidUntil

Stage 17: summarize

summarize imNWS_TimeGenerated by IndicatorId, DstIpAddr

Stage 18: extend

extend Description

Stage 19: extend

extend ActivityGroupNames

Stage 20: project

project ActivityGroupNames, Confidence, Description, DstIpAddr, Dvc, IndicatorId, SrcIpAddr, TI_ipEntity, ThreatType, Url, ValidUntil, imNWS_TimeGenerated

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
IndicatorTypein
  • ipv4-addr
  • ipv6-addr
  • network-traffic
field:"IndicatorType" kind:in
IsActiveeq
  • true transforms: boolean
field:"IsActive" kind:eq value:"true"
SrcIpAddris_not_null
  • (no value, null check)
field:"SrcIpAddr" kind:is_not_null
TI_ipEntityne
  • NO_IP
field:"TI_ipEntity" kind:ne value:"NO_IP"
ValidUntilis_null
  • (no value, null check)
field:"ValidUntil" kind:is_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
TI_ipEntitycidr_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:TI_ipEntity
TI_ipEntitystarts_with127.excludes:TI_ipEntity field:"TI_ipEntity" value:"127."
TI_ipEntitystarts_with::excludes:TI_ipEntity field:"TI_ipEntity" value:"::"
TI_ipEntitystarts_withfe80excludes:TI_ipEntity field:"TI_ipEntity" value:"fe80"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
Confidenceproject
Descriptionproject
DstIpAddrproject
Dvcproject
IndicatorIdproject
SrcIpAddrproject
TI_ipEntityproject
ThreatTypeproject
Urlproject
ValidUntilproject
imNWS_TimeGeneratedproject

Vectra Create Detection Alert for Accounts

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

This analytic rule is looking for new attacker behaviors observed by the Vectra Platform. The intent is to create entries in the SecurityAlert table for every new detection attached to an entity monitored by the Vectra Platform

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Rule Body

id: e796701f-6b39-4c54-bf8a-1d543a990784
name: Vectra Create Detection Alert for Accounts
version: 1.0.1
kind: Scheduled
description: This analytic rule is looking for new attacker behaviors observed by the Vectra Platform. The intent is to create entries in the SecurityAlert table for every new detection attached to an entity monitored by the Vectra Platform
status: Available
requiredDataConnectors:
  - connectorId: VectraXDR
    dataTypes:
      - Detections_Data_CL
severity: Medium
queryFrequency: 10m
queryPeriod: 10m
suppressionDuration: PT1H
suppressionEnabled: false
triggerOperator: GreaterThan
triggerThreshold: 0
tactics: 
  - Persistence
relevantTechniques:
  - T1546
query: |
  VectraDetections
  | where Type == "account"
  | extend
      entity_uid = ['Entity UID'],
      entity_id = ['Entity ID'],
      entity_type = ['Entity Type'],
      detection_id = ['Detection ID'],
      detection = ['Detection Name'],
      category = ['Detection Category'],
      detection_url = ['Vectra Pivot'],
      mitre = Mitre,
      tags = Tags
  | summarize arg_max(TimeGenerated, *) by ['Detection ID'], entity_uid
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: Name
    columnName: entity_uid
incidentConfiguration:
  createIncident: false
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
customDetails:
  entity_id: entity_id
  tags: tags
  entity_type: entity_type
  detection_id: detection_id
  mitre_techniques: mitre
alertDetailsOverride:
  alertDisplayNameFormat: Vectra AI Detection-  {{detection}}
  alertDescriptionFormat: Vectra AI has detected {{category}} - {{detection} on entity {{entity_uid}}.
  alertDynamicProperties:
  - alertProperty: AlertLink
    value: detection_url
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Stage 1: source

VectraDetections

Stage 2: where

| where Type == "account"

Stage 3: extend

| extend
    entity_uid = ['Entity UID'],
    entity_id = ['Entity ID'],
    entity_type = ['Entity Type'],
    detection_id = ['Detection ID'],
    detection = ['Detection Name'],
    category = ['Detection Category'],
    detection_url = ['Vectra Pivot'],
    mitre = Mitre,
    tags = Tags

Stage 4: summarize

| summarize arg_max(TimeGenerated, *) by ['Detection ID'], entity_uid

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Typeeq
  • account
field:"Type" kind:eq value:"account"

Output Fields

These fields are emitted when the rule matches.

FieldSource
entity_uidsummarize

Vulnerable Machines related to OMIGOD CVE-2021-38647

#
Severity
high
Time window
1d
Group by
Description, RecommendationName, RecommendationSubscriptionId, RemediationDescription, TenantId, VirtualMAchine, VulnerabilityId
Author
Microsoft Security Research
Source
github.com/Azure/Azure-Sentinel

'This query uses the Azure Defender Security Nested Recommendations data to find machines vulnerable to OMIGOD CVE-2021-38647. OMI is the Linux equivalent of Windows WMI and helps users manage configurations across remote and local environments. The query aims to find machines that have this OMI vulnerability (CVE-2021-38647). Security Nested Recommendations data is sent to Microsoft Sentinel using the continuous export feature of Azure Defender(refrence link below). Reference: https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure Reference: https://docs.microsoft.com/azure/security-center/continuous-export?tabs=azure-portal'

MITRE ATT&CK Coverage

Rule Body

id: 4d94d4a9-dc96-450a-9dea-4d4d4594199b
name: Vulnerable Machines related to OMIGOD CVE-2021-38647
description: |
  'This query uses the Azure Defender Security Nested Recommendations data to find machines vulnerable to OMIGOD CVE-2021-38647.
  OMI is the Linux equivalent of Windows WMI and helps users manage configurations across remote and local environments. The query aims to find machines that have this OMI vulnerability (CVE-2021-38647).
   Security Nested Recommendations data is sent to Microsoft Sentinel using the continuous export feature of Azure Defender(refrence link below).
   Reference: https://www.wiz.io/blog/omigod-critical-vulnerabilities-in-omi-azure
   Reference: https://docs.microsoft.com/azure/security-center/continuous-export?tabs=azure-portal'
severity: High
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Execution
relevantTechniques:
  - T1190
  - T1203
tags:
  - OMIGOD
  - CVE-2021-38647
query: |
  SecurityNestedRecommendation
  | where RemediationDescription has 'CVE-2021-38647'
  | parse ResourceDetails with * 'virtualMachines/' VirtualMAchine '"' *
  | summarize arg_min(TimeGenerated, *) by TenantId, RecommendationSubscriptionId, VirtualMAchine, RecommendationName,Description,RemediationDescription, tostring(AdditionalData),VulnerabilityId
  | extend HostName = tostring(split(VirtualMAchine, ".")[0]), DomainIndex = toint(indexof(VirtualMAchine, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(VirtualMAchine, DomainIndex + 1), VirtualMAchine)
entityMappings: 
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: VirtualMAchine
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
version: 1.0.5
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Protection" ]

Stages and Predicates

Stage 1: source

SecurityNestedRecommendation

Stage 2: where

| where RemediationDescription has 'CVE-2021-38647'

Stage 3: parse

| parse ResourceDetails with * 'virtualMachines/' VirtualMAchine '"' *

Stage 4: summarize

| summarize arg_min(TimeGenerated, *) by TenantId, RecommendationSubscriptionId, VirtualMAchine, RecommendationName,Description,RemediationDescription, tostring(AdditionalData),VulnerabilityId

Stage 5: extend

| extend HostName = tostring(split(VirtualMAchine, ".")[0]), DomainIndex = toint(indexof(VirtualMAchine, '.'))

Stage 6: extend

| extend HostNameDomain = iff(DomainIndex != -1, substring(VirtualMAchine, DomainIndex + 1), VirtualMAchine)
HostNameDomain =
ifDomainIndex != -1substring(VirtualMAchine, (DomainIndex + 1))
elseVirtualMAchine

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Descriptionsummarize
RecommendationNamesummarize
RecommendationSubscriptionIdsummarize
RemediationDescriptionsummarize
TenantIdsummarize
VirtualMAchinesummarize
VulnerabilityIdsummarize
DomainIndexextend
HostNameextend
HostNameDomainextend

Zscaler - Connections by dormant user

#
Status
available
Severity
high
Time window
14d
Group by
DstUserName
Source
github.com/Azure/Azure-Sentinel

'Detects ZPA connections by dormant user.'

MITRE ATT&CK Coverage

TacticTechniques
Persistence

Rule Body

id: 66bc77ee-3e45-11ec-9bbc-0242ac130002
name: Zscaler - Connections by dormant user
description: |
  'Detects ZPA connections by dormant user.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: CustomLogsAma
    datatypes:
      - ZPA_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1078
query: |
  let dt_lastlogintime = 14d;
  let activeUsers =
  ZPAEvent 
  | where TimeGenerated > ago(dt_lastlogintime)
  | where DvcAction == 'open'
  | summarize lastLoginTime = max(TimeGenerated) by DstUserName
  | summarize makelist(DstUserName);
  ZPAEvent
  | where DstUserName !in~ (activeUsers)
  | summarize EventCount = count() by DstUserName
  | project-away EventCount
  | extend AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let dt_lastlogintime = 14d;

Let Binding: activeUsers used in Stage 2

let activeUsers = ZPAEvent 
| where TimeGenerated > ago(dt_lastlogintime)
| where DvcAction == 'open'
| summarize lastLoginTime = max(TimeGenerated) by DstUserName
| summarize makelist(DstUserName);

Stage 1: source

ZPAEvent

Stage 2: where

| where DstUserName !in~ (activeUsers)

Stage 3: summarize

| summarize EventCount = count() by DstUserName

Stage 4: project-away

| project-away EventCount

Stage 5: extend

| extend AccountCustomEntity = DstUserName

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
DstUserNameeqactiveUsersexcludes:DstUserName field:"DstUserName" value:"activeUsers"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNamesummarize
AccountCustomEntityextend

Back to all Kusto rules