Detection rules › Kusto

Kusto rules: 2t

1Password - Potential insider privilege escalation via vault

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

This will alert when an actor grants, or updates their own permissions via a vault. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same. Ref: https://1password.com/ Ref: https://github.com/securehats/

MITRE ATT&CK Coverage

TacticTechniques
Privilege Escalation

Telemetry Coverage

Rule Body

id: a00ffbd8-1d1c-47a3-b0a6-7d70bd8017ed
name: 1Password - Potential insider privilege escalation via vault
version: 1.0.0
kind: Scheduled
description: |-
  This will alert when an actor grants, or updates their own permissions via a vault. Once this analytics rule is triggered it will group all related future alerts for upto an hour when all related entities are the same.

  Ref: https://1password.com/
  Ref: https://github.com/securehats/
severity: Medium
requiredDataConnectors:
  - connectorId: 1Password
    dataTypes:
      - OnePasswordEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1078
query: |-
  OnePasswordEventLogs_CL
  | where log_source == "auditevents"
  | where action has_any("grant", "update")
  | where object_type == "uva"
  | where tostring(actor_details.email) == tostring(aux_details.email)
  | extend
      TargetUsername = aux_details.email
      , ActorUsername = actor_details.email
      , SrcIpAddr = session.ip
subTechniques:
- T1078.004
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
suppressionDuration: 5h
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: ActorUsername
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: TargetUsername
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr

Stages and Predicates

Stage 1: source

OnePasswordEventLogs_CL

Stage 2: where

| where log_source == "auditevents"

Stage 3: where

| where action has_any("grant", "update")

Stage 4: where

| where object_type == "uva"

Stage 5: where

| where tostring(actor_details.email) == tostring(aux_details.email)

Stage 6: extend

| extend
    TargetUsername = aux_details.email
    , ActorUsername = actor_details.email
    , SrcIpAddr = session.ip

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actionmatch
  • grant transforms: term corpus 4 (kusto 4)
  • update transforms: term corpus 3 (kusto 3)
field:"action" kind:match
log_sourceeq
  • auditevents corpus 12 (kusto 12)
field:"log_source" kind:eq value:"auditevents"
object_typeeq
  • uva corpus 4 (kusto 4)
field:"object_type" kind:eq value:"uva"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActorUsernameextend
SrcIpAddrextend
TargetUsernameextend

[Entra ID] Application Granted Administrative Permission to Assign Microsoft Entra ID Roles

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

Detects when an application receives permission to assign Microsoft Entra ID roles. This can enable directory privilege escalation and should be reviewed immediately.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: ef34b272-930c-41c8-a682-8c2093cd2024
name: "[Entra ID] Application Granted Administrative Permission to Assign Microsoft Entra ID Roles"
version: 1.0.0
kind: Scheduled
description: |
  Detects when an application receives permission to assign Microsoft Entra ID roles. This can enable directory privilege escalation and should be reviewed immediately.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
  - Impact
  - PrivilegeEscalation
relevantTechniques:
  - T1098.003
query: |
  AuditLogs
  | where Category =~ "ApplicationManagement"
      and LoggedByService =~ "Core Directory"
      and OperationName in~ ("Add delegated permission grant", "Add app role assignment to service principal")
  | mv-apply TargetResource = TargetResources on
      (
      where TargetResource.type =~ "ServicePrincipal"
          and array_length(TargetResource.modifiedProperties) > 0
          and isnotnull(TargetResource.displayName)
      | extend props = TargetResource.modifiedProperties
      )
  | mv-apply Property = props on
      (
      where Property.displayName in~ ("AppRole.Value", "DelegatedPermissionGrant.Scope")
      | extend
          DisplayName = tostring(Property.displayName),
          PermissionGrant = trim('"', tostring(Property.newValue))
      )
  | where PermissionGrant has "RoleManagement.ReadWrite.Directory"
  | mv-apply Property = props on
      (
      where Property.displayName =~ "ServicePrincipal.DisplayName"
      | extend TargetAppDisplayName = trim('"', tostring(Property.newValue))
      )
  | mv-apply Property = props on
      (
      where Property.displayName =~ "ServicePrincipal.ObjectID"
      | extend TargetAppServicePrincipalId = trim('"', tostring(Property.newValue))
      )
  | 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 TargetAppDisplayName = tostring(iff(isnotempty(TargetAppDisplayName), TargetAppDisplayName, TargetAppServicePrincipalId))
  | extend
      InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]),
      InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
  | extend Initiator = InitiatingAccountName, Target = TargetAppDisplayName
  | extend
      Source_Network_IPLocation = ""
  | project
          Alert_Category_en = "Entra ID",
      Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
      Alert_Name_en = "Application Granted Administrative Permission to Assign Microsoft Entra ID Roles",
      Alert_Description_en = strcat("At Taiwan time", format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"), ". ", "EntraID:", "", ". ", "detected source user/service principal ", iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName), " granted application: ", TargetAppDisplayName, ".", " administrative permission to assign Microsoft Entra ID roles: ", ActivityDisplayName, ". Source IP: ", InitiatingIpAddress, ", source location: ", Source_Network_IPLocation),
      Alert_TriageStep_en = strcat(
                            "1. Confirm whether the operating user/service principal: ",
                            iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName),
                            ", source IP: ",
                            InitiatingIpAddress,
                            " is abnormal. ",
                            "2. Review the authorized target application ",
                            TargetAppDisplayName,
                            "  authorization content: ",
                            ActivityDisplayName,
                            " to determine whether it is an expected operation or maintenance task."
                        ),
      Alert_Containment_en = strcat(
                             "1. If determined to be unauthorized or suspicious privilege escalation, immediately remove the permissions from target application: ",
                             TargetAppDisplayName,
                             " and disable it.",
                             "2. Perform risk handling for the initiator  ",
                             iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName),
                             "  (revoke sessions/force re-authentication, and suspend directory management permissions if necessary)."
                         ),
      Alert_Remediation_en =
      "1. Strengthen application and permission governance: apply least privilege and administrator approval processes for high-risk Graph/Directory permissions, and include Service Principal credential/permission changes in regular audits.  "
      "2. Strengthen detection and automation: establish automated response (SOAR) for notification/source blocking/user verification.",
              Alert_Time_TW = datetime_utc_to_local(TimeGenerated, "Asia/Taipei"),
      Alert_Time_UTC0 = TimeGenerated,
      Event_Action = ActivityDisplayName,
      Event_Status = Result,
      Event_Description = PermissionGrant,
      Source_Identity_FullName = iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName),
      Source_Identity_ID = iff(isnotempty(InitiatingAadUserId), InitiatingAadUserId, InitiatingAppServicePrincipalId),
      Source_Identity_Type = iff(isnotempty(InitiatingUserPrincipalName), "User", "Service"),
      Source_Network_IPAddress = InitiatingIpAddress,
      Source_Network_IPLocation = Source_Network_IPLocation,
      Target_Identity_FullName = TargetAppDisplayName,
      Target_Identity_ID = TargetAppServicePrincipalId,
      Target_Identity_Type = "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: IP
    fieldMappings:
      - identifier: Address
        columnName: Source_Network_IPAddress
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: Target_Identity_FullName
alertDetailsOverride:
  alertDynamicProperties: []

Stages and Predicates

Stage 1: source

AuditLogs

Stage 2: where

