Detection rules › Kusto

Kusto rules: 0u

1Password - Secret extraction post vault access change by administrator

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

This will alert when a secret extraction has occurred after an administrator has changed their own vault access permissions within that same vault. Ref: https://1password.com/ Ref: https://github.com/securehats/

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

Rule Body

id: 6711b747-16d7-4df4-9f61-8633617f45d7
name: 1Password - Secret extraction post vault access change by administrator
version: 1.0.0
kind: Scheduled
description: |-
  This will alert when a secret extraction has occurred after an administrator has changed their own vault access permissions within that same vault.
  Ref: https://1password.com/
  Ref: https://github.com/securehats/
severity: High
requiredDataConnectors:
  - connectorId: 1Password
    dataTypes:
      - OnePasswordEventLogs_CL
queryFrequency: 5m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1555
query: |-
  let lookback = 1h;
  let secretExtractionActivity =
      OnePasswordEventLogs_CL
      | where TimeGenerated between (ago(lookback) .. now())
      | where log_source == "itemusages"
      | where action has_any("server-fetch", "reveal", "secure-copy")
  ;
  OnePasswordEventLogs_CL
  | where log_source == "auditevents"
  | where (action == "grant" and object_type  == "uva") or (action == "update" and object_type  == "uva")
  | where tostring(actor_details.uuid) == tostring(aux_details.uuid)
  | extend
      userUuid = tostring(actor_details.uuid)
      , vaultUuid = tostring(object_uuid)
  | join (
      secretExtractionActivity
      | extend
          userUuid = tostring(user.uuid)
          , vaultUuid = tostring(vault_uuid)
      )
      on $left.userUuid == $right.userUuid and $left.vaultUuid == $right.vaultUuid
  | extend
      auditevents = bag_pack("action", action, "object_type", object_type)
      , itemusages = bag_pack("action", action1, "object_type", object_type1)
      , vault_details = bag_pack("vault_uuid", vault_uuid1, "item_uuid", item_uuid1)
  | project
      TimeGenerated
      , actor_details
      , target_details = aux_details
      , location_details = location
      , client_details = client1
      , auditevents
      , itemusages
      , vault_details
      , TargetUsername = tostring(user1.email)
      , SrcIpAddr = tostring(client1.ip_address)
suppressionEnabled: false
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: AllEntities
suppressionDuration: 1h
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: TargetUsername
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: SrcIpAddr

Stages and Predicates

Parameters

let lookback = 1h;

Let Binding: secretExtractionActivity used in Stage 6

let secretExtractionActivity = OnePasswordEventLogs_CL
    | where TimeGenerated between (ago(lookback) .. now())
    | where log_source == "itemusages"
    | where action has_any("server-fetch", "reveal", "secure-copy");

Stage 1: source

OnePasswordEventLogs_CL

Stage 2: where

| where log_source == "auditevents"

Stage 3: where

| where (action == "grant" and object_type  == "uva") or (action == "update" and object_type  == "uva")

Stage 4: where

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

Stage 5: extend

| extend
    userUuid = tostring(actor_details.uuid)
    , vaultUuid = tostring(object_uuid)

Stage 6: join

| join (
    secretExtractionActivity
    | extend
        userUuid = tostring(user.uuid)
        , vaultUuid = tostring(vault_uuid)
    )
    on $left.userUuid == $right.userUuid and $left.vaultUuid == $right.vaultUuid

Stage 7: extend

extend auditevents, itemusages, vault_details

Stage 8: project

project SrcIpAddr, TargetUsername, TimeGenerated, actor_details, auditevents, client_details, itemusages, location_details, target_details, vault_details

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
actioneq
  • grant
  • update corpus 3 (panther 2, kusto 1)
field:"action" kind:eq
actionmatch
  • reveal transforms: term
  • secure-copy transforms: term
  • server-fetch transforms: term
field:"action" kind:match
log_sourceeq
  • auditevents corpus 12 (kusto 12)
  • itemusages
field:"log_source" kind:eq
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
SrcIpAddrproject
TargetUsernameproject
TimeGeneratedproject
actor_detailsproject
auditeventsproject
client_detailsproject
itemusagesproject
location_detailsproject
target_detailsproject
vault_detailsproject

Adding User or Group Failed

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

Detects failed attempts to add a user or user group to Veeam Backup & Replication.

Rule Body

id: 6d1a5478-e613-44f4-a48f-12cc18568522
name: Adding User or Group Failed
description: Detects failed attempts to add a user or user group to Veeam Backup &
  Replication.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: Syslog
  dataTypes:
  - Syslog
- connectorId: SyslogAma
  dataTypes:
  - Syslog
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
  aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 31210\n| project\n    Date\
  \ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n    DataSource = original_host,\n\
  \    EventId = instanceId,\n    UserName = user,\n    MessageDetails = Description,\n\
  \    Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
  Date: Date
  VbrHostName: DataSource
  EventId: EventId
  MessageDetails: MessageDetails
  Severity: Severity

Stages and Predicates

Stage 1: source

Veeam_GetSecurityEvents

Stage 2: where

| where instanceId == 31210

Stage 3: project

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

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DataSourceproject
Dateproject
EventIdproject
MessageDetailsproject
Severityproject
UserNameproject

AWSCloudTrail - Changes to AWS Security Group ingress and egress settings

#
Status
available
Severity
low
Time window
1d
Group by
AWSRegion, AccountName, AccountUPNSuffix, AdditionalEventData, EventName, EventSource, RecipientAccountId, ResponseElements, SessionMfaAuthenticated, SourceIpAddress, UserAgent, UserIdentityAccountId, UserIdentityPrincipalid, UserIdentityType
Source
github.com/Azure/Azure-Sentinel

A Security Group acts as a virtual firewall for an AWS instance to control inbound and outbound traffic. This rule detects AWS CloudTrail events for changes to Security Group ingress and egress settings. Investigate to validate the legitimacy of the activity and identify potential malicious activity.

MITRE ATT&CK Coverage

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 4f19d4e3-ec5f-4abc-9e61-819eb131758c
name: AWSCloudTrail - Changes to AWS Security Group ingress and egress settings
description: |
  A Security Group acts as a virtual firewall for an AWS instance to control inbound and outbound traffic. This rule detects AWS CloudTrail events for changes to Security Group ingress and egress settings.
  Investigate to validate the legitimacy of the activity and identify potential malicious activity.
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
  - connectorId: AWSS3
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562.007
query: |
  let EventNameList = dynamic([ "AuthorizeSecurityGroupEgress", "AuthorizeSecurityGroupIngress", "RevokeSecurityGroupEgress", "RevokeSecurityGroupIngress"]);
  AWSCloudTrail
  | where EventName in~ (EventNameList)
  | extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
  | extend UserName = tostring(split(UserIdentityArn, '/')[-1])
  | extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
  | extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
    AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
  | summarize EventCount=count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
  by EventSource, EventName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, UserAgent, SessionMfaAuthenticated, AWSRegion,
  AdditionalEventData, UserIdentityAccountId, UserIdentityPrincipalid, ResponseElements
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
  UserIdentityArn: UserIdentityArn
  UserIdentityUserName: UserIdentityUserName
  UserIdentityType: UserIdentityType
  EventName: EventName
  AWSRegion: AWSRegion
  UserAgent: UserAgent
alertDetailsOverride:
  alertDisplayNameFormat: 'AWS Security Group ingress/egress change by {{AccountName}} from {{SourceIpAddress}}'
  alertDescriptionFormat: 'AWS Security Group ingress/egress change activity {{EventName}} was detected for {{AccountName}} from {{SourceIpAddress}}.' 
version: 1.0.5
kind: Scheduled

Stages and Predicates

Parameters

let EventNameList = dynamic([ "AuthorizeSecurityGroupEgress", "AuthorizeSecurityGroupIngress", "RevokeSecurityGroupEgress", "RevokeSecurityGroupIngress"]);

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName in~ (EventNameList)

Stage 3: extend (4 consecutive steps)

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

Stage 4: summarize

| summarize EventCount=count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
by EventSource, EventName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, UserAgent, SessionMfaAuthenticated, AWSRegion,
AdditionalEventData, UserIdentityAccountId, UserIdentityPrincipalid, ResponseElements

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
EventNamein
  • AuthorizeSecurityGroupEgress
  • AuthorizeSecurityGroupIngress
  • RevokeSecurityGroupEgress
  • RevokeSecurityGroupIngress
field:"aws::eventName" kind:in

Output Fields

These fields are emitted when the rule matches.

FieldSource
AWSRegionsummarize
AccountNamesummarize
AccountUPNSuffixsummarize
AdditionalEventDatasummarize
EndTimeUtcsummarize
EventCountsummarize
EventNamesummarize
EventSourcesummarize
RecipientAccountIdsummarize
ResponseElementssummarize
SessionMfaAuthenticatedsummarize
SourceIpAddresssummarize
StartTimeUtcsummarize
UserAgentsummarize
UserIdentityAccountIdsummarize
UserIdentityPrincipalidsummarize
UserIdentityTypesummarize

BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based NTLM Relay to LDAP Attack

#

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 - Tier Zero Computer Vulnerable to Coercion-Based NTLM Relay to LDAP Attack

Rule Body

id: 1bc4eeaa-08d8-47b6-85ab-bbea5a062282
name: BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based
  NTLM Relay to LDAP Attack
description: BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based
  NTLM Relay to LDAP Attack
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 == "T0CoerceAndRelayNTLMToLDAP"
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 == "T0CoerceAndRelayNTLMToLDAP"

Indicators

These rows show field, operator, and value matches.

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

COM Registry Key Modified to Point to File in Color Profile Folder

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

This query looks for changes to COM registry keys to point to files in C:\Windows\System32\spool\drivers\color. This can be used to enable COM hijacking for persistence. Ref: https://www.microsoft.com/security/blog/2022/07/27/untangling-knotweed-european-private-sector-offensive-actor-using-0-day-exploits/

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: ed8c9153-6f7a-4602-97b4-48c336b299e1
name: COM Registry Key Modified to Point to File in Color Profile Folder
description: |
  'This query looks for changes to COM registry keys to point to files in C:\Windows\System32\spool\drivers\color\.
    This can be used to enable COM hijacking for persistence.
    Ref: https://www.microsoft.com/security/blog/2022/07/27/untangling-knotweed-european-private-sector-offensive-actor-using-0-day-exploits/'
severity: Medium
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceRegistryEvents
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Persistence
relevantTechniques:
  - T1574
tags:
  - KNOTWEED
query: |
  let guids = dynamic(["{ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea}","{1f486a52-3cb1-48fd-8f50-b8dc300d9f9d}","{4590f811-1d3a-11d0-891f-00aa004b2e24}", "{4de225bf-cf59-4cfc-85f7-68b90f185355}", "{F56F6FDD-AA9D-4618-A949-C1B91AF43B1A}"]);
    let mde_data = DeviceRegistryEvents
    | where ActionType =~ "RegistryValueSet"
    | where RegistryKey contains "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID"
    | where RegistryKey has_any (guids)
    | where RegistryValueData has "System32\\spool\\drivers\\color";
    let event_data = SecurityEvent
    | where EventID == 4657
    | where ObjectName contains "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID"
    | where ObjectName has_any (guids)
    | where NewValue has "System32\\spool\\drivers\\color"
    | extend RegistryKey = ObjectName, RegistryValueData = NewValue, DeviceName=Computer, InitiatingProcessFileName = Process, InitiatingProcessAccountName=SubjectUserName, InitiatingProcessAccountDomain = SubjectDomainName;
    union mde_data, event_data
    | extend HostName = tostring(split(DeviceName, ".")[0]), DomainIndex = toint(indexof(DeviceName, '.'))
    | extend HostNameDomain = iff(DomainIndex != -1, substring(DeviceName, DomainIndex + 1), DeviceName)
entityMappings:
  - entityType: RegistryKey
    fieldMappings:
      - identifier: Key
        columnName: RegistryKey
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: DeviceName
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
  - entityType: Process
    fieldMappings:
      - identifier: ProcessId
        columnName: InitiatingProcessFileName
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: InitiatingProcessAccountName
      - identifier: NTDomain
        columnName: InitiatingProcessAccountName
version: 1.1.1
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Let Binding: guids

let guids = dynamic(["{ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea}","{1f486a52-3cb1-48fd-8f50-b8dc300d9f9d}","{4590f811-1d3a-11d0-891f-00aa004b2e24}", "{4de225bf-cf59-4cfc-85f7-68b90f185355}", "{F56F6FDD-AA9D-4618-A949-C1B91AF43B1A}"]);

Union (2 Sources)

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

Leg 1: mde_data

Leg 2: event_data

Applied to the Combined Result

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
ActionTypeeq
  • RegistryValueSet
field:"ActionType" kind:eq value:"RegistryValueSet"
EventIDeq
  • 4657
field:"EventID" kind:eq value:"4657"
NewValuematch
  • System32\spool\drivers\color transforms: term
field:"NewValue" kind:match value:"System32\spool\drivers\color"
ObjectNamecontains
  • HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
field:"ObjectName" kind:contains value:"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID"
ObjectNamematch
  • {1f486a52-3cb1-48fd-8f50-b8dc300d9f9d} transforms: term
  • {4590f811-1d3a-11d0-891f-00aa004b2e24} transforms: term
  • {4de225bf-cf59-4cfc-85f7-68b90f185355} transforms: term
  • {F56F6FDD-AA9D-4618-A949-C1B91AF43B1A} transforms: term
  • {ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea} transforms: term
field:"ObjectName" kind:match
RegistryKeycontains
  • HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID
field:"RegistryKey" kind:contains value:"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID"
RegistryKeymatch
  • {1f486a52-3cb1-48fd-8f50-b8dc300d9f9d} transforms: term
  • {4590f811-1d3a-11d0-891f-00aa004b2e24} transforms: term
  • {4de225bf-cf59-4cfc-85f7-68b90f185355} transforms: term
  • {F56F6FDD-AA9D-4618-A949-C1B91AF43B1A} transforms: term
  • {ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea} transforms: term
field:"RegistryKey" kind:match
RegistryValueDatamatch
  • System32\spool\drivers\color transforms: term
field:"RegistryValueData" kind:match value:"System32\spool\drivers\color"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DeviceNameextend
InitiatingProcessAccountDomainextend
InitiatingProcessAccountNameextend
InitiatingProcessFileNameextend
RegistryKeyextend
RegistryValueDataextend
DomainIndexextend
HostNameextend
HostNameDomainextend

CYFIRMA - Medium severity Trojan Network Indicators - Block Recommended Rule

#

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

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

"This analytics rule detects network-based indicators flagged by CYFIRMA threat intelligence as associated with Command & Control (C2) infrastructure. These indicators may represent attacker-controlled endpoints used for persistence, data exfiltration, or command delivery to compromised systems."

MITRE ATT&CK Coverage

Rule Body

id: baa63d52-285d-43bf-a34e-8ed2fa260f9e
name: CYFIRMA - Medium severity Trojan Network Indicators - Block Recommended Rule
description: |
  "This analytics rule detects network-based indicators flagged by CYFIRMA threat intelligence as associated with Command & Control (C2) infrastructure. 
  These indicators may represent attacker-controlled endpoints used for persistence, data exfiltration, or command delivery to compromised systems."
version: 1.0.1
kind: Scheduled
severity: Medium
enabled: false
requiredDataConnectors:
  - connectorId: CyfirmaCyberIntelligenceDC
    dataTypes:
      - CyfirmaIndicators_CL
query: |
  //Trojan Network Indicators - Block Recommended
  let timeFrame= 5m;
  CyfirmaIndicators_CL 
  | where (ConfidenceScore < 80 and ConfidenceScore >= 50)
      and TimeGenerated between (ago(timeFrame) .. now())
      and pattern !contains 'file:hashes' and RecommendedActions has 'Block' and Roles has 'Trojan'
  | extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
  | extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
  | extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
  | extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
  | extend parsed = parse_json(extensions)
  | extend extensionKeys = bag_keys(parsed)
  | mv-expand extensionKeys
  | extend extensionKeyStr = tostring(extensionKeys)
  | extend ext = parsed[extensionKeyStr]
  | extend props = ext.properties
  | extend 
      extension_id = extensionKeyStr,
      ASN_Owner = props.asn_owner,
      ASN = props.asn,
      ProviderName = 'CYFIRMA',
      ProductName = 'DeCYFIR/DeTCT'
  | project
      IPv4,
      IPv6,
      URL,
      Domain,
      ThreatActors,
      RecommendedActions,
      Sources,
      Roles,
      Country,
      IPAbuse,
      name,
      Description,
      ConfidenceScore,
      IndicatorID,
      created,
      modified,
      valid_from,
      Tags,
      ThreatType,
      TimeGenerated,
      SecurityVendors,
      ProductName,
      ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
  - Impact
  - Persistence
  - DefenseEvasion
  - CredentialAccess
  - CommandAndControl
  - Execution
  - InitialAccess
relevantTechniques:
  - T1496
  - T1053
  - T1555
  - T1090
  - T1071
  - T1204
  - T1189
  - T1053.005
  - T1555.003
  - T1071.001
  - T1204.001
alertDetailsOverride:
  alertDisplayNameFormat: "High-Confidence Trojan Network Indicators - Block Recommended Rule - {{name}} "
  alertDescriptionFormat: "{{Description}} - {{name}} "
  alertDynamicProperties:
    - alertProperty: ProductName
      value: ProductName
    - alertProperty: ProviderName
      value: ProviderName
customDetails:
  ThreatActors: ThreatActors
  Sources: Sources
  RecommendedActions: RecommendedActions
  Roles: Roles
  Country: Country
  Description: Description
  ConfidenceScore: ConfidenceScore
  SecurityVendors: SecurityVendors
  IndicatorID: IndicatorID
  Created: created
  Modified: modified
  ValidFrom: valid_from
  Tags: Tags
  ThreatType: ThreatType
  TimeGenerated: TimeGenerated
  IPAbuse: IPAbuse
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPv4
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPv6
  - entityType: DNS
    fieldMappings:
      - identifier: DomainName
        columnName: Domain
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: URL
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: AlertPerResult

Stages and Predicates

Parameters

let timeFrame = 5m;

Stage 1: source

CyfirmaIndicators_CL

Stage 2: where

| where (ConfidenceScore < 80 and ConfidenceScore >= 50)
    and TimeGenerated between (ago(timeFrame) .. now())
    and pattern !contains 'file:hashes' and RecommendedActions has 'Block' and Roles has 'Trojan'

Stage 3: extend (6 consecutive steps)

| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)

Stage 4: mv-expand

| mv-expand extensionKeys

Stage 5: extend (4 consecutive steps)

| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend 
    extension_id = extensionKeyStr,
    ASN_Owner = props.asn_owner,
    ASN = props.asn,
    ProviderName = 'CYFIRMA',
    ProductName = 'DeCYFIR/DeTCT'