| where Category =~ "ApplicationManagement"
    and LoggedByService =~ "Core Directory"
    and OperationName in~ ("Add delegated permission grant", "Add app role assignment to service principal")

Stage 3: kusto:mv-apply

| mv-apply TargetResource = TargetResources on
    (
    where TargetResource.type =~ "ServicePrincipal"
        and array_length(TargetResource.modifiedProperties) > 0
        and isnotnull(TargetResource.displayName)
    | extend props = TargetResource.modifiedProperties
    )

Stage 4: kusto:mv-apply

| mv-apply Property = props on
    (
    where Property.displayName in~ ("AppRole.Value", "DelegatedPermissionGrant.Scope")
    | extend
        DisplayName = tostring(Property.displayName),
        PermissionGrant = trim('"', tostring(Property.newValue))
    )

Stage 5: where

| where PermissionGrant has "RoleManagement.ReadWrite.Directory"

Stage 6: kusto:mv-apply

| mv-apply Property = props on
    (
    where Property.displayName =~ "ServicePrincipal.DisplayName"
    | extend TargetAppDisplayName = trim('"', tostring(Property.newValue))
    )

Stage 7: kusto:mv-apply

| mv-apply Property = props on
    (
    where Property.displayName =~ "ServicePrincipal.ObjectID"
    | extend TargetAppServicePrincipalId = trim('"', tostring(Property.newValue))
    )

Stage 8: extend (9 consecutive steps)

| 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 TargetAppDisplayName = tostring(iff(isnotempty(TargetAppDisplayName), TargetAppDisplayName, TargetAppServicePrincipalId))
| extend
    InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]),
    InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
| extend Initiator = InitiatingAccountName, Target = TargetAppDisplayName
| extend
    Source_Network_IPLocation = ""

Stage 9: project

| project
        Alert_Category_en = "Entra ID",
    Alert_SubCategory_en = "Anomaly Identity Privilege Modification",
    Alert_Name_en = "Application Granted Administrative Permission to Assign Microsoft Entra ID Roles",
    Alert_Description_en = strcat("At Taiwan time", format_datetime(datetime_utc_to_local(TimeGenerated, "Asia/Taipei"), "yyyy-MM-dd HH:mm:ss"), ". ", "EntraID:", "", ". ", "detected source user/service principal ", iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName), " granted application: ", TargetAppDisplayName, ".", " administrative permission to assign Microsoft Entra ID roles: ", ActivityDisplayName, ". Source IP: ", InitiatingIpAddress, ", source location: ", Source_Network_IPLocation),
    Alert_TriageStep_en = strcat(
                          "1. Confirm whether the operating user/service principal: ",
                          iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName),
                          ", source IP: ",
                          InitiatingIpAddress,
                          " is abnormal. ",
                          "2. Review the authorized target application ",
                          TargetAppDisplayName,
                          "  authorization content: ",
                          ActivityDisplayName,
                          " to determine whether it is an expected operation or maintenance task."
                      ),
    Alert_Containment_en = strcat(
                           "1. If determined to be unauthorized or suspicious privilege escalation, immediately remove the permissions from target application: ",
                           TargetAppDisplayName,
                           " and disable it.",
                           "2. Perform risk handling for the initiator  ",
                           iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName),
                           "  (revoke sessions/force re-authentication, and suspend directory management permissions if necessary)."
                       ),
    Alert_Remediation_en =
    "1. Strengthen application and permission governance: apply least privilege and administrator approval processes for high-risk Graph/Directory permissions, and include Service Principal credential/permission changes in regular audits.  "
    "2. Strengthen detection and automation: establish automated response (SOAR) for notification/source blocking/user verification.",
            Alert_Time_TW = datetime_utc_to_local(TimeGenerated, "Asia/Taipei"),
    Alert_Time_UTC0 = TimeGenerated,
    Event_Action = ActivityDisplayName,
    Event_Status = Result,
    Event_Description = PermissionGrant,
    Source_Identity_FullName = iff(isnotempty(InitiatingUserPrincipalName), InitiatingUserPrincipalName, InitiatingAppName),
    Source_Identity_ID = iff(isnotempty(InitiatingAadUserId), InitiatingAadUserId, InitiatingAppServicePrincipalId),
    Source_Identity_Type = iff(isnotempty(InitiatingUserPrincipalName), "User", "Service"),
    Source_Network_IPAddress = InitiatingIpAddress,
    Source_Network_IPLocation = Source_Network_IPLocation,
    Target_Identity_FullName = TargetAppDisplayName,
    Target_Identity_ID = TargetAppServicePrincipalId,
    Target_Identity_Type = "Service",
    Target_Resource_ID = "",
    Target_Resource_Name = "Microsoft Entra ID",
    Target_Resource_Type = "Microsoft Entra ID"

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Categoryeq
  • ApplicationManagement corpus 13 (kusto 13)
field:"Category" kind:eq value:"ApplicationManagement"
LoggedByServiceeq
  • Core Directory corpus 11 (kusto 10, sigma 1)
field:"azure_ad::logged_by_service" kind:eq value:"Core Directory"
OperationNamein
  • Add app role assignment to service principal corpus 2 (kusto 2)
  • Add delegated permission grant corpus 2 (kusto 2)
field:"OperationName" kind:in
PermissionGrantmatch
  • RoleManagement.ReadWrite.Directory transforms: term
field:"PermissionGrant" kind:match value:"RoleManagement.ReadWrite.Directory"
Property.displayNameeq
  • ServicePrincipal.DisplayName transforms: kusto_dynamic_path
  • ServicePrincipal.ObjectID transforms: kusto_dynamic_path
field:"Property.displayName" kind:eq
Property.displayNamein
  • AppRole.Value transforms: kusto_dynamic_path
  • DelegatedPermissionGrant.Scope transforms: kusto_dynamic_path
field:"Property.displayName" kind:in
TargetResource.displayNameis_not_null
  • (no value, null check)
field:"TargetResource.displayName" kind:is_not_null
TargetResource.typeeq
  • ServicePrincipal transforms: kusto_dynamic_path
field:"TargetResource.type" kind:eq value:"ServicePrincipal"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Alert_Category_enproject
Alert_Containment_enproject
Alert_Description_enproject
Alert_Name_enproject
Alert_Remediation_enproject
Alert_SubCategory_enproject
Alert_Time_TWproject
Alert_Time_UTC0project
Alert_TriageStep_enproject
Event_Actionproject
Event_Descriptionproject
Event_Statusproject
Source_Identity_FullNameproject
Source_Identity_IDproject
Source_Identity_Typeproject
Source_Network_IPAddressproject
Source_Network_IPLocationproject
Target_Identity_FullNameproject
Target_Identity_IDproject
Target_Identity_Typeproject
Target_Resource_IDproject
Target_Resource_Nameproject
Target_Resource_Typeproject

Access to AWS without MFA

#

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

This detects users with access to AWS (IAM or Federated via Okta) without enabled MFA. This is a default definition by Authomize and can be updated using the edit modal.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 48a9478b-440a-4330-b42c-94bd84dc904c
name: Access to AWS without MFA
kind: Scheduled
description: This detects users with access to AWS (IAM or Federated via Okta) without enabled MFA. This is a default definition by Authomize and can be updated using the edit modal.
severity: Medium
status: Available
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |-
  Authomize_v2_CL
  | where ingestion_time() >= ago(30m)
  | extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s
  | where Policy has "Access to AWS without MFA"
  | project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