Stage 6: project

| project
    IPv4,
    IPv6,
    URL,
    Domain,
    ThreatActors,
    RecommendedActions,
    Sources,
    Roles,
    Country,
    IPAbuse,
    name,
    Description,
    ConfidenceScore,
    IndicatorID,
    created,
    modified,
    valid_from,
    Tags,
    ThreatType,
    TimeGenerated,
    SecurityVendors,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
patterncontainsfile:hashesexcludes:pattern field:"pattern" value:"file:hashes"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ConfidenceScoreproject
Countryproject
Descriptionproject
Domainproject
IPAbuseproject
IPv4project
IPv6project
IndicatorIDproject
ProductNameproject
ProviderNameproject
RecommendedActionsproject
Rolesproject
SecurityVendorsproject
Sourcesproject
Tagsproject
ThreatActorsproject
ThreatTypeproject
TimeGeneratedproject
URLproject
createdproject
modifiedproject
nameproject
valid_fromproject

EatonForeseer - Unauthorized Logins

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

Detects Unauthorized Logins into Eaton Foreseer

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Telemetry Coverage

Rule Body

id: 5a7fccb8-3ed0-44f2-8477-540af3ef4d92
name: EatonForeseer - Unauthorized Logins
description: |
  'Detects Unauthorized Logins into Eaton Foreseer'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078
query: |
  SecurityEvent
  // Look for all events relating to user logins
  | where EventID in (4624,4625,4648,4675,4634,4647)
  | where AccountType == "User"
  // Filter for data present
  | where isnotempty(TargetUserName)
  // Look for all events that relate to the Eaton Foreseer application
  | where ProcessName has "Foreseer"
  // Ignore known user accounts (please edit based on your allowed users)
  | where TargetUserName !in ("janedoe", "johndoe")
  // De-duplicate multiple entries for the same user accessing a particular device
  | summarize TimeGenerated=arg_min(TimeGenerated, *) by TargetUserName, Computer
  | project
      TimeGenerated,
      IPCustomEntity = IpAddress,
      HostCustomEntity = Computer,
      UserEntity = TargetUserName,
      Process,
      ProcessName
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: UserEntity
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostCustomEntity
  - entityType: Process
    fieldMappings:
      - identifier: CommandLine
        columnName: Process
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID in (4624,4625,4648,4675,4634,4647)

Stage 3: where

| where AccountType == "User"

Stage 4: where

| where isnotempty(TargetUserName)

Stage 5: where

| where ProcessName has "Foreseer"

Stage 6: where

| where TargetUserName !in ("janedoe", "johndoe")

Stage 7: summarize

| summarize TimeGenerated=arg_min(TimeGenerated, *) by TargetUserName, Computer

Stage 8: project

| project
    TimeGenerated,
    IPCustomEntity = IpAddress,
    HostCustomEntity = Computer,
    UserEntity = TargetUserName,
    Process,
    ProcessName

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AccountTypeeq
  • User corpus 4 (kusto 4)
field:"AccountType" kind:eq value:"User"
EventIDin
  • 4624 corpus 6 (elastic 3, kusto 3)
  • 4625 corpus 6 (elastic 3, kusto 3)
  • 4634
  • 4647
  • 4648
  • 4675
field:"EventID" kind:in
ProcessNamematch
  • Foreseer transforms: term
field:"process_name" kind:match value:"Foreseer"
TargetUserNameis_not_null
  • (no value, null check)
field:"TargetUserName" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
HostCustomEntityproject
IPCustomEntityproject
Processproject
ProcessNameproject
TimeGeneratedproject
UserEntityproject

GSA - Detect Protocol Changes for Destination Ports

#
Status
available
Severity
medium
Time window
8d
Group by
AlertTimeDstPort, AlertTimeProtocol, DestinationFqdn, SourceIp
Source
github.com/Azure/Azure-Sentinel

Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline. This can indicate potential protocol misuse or configuration changes. Configurable Parameters: - Learning period: The time range to establish the baseline. Default is set to 7 days. - Run time: The time range for current analysis. Default is set to 1 day.

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a
name: GSA - Detect Protocol Changes for Destination Ports
description: |
  Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline.
  This can indicate potential protocol misuse or configuration changes.
  Configurable Parameters:
  - Learning period: The time range to establish the baseline. Default is set to 7 days.
  - Run time: The time range for current analysis. Default is set to 1 day.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - NetworkAccessTrafficLogs
queryFrequency: 1h
queryPeriod: 8d
triggerOperator: gt
triggerThreshold: 1
tactics:
  - DefenseEvasion
  - Exfiltration
  - CommandAndControl
relevantTechniques:
  - T1571
query: |
  let LearningPeriod = 7d;
  let RunTime = 1d;
  let StartLearningPeriod = ago(LearningPeriod + RunTime);
  let EndRunTime = ago(RunTime);
  let LearningPortToProtocol = 
    NetworkAccessTraffic
    | where TimeGenerated between (StartLearningPeriod .. EndRunTime)
    | where isnotempty(DestinationPort)
    | summarize LearningTimeCount = count() by LearningTimeDstPort = DestinationPort, LearningTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;
  let AlertTimePortToProtocol = 
    NetworkAccessTraffic
    | where TimeGenerated between (EndRunTime .. now())
    | where isnotempty(DestinationPort)
    | summarize AlertTimeCount = count() by AlertTimeDstPort = DestinationPort, AlertTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;
  AlertTimePortToProtocol
    | join kind=leftouter (LearningPortToProtocol) on $left.AlertTimeDstPort == $right.LearningTimeDstPort and $left.SourceIp == $right.SourceIp and $left.DestinationFqdn == $right.DestinationFqdn
    | where isnotempty(LearningTimeProtocol) and isnotempty(AlertTimeProtocol) and LearningTimeProtocol != AlertTimeProtocol
    | project AlertTimeDstPort, AlertTimeProtocol, LearningTimeProtocol, SourceIp, DestinationFqdn
    | extend IPCustomEntity = SourceIp, FqdnCustomEntity = DestinationFqdn
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: FqdnCustomEntity
version: 1.0.4
kind: Scheduled

Stages and Predicates

Parameters

let LearningPeriod = 7d;
let RunTime = 1d;
let StartLearningPeriod = ago(LearningPeriod + RunTime);
let EndRunTime = ago(RunTime);

let AlertTimePortToProtocol is inlined into the numbered stages below.

Let Binding: LearningPortToProtocol used in Stage 5

let LearningPortToProtocol = NetworkAccessTraffic
  | where TimeGenerated between (StartLearningPeriod .. EndRunTime)
  | where isnotempty(DestinationPort)
  | summarize LearningTimeCount = count() by LearningTimeDstPort = DestinationPort, LearningTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;

Stages 1 to 4 define let AlertTimePortToProtocol as the rule's main pipeline source. Stages 5 to 8 use it.

Stage 1: source

NetworkAccessTraffic

Stage 2: where

| where TimeGenerated between (EndRunTime .. now())

Stage 3: where

| where isnotempty(DestinationPort)

Stage 4: summarize

| summarize AlertTimeCount = count() by AlertTimeDstPort = DestinationPort, AlertTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn

Stage 5: join

AlertTimePortToProtocol
| join kind=leftouter (LearningPortToProtocol) on $left.AlertTimeDstPort == $right.LearningTimeDstPort and $left.SourceIp == $right.SourceIp and $left.DestinationFqdn == $right.DestinationFqdn

Stage 6: where

where isnotempty(AlertTimeProtocol) and LearningTimeProtocol != AlertTimeProtocol and isnotempty(LearningTimeProtocol)

Stage 7: project

project AlertTimeDstPort, AlertTimeProtocol, DestinationFqdn, LearningTimeProtocol, SourceIp

Stage 8: extend

extend FqdnCustomEntity, IPCustomEntity

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AlertTimeDstPortproject
AlertTimeProtocolproject
DestinationFqdnproject
LearningTimeProtocolproject
SourceIpproject
FqdnCustomEntityextend
IPCustomEntityextend

Guardian- Block Competitor Policy Violation Detection

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

'This alert creates an incident when a Block Competitor Policy Violation is detected from the Guardian.'

Rule Body

id: e5d9eea1-3250-49ba-9e39-b5678ec7496b
name: Guardian- Block Competitor Policy Violation Detection
description: |
  'This alert creates an incident when a Block Competitor Policy Violation is 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 =~ 'Block Competitor'
  | 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- Block Competitor Policy Violation detection
  alertDescriptionFormat: |
    This query detects Block Competitor 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 =~ 'Block Competitor'

Stage 3: where

| where Severity =~ 'Low'

Indicators

These rows show field, operator, and value matches.

Imperva - Abnormal protocol usage

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

'Detects abnormal protocol usage.'

MITRE ATT&CK Coverage

Rule Body

id: 363307f6-09ba-4926-ad52-03aadfd24b5e
name: Imperva - Abnormal protocol usage
description: |
  'Detects abnormal protocol usage.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: ImpervaWAFCloudAPI
    dataTypes:
      - ImpervaWAFCloud
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
  - T1133
query: |
  ImpervaWAFCloud
  | where NetworkApplicationProtocol in~ ('HTTP', 'HTTPs')
  | where DstPortNumber !in ('80', '443')
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

ImpervaWAFCloud

Stage 2: where

| where NetworkApplicationProtocol in~ ('HTTP', 'HTTPs')

Stage 3: where

| where DstPortNumber !in ('80', '443')

Stage 4: extend