requiredDataConnectors:
- connectorId: Authomize
  dataTypes: [ "Authomize_v2_CL" ]
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertnameFormat: Alert from Authomize - Access to AWS without MFA
  alertDescriptionFormat: |
    Refactor AWS policy based on activities in the last 60 days. This is a recommended update to IAM policy on AWS. Review the policy and apply it according to change control process. Authomize will have a recommended policy to be downloaded. See the URL for further details within the event details in your Authomize Tenant. 
  alertSeverity: Severity
  alertTactics: Tactics
  alertDynamicProperties:
  - alertProperty: AlertLink
    value: URL
customDetails:
  AuthomizeEventID: EventID
  EventName: Policy
  EventDescription: Description
  EventRecommendation: Recommendation
  ReferencedURL: URL
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.2

Stages and Predicates

Stage 1: source

Authomize_v2_CL

Stage 2: where

| where ingestion_time() >= ago(30m)

Stage 3: extend

| extend EventID = id_s, Policy = policy_name_s, Severity = severity_s,Description = description_s,Recommendation = recommendation_s,URL = url_s,Tactics = tactics_s

Stage 4: where

| where Policy has "Access to AWS without MFA"

Stage 5: project

| project  EventID, Policy, Severity, Description, Recommendation, URL, Category, Tactics

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Policymatch
  • Access to AWS without MFA transforms: term
field:"policy_name_s" kind:match value:"Access to AWS without MFA"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Categoryproject
Descriptionproject
EventIDproject
Policyproject
Recommendationproject
Severityproject
Tacticsproject
URLproject

Base64 encoded Windows process command-lines

#
Status
available
Severity
medium
Time window
1d
Group by
Account, AccountDomain, CommandLine, Computer, FileName, ParentProcessName
Source
github.com/Azure/Azure-Sentinel

Identifies instances of a base64-encoded PE file header seen in the process command line parameter.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: ca67c83e-7fff-4127-a3e3-1af66d6d4cad
name: Base64 encoded Windows process command-lines
description: |
  'Identifies instances of a base64-encoded PE file header seen in the process command line parameter.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsForwardedEvents
    dataTypes:
      - WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1059
  - T1027
  - T1140
query: |
  let ProcessCreationEvents=(union isfuzzy=true
  (SecurityEvent
  | where EventID==4688
  | where isnotempty(CommandLine)
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName,
  FileName = Process, CommandLine, ParentProcessName
  ),
  (WindowsEvent
  | where EventID==4688
  | where EventData has "TVqQAAMAAAAEAAA"
  | extend CommandLine = tostring(EventData.CommandLine)
  | where isnotempty(CommandLine)
  | extend ParentProcessName = tostring(EventData.ParentProcessName)
  | extend NewProcessName = tostring(EventData.NewProcessName)
  | extend Process=tostring(split(NewProcessName, '\\')[-1])
  | extend SubjectUserName = tostring(EventData.SubjectUserName)
  | extend SubjectDomainName = tostring(EventData.SubjectDomainName)
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName,
  FileName = Process, CommandLine, ParentProcessName));
  ProcessCreationEvents
  | where CommandLine contains "TVqQAAMAAAAEAAA"
  | extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Account
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.1.5
kind: Scheduled

Stages and Predicates

Stage 0: let

let ProcessCreationEvents = (union <inlined as stages below>;

Union isfuzzy=true (2 Sources)

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

Leg 1: SecurityEvent

SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName,
FileName = Process, CommandLine, ParentProcessName

Leg 2: WindowsEvent

WindowsEvent
| where EventID==4688
| where EventData has "TVqQAAMAAAAEAAA"
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), count() by Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName,
FileName = Process, CommandLine, ParentProcessName

Applied to the Combined Result

| where CommandLine contains "TVqQAAMAAAAEAAA" | extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountsummarize
AccountDomainsummarize
CommandLinesummarize
Computersummarize
EndTimeUtcsummarize
FileNamesummarize
ParentProcessNamesummarize
StartTimeUtcsummarize
DnsDomainextend
HostNameextend

BloodHound Attack Path Finding - Contributor Role on Tier Zero Resource

#

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 - Contributor Role on Tier Zero Resource

Rule Body

id: 36191460-f26a-4c4e-8492-71e6049e6bbb
name: BloodHound Attack Path Finding - Contributor Role on Tier Zero Resource
description: BloodHound Attack Path Finding - Contributor Role on Tier Zero Resource
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 == "AzureT0Contributor"
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 == "AzureT0Contributor"

Indicators

These rows show field, operator, and value matches.

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

BloodHound Attack Path Finding - Large Default Groups With GenericWrite 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 GenericWrite Privileges

Rule Body

id: 8193f4fc-383e-45b0-8ef5-15ef1eeae7e3
name: BloodHound Attack Path Finding - Large Default Groups With GenericWrite Privileges
description: BloodHound Attack Path Finding - Large Default Groups With GenericWrite
  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 == "LargeDefaultGroupsGenericWrite"
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 == "LargeDefaultGroupsGenericWrite"

Indicators

These rows show field, operator, and value matches.

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

Chia_Crypto_Mining IOC - June 2021

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

Identifies a match across IOC's related to Chia cryptocurrency farming/plotting activity

MITRE ATT&CK Coverage

TacticTechniques
Impact

Telemetry Coverage

Rule Body

id: 4d173248-439b-4741-8b37-f63ad0c896ae
name: Chia_Crypto_Mining IOC - June 2021
description: | 
  'Identifies a match across IOC's related to Chia cryptocurrency farming/plotting activity'
severity: Low
status: Available 
requiredDataConnectors: 
  - connectorId: WindowsForwardedEvents
    dataTypes: 
      - WindowsEvent 
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - Impact
relevantTechniques:
  - T1496
tags:
  - Chia 
  - ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/MultipleDataSources/ChiaCryptoMining.yaml
    version: 1.1.0