| extend IPCustomEntity = SrcIpAddr

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

Output Fields

These fields are emitted when the rule matches.

FieldSource
IPCustomEntityextend

Infoblox - Many High Threat Level Queries From Single Host Detected

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

'At least 200 high threat level queries generated by single host in 1 hour. Queries do not need to be the same. Customize query count, scheduling, responses and more. This rule depends on a parser based on a Kusto Function to work as expected called InfobloxCDC.'

MITRE ATT&CK Coverage

Rule Body

id: 3822b794-fa89-4420-aad6-0e1a2307f419
name: Infoblox - Many High Threat Level Queries From Single Host Detected
description: |
  'At least 200 high threat level queries generated by single host in 1 hour. Queries do not need to be the same. Customize query count, scheduling, responses and more. This rule depends on a parser based on a Kusto Function to work as expected called [**InfobloxCDC**](https://aka.ms/sentinel-InfobloxCloudDataConnector-parser).'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: CefAma
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1498
  - T1565
query: |
  let threshold = 200;
  InfobloxCDC
  | where DeviceEventClassID has_cs "RPZ"
  | where ThreatLevel_Score >= 80
  | summarize count() by SourceIP
  | where count_ > threshold
  | join kind=inner (InfobloxCDC
    | where DeviceEventClassID has_cs "RPZ"
    | where ThreatLevel_Score >= 80
    ) on SourceIP
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
      - identifier: OSVersion
        columnName: InfobloxB1SrcOSVersion
      - identifier: FullName
        columnName: SourceUserName
customDetails:
  SourceMACAddress: SourceMACAddress
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 200;

Stage 1: source

InfobloxCDC

Stage 2: where

| where DeviceEventClassID has_cs "RPZ"

Stage 3: where

| where ThreatLevel_Score >= 80

Stage 4: summarize

| summarize count() by SourceIP

Stage 5: where

| where count_ > threshold

Stage 6: join

| join kind=inner (InfobloxCDC
  | where DeviceEventClassID has_cs "RPZ"
  | where ThreatLevel_Score >= 80
  ) on SourceIP

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SourceIPsummarize

IP address of Windows host encoded in web request

#
Severity
medium
Time window
1d
Group by
$left.ipmatch, $right.RemoteIP, End, Start, decoded_ip_candidate, extracted_encoded_ip_candidate
Author
Thomas McElroy
Source
github.com/Azure/Azure-Sentinel

This detection will identify network requests in HTTP proxy data that contains Base64 encoded IP addresses. After identifying candidates the query joins with DeviceNetworkEvents to idnetify any machine within the network using that IP address. Alerts indicate that the IP address of a machine within your network was seen with it's IP address base64 encoded in an outbound web request. This method of egressing the IP was seen used in POLONIUM's RunningRAT tool, however the detection is generic.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: a4ce20ae-a2e4-4d50-b40d-d49f1353b6cc
name: IP address of Windows host encoded in web request
description: |
  'This detection will identify network requests in HTTP proxy data that contains Base64 encoded IP addresses. After identifying candidates the query joins with DeviceNetworkEvents to idnetify any machine within the network using that IP address. Alerts indicate that the IP address of a machine within your network was seen with it's IP address base64 encoded in an outbound web request. This method of egressing the IP was seen used in POLONIUM's RunningRAT tool, however the detection is generic.'
severity: Medium
requiredDataConnectors:
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
  - connectorId: Fortinet
    dataTypes:
      - CommonSecurityLog
  - connectorId: CheckPoint
    dataTypes:
      - CommonSecurityLog
  - connectorId: PaloAltoNetworks
    dataTypes:
      - CommonSecurityLog
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceNetworkEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
  - CommandAndControl
relevantTechniques:
  - T1041
  - T1071.001
tags:
  - POLONIUM
query: |
  // Extracts plaintext IPv4 addresses
  let ipv4_plaintext_extraction_regex = @"((?:(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.)){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){1,3})";
  // Identified base64 encoded IPv4 addresses
  let ipv4_encoded_identification_regex = @"\=([a-zA-Z0-9\/\+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/\+]{2,4}){3}[a-zA-Z0-9\/\+\=]*)";
  // Extractes IPv4 addresses as hex values
  let ipv4_decoded_hex_extract = @"((?:(?:61|62|63|64|65|66|67|68|69|6a|6b|6c|6d|6e|6f|70|71|72|73|74|75|76|77|78|79|7a|41|42|43|44|45|46|47|48|49|4a|4b|4c|4d|4e|4f|50|51|52|53|54|55|56|57|58|59|5a|2f|2b|3d),){7,15})";
  CommonSecurityLog
  | where isnotempty(RequestURL)
  // Identify requests with encoded IPv4 addresses
  | where RequestURL matches regex ipv4_encoded_identification_regex
  | project TimeGenerated, RequestURL
  // Extract IP candidates in their base64 encoded format, significantly reducing the dataset
  | extend extracted_encoded_ip_candidate = extract_all(ipv4_encoded_identification_regex, RequestURL)
  // We could have more than one candidate, expand them out
  | mv-expand extracted_encoded_ip_candidate to typeof(string)
  | summarize Start=min(TimeGenerated), End=max(TimeGenerated), make_set(RequestURL) by extracted_encoded_ip_candidate
  // Pad if we need to
  | extend extracted_encoded_ip_candidate = iff(strlen(extracted_encoded_ip_candidate) % 2 == 0, extracted_encoded_ip_candidate, strcat(extracted_encoded_ip_candidate, "="))
  // Now decode the candidate to a long array, we cannot go straight to string as it cannot handle non-UTF8, we need to strip that first
  | extend extracted_encoded_ip_candidate = tostring(base64_decode_toarray(extracted_encoded_ip_candidate))
  // Extract the IP candidates from the array
  | extend hex_extracted = extract_all(ipv4_decoded_hex_extract, extracted_encoded_ip_candidate)
  // Expand, it's still possible that we might have more than 1 IP
  | mv-expand hex_extracted
  // Now we should have a clean string. We need to put it back into a dynamic array to convert back to a string.
  | extend hex_extracted = trim_end(",", tostring(hex_extracted))
  | extend hex_extracted = strcat("[",hex_extracted,"]")
  | extend hex_extracted = todynamic(hex_extracted)
  | extend extracted_encoded_ip_candidate = todynamic(extracted_encoded_ip_candidate)
  // Convert the array back into a string
  | extend decoded_ip_candidate = make_string(hex_extracted)
  | summarize by decoded_ip_candidate, tostring(set_RequestURL), Start, End
  // Now the IP candidates will be in plaintext, extract the IPs using a regex
  | extend ipmatch = extract_all(ipv4_plaintext_extraction_regex, decoded_ip_candidate)
  // If it's not an IP, throw it out
  | where isnotnull(ipmatch)
  | mv-expand ipmatch to typeof(string)
  // Join with DeviceNetworkEvents to find instances where an IP of a machine in our MDE estate sent it's IP in a base64 encoded string
  | join (
      DeviceNetworkEvents
      | summarize make_set(DeviceId), make_set(DeviceName) by RemoteIP
  ) on $left.ipmatch == $right.RemoteIP
  | project Start, End, IPmatch=ipmatch, RequestURL=set_RequestURL, DeviceNames=set_DeviceName, DeviceIds=set_DeviceId, RemoteIP
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPmatch
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceNames
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: RequestURL
version: 1.0.2
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Thomas McElroy
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Networking" ]

Stages and Predicates

Let Binding: ipv4_plaintext_extraction_regex used in Stage 12

let ipv4_plaintext_extraction_regex = @"((?:(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.)){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){1,3})";

Let Binding: ipv4_encoded_identification_regex used in Stages 3, 5

let ipv4_encoded_identification_regex = @"\=([a-zA-Z0-9\/\+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/\+]{2,4}){3}[a-zA-Z0-9\/\+\=]*)";

Let Binding: ipv4_decoded_hex_extract used in Stage 8

let ipv4_decoded_hex_extract = @"((?:(?:61|62|63|64|65|66|67|68|69|6a|6b|6c|6d|6e|6f|70|71|72|73|74|75|76|77|78|79|7a|41|42|43|44|45|46|47|48|49|4a|4b|4c|4d|4e|4f|50|51|52|53|54|55|56|57|58|59|5a|2f|2b|3d),){7,15})";

Stage 1: source

CommonSecurityLog

Stage 2: where

| where isnotempty(RequestURL)

Stage 3: where

| where RequestURL matches regex ipv4_encoded_identification_regex

Stage 4: project

| project TimeGenerated, RequestURL

Stage 5: extend

| extend extracted_encoded_ip_candidate = extract_all(ipv4_encoded_identification_regex, RequestURL)

Stage 6: mv-expand

| mv-expand extracted_encoded_ip_candidate to typeof(string)

Stage 7: summarize

| summarize Start=min(TimeGenerated), End=max(TimeGenerated), make_set(RequestURL) by extracted_encoded_ip_candidate

Stage 8: extend (3 consecutive steps)

| extend extracted_encoded_ip_candidate = iff(strlen(extracted_encoded_ip_candidate) % 2 == 0, extracted_encoded_ip_candidate, strcat(extracted_encoded_ip_candidate, "="))
| extend extracted_encoded_ip_candidate = tostring(base64_decode_toarray(extracted_encoded_ip_candidate))
| extend hex_extracted = extract_all(ipv4_decoded_hex_extract, extracted_encoded_ip_candidate)
extracted_encoded_ip_candidate =
if/* macro: ((strlen(extracted_encoded_ip_candidate) % 2) == 0) */extracted_encoded_ip_candidate
elsestrcat(extracted_encoded_ip_candidate, "=")