query:  |  
  let iocs = externaldata(DateAdded:string,IoC:string,Type:string,TLP:string) [@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/ChiaCryptoIOC.csv"] with (format="csv", ignoreFirstRecord=True);
  let process = (iocs | where Type =~ "process" | project IoC);
  //This query uses sysmon data, sections that have - | where Source == "Microsoft-Windows-Sysmon" - may need to be updated with latest
  WindowsEvent
  | where EventID == '4688' and EventData has_any (process)
  | extend NewProcessName = tostring(EventData.NewProcessName)
  | where NewProcessName  has_any (process)
  | extend ParentProcessName = tostring(EventData.ParentProcessName)
    , Account =  strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
    , NewProcessId = tostring(EventData.NewProcessId)
  | extend Computer, Account, File = tostring(split(NewProcessName, '\\', -1)[-1]), AlertDetail = 'Chia crypto IOC detected'
  | extend FilePath = replace_string(NewProcessName, File, '')
  | project TimeGenerated, File, AlertDetail, FilePath,Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type
  | extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
  - entityType: File
    fieldMappings:
      - identifier: Name
        columnName: File
      - identifier: Directory
        columnName: FilePath
version: 1.0.4
kind: Scheduled

Stages and Predicates

Let Binding: iocs

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

Let Binding: process used in Stages 2, 4

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

Stage 1: source

WindowsEvent

Stage 2: where

| where EventID == '4688' and EventData has_any (process)

Stage 3: extend

| extend NewProcessName = tostring(EventData.NewProcessName)

Stage 4: where

| where NewProcessName  has_any (process)

Stage 5: extend (3 consecutive steps)

| extend ParentProcessName = tostring(EventData.ParentProcessName)
  , Account =  strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
  , NewProcessId = tostring(EventData.NewProcessId)
| extend Computer, Account, File = tostring(split(NewProcessName, '\\', -1)[-1]), AlertDetail = 'Chia crypto IOC detected'
| extend FilePath = replace_string(NewProcessName, File, '')

Stage 6: project

| project TimeGenerated, File, AlertDetail, FilePath,Computer, NewProcessName, ParentProcessName, Account, NewProcessId, Type

Stage 7: extend (3 consecutive steps)

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventDatamatch
  • process transforms: term
field:"EventData" kind:match value:"process"
EventIDeq
  • 4688 corpus 23 (kusto 23)
field:"EventID" kind:eq value:"4688"
NewProcessNamematch
  • process transforms: term
field:"NewProcessName" kind:match value:"process"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountproject
AlertDetailproject
Computerproject
Fileproject
FilePathproject
NewProcessIdproject
NewProcessNameproject
ParentProcessNameproject
TimeGeneratedproject
Typeproject
AccountNTDomainextend
AccountNameextend
DomainIndexextend
HostNameextend
HostNameDomainextend

File Server Deleted

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

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

Rule Body

id: d7f23105-6756-43ae-973c-48b7441559d5
name: File Server Deleted
description: Detects when a file server is deleted from Veeam Backup & Replication.
  This might indicate unauthorized removal of critical components.
severity: High
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 28950\n| extend Name =  extract(\"\
  Name=\\\"(?<Name>[^\\\"]*)\\\"\", 1, SyslogMessage)\n| project\n    Date = format_datetime(TimeGenerated,\
  \ 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n    EventId = instanceId,\n\
  \    UserName = user,\n    [\"Object Name\"] = Name,\n    MessageDetails = Description,\n\
  \    Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 28950

Stage 3: extend

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

Stage 4: project

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

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Object Nameproject
Severityproject
UserNameproject

GCP IAM - Publicly exposed storage bucket

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

Detects possible misconfiguration for bucket policy making it publicly available.

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Telemetry Coverage

Rule Body

id: 4a433846-4b05-4a27-99d7-92093feded79
name: GCP IAM - Publicly exposed storage bucket
description: |
  'Detects possible misconfiguration for bucket policy making it publicly available.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: GCPIAMDataConnector
    dataTypes:
      - GCP_IAM
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1069
query: |
  GCP_IAM
  | where PayloadMethodname =~ 'google.iam.v1.IAMPolicy.SetIamPolicy'
  | where ResourceType has 'bucket'
  | extend members = parse_json(todynamic(PayloadRequestPolicyBindings))[0]['members']
  | extend action = parse_json(todynamic(PayloadRequestPolicyBindings))[0]['action']
  | where members in~ ('allUsers', 'allAuthenticatedUsers')
  | where action =~ 'ADD'
  | extend AccountName = tostring(split(PayloadAuthenticationinfoPrincipalemail, "@")[0]), AccountUPNSuffix = tostring(split(PayloadAuthenticationinfoPrincipalemail, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: PayloadAuthenticationinfoPrincipalemail
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

GCP_IAM

Stage 2: where

| where PayloadMethodname =~ 'google.iam.v1.IAMPolicy.SetIamPolicy'

Stage 3: where

| where ResourceType has 'bucket'

Stage 4: extend

| extend members = parse_json(todynamic(PayloadRequestPolicyBindings))[0]['members']

Stage 5: extend

| extend action = parse_json(todynamic(PayloadRequestPolicyBindings))[0]['action']

Stage 6: where

| where members in~ ('allUsers', 'allAuthenticatedUsers')

Stage 7: where

| where action =~ 'ADD'

Stage 8: extend

| extend AccountName = tostring(split(PayloadAuthenticationinfoPrincipalemail, "@")[0]), AccountUPNSuffix = tostring(split(PayloadAuthenticationinfoPrincipalemail, "@")[1])

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
membersextend
actionextend
AccountNameextend
AccountUPNSuffixextend

GitHub - User was blocked

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

Detect activities when a user was blocked on the repository. This query runs every day and its severity is Medium.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 0b85a077-8ba5-4cb5-90f7-1e882afe10c8
name: GitHub - User was blocked
description: |
  'Detect activities when a user was blocked on the repository. 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 Action == "org.block_user"
    | project Actor, Action 
    | 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 Action == "org.block_user"

Stage 3: project

| project Actor, Action

Stage 4: extend

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

Stage 5: extend

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actioneq
  • org.block_user
field:"Action" kind:eq value:"org.block_user"

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionproject
Actorproject
Nameextend
UPNSuffixextend

Google DNS - UNC2452 (Nobelium) APT Group activity

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

'Detects UNC2452 (Nobelium) APT Group activity.'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 22a613ea-c338-4f91-bbd3-3be97b00ebf9
name: Google DNS - UNC2452 (Nobelium) APT Group activity
description: |
  'Detects UNC2452 (Nobelium) APT Group activity.'
severity: High
requiredDataConnectors:
  - connectorId: GCPDNSDataConnector
    dataTypes:
      - GCPCloudDNS
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1095
query: |
  GCPCloudDNS
  | where Query has_any ('tomasubiera.com', 'theandersonco.com', 'stonecrestnews.com', 'nordicmademedia.com')
  | extend DNSCustomEntity = Query, IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: DNS
    fieldMappings:
      - identifier: DomainName
        columnName: DNSCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

GCPCloudDNS

Stage 2: where

| where Query has_any ('tomasubiera.com', 'theandersonco.com', 'stonecrestnews.com', 'nordicmademedia.com')

Stage 3: extend

| extend DNSCustomEntity = Query, IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Querymatch
  • nordicmademedia.com transforms: term
  • stonecrestnews.com transforms: term
  • theandersonco.com transforms: term
  • tomasubiera.com transforms: term
field:"Query" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
DNSCustomEntityextend
IPCustomEntityextend

Guardian- Content Access Control Blocked List Policy Violation Detection

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

'This alert creates an incident when Content Access Control Blocked List Policy Violation detected from the Guardian.'

Rule Body

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

Stages and Predicates

Stage 1: source

Guardian

Stage 2: where

| where PolicyViolatedControlFeature =~ 'Blocked List'

Stage 3: where

| where Severity =~ 'Medium'

Indicators

These rows show field, operator, and value matches.

Guardian- Not Safe For Work Policy Violation Detection

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

'This alert creates an incident when Not Safe For Work Policy Violation detected from the Guardian.'

Rule Body

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

Stages and Predicates

Stage 1: source

Guardian

Stage 2: where

| where PolicyViolatedControlFeature =~ 'Not Safe For Work'

Stage 3: where

| where Severity =~ 'Low'

Indicators

These rows show field, operator, and value matches.

Known Malware Detected

#
Status
available
Severity
medium
Time window
1h
Group by
deviceDetails_deviceIpAddress_s, deviceDetails_deviceName_s, eventTime, processDetails_fullUserName_s, processDetails_targetName_s
Source
github.com/Azure/Azure-Sentinel

'This creates an incident when a known Malware is detected on a endpoint managed by a Carbon Black.'

MITRE ATT&CK Coverage

TacticTechniques
Execution

Rule Body

id: 9f86885f-f31f-4e66-a39d-352771ee789e
name: Known Malware Detected
description: |
  'This creates an incident when a known Malware is detected on a endpoint managed by a Carbon Black.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: VMwareCarbonBlack
    dataTypes:
      - CarbonBlackEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204
query: |
  CarbonBlackEvents_CL
  | extend eventTime = datetime(1970-01-01) + tolong(eventTime_d/1000) * 1sec
  | where targetApp_effectiveReputation_s =~ "KNOWN_MALWARE"
  | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by eventTime, deviceDetails_deviceName_s, deviceDetails_deviceIpAddress_s, processDetails_fullUserName_s, processDetails_targetName_s

entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: processDetails_fullUserName_s
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: deviceDetails_deviceName_s
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: deviceDetails_deviceIpAddress_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

CarbonBlackEvents_CL

Stage 2: extend

| extend eventTime = datetime(1970-01-01) + tolong(eventTime_d/1000) * 1sec

Stage 3: where

| where targetApp_effectiveReputation_s =~ "KNOWN_MALWARE"

Stage 4: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), count() by eventTime, deviceDetails_deviceName_s, deviceDetails_deviceIpAddress_s, processDetails_fullUserName_s, processDetails_targetName_s

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
EndTimesummarize
StartTimesummarize
deviceDetails_deviceIpAddress_ssummarize
deviceDetails_deviceName_ssummarize
eventTimesummarize
processDetails_fullUserName_ssummarize
processDetails_targetName_ssummarize

Microsoft Entra ID Role Management Permission Grant

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

Identifies when the Microsoft Graph RoleManagement.ReadWrite.Directory (Delegated or Application) permission is granted to a service principal. This permission allows an application to read and manage the role-based access control (RBAC) settings for your company's directory. An adversary could use this permission to add an Microsoft Entra ID object to an Admin directory role and escalate privileges. Ref : https://docs.microsoft.com/graph/permissions-reference#role-management-permissions Ref : https://docs.microsoft.com/graph/api/directoryrole-post-members?view=graph-rest-1.0&tabs=http

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 1ff56009-db01-4615-8211-d4fda21da02d
name: Microsoft Entra ID Role Management Permission Grant
description: |
  'Identifies when the Microsoft Graph RoleManagement.ReadWrite.Directory (Delegated or Application) permission is granted to a service principal.
  This permission allows an application to read and manage the role-based access control (RBAC) settings for your company's directory.
  An adversary could use this permission to add an Microsoft Entra ID object to an Admin directory role and escalate privileges.
  Ref : https://docs.microsoft.com/graph/permissions-reference#role-management-permissions
  Ref : https://docs.microsoft.com/graph/api/directoryrole-post-members?view=graph-rest-1.0&tabs=http'
severity: High
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Persistence
  - Impact
relevantTechniques:
  - T1098.003
  - T1078.004
tags:
  - SimuLand
query: |
  AuditLogs
  | where Category =~ "ApplicationManagement" and LoggedByService =~ "Core Directory" and OperationName in~ ("Add delegated permission grant", "Add app role assignment to service principal")
  | mv-apply TargetResource = TargetResources on
    (
        where TargetResource.type =~ "ServicePrincipal" and array_length(TargetResource.modifiedProperties) > 0 and isnotnull(TargetResource.displayName)
        | extend props = TargetResource.modifiedProperties
    )
  | mv-apply Property = props on
    (
        where Property.displayName in~ ("AppRole.Value","DelegatedPermissionGrant.Scope")
        | extend DisplayName = tostring(Property.displayName), PermissionGrant = trim('"',tostring(Property.newValue))
    )
  | where PermissionGrant has "RoleManagement.ReadWrite.Directory"
  | mv-apply Property = props on
    (
        where Property.displayName =~ "ServicePrincipal.DisplayName"
        | extend TargetAppDisplayName = trim('"',tostring(Property.newValue))
    )
  | mv-apply Property = props on
    (
        where Property.displayName =~ "ServicePrincipal.ObjectID"
        | extend TargetAppServicePrincipalId = trim('"',tostring(Property.newValue))
    )
  | 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))
  | project TimeGenerated, OperationName, Result, PermissionGrant, TargetAppDisplayName, TargetAppServicePrincipalId, InitiatingAppName, InitiatingAppServicePrincipalId,
  InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIpAddress, TargetResources, AdditionalDetails, CorrelationId
  | extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: TargetAppDisplayName
      - identifier: AadUserId
        columnName: TargetAppServicePrincipalId
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: InitiatingAppName
      - identifier: AadUserId
        columnName: InitiatingAppServicePrincipalId
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: InitiatingUserPrincipalName
      - identifier: Name
        columnName: InitiatingAccountName
      - identifier: UPNSuffix
        columnName: InitiatingAccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: InitiatingAadUserId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: InitiatingIpAddress
version: 1.1.1
kind: Scheduled

Stages and Predicates

Stage 1: source

AuditLogs

Stage 2: where

| where Category =~ "ApplicationManagement" and LoggedByService =~ "Core Directory" and OperationName in~ ("Add delegated permission grant", "Add app role assignment to service principal")

Stage 3: kusto:mv-apply

| mv-apply TargetResource = TargetResources on
  (
      where TargetResource.type =~ "ServicePrincipal" and array_length(TargetResource.modifiedProperties) > 0 and isnotnull(TargetResource.displayName)
      | extend props = TargetResource.modifiedProperties
  )

Stage 4: kusto:mv-apply

| mv-apply Property = props on
  (
      where Property.displayName in~ ("AppRole.Value","DelegatedPermissionGrant.Scope")
      | extend DisplayName = tostring(Property.displayName), PermissionGrant = trim('"',tostring(Property.newValue))
  )

Stage 5: where

| where PermissionGrant has "RoleManagement.ReadWrite.Directory"

Stage 6: kusto:mv-apply

| mv-apply Property = props on
  (
      where Property.displayName =~ "ServicePrincipal.DisplayName"
      | extend TargetAppDisplayName = trim('"',tostring(Property.newValue))
  )

Stage 7: kusto:mv-apply

| mv-apply Property = props on
  (
      where Property.displayName =~ "ServicePrincipal.ObjectID"
      | extend TargetAppServicePrincipalId = trim('"',tostring(Property.newValue))
  )

Stage 8: extend (5 consecutive steps)

| 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))

Stage 9: project

| project TimeGenerated, OperationName, Result, PermissionGrant, TargetAppDisplayName, TargetAppServicePrincipalId, InitiatingAppName, InitiatingAppServicePrincipalId,
InitiatingUserPrincipalName, InitiatingAadUserId, InitiatingIpAddress, TargetResources, AdditionalDetails, CorrelationId

Stage 10: extend

| extend InitiatingAccountName = tostring(split(InitiatingUserPrincipalName, "@")[0]), InitiatingAccountUPNSuffix = tostring(split(InitiatingUserPrincipalName, "@")[1])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Categoryeq
  • ApplicationManagement corpus 13 (kusto 13)
field:"Category" kind:eq value:"ApplicationManagement"
LoggedByServiceeq
  • Core Directory corpus 11 (kusto 10, sigma 1)
field:"azure_ad::logged_by_service" kind:eq value:"Core Directory"
OperationNamein
  • Add app role assignment to service principal corpus 2 (kusto 2)
  • Add delegated permission grant corpus 2 (kusto 2)
field:"OperationName" kind:in
PermissionGrantmatch
  • RoleManagement.ReadWrite.Directory transforms: term
field:"PermissionGrant" kind:match value:"RoleManagement.ReadWrite.Directory"
Property.displayNameeq
  • ServicePrincipal.DisplayName transforms: kusto_dynamic_path
  • ServicePrincipal.ObjectID transforms: kusto_dynamic_path