Stage 9: mv-expand

| mv-expand hex_extracted

Stage 10: extend (5 consecutive steps)

| extend hex_extracted = trim_end(",", tostring(hex_extracted))
| extend hex_extracted = strcat("[",hex_extracted,"]")
| extend hex_extracted = todynamic(hex_extracted)
| extend extracted_encoded_ip_candidate = todynamic(extracted_encoded_ip_candidate)
| extend decoded_ip_candidate = make_string(hex_extracted)

Stage 11: summarize

| summarize by decoded_ip_candidate, tostring(set_RequestURL), Start, End

Stage 12: extend

| extend ipmatch = extract_all(ipv4_plaintext_extraction_regex, decoded_ip_candidate)

Stage 13: where

| where isnotnull(ipmatch)

Stage 14: mv-expand

| mv-expand ipmatch to typeof(string)

Stage 15: join

| join (
    DeviceNetworkEvents
    | summarize make_set(DeviceId), make_set(DeviceName) by RemoteIP
) on $left.ipmatch == $right.RemoteIP

Stage 16: project

project DeviceIds, DeviceNames, End, IPmatch, RemoteIP, RequestURL, Start

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
RequestURLis_not_null
  • (no value, null check)
field:"RequestURL" kind:is_not_null
RequestURLregex_match
  • \=([a-zA-Z0-9\/+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/+]{2,4}){3}[a-zA-Z0-9\/+\=]*)
field:"RequestURL" kind:regex_match value:"\=([a-zA-Z0-9\/+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/+]{2,4}){3}[a-zA-Z0-9\/+\=]*)"
ipmatchis_not_null
  • (no value, null check)
field:"ipmatch" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
DeviceIdsproject
DeviceNamesproject
Endproject
IPmatchproject
RemoteIPproject
RequestURLproject
Startproject

Malware Detected

#
Status
available
Severity
medium
Time window
1h
Group by
ActualAction, CategorySet, CategoryType, FilePath, SrcHostName, SrcIpAddr, UserName
Source
github.com/Azure/Azure-Sentinel

'Creates an incident when a Symantec Endpoint Proection agent detects malware and the malware was not cleaned.'

MITRE ATT&CK Coverage

Rule Body

id: 072ee087-17e1-474d-b162-bbe38bcab9f9
name: Malware Detected
description: |
  'Creates an incident when a Symantec Endpoint Proection agent detects malware and the malware was not cleaned.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
relevantTechniques:
  - T1204.002
query: |
  SymantecEndpointProtection
  | where LogType == "Agent Risk Logs"
  | where CategorySet == "Malware"
  | where ActualAction !contains "Cleaned"
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by SrcIpAddr, SrcHostName, UserName, FilePath, ActualAction, CategorySet, CategoryType

entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: SrcHostName
version: 1.0.3
kind: Scheduled

Stages and Predicates

Stage 1: source

SymantecEndpointProtection

Stage 2: where

| where LogType == "Agent Risk Logs"

Stage 3: where

| where CategorySet == "Malware"

Stage 4: where

| where ActualAction !contains "Cleaned"

Stage 5: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by SrcIpAddr, SrcHostName, UserName, FilePath, ActualAction, CategorySet, CategoryType

Indicators

These rows show field, operator, and value matches.

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
ActualActioncontainsCleanedexcludes:ActualAction field:"ActualAction" value:"Cleaned"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActualActionsummarize
CategorySetsummarize
CategoryTypesummarize
EndTimeUtcsummarize
FilePathsummarize
SrcHostNamesummarize
SrcIpAddrsummarize
StartTimeUtcsummarize
UserNamesummarize

Mimecast Secure Email Gateway - AV

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

'Detects threats from email anti virus scan.'

MITRE ATT&CK Coverage

TacticTechniques
Execution

Rule Body

id: 33bf0cc9-e568-42bf-9571-c22adf7be66d
name: Mimecast Secure Email Gateway - AV
description: |
  'Detects threats from email anti virus scan.'
severity: Informational
status: Available
requiredDataConnectors:
  - connectorId: MimecastSEGAPI
    dataTypes:
      - MimecastCG
enabled: true
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1053
query: |
  MimecastCG
  | where Type == "email_antivirus"
  | extend  SenderEnvelope = ['Sender Envelope'] 
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
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

MimecastCG

Stage 2: where

| where Type == "email_antivirus"

Stage 3: extend

| extend  SenderEnvelope = ['Sender Envelope']

Indicators

These rows show field, operator, and value matches.

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

Output Fields

These fields are emitted when the rule matches.

FieldSource
SenderEnvelopeextend

Mimecast Secure Email Gateway - AV

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

Detects threats from email anti virus scan

MITRE ATT&CK Coverage

TacticTechniques
Execution

Rule Body

id: 0f0dc725-29dc-48c3-bf10-bd2f34fd1cbb
name: Mimecast Secure Email Gateway - AV
description: Detects threats from email anti virus scan
severity: Informational
requiredDataConnectors:
  - connectorId: MimecastSIEMAPI
    dataTypes:
      - MimecastSIEM_CL
enabled: true
query: MimecastSIEM_CL| where mimecastEventId_s=="mail_av"
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Execution
relevantTechniques:
- T1053
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1d
    matchingMethod: AllEntities
eventGroupingSettings:
  aggregationKind: SingleAlert
customDetails:
  IP: IP_s
  MsgId_s: MsgId_s
  Route: Route_s
  SenderDomain: SenderDomain_s
  MimecastIP: MimecastIP_s
  fileName: fileName_s
  sha256: sha256_s
  Size: Size_s
  fileExt: fileExt_s
  Virus: Virus_s
  sha1: sha1_s
  SenderDomainInternal: SenderDomainInternal_s
  fileMime: fileMime_s
  CustomerIP: CustomerIP_s
  md5: md5_g
entityMappings:
- entityType: MailMessage
  fieldMappings:
  - identifier: Sender
    columnName: Sender_s
  - identifier: Recipient
    columnName: Recipient_s
  - identifier: Subject
    columnName: Subject_s
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

MimecastSIEM_CL

Stage 2: where

| where mimecastEventId_s=="mail_av"

Indicators

These rows show field, operator, and value matches.

NordPass - Deleting items of deleted member

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

This will alert you if the deleted user's items have been removed without being transferred to another active user, as this could result in the loss of access to critical tools or information.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: 0068dca4-dea0-46a3-a970-655e067a145f
name: NordPass - Deleting items of deleted member
kind: Scheduled
version: 1.0.0
displayName: Deleting items of deleted member
description: |
  This will alert you if the deleted user's items have been removed without being transferred to another active user, as this could result in the loss of access to critical tools or information.
severity: High
query: |
  NordPassEventLogs_CL
  | where event_type == "item_delete"
  | where action == "item_reassignment_deleted"
  | extend TargetEmail = user_email
requiredDataConnectors:
  - connectorId: NordPass
    dataTypes:
      - NordPassEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1485
incidentConfiguration:
  createIncident: false
entityMappings:
  - entityType: Mailbox
    fieldMappings:
      - identifier: MailboxPrimaryAddress
        columnName: TargetEmail

Stages and Predicates

Stage 1: source

NordPassEventLogs_CL

Stage 2: where

| where event_type == "item_delete"

Stage 3: where

| where action == "item_reassignment_deleted"

Stage 4: extend

| extend TargetEmail = user_email

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
TargetEmailextend

NRT Base64 Encoded Windows Process Command-lines

#
Status
available
Severity
medium
Source
github.com/Azure/Azure-Sentinel

This detection 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: c3e5dbaa-a540-408c-8b36-68bdfb3df088
name: NRT Base64 Encoded Windows Process Command-lines
description: |
  'This detection 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
tactics:
  - Execution
  - DefenseEvasion
relevantTechniques:
  - T1059
  - T1027
  - T1140
query: |
 SecurityEvent
  | where EventID == 4688
  | where isnotempty(CommandLine)
  | where CommandLine contains "TVqQAAMAAAAEAAA"
  | extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SubjectAccount
      - identifier: Name
        columnName: SubjectUserName
      - identifier: NTDomain
        columnName: SubjectDomainName
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.0.2
kind: NRT

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 4688

Stage 3: where

| where isnotempty(CommandLine)

Stage 4: where

| where CommandLine contains "TVqQAAMAAAAEAAA"

Stage 5: extend

| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinecontains
  • TVqQAAMAAAAEAAA corpus 2 (kusto 2)
field:"CommandLine" kind:contains value:"TVqQAAMAAAAEAAA"
CommandLineis_not_null
  • (no value, null check)
field:"CommandLine" kind:is_not_null
EventIDeq
  • 4688 corpus 23 (kusto 23)
field:"EventID" kind:eq value:"4688"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

OracleDBAudit - User activity after long inactivity time

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

'Detects when an action was made by a user which last activity was observed more than 30 days ago.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Persistence

Rule Body

id: 5e93a535-036b-4570-9e58-d8992f30e1ae
name: OracleDBAudit - User activity after long inactivity time
description: |
  'Detects when an action was made by a user which last activity was observed more than 30 days ago.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    datatypes:
      - Syslog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Persistence
relevantTechniques:
  - T1078