field:"Property.displayName" kind:eq
Property.displayNamein
  • AppRole.Value transforms: kusto_dynamic_path
  • DelegatedPermissionGrant.Scope transforms: kusto_dynamic_path
field:"Property.displayName" kind:in
TargetResource.displayNameis_not_null
  • (no value, null check)
field:"TargetResource.displayName" kind:is_not_null
TargetResource.typeeq
  • ServicePrincipal transforms: kusto_dynamic_path
field:"TargetResource.type" kind:eq value:"ServicePrincipal"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AdditionalDetailsproject
CorrelationIdproject
InitiatingAadUserIdproject
InitiatingAppNameproject
InitiatingAppServicePrincipalIdproject
InitiatingIpAddressproject
InitiatingUserPrincipalNameproject
OperationNameproject
PermissionGrantproject
Resultproject
TargetAppDisplayNameproject
TargetAppServicePrincipalIdproject
TargetResourcesproject
TimeGeneratedproject
InitiatingAccountNameextend
InitiatingAccountUPNSuffixextend

OracleDBAudit - Query on Sensitive Table

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

'Detects when user queries sensitive tables.'

MITRE ATT&CK Coverage

TacticTechniques
Collection

Rule Body

id: d7fdcad5-ce96-4db6-9a5e-4a86a5166e5e
name: OracleDBAudit - Query on Sensitive Table
description: |
  'Detects when user queries sensitive tables.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
relevantTechniques:
  - T1005
query: |
  let sensitive_tbls = dynamic(['table_name1', 'table_name2']);
  OracleDatabaseAuditEvent
  | where isnotempty(DstUserName)
  | where isnotempty(Action)
  | extend TableName = replace(@'[,\(\)]', '', extract(@'(?i)SELECT(.*?)FROM\s(.*?)\s', 2, Action))
  | where isnotempty(TableName)
  | where TableName in (sensitive_tbls)
  | project TableName, DstUserName, DbAction
  | extend AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let sensitive_tbls = dynamic(['table_name1', 'table_name2']);

Stage 1: source

OracleDatabaseAuditEvent

Stage 2: where

| where isnotempty(DstUserName)

Stage 3: where

| where isnotempty(Action)

Stage 4: extend

| extend TableName = replace(@'[,\(\)]', '', extract(@'(?i)SELECT(.*?)FROM\s(.*?)\s', 2, Action))

Stage 5: where

| where isnotempty(TableName)

Stage 6: where

| where TableName in (sensitive_tbls)

Stage 7: project

| project TableName, DstUserName, DbAction

Stage 8: extend

| extend AccountCustomEntity = DstUserName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Actionis_not_null
  • (no value, null check)
field:"Action" kind:is_not_null
DstUserNameis_not_null
  • (no value, null check)
field:"DstUserName" kind:is_not_null
TableNamein
  • table_name1
  • table_name2
field:"TableName" kind:in
TableNameis_not_null
  • (no value, null check)
field:"TableName" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
DbActionproject
DstUserNameproject
TableNameproject
AccountCustomEntityextend

PROD (TM008.1) - GPO - Linked, Unlinked, or Enforced at Root of Domain

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

Linked, Unlinked, or Enforced GPOs at the Root of the Domain which could overwrite Tier Model GPOs.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: fe468726-b1dc-4e76-adad-3480de4a2c56
name: 'PROD (TM008.1) - GPO - Linked, Unlinked, or Enforced at Root of Domain'
description: |
  Linked, Unlinked, or Enforced GPOs at the Root of the Domain which could overwrite Tier Model GPOs.
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1484.001
query: |
  SecurityEvent 
  | where EventID == 5136 
      and EventData matches regex @"(?i)DC="
      and Activity has "modified" 
      and EventData has '"ObjectClass">domainDNS'
      and EventData has '<Data Name="AttributeLDAPDisplayName">gPLink</Data>' 
      and (EventData has ';0]' 
          or EventData has ';1]' 
          or EventData has ';2]')
      and EventData has '"OperationType">%%14674'
  | extend ObjectName = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
  | where ObjectName startswith "DC=" or ObjectName startswith "dc="
  | extend ObjectTier = "Tier 0"
  | extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
  | extend AttributeValue = extract("\\[([^\\]]+)\\]", 1, EventData)
  | extend LastThreeChars = substring(AttributeValue, strlen(AttributeValue) - 2, 3)
  | extend AttributeValueDescription = case(
      LastThreeChars == ";0", "Linked",
      LastThreeChars == ";1", "Unlinked",
      LastThreeChars == ";2", "Enforced",
      "Unknown")
  | extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
  | extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
  | project TimeGenerated
     , ObjectName
     , ObjectTier
     , ObjectClass
     , ActivityType = AttributeValueDescription
     , AttributeLDAP
     , Account
     , Domain
     , Computer
     , Channel
     , EventID
     , EventData
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ObjectName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Computer
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
customDetails:
  ObjectName: ObjectName
  ObjectTier: ObjectTier
  ObjectClass: ObjectClass
  ActivityType: ActivityType
  AttributeLDAP: AttributeLDAP
  Account: Account
  Domain: Domain
  Computer: Computer
  Channel: Channel
  EventID: EventID
  EventData: EventData
eventGroupingSettings:
  aggregationKind: AlertPerResult
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: '(TM008.1) A Tier 0 GPO was {{ActivityType}} at the Root of the Domain'
  alertDescriptionFormat: 'Changes to the Root of the Domain GPO could impact the Tier Model GPOs and other security controls.'
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136 
    and EventData matches regex @"(?i)DC="
    and Activity has "modified" 
    and EventData has '"ObjectClass">domainDNS'
    and EventData has '<Data Name="AttributeLDAPDisplayName">gPLink</Data>' 
    and (EventData has ';0]' 
        or EventData has ';1]' 
        or EventData has ';2]')
    and EventData has '"OperationType">%%14674'

Stage 3: extend

| extend ObjectName = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)

Stage 4: where

| where ObjectName startswith "DC=" or ObjectName startswith "dc="

Stage 5: extend (7 consecutive steps)

| extend ObjectTier = "Tier 0"
| extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
| extend AttributeValue = extract("\\[([^\\]]+)\\]", 1, EventData)
| extend LastThreeChars = substring(AttributeValue, strlen(AttributeValue) - 2, 3)
| extend AttributeValueDescription = case(
    LastThreeChars == ";0", "Linked",
    LastThreeChars == ";1", "Unlinked",
    LastThreeChars == ";2", "Enforced",
    "Unknown")
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)

Stage 6: project

| project TimeGenerated
   , ObjectName
   , ObjectTier
   , ObjectClass
   , ActivityType = AttributeValueDescription
   , AttributeLDAP
   , Account
   , Domain
   , Computer
   , Channel
   , EventID
   , EventData

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Activitymatch
  • modified transforms: term corpus 10 (kusto 10)
field:"Activity" kind:match value:"modified"
EventDatamatch
  • "ObjectClass">domainDNS transforms: term corpus 2 (kusto 2)
  • "OperationType">%%14674 transforms: term corpus 7 (kusto 7)
  • ;0] transforms: term corpus 4 (kusto 4)
  • ;1] transforms: term corpus 4 (kusto 4)
  • ;2] transforms: term corpus 4 (kusto 4)
  • <Data Name="AttributeLDAPDisplayName">gPLink</Data> transforms: term corpus 4 (kusto 4)
field:"EventData" kind:match
EventDataregex_match
  • (?i)DC= corpus 2 (kusto 2)
field:"EventData" kind:regex_match value:"(?i)DC="
EventIDeq
  • 5136 corpus 20 (kusto 18, elastic 2)
field:"EventID" kind:eq value:"5136"
ObjectNamestarts_with
  • DC=
  • dc=
field:"ObjectName" kind:starts_with

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountproject
ActivityTypeproject
AttributeLDAPproject
Channelproject
Computerproject
Domainproject
EventDataproject
EventIDproject
ObjectClassproject
ObjectNameproject
ObjectTierproject
TimeGeneratedproject

PRODAFT USTA - Payment card exposed

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

'Identifies a new PRODAFT USTA Payment Card Fraud Intelligence ticket that exposes a compromised payment card belonging to the monitored company. These typically originate from card breaches, skimmers, or infostealer infections and indicate that a payment card may be available to fraudsters. The full card number is never stored - only the BIN, last 4 digits, brand, and length are retained. Investigate the exposure and, where the card is your own, coordinate reissue with the issuing bank.'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: bd550c7f-5455-48de-b23d-30fced82d02b
name: PRODAFT USTA - Payment card exposed
description: |
  'Identifies a new PRODAFT USTA Payment Card Fraud Intelligence ticket that exposes a
  compromised payment card belonging to the monitored company. These typically originate
  from card breaches, skimmers, or infostealer infections and indicate that a payment card
  may be available to fraudsters. The full card number is never stored - only the BIN,
  last 4 digits, brand, and length are retained. Investigate the exposure and, where the
  card is your own, coordinate reissue with the issuing bank.'
severity: Medium
status: Available
tactics:
- Impact
relevantTechniques:
- T1657
requiredDataConnectors:
  - connectorId: PRODAFTUstaPCFICCPDefinition
    dataTypes:
      - PRODAFTUstaCompromisedCards_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
query: |
  PRODAFTUstaCompromisedCards
  | where Created > ago(1h)
  | project
      TimeGenerated,
      Created,
      TicketId,
      CompanyName,
      CardBrand,
      CardBin,
      CardLast4,
      CardMasked,
      CardLength,
      ExpirationDate,
      Status
customDetails:
  Ticket: TicketId
  Company: CompanyName
  Brand: CardBrand
  Card: CardMasked
  Expiration: ExpirationDate
  Status: Status
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AnyAlert
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

PRODAFTUstaCompromisedCards

Stage 2: where

| where Created > ago(1h)

Stage 3: project

| project
    TimeGenerated,
    Created,
    TicketId,
    CompanyName,
    CardBrand,
    CardBin,
    CardLast4,
    CardMasked,
    CardLength,
    ExpirationDate,
    Status

Output Fields

These fields are emitted when the rule matches.

FieldSource
CardBinproject
CardBrandproject
CardLast4project
CardLengthproject
CardMaskedproject
CompanyNameproject
Createdproject
ExpirationDateproject
Statusproject
TicketIdproject
TimeGeneratedproject

PulseConnectSecure - Large Number of Distinct Failed User Logins

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

'This query identifies evidence of failed login attempts from a large number of distinct users on a Pulse Connect Secure VPN server'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 1fa1528e-f746-4794-8a41-14827f4cb798
name: PulseConnectSecure - Large Number of Distinct Failed User Logins
description: |
  'This query identifies evidence of failed login attempts from a large number of distinct users on a Pulse Connect Secure VPN server'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
  let threshold = 100;
  PulseConnectSecure
  | where Messages startswith "Login failed"
  | summarize dcount(User) by Computer, bin(TimeGenerated, 15m)
  | where dcount_User > threshold
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
version: 1.0.4
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 100;

Stage 1: source

PulseConnectSecure

Stage 2: where

| where Messages startswith "Login failed"

Stage 3: summarize

| summarize dcount(User) by Computer, bin(TimeGenerated, 15m)

Stage 4: where

| where dcount_User > threshold

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Computersummarize

Snowflake - Abnormal query process time

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

'Detects query with abnormal proccess time.'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: 1376f5e5-855a-4f88-8591-19eba4575a0f
name: Snowflake - Abnormal query process time
description: |
  'Detects query with abnormal proccess time.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Snowflake
    dataTypes:
      - Snowflake
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1499
query: |
  let threshold = 10000;
  Snowflake
  | where QueryTotalElapsedTime > threshold
  | extend AccountCustomEntity = TargetUsername
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 10000;

Stage 1: source

Snowflake

Stage 2: where

| where QueryTotalElapsedTime > threshold

Stage 3: extend

| extend AccountCustomEntity = TargetUsername

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityextend

Suspicious LDAP Queries from Information Gathering Tools

#
Group by
DeviceName, Query, ReportId
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

This rule detects usage of LDAP information gathering tools such as BloodHound, SharpHound or potential custom tools mimicking the behavior of the legitimate tool ADExplorer from Sysinternals. The rule detects tool-specific LDAP queries and also contains a custom "Signature" field, providing information about the exact tool that most probably created the detected LDAP query.

Known False Positives

  • None expected.

MITRE ATT&CK Coverage

References

Telemetry Coverage

Rule Body

let timeframe = 2*1h;
let SharphoundCheck = IdentityQueryEvents
| where ActionType == "LDAP query"
| where QueryType contains "AllSecurityPrincipals" and QueryType contains "AllDomains" // Sharphound-specific behavior.
| where ingestion_time() >= ago(timeframe)
| extend Signature = "Sharphound";
let BloodhoundCheck = IdentityQueryEvents
| where ActionType == "LDAP query"
| where Query contains "sAMAccountType=805306369" and Query contains "userAccountControl&2" // Bloodhound.py-specific behavior.
| where ingestion_time() >= ago(timeframe)
| extend Signature = "Bloodhound.py";
let ADExplorerMimickCheck = IdentityQueryEvents
| where ActionType == "LDAP query"
| where Query contains "GUID=*" // ADExplorer-specific behavior.
| where ingestion_time() >= ago(timeframe)
| extend Signature = "ADExplorer mimicker";
SharphoundCheck | union ADExplorerMimickCheck, BloodhoundCheck
| summarize arg_min(Timestamp,*),Signature=make_set(Signature) by DeviceName, Query, ReportId
| sort by Timestamp desc
// Begin environment-specific filter.
// End environment-specific filter.

Stages and Predicates

Parameters

let timeframe = 2*1h;

let SharphoundCheck, let BloodhoundCheck and let ADExplorerMimickCheck are inlined into the numbered stages below.

Stage 1: source

let SharphoundCheck

Stage 2: source

let BloodhoundCheck

Stage 3: source

let ADExplorerMimickCheck

Stage 4: source

IdentityQueryEvents

Stage 5: where