query: |
  let lbtime_14d = 14d;
  let lbtime_7d = 7d;
  let lbtime_24h = 24h;
  let known_users_14d = OracleDatabaseAuditEvent
  | where TimeGenerated between (ago(lbtime_14d) .. ago(lbtime_14d))
  | where isnotempty(DstUserName)
  | where isnotempty(Action)
  | summarize makeset(DstUserName);
  let known_users_7d = OracleDatabaseAuditEvent
  | where TimeGenerated between (ago(lbtime_7d) .. ago(lbtime_24h))
  | where isnotempty(DstUserName)
  | where isnotempty(Action)
  | summarize makeset(DstUserName);
  OracleDatabaseAuditEvent
  | where isnotempty(DstUserName)
  | where isnotempty(Action)
  | where DstUserName !in (known_users_7d)
  | where DstUserName in (known_users_14d)
  | project DstUserName
  | extend AccountCustomEntity = DstUserName
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let lbtime_14d = 14d;
let lbtime_7d = 7d;
let lbtime_24h = 24h;

Let Binding: known_users_14d used in Stage 5

let known_users_14d = OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_14d) .. ago(lbtime_14d))
| where isnotempty(DstUserName)
| where isnotempty(Action)
| summarize makeset(DstUserName);

Let Binding: known_users_7d used in Stage 4

let known_users_7d = OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_7d) .. ago(lbtime_24h))
| where isnotempty(DstUserName)
| where isnotempty(Action)
| summarize makeset(DstUserName);

Stage 1: source

OracleDatabaseAuditEvent

Stage 2: where

| where isnotempty(DstUserName)

Stage 3: where

| where isnotempty(Action)

Stage 4: where

| where DstUserName !in (known_users_7d)

Stage 5: where

| where DstUserName in (known_users_14d)

Stage 6: project

| project DstUserName

Stage 7: 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
DstUserNamein
  • known_users_14d
field:"DstUserName" kind:in value:"known_users_14d"
DstUserNameis_not_null
  • (no value, null check)
field:"DstUserName" kind:is_not_null

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
DstUserNameeqknown_users_7dexcludes:DstUserName field:"DstUserName" value:"known_users_7d"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DstUserNameproject
AccountCustomEntityextend

Pathlock TDnR - SAP System Log Events

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

Detects security-relevant events from the SAP System Log (SM21), forwarded by Pathlock Threat Detection and Response. System log events capture low-level system activity including kernel errors, authorization violations, and system configuration events that may indicate attacks or misuse.

MITRE ATT&CK Coverage

TacticTechniques
Discovery

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c63
name: Pathlock TDnR - SAP System Log Events
kind: Scheduled
description: >-
  Detects security-relevant events from the SAP System Log (SM21), forwarded by Pathlock Threat
  Detection and Response. System log events capture low-level system activity including kernel
  errors, authorization violations, and system configuration events that may indicate
  attacks or misuse.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1082
query: |
  Pathlock_TDnR_CL
  | where DataSource == "SYSLOG"
  | project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
            Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
            MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Bname
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Hostname
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIp
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: AnyAlert
    groupByEntities: []
    groupByAlertDetails: []
    groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0

Stages and Predicates

Stage 1: source

Pathlock_TDnR_CL

Stage 2: where

| where DataSource == "SYSLOG"

Stage 3: project

| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
          Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
          MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AffectedUserproject
Areaproject
Bnameproject
CentralTsproject
DataSourceproject
DestIpproject
Eventidproject
Hostnameproject
Instanceproject
LogLineproject
MessageV1project
MessageV2project
MessageV3project
MessageV4project
MsgIdproject
MsgNoproject
MsgTypeproject
Reportproject
SrcIpproject
Subidproject
Sysidproject
Tcodeproject
TimeGeneratedproject

Recorded Future Playbook Alerts Incident Creation

#

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

'Create Recorded Future Playbook Alerts in Microsoft Defender'

Rule Body

id: 0aded97e-4e2d-4e4d-93cf-4a7e45c7bab2
name: Recorded Future Playbook Alerts Incident Creation
description: |
  'Create Recorded Future Playbook Alerts in Microsoft Defender'
severity: Medium
status: Available
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
  RecordedFuturePlaybookAlerts_CL
  | where TimeGenerated >= now(-1h)
eventGroupingSettings:
  aggregationKind: AlertPerResult
alertDetailsOverride:
  alertDisplayNameFormat: 'Alert: {{rule_label_s}}  - {{title_s}}'
  alertDescriptionFormat: |
    _Recorded Future Alert_

    Evidence Summary:
    {{alert_description}}

    Priority: {{priority_s}}

    Recorded Future Playbook Alert ID
    {{id_s}}
  alertDynamicProperties:
    - alertProperty: AlertLink
      value: link_s
customDetails:
  RFLabel: SourceSystem
  Category: Category
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 1h
    matchingMethod: Selected
    groupByCustomDetails:
      - Category
version: 1.0.0
kind: NRT

Stages and Predicates

Stage 1: source

RecordedFuturePlaybookAlerts_CL

Stage 2: where

| where TimeGenerated >= now(-1h)

Semperis DSP Operations Critical Notifications

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

Alerts when there are critical notifications fired in the DSP system.

MITRE ATT&CK Coverage

TacticTechniques
Resource Development
Initial Access
Credential Access

Telemetry Coverage

Rule Body

id: "8f471e21-3bb2-466f-9bc2-0a0326a60788"
name: Semperis DSP Operations Critical Notifications 
description: |
  'Alerts when there are critical notifications fired in the DSP system.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SemperisDSP
    dataTypes:
      - dsp_parser
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CredentialAccess
  - ResourceDevelopment
relevantTechniques:
  - T1133
  - T1110
  - T1584
query: |
  SecurityEvent
  | where EventSourceName == 'Semperis-DSP-Notifications' and EventID == 30001
  | extend p1Xml = parse_xml(EventData).EventData.Data
  | mv-expand bagexpansion=array p1Xml
  | evaluate bag_unpack(p1Xml)
  | extend Name=column_ifexists('@Name', ''), Value=column_ifexists('#text', '')
  | evaluate pivot(Name, any(Value), TimeGenerated, EventSourceName, Channel, Computer, Level, EventLevelName, EventID, Task, Type, _ResourceId)
  | parse column_ifexists('objectDN', '') with * "CN=" cnName "," *
  | where "Critical" == column_ifexists('severity', "")
  | extend changedBy = column_ifexists('changedBy', "")
  | extend NTDomain = tostring(split(changedBy, '\\', 0)[0]), LoginUser = tostring(split(changedBy, '\\', 1)[0])
  | extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: LoginUser
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
eventGroupingSettings:
  aggregationKind: SingleAlert
alertDetailsOverride:
  alertDisplayNameFormat: Critical Notification -- Alert from Semperis Directory Services Protector
  alertDescriptionFormat: A critical notification was created in the DSP system.
version: 2.0.7
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventSourceName == 'Semperis-DSP-Notifications' and EventID == 30001

Stage 3: extend

| extend p1Xml = parse_xml(EventData).EventData.Data

Stage 4: mv-expand

| mv-expand bagexpansion=array p1Xml

Stage 5: evaluate

| evaluate bag_unpack(p1Xml)

Stage 6: extend

| extend Name=column_ifexists('@Name', ''), Value=column_ifexists('#text', '')

Stage 7: evaluate

| evaluate pivot(Name, any(Value), TimeGenerated, EventSourceName, Channel, Computer, Level, EventLevelName, EventID, Task, Type, _ResourceId)

Stage 8: parse

| parse column_ifexists('objectDN', '') with * "CN=" cnName "," *

Stage 9: where

| where "Critical" == column_ifexists('severity', "")

Stage 10: extend (3 consecutive steps)

| extend changedBy = column_ifexists('changedBy', "")
| extend NTDomain = tostring(split(changedBy, '\\', 0)[0]), LoginUser = tostring(split(changedBy, '\\', 1)[0])
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
p1Xmlextend
Nameextend
Valueextend
changedByextend
LoginUserextend
NTDomainextend
DnsDomainextend
HostNameextend

Sensitive Azure Key Vault operations

#
Status
available
Severity
low
Time window
1d
Group by
Resource, ResourceType, ResultType, clientInfo_s, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g, identity_claim_upn_s
Source
github.com/Azure/Azure-Sentinel

'Identifies when sensitive Azure Key Vault operations are used. This includes: VaultDelete, KeyDelete, SecretDelete, SecretPurge, KeyPurge, SecretBackup, KeyBackup. Any Backup operations should match with expected scheduled backup activity.'

MITRE ATT&CK Coverage

TacticTechniques
Impact

Rule Body

id: d6491be0-ab2d-439d-95d6-ad8ea39277c5
name: Sensitive Azure Key Vault operations
description: |
  'Identifies when sensitive Azure Key Vault operations are used. This includes: VaultDelete, KeyDelete, SecretDelete, SecretPurge, KeyPurge, SecretBackup, KeyBackup.
  Any Backup operations should match with expected scheduled backup activity.'