where ActionType == "LDAP query"

Stage 6: where

where QueryType contains "AllDomains" and QueryType contains "AllSecurityPrincipals"

Stage 7: where

where ...

Stage 8: extend

extend Signature

Stage 9: union

union of 2 branches

Stage 10: source

IdentityQueryEvents

Stage 11: where

where ActionType == "LDAP query"

Stage 12: where

where Query contains "GUID=*"

Stage 13: where

where ...

Stage 14: extend

extend Signature

Stage 15: source

IdentityQueryEvents

Stage 16: where

where ActionType == "LDAP query"

Stage 17: where

where Query contains "sAMAccountType=805306369" and Query contains "userAccountControl&2"

Stage 18: where

where ...

Stage 19: extend

extend Signature

Stage 20: summarize

summarize Signature by DeviceName, Query, ReportId

Stage 21: sort

sort by Timestamp

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypeeq
  • LDAP query
field:"ActionType" kind:eq value:"LDAP query"
Querycontains
  • GUID=*
  • sAMAccountType=805306369
  • userAccountControl&2
field:"Query" kind:contains
QueryTypecontains
  • AllDomains
  • AllSecurityPrincipals
field:"QueryType" kind:contains

Output Fields

These fields are emitted when the rule matches.

FieldSource
DeviceNamesummarize
Querysummarize
ReportIdsummarize
Signaturesummarize

TI map Domain entity to PaloAlto CommonSecurityLog

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

Identifies a match in PaloAlto CommonSecurityLog table from any Domain IOC from TI

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: dd0a6029-ecef-4507-89c4-fc355ac52111
name: TI map Domain entity to PaloAlto CommonSecurityLog
description: |
  Identifies a match in PaloAlto CommonSecurityLog table from any Domain IOC from TI
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h; // Look back 1 hour
  let ioc_lookBack = 14d; // Look back 14 days
  // Create a list of top-level domains (TLDs) from the threat feed data for later validation
  let SecurityLog = materialize(
    CommonSecurityLog
      // Filter common security logs based on the specified time range
      | extend IngestionTime = ingestion_time()
      | where IngestionTime > ago(dt_lookBack)
      | where DeviceEventClassID =~ 'url'
      // Uncomment the line below to only alert on allowed connections
      //| where DeviceAction !~ "block-url"
      // Extract the domain from RequestURL, if not present, extract it from AdditionalExtensions
      | extend PA_Url = column_ifexists("RequestURL", "None")
      | extend PA_Url = iif(isempty(PA_Url) and AdditionalExtensions !startswith "PanOS", extract("([^\\\"]+)", 1, tolower(AdditionalExtensions)), trim('"', PA_Url))
      | extend PA_Url = iif(PA_Url !startswith "http://" and ApplicationProtocol !~ "ssl", strcat('http://', PA_Url), iif(PA_Url !startswith "https://" and ApplicationProtocol =~ "ssl", strcat('https://', PA_Url), PA_Url))
      | extend Domain = trim('"', tostring(parse_url(PA_Url).Host))
      | where isnotempty(Domain)
      | extend Domain = tolower(Domain)
      | extend CommonSecurityLog_TimeGenerated = TimeGenerated
  );
  let LogDomains = SecurityLog | distinct Domain | summarize make_list(Domain);
  // Retrieve threat intelligence indicators within the specified time range
  let Domain_Indicators = materialize(
      ThreatIntelligenceIndicator
      | where isnotempty(DomainName)
      | where TimeGenerated >= ago(ioc_lookBack)
      | extend TI_DomainEntity = tolower(DomainName)
      | where TI_DomainEntity in (LogDomains)
      | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
      | where Active == true and ExpirationDateTime > now());
  // Join threat intelligence indicators with common security logs
  Domain_Indicators | join kind=innerunique (SecurityLog) on $left.TI_DomainEntity == $right.Domain
  | where CommonSecurityLog_TimeGenerated < ExpirationDateTime
  | summarize CommonSecurityLog_TimeGenerated = arg_max(CommonSecurityLog_TimeGenerated, *) by IndicatorId
  | project CommonSecurityLog_TimeGenerated, Description, ActivityGroupNames, PA_Url, Domain, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore, DeviceAction, DestinationIP, DestinationPort, DeviceName, SourceIP, SourcePort, ApplicationProtocol, RequestMethod, Type, TI_DomainEntity
  | extend timestamp = CommonSecurityLog_TimeGenerated
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: PA_Url
version: 1.4.2
kind: Scheduled

Stages and Predicates

Parameters

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

let Domain_Indicators is inlined into the numbered stages below.

Let Binding: SecurityLog used in Stage 8

let SecurityLog = materialize(
  CommonSecurityLog
    | extend IngestionTime = ingestion_time()
    | where IngestionTime > ago(dt_lookBack)
    | where DeviceEventClassID =~ 'url'
    | extend PA_Url = column_ifexists("RequestURL", "None")
    | extend PA_Url = iif(isempty(PA_Url) and AdditionalExtensions !startswith "PanOS", extract("([^\\\"]+)", 1, tolower(AdditionalExtensions)), trim('"', PA_Url))
    | extend PA_Url = iif(PA_Url !startswith "http://" and ApplicationProtocol !~ "ssl", strcat('http://', PA_Url), iif(PA_Url !startswith "https://" and ApplicationProtocol =~ "ssl", strcat('https://', PA_Url), PA_Url))
    | extend Domain = trim('"', tostring(parse_url(PA_Url).Host))
    | where isnotempty(Domain)
    | extend Domain = tolower(Domain)
    | extend CommonSecurityLog_TimeGenerated = TimeGenerated
);

Let Binding: LogDomains used in Stage 5

let LogDomains = SecurityLog | distinct Domain | summarize make_list(Domain);

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

Stage 1: source

ThreatIntelligenceIndicator

Stage 2: where

| where isnotempty(DomainName)

Stage 3: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 4: extend

| extend TI_DomainEntity = tolower(DomainName)

Stage 5: where

| where TI_DomainEntity in (LogDomains)

Stage 6: summarize

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

Stage 7: where

| where Active == true and ExpirationDateTime > now()

Stage 8: join

Domain_Indicators
| join kind=innerunique (SecurityLog) on $left.TI_DomainEntity == $right.Domain

Stage 9: where

where CommonSecurityLog_TimeGenerated < ExpirationDateTime

Stage 10: summarize

summarize CommonSecurityLog_TimeGenerated by IndicatorId

Stage 11: project

project ActivityGroupNames, ApplicationProtocol, CommonSecurityLog_TimeGenerated, ConfidenceScore, Description, DestinationIP, DestinationPort, DeviceAction, DeviceName, Domain, ExpirationDateTime, IndicatorId, PA_Url, RequestMethod, SourceIP, SourcePort, TI_DomainEntity, ThreatType, Type

Stage 12: extend

extend timestamp

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActivityGroupNamesproject
ApplicationProtocolproject
CommonSecurityLog_TimeGeneratedproject
ConfidenceScoreproject
Descriptionproject
DestinationIPproject
DestinationPortproject
DeviceActionproject
DeviceNameproject
Domainproject
ExpirationDateTimeproject
IndicatorIdproject
PA_Urlproject
RequestMethodproject
SourceIPproject
SourcePortproject
TI_DomainEntityproject
ThreatTypeproject
Typeproject
timestampextend

Back to all Kusto rules