severity: Low
status: Available
requiredDataConnectors:
  - connectorId: AzureKeyVault
    dataTypes:
      - KeyVaultData
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1485
query: |
  let SensitiveOperationList = dynamic(
  ["VaultDelete", "KeyDelete", "SecretDelete", "SecretPurge", "KeyPurge", "SecretBackup", "KeyBackup"]);
  AzureDiagnostics
  | extend ResultType = column_ifexists("ResultType", "NoResultType"), 
  requestUri_s = column_ifexists("requestUri_s", "None"), 
  identity_claim_oid_g = column_ifexists("identity_claim_oid_g", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None"), 
  clientInfo_s = column_ifexists("clientInfo_s", "None"), 
  identity_claim_upn_s = column_ifexists("identity_claim_upn_s", "None"),
  identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g = column_ifexists("identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g", "None")
  | where ResourceType =~ "VAULTS" and ResultType =~ "Success"
  | where OperationName in~ (SensitiveOperationList)
  | summarize EventCount=count(), StartTimeUtc=min(TimeGenerated), EndTimeUtc=max(TimeGenerated), TimeTriggered=make_list(TimeGenerated),OperationNameList=make_set(OperationName), RequestURLList=make_set(requestUri_s), CallerIPList = make_set(CallerIPAddress),  CallerIPMax= arg_max(CallerIPAddress,*) by ResourceType, ResultType, Resource, identity_claim_upn_s, clientInfo_s, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g
  | extend timestamp = StartTimeUtc
  | extend Name = tostring(split(identity_claim_upn_s,'@',0)[0]), UPNSuffix = tostring(split(identity_claim_upn_s,'@',1)[0]), AadUserId = identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g 
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: AadUserId
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIPMax
version: 1.0.4
kind: Scheduled

Stages and Predicates

Let Binding: SensitiveOperationList used in Stage 4

let SensitiveOperationList = dynamic(
["VaultDelete", "KeyDelete", "SecretDelete", "SecretPurge", "KeyPurge", "SecretBackup", "KeyBackup"]);

Stage 1: source

AzureDiagnostics

Stage 2: extend

| extend ResultType = column_ifexists("ResultType", "NoResultType"), 
requestUri_s = column_ifexists("requestUri_s", "None"), 
identity_claim_oid_g = column_ifexists("identity_claim_oid_g", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None"), 
clientInfo_s = column_ifexists("clientInfo_s", "None"), 
identity_claim_upn_s = column_ifexists("identity_claim_upn_s", "None"),
identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g = column_ifexists("identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g", "None")

Stage 3: where

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

Stage 4: where

| where OperationName in~ (SensitiveOperationList)

Stage 5: summarize

| summarize EventCount=count(), StartTimeUtc=min(TimeGenerated), EndTimeUtc=max(TimeGenerated), TimeTriggered=make_list(TimeGenerated),OperationNameList=make_set(OperationName), RequestURLList=make_set(requestUri_s), CallerIPList = make_set(CallerIPAddress),  CallerIPMax= arg_max(CallerIPAddress,*) by ResourceType, ResultType, Resource, identity_claim_upn_s, clientInfo_s, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g

Stage 6: extend

| extend timestamp = StartTimeUtc

Stage 7: extend

| extend Name = tostring(split(identity_claim_upn_s,'@',0)[0]), UPNSuffix = tostring(split(identity_claim_upn_s,'@',1)[0]), AadUserId = identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
OperationNamein
  • KeyBackup
  • KeyDelete
  • KeyPurge
  • SecretBackup
  • SecretDelete
  • SecretPurge
  • VaultDelete
field:"OperationName" kind:in
ResourceTypeeq
  • VAULTS
field:"ResourceType" kind:eq value:"VAULTS"
ResultTypeeq
  • Success
field:"ResultType" kind:eq value:"Success"

Output Fields

These fields are emitted when the rule matches.

FieldSource
CallerIPListsummarize
CallerIPMaxsummarize
EndTimeUtcsummarize
EventCountsummarize
OperationNameListsummarize
RequestURLListsummarize
Resourcesummarize
ResourceTypesummarize
ResultTypesummarize
StartTimeUtcsummarize
TimeTriggeredsummarize
clientInfo_ssummarize
identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_gsummarize
identity_claim_upn_ssummarize
timestampextend
AadUserIdextend
Nameextend
UPNSuffixextend

Silk Typhoon Suspicious Exchange Request

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

'This query looks for suspicious request patterns to Exchange servers that fit a pattern observed by Silk Typhoon actors. The same query can be run on HTTPProxy logs from on-premise hosted Exchange servers. Reference: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 23005e87-2d3a-482b-b03d-edbebd1ae151
name: Silk Typhoon Suspicious Exchange Request
description: |
  'This query looks for suspicious request patterns to Exchange servers that fit a pattern observed by Silk Typhoon actors.
  The same query can be run on HTTPProxy logs from on-premise hosted Exchange servers.
  Reference: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureMonitor(IIS)
    dataTypes:
      - W3CIISLog
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1190
query: |
  let exchange_servers = (
  W3CIISLog
  | where TimeGenerated > ago(14d)
  | where sSiteName =~ "Exchange Back End"
  | summarize by Computer);
  W3CIISLog
  | where TimeGenerated > ago(1d)
  | where Computer in (exchange_servers)
  | where csUriQuery startswith "t="
  | project-reorder TimeGenerated, Computer, csUriStem, csUriQuery, csUserName, csUserAgent, cIP
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | extend AccountName = tostring(split(csUserName, "@")[0]), AccountUPNSuffix = tostring(split(csUserName, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: csUserName
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix  
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: NTDomain
        columnName: HostNameDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: cIP
version: 1.0.3
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Threat Intelligence" ]

Stages and Predicates

Let Binding: exchange_servers used in Stage 3

let exchange_servers = (
W3CIISLog
| where TimeGenerated > ago(14d)
| where sSiteName =~ "Exchange Back End"
| summarize by Computer);

Stage 1: source

W3CIISLog

Stage 2: where

| where TimeGenerated > ago(1d)

Stage 3: where

| where Computer in (exchange_servers)

Stage 4: where

| where csUriQuery startswith "t="

Stage 5: project-reorder

| project-reorder TimeGenerated, Computer, csUriStem, csUriQuery, csUserName, csUserAgent, cIP

Stage 6: extend (3 consecutive steps)

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

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
DomainIndexextend
HostNameextend
HostNameDomainextend
AccountNameextend
AccountUPNSuffixextend

Tenable.ad LSASS Memory

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

'Searches for OS Credentials dumping attacks.'

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Rule Body

id: 6f7fa5f9-7d21-42c1-bc52-ac355b87c6cf
name: Tenable.ad LSASS Memory
description: |
    'Searches for OS Credentials dumping attacks.'
severity: High
requiredDataConnectors:
  - connectorId: Tenable.ad
    dataTypes:
      - Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
relevantTechniques:
  - T1003.001
query: |
  // For the query to work properly, make sure you have imported the afad_parser.kql parser into the workspace
  // Retrieve the parser here: https://raw.githubusercontent.com/tenable/Azure-Sentinel/Tenable.ad-connector/Solutions/TenableAD/Parsers/afad_parser.kql
  // Then, create the Kusto Function with alias afad_parser
  afad_parser
    | where MessageType == 2 and Codename == "OS Credential Dumping: LSASS Memory"
    | extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
version: 1.0.1
kind: Scheduled

Stages and Predicates

Stage 1: source

afad_parser

Stage 2: where

| where MessageType == 2 and Codename == "OS Credential Dumping: LSASS Memory"

Stage 3: extend

| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Codenameeq
  • OS Credential Dumping: LSASS Memory corpus 3 (kusto 3)
field:"Codename" kind:eq value:"OS Credential Dumping: LSASS Memory"
MessageTypeeq
  • 2 corpus 21 (kusto 21)
field:"MessageType" kind:eq value:"2"

Output Fields

These fields are emitted when the rule matches.

FieldSource
DnsDomainextend
HostNameextend

Unauthorized user access across AWS and Azure

#
Severity
medium
Time window
1d
Group by
$left.AWSNetworkEntity, $right.IPAddress, AuthenticationRequirement, ClientAppUsed, ConditionalAccessStatus, IPAddress, OperationName, RiskDetail, UserAgent, UserPrincipalName
Source
github.com/Azure/Azure-Sentinel

This detection compiles and correlates unauthorized user access alerts originating from AWS GuardDuty with Azure portal sign-in activities. It focuses on AWS GuardDuty alerts related to unauthorized user access, specifically targeting network IP associations tied to activities such as logins from malicious IP addresses or instance credential exfiltration attempts. The ditection leverages these common network IP advisories to detect and pinpoint unauthorized users attempting to access both AWS and Azure resources.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 60f31001-018a-42bf-8045-a92e1f361b7b
name: Unauthorized user access across AWS and Azure
description: |
  'This detection compiles and correlates unauthorized user access alerts originating from AWS GuardDuty with Azure portal sign-in activities. It focuses on AWS GuardDuty alerts related to unauthorized user access, specifically targeting network IP associations tied to activities such as logins from malicious IP addresses or instance credential exfiltration attempts. The ditection leverages these common network IP advisories to detect and pinpoint unauthorized users attempting to access both AWS and Azure resources.'
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
      - SigninLogs
  - connectorId: AWSS3
    dataTypes:
      - AWSGuardDuty
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CredentialAccess
  - Exfiltration
  - Discovery
relevantTechniques:
  - T1557
  - T1110
  - T1110.003
  - T1110.004
  - T1212
  - T1048
  - T1087
  - T1580
query: |
    // Define a variable 'AwsAlert' to collect Unauthorized user access alerts from AWS GuardDuty table
    let AwsAlert = materialize (
        AWSGuardDuty
        | where ActivityType has_any ("UnauthorizedAccess:IAMUser/TorIPCaller", "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom", 
        "UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS", "UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS",
        "UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B","UnauthorizedAccess:IAMUser/MaliciousIPCaller")
        | extend
            AWSAlertId = Id, 
            AWSAlertTitle = Title,
            AWSAlertDescription = Description,
            AWSresourceType = tostring(parse_json(ResourceDetails).resourceType),
            AWSNetworkEntity = tostring(parse_json(ServiceDetails).action.awsApiCallAction.remoteIpDetails.ipAddressV4),
            AWSAlertUserNameEntity = tostring(parse_json(ResourceDetails).accessKeyDetails.userName),
            InstanceType = tostring(parse_json(ResourceDetails).instanceDetails.instanceType),
            AWSTargetingService = parse_json(ServiceDetails).additionalInfo.apiCalls,
            AWSAlertTime = TimeCreated,
            AWSAlertLink= tostring(strcat('https://us-east-1.console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?macros=current&fId=',Id)),
            Severity = 
      case (
        Severity >= 7.0, "High",
        Severity between (4.0 .. 6.9), "Medium",
        Severity between (1.0 .. 3.9), "Low",
        "Unknown")
        | mv-apply AIPCall = AWSTargetingService on 
            ( 
            where AIPCall has "name"    
            | extend APICallName = tostring(AIPCall.name), APICallCount = tostring(AIPCall["count"])
            ) 
        | distinct
            AWSAlertTime,
            ActivityType,
            Severity,
            AWSAlertId,
            AWSAlertTitle,
            AWSAlertDescription,
            AWSAlertLink,
            Arn,
            AWSresourceType,
            AWSNetworkEntity,
            AWSAlertUserNameEntity,
            InstanceType,
            APICallName,
            APICallCount      
        );
        // Define a variable 'Azure_sigin' to collect Azure portal Signing activity from SigninLogs Table
        let Azure_sigin = materialize (SigninLogs
            | where AppDisplayName == "Azure Portal"
            | where isnotempty(OriginalRequestId)
            | summarize 
                totalAzureLoginEventId = dcount(OriginalRequestId), 
                AzureFailedEventsCount = dcountif(OriginalRequestId, ResultType != 0), 
                AzureSuccessfulEventsCount = dcountif(OriginalRequestId, ResultType == 0),
                AzureSetOfFailedEvents = makeset(iff(ResultType != 0, OriginalRequestId, ""), 5), 
                AzureSetOfSuccessfulEvents = makeset(iff(ResultType == 0, OriginalRequestId, ""), 5) 
            by 
                IPAddress, 
                UserPrincipalName, 
                bin(TimeGenerated, 1min), 
                UserAgent,
                ConditionalAccessStatus,
                OperationName,
                RiskDetail,
                AuthenticationRequirement,
                ClientAppUsed 
            // Extracting the name and UPN suffix from UserPrincipalName
            | extend
                Name = tostring(split(UserPrincipalName, "@")[0]),
                UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
        );
        // Join 'AwsAlert' and 'Azure_sigin' on the AWS Network Entity and Azure IP Address
        AwsAlert
        | join kind=inner Azure_sigin on $left.AWSNetworkEntity == $right.IPAddress
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserPrincipalName
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
customDetails:
  AWSAlertUserName: AWSAlertUserNameEntity
  AWSArn: Arn
  AWSresourceType: AWSresourceType
  AWSInstanceType: InstanceType
  AWSAPICallName: APICallName
  AWSAPICallCount: APICallCount
  AzureUserAgent: UserAgent
  AzureUser: UserPrincipalName
  AzureClientAppUsed: ClientAppUsed
  AzConditionalAccess: ConditionalAccessStatus
  AzureOperationName: OperationName
  AzureRiskDetail: RiskDetail
  AzAuthRequirement: AuthenticationRequirement
  alertSeverity: Severity
alertDetailsOverride:
  alertDisplayNameFormat: "{{AWSNetworkEntity}} from {{AWSAlertTitle}} observed in Azure Singins with {{UserPrincipalName}}"
  alertDescriptionFormat: "  This detection compiles and correlates unauthorized user access alerts originating from AWS GuardDuty With Alert Description '{{AWSAlertDescription}}' with Azure portal sign-in activities. It focuses on AWS GuardDuty alerts related to unauthorized user access, specifically targeting network IP associations tied to activities such as logins from malicious IP addresses or instance credential exfiltration attempts. The detection leverages these common network IP advisories to detect and pinpoint unauthorized users attempting to access both AWS and Azure resources.  \n\n AWS ALert Link : '{{AWSAlertLink}}' \n\n Find More Details :https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html"
  alertSeverityColumnName: Severity
  alertDynamicProperties:
    - alertProperty: AlertLink
      value: AWSAlertLink
    - alertProperty: ProviderName
      value: "AWS"
    - alertProperty: ProductName
      value: "AWSGuardDuty"
    - alertProperty: ProductComponentName
      value: "AWSGuardDuty"
kind: Scheduled
version: 1.0.4

Stages and Predicates

let AwsAlert is inlined into the numbered stages below.

Let Binding: Azure_sigin used in Stage 6

let Azure_sigin = materialize (SigninLogs
        | where AppDisplayName == "Azure Portal"
        | where isnotempty(OriginalRequestId)
        | summarize 
            totalAzureLoginEventId = dcount(OriginalRequestId), 
            AzureFailedEventsCount = dcountif(OriginalRequestId, ResultType != 0), 
            AzureSuccessfulEventsCount = dcountif(OriginalRequestId, ResultType == 0),
            AzureSetOfFailedEvents = makeset(iff(ResultType != 0, OriginalRequestId, ""), 5), 
            AzureSetOfSuccessfulEvents = makeset(iff(ResultType == 0, OriginalRequestId, ""), 5) 
        by 
            IPAddress, 
            UserPrincipalName, 
            bin(TimeGenerated, 1min), 
            UserAgent,
            ConditionalAccessStatus,
            OperationName,
            RiskDetail,
            AuthenticationRequirement,
            ClientAppUsed 
        | extend
            Name = tostring(split(UserPrincipalName, "@")[0]),
            UPNSuffix = tostring(split(UserPrincipalName, "@")[1])
    );

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

Stage 1: source

AWSGuardDuty

Stage 2: where

| where ActivityType has_any ("UnauthorizedAccess:IAMUser/TorIPCaller", "UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom", 
    "UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS", "UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS",
    "UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B","UnauthorizedAccess:IAMUser/MaliciousIPCaller")

Stage 3: extend

| extend
        AWSAlertId = Id, 
        AWSAlertTitle = Title,
        AWSAlertDescription = Description,
        AWSresourceType = tostring(parse_json(ResourceDetails).resourceType),
        AWSNetworkEntity = tostring(parse_json(ServiceDetails).action.awsApiCallAction.remoteIpDetails.ipAddressV4),
        AWSAlertUserNameEntity = tostring(parse_json(ResourceDetails).accessKeyDetails.userName),
        InstanceType = tostring(parse_json(ResourceDetails).instanceDetails.instanceType),
        AWSTargetingService = parse_json(ServiceDetails).additionalInfo.apiCalls,
        AWSAlertTime = TimeCreated,
        AWSAlertLink= tostring(strcat('https://us-east-1.console.aws.amazon.com/guardduty/home?region=us-east-1#/findings?macros=current&fId=',Id)),
        Severity = 
  case (
    Severity >= 7.0, "High",
    Severity between (4.0 .. 6.9), "Medium",
    Severity between (1.0 .. 3.9), "Low",
    "Unknown")
Severity =
ifSeverity >= 7.0"High"
elifSeverity >= 4.0 and Severity <= 6.9"Medium"
elifSeverity >= 1.0 and Severity <= 3.9"Low"
else"Unknown"

Stage 4: kusto:mv-apply

| mv-apply AIPCall = AWSTargetingService on 
        ( 
        where AIPCall has "name"    
        | extend APICallName = tostring(AIPCall.name), APICallCount = tostring(AIPCall["count"])
        )

Stage 5: distinct

| distinct
        AWSAlertTime,
        ActivityType,
        Severity,
        AWSAlertId,
        AWSAlertTitle,
        AWSAlertDescription,
        AWSAlertLink,
        Arn,
        AWSresourceType,
        AWSNetworkEntity,
        AWSAlertUserNameEntity,
        InstanceType,
        APICallName,
        APICallCount

Stage 6: join

AwsAlert
| join kind=inner Azure_sigin on $left.AWSNetworkEntity == $right.IPAddress

Stage 7: summarize aggregation inside the join branch

summarize by IPAddress, UserPrincipalName, UserAgent, ConditionalAccessStatus, OperationName, RiskDetail, AuthenticationRequirement, ClientAppUsed

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
AIPCallmatch
  • name transforms: term
field:"AIPCall" kind:match value:"name"
ActivityTypematch
  • UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.B transforms: term corpus 2 (kusto 2)
  • UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.InsideAWS transforms: term corpus 2 (kusto 2)
  • UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS transforms: term corpus 2 (kusto 2)
  • UnauthorizedAccess:IAMUser/MaliciousIPCaller transforms: term
  • UnauthorizedAccess:IAMUser/MaliciousIPCaller.Custom transforms: term corpus 2 (kusto 2)
  • UnauthorizedAccess:IAMUser/TorIPCaller transforms: term corpus 2 (kusto 2)
field:"ActivityType" kind:match
AppDisplayNameeq
  • Azure Portal
field:"AppDisplayName" kind:eq value:"Azure Portal"
OriginalRequestIdis_not_null
  • (no value, null check)
field:"OriginalRequestId" kind:is_not_null

Output Fields

These fields are emitted when the rule matches.

FieldSource
AuthenticationRequirementsummarize
ClientAppUsedsummarize
ConditionalAccessStatussummarize
IPAddresssummarize
OperationNamesummarize
RiskDetailsummarize
UserAgentsummarize
UserPrincipalNamesummarize

Back to all Kusto rules