Detection rules › Kusto

Kusto rules: 2c

Alsid DCShadow

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

'Searches for DCShadow attacks'

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment

Rule Body

id: 25e0b2dd-3ad3-4d5b-80dd-720f4ef0f12c
name: Alsid DCShadow
description: |
    'Searches for DCShadow attacks'
severity: High
requiredDataConnectors:
  - connectorId: AlsidForAD
    dataTypes:
      - AlsidForADLog_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1207
query: |
    afad_parser
    | where MessageType == 2 and Codename == "DCShadow"
    | 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 == "DCShadow"

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
  • DCShadow corpus 3 (kusto 3)
field:"Codename" kind:eq value:"DCShadow"
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

AWSCloudTrail - Suspicious AWS EC2 Compute Resource Deployments

#
Severity
medium
Time window
1d
Group by
AccountName, AccountUPNSuffix, RecipientAccountId, SourceIpAddress, UserAgent, UserIdentityArn
Source
github.com/Azure/Azure-Sentinel

'This detection focused on Suspicious deployment of AWS EC2 resource (virtual machine) scale sets was detected. This behavior might indicate that the threat actor is deploying computing resources for cryptocurrency mining activities.This detection centers around identifying suspicious instances of AWS EC2 resource deployment, particularly scale sets. Such behavior raises concerns of potential threat actor involvement, potentially indicative of efforts to deploy computing resources for the purpose of cryptocurrency mining activities.

MITRE ATT&CK Coverage

TacticTechniques
Impact

Telemetry Coverage

Rules Detecting the Same Action

These rules filter on the same operation.

Rule Body

id: 9e457dc4-81f0-4d25-bc37-a5fa4a17946a
name: AWSCloudTrail - Suspicious AWS EC2 Compute Resource Deployments
description: |
  'This detection focused on Suspicious deployment of AWS EC2 resource (virtual machine) scale sets was detected. This behavior might indicate that the threat actor is deploying computing resources for cryptocurrency mining activities.This detection centers around identifying suspicious instances of AWS EC2 resource deployment, particularly scale sets. Such behavior raises concerns of potential threat actor involvement, potentially indicative of efforts to deploy computing resources for the purpose of cryptocurrency mining activities.
severity: Medium
requiredDataConnectors:
  - connectorId: AWS
    dataTypes:
      - AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  -  Impact
relevantTechniques:
  - T1496
query: |
  // Retrieve AWS CloudTrail events generated within the last day
  AWSCloudTrail
  // Filter events related to instance creation
  | where EventName =~ "RunInstances"
  // Exclude events with error messages
  | where isempty(ErrorMessage)
  // Extract the event source type
  | extend EventSourceSplit = split(EventSource, ".")
  | extend Type = tostring(EventSourceSplit[0])
  // Extract instance-related details from the event data
  | extend instance = tostring(parse_json(RequestParameters).instanceType),platform = tostring(parse_json(ResponseElements).instancesSet.items[0].platform)
  // Determine the operating system platform
  | extend OSplatform = iff(isempty(platform), tostring("Linux"), platform),CPU = tostring(parse_json(ResponseElements).instancesSet.items[0].cpuOptions),core = toint(parse_json(ResponseElements).instancesSet.items[0].cpuOptions.coreCount),corThread = toint(parse_json(ResponseElements).instancesSet.items[0].cpuOptions.threadsPerCore),InstanceId = tostring(parse_json(ResponseElements).instancesSet.items[0].instanceId)
  // Filter out instances with empty core values
  | where isnotempty(core)
  // Calculate the total compute based on core and thread counts
  | extend totalCorecompute = core * corThread
  | 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 relevant information for analysis
  | summarize Start= min(TimeGenerated),
    end=   max(TimeGenerated),
    totalgpu= sum(totalCorecompute)
    by SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn, UserAgent
  // Filter results based on total GPU compute and time duration
  | where totalgpu > 800
  | where datetime_diff('hour', end, Start) < 8
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
      - identifier: CloudAppAccountId
        columnName: RecipientAccountId
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIpAddress
customDetails:
    UserAgent: UserAgent
    AWSUser: UserIdentityArn
    SourceIpAddress: SourceIpAddress
    TotalCoreCount: totalgpu
alertDetailsOverride:
  alertDisplayNameFormat: Suspicious EC2 deployment - {{totalgpu}} cores deployed by {{AccountName}}
  alertDescriptionFormat: User {{AccountName}} deployed {{totalgpu}} total CPU cores across EC2 instances from {{SourceIpAddress}} within an 8-hour window, suggesting potential cryptocurrency mining activity.
kind: Scheduled
version: 1.0.2

Stages and Predicates

Stage 1: source

AWSCloudTrail

Stage 2: where

| where EventName =~ "RunInstances"

Stage 3: where

| where isempty(ErrorMessage)

Stage 4: extend (4 consecutive steps)

| extend EventSourceSplit = split(EventSource, ".")
| extend Type = tostring(EventSourceSplit[0])
| extend instance = tostring(parse_json(RequestParameters).instanceType),platform = tostring(parse_json(ResponseElements).instancesSet.items[0].platform)
| extend OSplatform = iff(isempty(platform), tostring("Linux"), platform),CPU = tostring(parse_json(ResponseElements).instancesSet.items[0].cpuOptions),core = toint(parse_json(ResponseElements).instancesSet.items[0].cpuOptions.coreCount),corThread = toint(parse_json(ResponseElements).instancesSet.items[0].cpuOptions.threadsPerCore),InstanceId = tostring(parse_json(ResponseElements).instancesSet.items[0].instanceId)

Stage 5: where

| where isnotempty(core)

Stage 6: extend (5 consecutive steps)

| extend totalCorecompute = core * corThread
| 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 7: summarize

| summarize Start= min(TimeGenerated),
  end=   max(TimeGenerated),
  totalgpu= sum(totalCorecompute)
  by SourceIpAddress, RecipientAccountId, AccountName, AccountUPNSuffix, UserIdentityArn, UserAgent

Stage 8: where

| where totalgpu > 800

Stage 9: where end - Start < 8h

| where datetime_diff('hour', end, Start) < 8

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountNamesummarize
AccountUPNSuffixsummarize
RecipientAccountIdsummarize
SourceIpAddresssummarize
Startsummarize
UserAgentsummarize
UserIdentityArnsummarize
endsummarize
totalgpusummarize

BloodHound Attack Path Finding - Constrained Delegation on Tier Zero Computers

#

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 - Constrained Delegation on Tier Zero Computers

Rule Body

id: 7b2cc2d2-052f-4ac9-9be0-fb6f22cbca33
name: BloodHound Attack Path Finding - Constrained Delegation on Tier Zero Computers
description: BloodHound Attack Path Finding - Constrained Delegation on Tier Zero
  Computers
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: BloodHoundEnterprise
    dataTypes:
      - BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: "BHEAttackPathsData_CL \n| where Finding == \"T0AllowedToDelegate\""
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 == "T0AllowedToDelegate"

Indicators

These rows show field, operator, and value matches.

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

CYFIRMA - Medium severity Trojan File Hash Indicators with Block Action 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 KQL query extracts file hash indicators associated with Trojan activity from the CyfirmaIndicators_CL table. It specifically targets indicators containing file hashes linked to Trojan behavior and retrieves MD5, SHA1, and SHA256 values. The query also includes contextual threat intelligence such as threat actors, tags, sources, and geolocation information."

MITRE ATT&CK Coverage

Rule Body

id: 25686f44-5f5f-4388-95e2-eea244481438
name: CYFIRMA - Medium severity Trojan File Hash Indicators with Block Action Rule
description: |
  "This KQL query extracts file hash indicators associated with Trojan activity from the CyfirmaIndicators_CL table. 
  It specifically targets indicators containing file hashes linked to Trojan behavior and retrieves MD5, SHA1, and SHA256 values. 
  The query also includes contextual threat intelligence such as threat actors, tags, sources, and geolocation information."
version: 1.0.1
kind: Scheduled
severity: Medium
enabled: false
requiredDataConnectors:
  - connectorId: CyfirmaCyberIntelligenceDC
    dataTypes:
      - CyfirmaIndicators_CL
query: |
  //Trojan File Hash Indicators with Block Action
  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 MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
  | extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
  | extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
  | extend
      Algo_MD5='MD5',
      Algo_SHA1= 'SHA1',
      Algo_SHA256='SHA256',
      ProviderName = 'CYFIRMA',
      ProductName = 'DeCYFIR/DeTCT'
  | project  
      MD5,
      Algo_MD5,
      SHA1,
      Algo_SHA1,
      SHA256,
      Algo_SHA256,
      ThreatActors,
      Sources,
      RecommendedActions,
      Roles,
      Country,
      name,
      Description,
      ConfidenceScore,
      SecurityVendors,
      IndicatorID,
      created,
      modified,
      valid_from,
      Tags,
      ThreatType,
      TimeGenerated,
      ProductName,
      ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
  - InitialAccess
  - Execution
  - Persistence
  - DefenseEvasion
  - CommandAndControl
  - CredentialAccess
relevantTechniques:
  - T1566
  - T1204
  - T1547
  - T1027
  - T1071
  - T1003
  - T1566.001
  - T1547.001
alertDetailsOverride:
  alertDisplayNameFormat: "High-Confidence Trojan File Hash Indicators with Block Action 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
entityMappings:
  - entityType: FileHash
    fieldMappings:
      - identifier: Value
        columnName: MD5
      - identifier: Algorithm
        columnName: Algo_MD5
  - entityType: FileHash
    fieldMappings:
      - identifier: Value
        columnName: SHA1
      - identifier: Algorithm
        columnName: Algo_SHA1
  - entityType: FileHash
    fieldMappings:
      - identifier: Value
        columnName: SHA256
      - identifier: Algorithm
        columnName: Algo_SHA256
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 (4 consecutive steps)

| extend MD5 = extract(@"file:hashes\.md5\s*=\s*'([a-fA-F0-9]{32})'", 1, pattern)
| extend SHA1 = extract(@"file:hashes\.'SHA-1'\s*=\s*'([a-fA-F0-9]{40})'", 1, pattern)
| extend SHA256 = extract(@"file:hashes\.'SHA-256'\s*=\s*'([a-fA-F0-9]{64})'", 1, pattern)
| extend
    Algo_MD5='MD5',
    Algo_SHA1= 'SHA1',
    Algo_SHA256='SHA256',
    ProviderName = 'CYFIRMA',
    ProductName = 'DeCYFIR/DeTCT'

Stage 4: project

| project  
    MD5,
    Algo_MD5,
    SHA1,
    Algo_SHA1,
    SHA256,
    Algo_SHA256,
    ThreatActors,
    Sources,
    RecommendedActions,
    Roles,
    Country,
    name,
    Description,
    ConfidenceScore,
    SecurityVendors,
    IndicatorID,
    created,
    modified,
    valid_from,
    Tags,
    ThreatType,
    TimeGenerated,
    ProductName,
    ProviderName

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Algo_MD5project
Algo_SHA1project
Algo_SHA256project
ConfidenceScoreproject
Countryproject
Descriptionproject
IndicatorIDproject
MD5project
ProductNameproject
ProviderNameproject
RecommendedActionsproject
Rolesproject
SHA1project
SHA256project
SecurityVendorsproject
Sourcesproject
Tagsproject
ThreatActorsproject
ThreatTypeproject
TimeGeneratedproject
createdproject
modifiedproject
nameproject
valid_fromproject

Dev-0270 Registry IOC - September 2022

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

The query below identifies modification of registry by Dev-0270 actor to disable security feature as well as to add ransom notes

MITRE ATT&CK Coverage

TacticTechniques
Impact

Telemetry Coverage

Rule Body

id: 2566e99f-ad0f-472a-b9ac-d3899c9283e6
name: Dev-0270 Registry IOC - September 2022
description: | 
  'The query below identifies modification of registry by Dev-0270 actor to disable security feature as well as to add ransom notes'
severity: High 
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - DeviceProcessEvents
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
status: Available
tactics: 
  - Impact
relevantTechniques:
  - T1486
tags:
  - Dev-0270
query: |
  (union isfuzzy=true
  (SecurityEvent
  | where EventID == 4688
  | where (CommandLine has_all  ('reg', 'add', 'HKLM\\SOFTWARE\\Policies\\', '/v','/t', 'REG_DWORD', '/d', '/f') and CommandLine has_any('DisableRealtimeMonitoring', 'UseTPMKey', 'UseTPMKeyPIN', 'UseAdvancedStartup', 'EnableBDEWithNoTPM', 'RecoveryKeyMessageSource'))
    or CommandLine has_all ('reg', 'add', 'HKLM\\SOFTWARE\\Policies\\', '/v','/t', 'REG_DWORD', '/d', '/f', 'RecoveryKeyMessage', 'Your drives are Encrypted!', '@')
  | project TimeGenerated, Computer, Account, AccountDomain, ProcessName, ProcessNameFullPath = NewProcessName, EventID, Activity, CommandLine, EventSourceName, Type
  ),
  (DeviceProcessEvents 
  | where (InitiatingProcessCommandLine has_all(@'"reg"', 'add', @'"HKLM\SOFTWARE\Policies\', '/v','/t', 'REG_DWORD', '/d', '/f') 
     and InitiatingProcessCommandLine has_any('DisableRealtimeMonitoring', 'UseTPMKey', 'UseTPMKeyPIN', 'UseAdvancedStartup', 'EnableBDEWithNoTPM', 'RecoveryKeyMessageSource') ) 
     or InitiatingProcessCommandLine has_all('"reg"', 'add', @'"HKLM\SOFTWARE\Policies\', '/v','/t', 'REG_DWORD', '/d', '/f', 'RecoveryKeyMessage', 'Your drives are Encrypted!', '@')
  | extend Account = strcat(InitiatingProcessAccountDomain, @'\', InitiatingProcessAccountName), Computer = DeviceName
   )
   )
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | extend AccountName = tostring(split(Account, @'\')[1]), AccountNTDomain = tostring(split(Account, @'\')[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: AccountName
      - identifier: NTDomain
        columnName: AccountNTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: HostNameDomain
version: 1.0.3
kind: Scheduled

Stages and Predicates

Union isfuzzy=true (2 Sources)

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

Leg 1: SecurityEvent

SecurityEvent
| where EventID == 4688
| where (CommandLine has_all  ('reg', 'add', 'HKLM\\SOFTWARE\\Policies\\', '/v','/t', 'REG_DWORD', '/d', '/f') and CommandLine has_any('DisableRealtimeMonitoring', 'UseTPMKey', 'UseTPMKeyPIN', 'UseAdvancedStartup', 'EnableBDEWithNoTPM', 'RecoveryKeyMessageSource'))
  or CommandLine has_all ('reg', 'add', 'HKLM\\SOFTWARE\\Policies\\', '/v','/t', 'REG_DWORD', '/d', '/f', 'RecoveryKeyMessage', 'Your drives are Encrypted!', '@')
| project TimeGenerated, Computer, Account, AccountDomain, ProcessName, ProcessNameFullPath = NewProcessName, EventID, Activity, CommandLine, EventSourceName, Type

Leg 2: DeviceProcessEvents

DeviceProcessEvents 
| where (InitiatingProcessCommandLine has_all(@'"reg"', 'add', @'"HKLM\SOFTWARE\Policies\', '/v','/t', 'REG_DWORD', '/d', '/f') 
   and InitiatingProcessCommandLine has_any('DisableRealtimeMonitoring', 'UseTPMKey', 'UseTPMKeyPIN', 'UseAdvancedStartup', 'EnableBDEWithNoTPM', 'RecoveryKeyMessageSource') ) 
   or InitiatingProcessCommandLine has_all('"reg"', 'add', @'"HKLM\SOFTWARE\Policies\', '/v','/t', 'REG_DWORD', '/d', '/f', 'RecoveryKeyMessage', 'Your drives are Encrypted!', '@')
| extend Account = strcat(InitiatingProcessAccountDomain, @'\', InitiatingProcessAccountName), Computer = DeviceName

Applied to the Combined Result

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

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CommandLinematch
  • /d transforms: term
  • /f transforms: term
  • /t transforms: term
  • /v transforms: term
  • @ transforms: term
  • DisableRealtimeMonitoring transforms: term
  • EnableBDEWithNoTPM transforms: term
  • HKLM\SOFTWARE\Policies\ transforms: term
  • REG_DWORD transforms: term
  • RecoveryKeyMessage transforms: term
  • RecoveryKeyMessageSource transforms: term
  • UseAdvancedStartup transforms: term
  • UseTPMKey transforms: term
  • UseTPMKeyPIN transforms: term
  • Your drives are Encrypted! transforms: term
  • add transforms: term corpus 2 (kusto 2)
  • reg transforms: term
field:"CommandLine" kind:match
EventIDeq
  • 4688 corpus 23 (kusto 23)
field:"EventID" kind:eq value:"4688"
InitiatingProcessCommandLinematch
  • "HKLM\SOFTWARE\Policies\ transforms: term corpus 2 (kusto 2)
  • "reg" transforms: term corpus 2 (kusto 2)
  • /d transforms: term corpus 2 (kusto 2)
  • /f transforms: term corpus 2 (kusto 2)
  • /t transforms: term corpus 2 (kusto 2)
  • /v transforms: term corpus 2 (kusto 2)
  • @ transforms: term
  • DisableRealtimeMonitoring transforms: term corpus 2 (kusto 2)
  • EnableBDEWithNoTPM transforms: term corpus 2 (kusto 2)
  • REG_DWORD transforms: term corpus 2 (kusto 2)
  • RecoveryKeyMessage transforms: term
  • RecoveryKeyMessageSource transforms: term corpus 2 (kusto 2)
  • UseAdvancedStartup transforms: term corpus 2 (kusto 2)
  • UseTPMKey transforms: term corpus 2 (kusto 2)
  • UseTPMKeyPIN transforms: term corpus 2 (kusto 2)
  • Your drives are Encrypted! transforms: term
  • add transforms: term corpus 3 (kusto 3)
field:"ParentCommandLine" kind:match

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountextend
AccountDomainproject
Activityproject
CommandLineproject
Computerextend
EventIDproject
EventSourceNameproject
ProcessNameproject
ProcessNameFullPathproject
TimeGeneratedproject
Typeproject
DomainIndexextend
HostNameextend
HostNameDomainextend
AccountNTDomainextend
AccountNameextend

Dynatrace Application Security - Attack detection

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

'Dynatrace has detected an ongoing attack in your environment.'

MITRE ATT&CK Coverage

Rule Body

id: 1b0b2065-8bac-5a00-83c4-1b58f69ac212
name: Dynatrace Application Security - Attack detection
description: |
  'Dynatrace has detected an ongoing attack in your environment.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: DynatraceAttacks
    dataTypes:
      - DynatraceAttacks
  - connectorId: DynatraceAttacksCCPDefinition
    dataTypes:
      - DynatraceAttacksV2_CL
  - connectorId: DTAttacksV3CCPDefinition
    dataTypes:
      - DynatraceAttacksV3_CL
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Execution
  - Impact
  - InitialAccess
  - PrivilegeEscalation
relevantTechniques:
  - T1059
  - T1565
  - T1190
  - T1068
eventGroupingSettings:
  aggregationKind: AlertPerResult
query: |
  DynatraceAttacks
  | where State != 'ALLOWLISTED'
  | summarize  arg_max(TimeStamp, *) by AttackId
alertDetailsOverride:
  alertDisplayNameFormat: 'Dynatrace Attack {{State}} - {{DisplayId}} : {{DisplayName}}'
  alertDescriptionFormat: |
    Dynatrace has detected an ongoing attack in your environment which was {{State}}.
customDetails:
  AttackIdentifier: AttackId
  AttackType: AttackType
  AttackState: State
  DisplayIdentifier: DisplayId
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: true
    lookbackDuration: P7D
    matchingMethod: AllEntities
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: host
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: url
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: sourceIp
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

DynatraceAttacks

Stage 2: where

| where State != 'ALLOWLISTED'

Stage 3: summarize

| summarize  arg_max(TimeStamp, *) by AttackId

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Statene
  • ALLOWLISTED
field:"State" kind:ne value:"ALLOWLISTED"

Output Fields

These fields are emitted when the rule matches.

FieldSource
AttackIdsummarize

Infoblox - Data Exfiltration Attack

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

'Data exfiltration attack detected by Infoblox Threat Insight. 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: 8db2b374-0337-49bd-94c9-cfbf8e5d83ad
name: Infoblox - Data Exfiltration Attack
description: |
  'Data exfiltration attack detected by Infoblox Threat Insight. 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 = 1;
  InfobloxCDC
  | where DeviceEventClassID has_cs "RPZ"
  | where InfobloxB1FeedName == "Threat Insight - Data Exfiltration"
  | summarize count() by SourceIP
  | where count_ > threshold
  | join kind=innerunique (InfobloxCDC
    | where DeviceEventClassID has_cs "RPZ"
    | where InfobloxB1FeedName == "Threat Insight - Data Exfiltration"
    ) 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
  - entityType: Malware
    fieldMappings:
      - identifier: Name
        columnName: InfobloxB1FeedName
      - identifier: Category
        columnName: InfobloxB1FeedName
customDetails:
  SourceMACAddress: SourceMACAddress
  InfobloxB1FeedName: InfobloxB1FeedName
  InfobloxB1Network: InfobloxB1Network
  InfobloxB1Action: InfobloxB1PolicyAction
  InfobloxB1PolicyName: InfobloxB1PolicyName
eventGroupingSettings:
  aggregationKind: SingleAlert
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: true
    lookbackDuration: 7d
    matchingMethod: AllEntities
version: 1.0.3
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 1;

Stage 1: source

InfobloxCDC

Stage 2: where

| where DeviceEventClassID has_cs "RPZ"

Stage 3: where

| where InfobloxB1FeedName == "Threat Insight - Data Exfiltration"

Stage 4: summarize

| summarize count() by SourceIP

Stage 5: where

| where count_ > threshold

Stage 6: join

| join kind=innerunique (InfobloxCDC
  | where DeviceEventClassID has_cs "RPZ"
  | where InfobloxB1FeedName == "Threat Insight - Data Exfiltration"
  ) on SourceIP

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
SourceIPsummarize

IP with multiple failed Microsoft Entra ID logins successfully logs in to Palo Alto VPN

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

This query creates a list of IP addresses with the number of failed login attempts to Entra ID above a set threshold ( default of 5 ). It then looks for any successful Palo Alto VPN logins from any of these IPs within the same timeframe.

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Credential Access

Telemetry Coverage

Rule Body

id: ba144bf8-75b8-406f-9420-ed74397f9479
name: IP with multiple failed Microsoft Entra ID logins successfully logs in to Palo Alto VPN
description: |
    This query creates a list of IP addresses with the number of failed login attempts to Entra ID 
    above a set threshold ( default of 5 ).  It then looks for any successful Palo Alto VPN logins from any of these IPs within the same timeframe.
severity: Medium
requiredDataConnectors:
  - connectorId: AzureActiveDirectory
    dataTypes:
     - SigninLogs
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AADNonInteractiveUserSignInLogs
  - connectorId: PaloAltoNetworks
    dataTypes:
     - CommonSecurityLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - CredentialAccess
relevantTechniques:
  - T1078
  - T1110
query: |
  //Set a threshold of failed AAD signins from an IP address within 1 day above which we want to deem those logins suspicious.
  let signin_threshold = 5; 
  //Make a list of IPs with AAD signin failures above our threshold.
  let aadFunc = (tableName:string){
  let suspicious_signins = 
      table(tableName)
      //Looking for logon failure results
      | where ResultType !in ("0", "50125", "50140")
      //Exclude localhost addresses to reduce the chance of FPs
      | where IPAddress !in ("127.0.0.1", "::1")
      | summarize count() by IPAddress
      | where count_ >  signin_threshold
      | summarize make_set(IPAddress);
      suspicious_signins
  };
  let aadSignin = aadFunc("SigninLogs");
  let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");
  let suspicious_signins = 
  union isfuzzy=true aadSignin, aadNonInt
  | summarize make_set(set_IPAddress);
  //See if any of those IPs have sucessfully logged into PA VPNs during the same timeperiod
  CommonSecurityLog
      //Select only PA VPN sucessful logons
      | where DeviceVendor == "Palo Alto Networks" and DeviceEventClassID == "globalprotect"
      | where Message has "GlobalProtect gateway user authentication succeeded"
      //Parse out the logon source IP from the Message field to match on
      | extend SourceIP = extract("Login from: ([^,]+)", 1, Message) 
      | where SourceIP in (suspicious_signins)
      | extend Reason = "Multiple failed AAD logins from SourceIP"
      //Parse out other useful information from Message field
      | extend User = extract('User name: ([^,]+)', 1, Message) 
      | extend ClientOS = extract('Client OS version: ([^,\"]+)', 1, Message)
      | extend Location = extract('Source region: ([^,]{2})',1, Message)
      | project TimeGenerated, Reason, SourceIP, User, ClientOS, Location, Message, DeviceName, ReceiptTime, DeviceVendor, DeviceEventClassID, Computer, FileName
      | extend timestamp = TimeGenerated
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: User
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DeviceName
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SourceIP
version: 1.0.4
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Microsoft Security Research
    support:
        tier: Community
    categories:
        domains: [ "Security - Others", "Identity" ]

Stages and Predicates

Parameters

let signin_threshold = 5;
let aadSignin = aadFunc("SigninLogs");
let aadNonInt = aadFunc("AADNonInteractiveUserSignInLogs");

Let Binding: aadFunc

let aadFunc = (tableName:string){
let suspicious_signins = 
    table(tableName)
    | where ResultType !in ("0", "50125", "50140")
    | where IPAddress !in ("127.0.0.1", "::1")
    | summarize count() by IPAddress
    | where count_ >  signin_threshold
    | summarize make_set(IPAddress);

Union isfuzzy=true (2 Sources)

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

Leg 1: aadSignin

Leg 2: aadNonInt

Applied to the Combined Result

| summarize make_set(set_IPAddress) | where DeviceVendor == "Palo Alto Networks" and DeviceEventClassID == "globalprotect" | where Message has "GlobalProtect gateway user authentication succeeded" | extend SourceIP = extract("Login from: ([^,]+)", 1, Message) | where SourceIP in (suspicious_signins) | extend Reason = "Multiple failed AAD logins from SourceIP" | extend User = extract('User name: ([^,]+)', 1, Message) | extend ClientOS = extract('Client OS version: ([^,\"]+)', 1, Message) | extend Location = extract('Source region: ([^,]{2})',1, Message) | project TimeGenerated, Reason, SourceIP, User, ClientOS, Location, Message, DeviceName, ReceiptTime, DeviceVendor, DeviceEventClassID, Computer, FileName | extend timestamp = TimeGenerated

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ClientOSproject
Computerproject
DeviceEventClassIDproject
DeviceNameproject
DeviceVendorproject
FileNameproject
Locationproject
Messageproject
Reasonproject
ReceiptTimeproject
SourceIPproject
TimeGeneratedproject
Userproject
timestampextend

New PA, PCA, or PCAS added to Azure DevOps

#
Status
available
Severity
medium
Time window
1d
Group by
ActorUPN, ActorUserId, AddingUser, IpAddress, PermissionGrantDetails, TimeAdded, UserAgent, timekey
Source
github.com/Azure/Azure-Sentinel

'In order for an attacker to be able to conduct many potential attacks against Azure DevOps they will need to gain elevated permissions. This detection looks for users being granted key administrative permissions. If the principal of least privilege is applied, the number of users granted these permissions should be small. Note that permissions can also be granted via Microsoft Entra ID Protection groups and monitoring of these should also be conducted.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access

Rule Body

id: 35ce9aff-1708-45b8-a295-5e9a307f5f17
name: New PA, PCA, or PCAS added to Azure DevOps
description: |
  'In order for an attacker to be able to conduct many potential attacks against Azure DevOps they will need to gain elevated permissions. 
  This detection looks for users being granted key administrative permissions. If the principal of least privilege is applied, the number of users granted these permissions should be small. Note that permissions can also be granted via Microsoft Entra ID Protection groups and monitoring of these should also be conducted.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
relevantTechniques:
  - T1078.004
query: |
  ADOAuditLogs
  | where OperationName =~ "Group.UpdateGroupMembership.Add"
  | where Details has_any ("Project Administrators", "Project Collection Administrators", "Project Collection Service Accounts", "Build Administrator")
  | project-reorder TimeGenerated, Details, ActorUPN, IpAddress, UserAgent, AuthenticationMechanism, ScopeDisplayName
  | extend timekey = bin(TimeGenerated, 1h)
  | extend ActorUserId = tostring(Data.MemberId)
  | project timekey, ActorUserId, AddingUser=ActorUPN, TimeAdded=TimeGenerated, PermissionGrantDetails = Details
  // Get details of operations conducted by user soon after elevation of permissions
  | join (ADOAuditLogs
  | extend ActorUserId = tostring(Data.MemberId)
  | extend timekey = bin(TimeGenerated, 1h)) on timekey, ActorUserId
  | summarize ActionsWhenAdded = make_set(OperationName) by ActorUPN, AddingUser, TimeAdded, PermissionGrantDetails, IpAddress, UserAgent
  | extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])
  | extend AddingUserAccountName = tostring(split(AddingUser, "@")[0]), AddingUserAccountUPNSuffix = tostring(split(AddingUser, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ActorUPN
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AddingUser
      - identifier: Name
        columnName: AddingUserAccountName
      - identifier: UPNSuffix
        columnName: AddingUserAccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IpAddress
version: 1.0.6
kind: Scheduled

Stages and Predicates

Stage 1: source

ADOAuditLogs

Stage 2: where

| where OperationName =~ "Group.UpdateGroupMembership.Add"

Stage 3: where

| where Details has_any ("Project Administrators", "Project Collection Administrators", "Project Collection Service Accounts", "Build Administrator")

Stage 4: project-reorder

| project-reorder TimeGenerated, Details, ActorUPN, IpAddress, UserAgent, AuthenticationMechanism, ScopeDisplayName

Stage 5: extend

| extend timekey = bin(TimeGenerated, 1h)

Stage 6: extend

| extend ActorUserId = tostring(Data.MemberId)

Stage 7: project

| project timekey, ActorUserId, AddingUser=ActorUPN, TimeAdded=TimeGenerated, PermissionGrantDetails = Details

Stage 8: join

| join (ADOAuditLogs
| extend ActorUserId = tostring(Data.MemberId)
| extend timekey = bin(TimeGenerated, 1h)) on timekey, ActorUserId

Stage 9: summarize

summarize ActionsWhenAdded by ActorUPN, AddingUser, TimeAdded, PermissionGrantDetails, IpAddress, UserAgent

Stage 10: extend

extend AccountName, AccountUPNSuffix

Stage 11: extend

extend AddingUserAccountName, AddingUserAccountUPNSuffix

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Detailsmatch
  • Build Administrator transforms: term
  • Project Administrators transforms: term
  • Project Collection Administrators transforms: term
  • Project Collection Service Accounts transforms: term
field:"Details" kind:match
OperationNameeq
  • Group.UpdateGroupMembership.Add
field:"OperationName" kind:eq value:"Group.UpdateGroupMembership.Add"

Output Fields

These fields are emitted when the rule matches.

FieldSource
ActionsWhenAddedsummarize
ActorUPNsummarize
AddingUsersummarize
IpAddresssummarize
PermissionGrantDetailssummarize
TimeAddedsummarize
UserAgentsummarize
AccountNameextend
AccountUPNSuffixextend
AddingUserAccountNameextend
AddingUserAccountUPNSuffixextend

Pathlock TDnR - SAP HANA Parameter Changes

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

Detects changes to SAP HANA database configuration parameters, forwarded by Pathlock Threat Detection and Response. Unauthorized HANA parameter modifications may be used to disable security controls, weaken audit logging, or alter database behavior to facilitate attacks.

MITRE ATT&CK Coverage

TacticTechniques
Defense Impairment

Rule Body

id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c29
name: Pathlock TDnR - SAP HANA Parameter Changes
kind: Scheduled
description: >-
  Detects changes to SAP HANA database configuration parameters, forwarded by Pathlock Threat
  Detection and Response. Unauthorized HANA parameter modifications may be used to disable security
  controls, weaken audit logging, or alter database behavior to facilitate attacks.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Pathlock_TDnR
    dataTypes:
      - Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562
query: |
  Pathlock_TDnR_CL
  | where DataSource == "HANA_PARAM_CHANGED"
  | 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 == "HANA_PARAM_CHANGED"

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

PROD (TM009.1) - ACL - Modified at Root of the Domain

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

ACL Modification at the Root of the Domain.

MITRE ATT&CK Coverage

Telemetry Coverage

Rule Body

id: 7437402c-800b-4cf3-90d9-0c1f12204375
name: 'PROD (TM009.1) - ACL - Modified at Root of the Domain'
description: |
  ACL Modification at the Root of the Domain.
severity: High
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1222.001
query: |
  SecurityEvent 
  | where EventID == 5136 
      and EventData matches regex @"(?i)DC="
      and Activity has "modified" 
      and EventData has '"ObjectClass">domainDNS'
      and EventData has '<Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data>'
      and EventData has '"OperationType">%%14674'
  | extend ObjectName = extract("<Data Name=\"ObjectDN\">([^<]+)</Data>", 1, EventData)
  | where ObjectName startswith "DC=" or ObjectName startswith "dc="
  | extend ObjectTier = "Tier 0"
  | extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
  | extend AttributeValue = extract("\\[([^\\]]+)\\]", 1, EventData)
  | extend OperationType = iff(EventData has 'nTSecurityDescriptor', "ACL Modified", tostring(OperationType))
  | extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
  | extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
  | extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)
  | project TimeGenerated
     , ObjectName
     , ObjectTier
     , ObjectClass
     , OperationType
     , ActivityType
     , AttributeLDAP
     , Account
     , Domain
     , Computer
     , Channel
     , EventID
     , EventData
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: ObjectName
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: Computer
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
customDetails:
  ObjectName: ObjectName
  ObjectTier: ObjectTier
  ObjectClass: ObjectClass
  OperationType: OperationType
  ActivityType: ActivityType
  AttributeLDAP: AttributeLDAP
  Account: Account
  Domain: Domain
  Computer: Computer
  Channel: Channel
  EventID: EventID
  EventData: EventData
eventGroupingSettings:
  aggregationKind: AlertPerResult
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: false
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
alertDetailsOverride:
  alertDisplayNameFormat: '(TM009.1) A Tier 0 ACL was modified at the Root of the Domain'
  alertDescriptionFormat: 'Any ACL changes at the root of the domain could impact or bypass the Tier Model security controls.'
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

SecurityEvent

Stage 2: where

| where EventID == 5136 
    and EventData matches regex @"(?i)DC="
    and Activity has "modified" 
    and EventData has '"ObjectClass">domainDNS'
    and EventData has '<Data Name="AttributeLDAPDisplayName">nTSecurityDescriptor</Data>'
    and EventData has '"OperationType">%%14674'

Stage 3: extend

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

Stage 4: where

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

Stage 5: extend (7 consecutive steps)

| extend ObjectTier = "Tier 0"
| extend AttributeLDAP = extract("<Data Name=\"AttributeLDAPDisplayName\">([^<]+)</Data>", 1, EventData)
| extend AttributeValue = extract("\\[([^\\]]+)\\]", 1, EventData)
| extend OperationType = iff(EventData has 'nTSecurityDescriptor', "ACL Modified", tostring(OperationType))
| extend ActivityType = extract("([a-zA-Z]+)\\.$", 1, Activity)
| extend ObjectClass = extract("<Data Name=\"ObjectClass\">([^<]+)</Data>", 1, EventData)
| extend Domain = extract("([^.]+\\.[^.]+)$", 1, Computer)

Stage 6: project

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

Indicators

These rows show field, operator, and value matches.

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

Output Fields

These fields are emitted when the rule matches.

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

ProofpointPOD - Email sender IP in TI list

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

'Email sender IP in TI list.'

MITRE ATT&CK Coverage

TacticTechniques
Initial Access
Exfiltration

Rule Body

id: 78979d32-e63f-4740-b206-cfb300c735e0
name: ProofpointPOD - Email sender IP in TI list
description: |
  'Email sender IP in TI list.'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelligenceIndicator
  - connectorId: ProofpointPOD
    dataTypes:
      - ProofpointPOD_maillog_CL
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
  - InitialAccess
relevantTechniques:
  - T1078
  - T1567
query: |
  let dt_lookBack = 1h;
  let ioc_lookBack = 14d;
  ThreatIntelligenceIndicator
  | where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()
  | where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
  | extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
  | where Active == true
  // using innerunique to keep perf fast and result set low, we only need one match to indicate potential malicious activity that needs to be investigated
  | join kind=innerunique (
            ProofpointPOD 
            | where TimeGenerated >= ago(dt_lookBack)
            | where isnotempty(SrcIpAddr)
            | extend ProofpointPOD_TimeGenerated = TimeGenerated, ClientIP = SrcIpAddr
    )
  on $left.TI_ipEntity == $right.ClientIP
  | where ProofpointPOD_TimeGenerated < ExpirationDateTime
  | summarize ProofpointPOD_TimeGenerated = arg_max(ProofpointPOD_TimeGenerated, *) by IndicatorId, ClientIP
  | project ProofpointPOD_TimeGenerated, SrcUserUpn, DstUserUpn, SrcIpAddr, IndicatorId, ThreatType, ExpirationDateTime, ConfidenceScore,
  TI_ipEntity, ClientIP
  | extend timestamp = ProofpointPOD_TimeGenerated
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: SrcUserUpn
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
version: 1.1.1
kind: Scheduled

Stages and Predicates

Parameters

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

Stage 1: source

ThreatIntelligenceIndicator

Stage 2: where

| where TimeGenerated >= ago(ioc_lookBack) and ExpirationDateTime > now()

Stage 3: where

| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)

Stage 4: extend (3 consecutive steps)

| extend TI_ipEntity = iff(isnotempty(NetworkIP), NetworkIP, NetworkDestinationIP)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity)
TI_ipEntity =
ifisnotempty(NetworkIP)NetworkIP
elseNetworkDestinationIP

Stage 5: summarize

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

Stage 6: where

| where Active == true

Stage 7: join

| join kind=innerunique (
          ProofpointPOD 
          | where TimeGenerated >= ago(dt_lookBack)
          | where isnotempty(SrcIpAddr)
          | extend ProofpointPOD_TimeGenerated = TimeGenerated, ClientIP = SrcIpAddr
  )
on $left.TI_ipEntity == $right.ClientIP

Stage 8: where

where ProofpointPOD_TimeGenerated < ExpirationDateTime

Stage 9: summarize

summarize ProofpointPOD_TimeGenerated by IndicatorId, ClientIP

Stage 10: project

project ClientIP, ConfidenceScore, DstUserUpn, ExpirationDateTime, IndicatorId, ProofpointPOD_TimeGenerated, SrcIpAddr, SrcUserUpn, TI_ipEntity, ThreatType

Stage 11: extend

extend timestamp

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
ClientIPproject
ConfidenceScoreproject
DstUserUpnproject
ExpirationDateTimeproject
IndicatorIdproject
ProofpointPOD_TimeGeneratedproject
SrcIpAddrproject
SrcUserUpnproject
TI_ipEntityproject
ThreatTypeproject
timestampextend

Quokka - Malicious Results Detected

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

'Detects if there are any malicious results in the app events coming from organization devices.'

MITRE ATT&CK Coverage

Rule Body

id: 97ad71ed-e4c0-4f7a-b1a2-683108bece4f
name: Quokka - Malicious Results Detected
description: |
  'Detects if there are any malicious results in the app events coming from organization devices.'
severity: Medium
status : Available
requiredDataConnectors:
  - connectorId: QscoutAppEventsCCFDefinition
    dataTypes:
      - QscoutAppEvents_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - InitialAccess
  - Execution
  - Persistence
  - PrivilegeEscalation
  - DefenseEvasion
  - CredentialAccess
  - Discovery
  - Collection
  - CommandAndControl
  - Impact
relevantTechniques:
  - T1406
  - T1409
  - T1414
  - T1417
  - T1418
  - T1422
  - T1424
  - T1429
  - T1430
  - T1471
  - T1474
  - T1481
  - T1509
  - T1512
  - T1513
  - T1516
  - T1517
  - T1532
  - T1541
  - T1544
  - T1582
  - T1616
  - T1617
  - T1623
  - T1624
  - T1625
  - T1627
  - T1628
  - T1629
  - T1630
  - T1631
  - T1633
  - T1634
  - T1635
  - T1636
  - T1638
  - T1640
  - T1641
  - T1642
  - T1643
query: |
  let timeRange = 1h;
  let appEventsWithMaliciousResults = QscoutAppEvents_CL
  | extend AccountCustomEntity = tostring(org_id)
  | where TimeGenerated >= ago(timeRange)
  | where result_type == 'maliciousness'
  | where isnotempty(results.entries);
  appEventsWithMaliciousResults
  | mv-expand entry = results.entries
  | project
      TimeGenerated,
      AccountCustomEntity,
      app_id,
      mdm_source,
      mdm_device_id,
      package,
      platform,
      version,
      rule_id = tostring(entry.ruleId),
      desription = tostring(entry.description),
      threat_level = toint(entry.threatLevel),
      threat_types = tostring(entry.threatTypes)
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountCustomEntity
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let timeRange = 1h;

let appEventsWithMaliciousResults is inlined into the numbered stages below.

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

Stage 1: source

QscoutAppEvents_CL

Stage 2: extend

| extend AccountCustomEntity = tostring(org_id)

Stage 3: where

| where TimeGenerated >= ago(timeRange)

Stage 4: where

| where result_type == 'maliciousness'

Stage 5: where

| where isnotempty(results.entries)

Stage 6: mv-expand

appEventsWithMaliciousResults
| mv-expand entry = results.entries

Stage 7: project

| project
    TimeGenerated,
    AccountCustomEntity,
    app_id,
    mdm_source,
    mdm_device_id,
    package,
    platform,
    version,
    rule_id = tostring(entry.ruleId),
    desription = tostring(entry.description),
    threat_level = toint(entry.threatLevel),
    threat_types = tostring(entry.threatTypes)

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
AccountCustomEntityproject
TimeGeneratedproject
app_idproject
desriptionproject
mdm_device_idproject
mdm_sourceproject
packageproject
platformproject
rule_idproject
threat_levelproject
threat_typesproject
versionproject

SecurityEvent - Multiple authentication failures followed by a success

#
Status
available
Severity
low
Time window
10m
Group by
Account, Computer, IpAddress, Outcome, SessionStartedUtc
Source
github.com/Azure/Azure-Sentinel

Identifies accounts who have failed to logon to the domain multiple times in a row, followed by a successful authentication within a short time frame. Multiple failed attempts followed by a success can be an indication of a brute force attempt or possible mis-configuration of a service account within an environment. The lookback is set to 2h and the authentication window and threshold are set to 1h and 5, meaning we need to see a minimum of 5 failures followed by a success for an account within 1 hour to surface an alert.

MITRE ATT&CK Coverage

TacticTechniques
Credential Access

Telemetry Coverage

Rule Body

id: cf3ede88-a429-493b-9108-3e46d3c741f7
name: SecurityEvent - Multiple authentication failures followed by a success
description: |
  'Identifies accounts who have failed to logon to the domain multiple times in a row, followed by a successful authentication within a short time frame. Multiple failed attempts followed by a success can be an indication of a brute force attempt or possible mis-configuration of a service account within an environment.
  The lookback is set to 2h and the authentication window and threshold are set to 1h and 5, meaning we need to see a minimum of 5 failures followed by a success for an account within 1 hour to surface an alert.'
severity: Low
requiredDataConnectors:
  - connectorId: SecurityEvents
    dataTypes:
      - SecurityEvent
  - connectorId: WindowsSecurityEvents
    dataTypes:
      - SecurityEvent
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - CredentialAccess
relevantTechniques:
  - T1110
query: |
    let timeRange = 2h;
    let authenticationWindow = 1h;
    let authenticationThreshold = 5;
    SecurityEvent
    | where TimeGenerated > ago(timeRange)
    | where EventID in (4624, 4625)
    | where IpAddress != "-" and isnotempty(Account)
    | extend Outcome = iff(EventID == 4624, "Success", "Failure")
    // bin outcomes into 10 minute windows to reduce the volume of data
    | summarize OutcomeCount=count() by bin(TimeGenerated, 10m), Account, IpAddress, Computer, Outcome
    | project TimeGenerated, Account, IpAddress, Computer, Outcome, OutcomeCount
    // sort ready for sessionizing - by account and time of the authentication outcome
    | sort by TimeGenerated asc, Account, IpAddress, Computer, Outcome, OutcomeCount
    | serialize
    // sessionize into failure groupings until either the account changes or there is a success
    | extend SessionStartedUtc = row_window_session(TimeGenerated, timeRange, authenticationWindow, Account != prev(Account) or prev(Outcome) == "Success")
    // count the failures in each session
    | summarize FailureCountBeforeSuccess=sumif(OutcomeCount, Outcome == "Failure"), StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), make_list(Outcome, 128), make_set(Computer, 128), make_set(IpAddress, 128) by SessionStartedUtc, Account
    // the session must not start with a success, and must end with one
    | where array_index_of(list_Outcome, "Success") != 0
    | where array_index_of(list_Outcome, "Success") == array_length(list_Outcome) - 1
    | project-away SessionStartedUtc, list_Outcome
    // where the number of failures before the success is above the threshold
    | where FailureCountBeforeSuccess >= authenticationThreshold
    // expand out ip and computer for customer entity assignment
    | mv-expand set_IpAddress, set_Computer
    | extend IpAddress = tostring(set_IpAddress), Computer = tostring(set_Computer)
    | extend timestamp=StartTime, NTDomain = split(Account, '\\', 0)[0], Name = split(Account, '\\', 1)[0], HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Account
      - identifier: Name
        columnName: Name
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: Computer
      - identifier: HostName
        columnName: HostName
      - identifier: DnsDomain
        columnName: DnsDomain
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IpAddress
version: 1.0.7
kind: Scheduled

Stages and Predicates

Parameters

let timeRange = 2h;
let authenticationWindow = 1h;
let authenticationThreshold = 5;

Stage 1: source

SecurityEvent

Stage 2: where

| where TimeGenerated > ago(timeRange)

Stage 3: where

| where EventID in (4624, 4625)

Stage 4: where

| where IpAddress != "-" and isnotempty(Account)

Stage 5: extend

| extend Outcome = iff(EventID == 4624, "Success", "Failure")
Outcome =
ifEventID == 4624"Success"
else"Failure"

Stage 6: summarize

| summarize OutcomeCount=count() by bin(TimeGenerated, 10m), Account, IpAddress, Computer, Outcome

Stage 7: project

| project TimeGenerated, Account, IpAddress, Computer, Outcome, OutcomeCount

Stage 8: sort

| sort by TimeGenerated asc, Account, IpAddress, Computer, Outcome, OutcomeCount

Stage 9: kusto:serialize

| serialize

Stage 10: extend

| extend SessionStartedUtc = row_window_session(TimeGenerated, timeRange, authenticationWindow, Account != prev(Account) or prev(Outcome) == "Success")

Stage 11: summarize

| summarize FailureCountBeforeSuccess=sumif(OutcomeCount, Outcome == "Failure"), StartTime=min(TimeGenerated), EndTime=max(TimeGenerated), make_list(Outcome, 128), make_set(Computer, 128), make_set(IpAddress, 128) by SessionStartedUtc, Account

Stage 12: where

| where array_index_of(list_Outcome, "Success") != 0

Stage 13: where

| where array_index_of(list_Outcome, "Success") == array_length(list_Outcome) - 1

Stage 14: project-away

| project-away SessionStartedUtc, list_Outcome

Stage 15: where

| where FailureCountBeforeSuccess >= authenticationThreshold

Stage 16: mv-expand

| mv-expand set_IpAddress, set_Computer

Stage 17: extend

| extend IpAddress = tostring(set_IpAddress), Computer = tostring(set_Computer)

Stage 18: extend

| extend timestamp=StartTime, NTDomain = split(Account, '\\', 0)[0], Name = split(Account, '\\', 1)[0], 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
Accountis_not_null
  • (no value, null check)
field:"Account" kind:is_not_null
EventIDin
  • 4624 corpus 6 (elastic 3, kusto 3)
  • 4625 corpus 6 (elastic 3, kusto 3)
field:"EventID" kind:in
FailureCountBeforeSuccessge
  • 5 transforms: number
field:"FailureCountBeforeSuccess" kind:ge value:"5"
IpAddressne
  • -
field:"SourceIp" kind:ne value:"-"
list_Outcomein
  • Success
  • Success transforms: last_index
field:"list_Outcome" kind:in

Output Fields

These fields are emitted when the rule matches.

FieldSource
Accountsummarize
EndTimesummarize
FailureCountBeforeSuccesssummarize
StartTimesummarize
Computerextend
IpAddressextend
DnsDomainextend
HostNameextend
NTDomainextend
Nameextend
timestampextend

Sonrai Ticket Escalation Executed

#

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

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

'Checks if Sonrai tickets have had a comment added. It uses the action type to check if a ticket has had a comment added'

MITRE ATT&CK Coverage

Rule Body

id: 0d29c93e-b83f-4dfb-bbbb-76824b77eeca
name: Sonrai Ticket Escalation Executed
description: |
  'Checks if Sonrai tickets have had a comment added. 
  It uses the action type to check if a ticket has had a comment added'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SonraiDataConnector
    dataTypes:
      - Sonrai_Tickets_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Collection
  - CommandAndControl
  - CredentialAccess
  - DefenseEvasion
  - Discovery
  - Execution
  - Exfiltration
  - Impact
  - InitialAccess
  - LateralMovement
  - Persistence
  - PrivilegeEscalation
relevantTechniques:
  - T1566
  - T1059
  - T1547
  - T1548
  - T1562
  - T1003
  - T1087
  - T1021
  - T1119
  - T1071
  - T1041
  - T1499
query: |
  Sonrai_Tickets_CL
  | where action_d == 9
entityMappings: 
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: digest_criticalResourceName_s
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  ticketName: digest_title_s
  ticketStatus: digest_status_s
  ticketSeverity: digest_severityCategory_s
  resourceLabel: digest_resourceLabel_s
  resourceType: digest_resourceType_s
  criticalResource: digest_criticalResourceName_s
  ticketOrg: digest_org_s
alertDetailsOverride:
  alertSeverityColumnName: digest_severityCategory_s
  alertDisplayNameFormat: Comment Added - {{digest_ticketSrn_s}} - {{digest_ticketKeyName_s}}
  alertDescriptionFormat: digest_ticketKeyDescription_s
version: 1.0.2
kind: Scheduled

Stages and Predicates

Stage 1: source

Sonrai_Tickets_CL

Stage 2: where

| where action_d == 9

Indicators

These rows show field, operator, and value matches.

TI Map IP entity to GitHub_CL

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

'Identifies a match in GitHub_CL table from any IP IOC from TI'

MITRE ATT&CK Coverage

TacticTechniques
Command & Control

Rule Body

id: 43d6c173-64c8-4416-b32e-636a9f318d15
name: TI Map IP entity to GitHub_CL
description: |
  'Identifies a match in GitHub_CL table from any IP IOC from TI'
severity: Medium
requiredDataConnectors:
  - connectorId: ThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: ThreatIntelligenceTaxii
    dataTypes:
      - ThreatIntelIndicators
  - connectorId: MicrosoftDefenderThreatIntelligence
    dataTypes:
      - ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1071
query: |
  let dt_lookBack = 1h; // Look back 1 hour for VMConnection events
  let ioc_lookBack = 14d; // Look back 14 days for threat intelligence indicators
  ThreatIntelIndicators
  //extract key part of kv pair
       | extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
       | where IndicatorType in ("ipv4-addr", "ipv6-addr", "network-traffic")
       | extend NetworkSourceIP = toupper(ObservableValue)
       | extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
  // Picking up only IOC's that contain the entities we want
  // | where Action == true
  | where TimeGenerated >= ago(ioc_lookBack)
  // Taking the first non-empty value based on potential IOC match availability
  | extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
  | extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
  | summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
  | where IsActive and (ValidUntil > now() or isempty(ValidUntil))
    | project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
  | join (
    GitHubAudit
    | where TimeGenerated >= ago(dt_lookBack)
    | extend GitHubAudit_TimeGenerated = TimeGenerated
  )
  on $left.TI_ipEntity == $right.IPaddress
  | extend Description = tostring(parse_json(Data).description)
  | extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
  | project LatestIndicatorTime, Description, ActivityGroupNames, Id, ValidUntil, Confidence, GitHubAudit_TimeGenerated, TI_ipEntity, IPaddress, Actor, Action, Country, OperationType, NetworkSourceIP
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: Actor
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPaddress
version: 1.0.9
kind: Scheduled

Stages and Predicates

Parameters

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

Stage 1: source

ThreatIntelIndicators

Stage 2: extend

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

Stage 3: where

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

Stage 4: extend

| extend NetworkSourceIP = toupper(ObservableValue)

Stage 5: extend

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

Stage 6: where

| where TimeGenerated >= ago(ioc_lookBack)

Stage 7: extend

| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
TI_ipEntity =
ifisnotempty(NetworkSourceIP)NetworkSourceIP
elseNetworkSourceIP

Stage 8: extend

| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
TI_ipEntity =
ifisempty(TI_ipEntity) and isnotempty(NetworkSourceIP)NetworkSourceIP
elseTI_ipEntity

Stage 9: summarize

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

Stage 10: where

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

Stage 11: project-reorder

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

Stage 12: join

| join (
  GitHubAudit
  | where TimeGenerated >= ago(dt_lookBack)
  | extend GitHubAudit_TimeGenerated = TimeGenerated
)
on $left.TI_ipEntity == $right.IPaddress

Stage 13: extend

extend Description

Stage 14: extend

extend ActivityGroupNames

Stage 15: project

project Action, ActivityGroupNames, Actor, Confidence, Country, Description, GitHubAudit_TimeGenerated, IPaddress, Id, LatestIndicatorTime, NetworkSourceIP, OperationType, TI_ipEntity, ValidUntil

Indicators

These rows show field, operator, and value matches.

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

Output Fields

These fields are emitted when the rule matches.

FieldSource
Actionproject
ActivityGroupNamesproject
Actorproject
Confidenceproject
Countryproject
Descriptionproject
GitHubAudit_TimeGeneratedproject
IPaddressproject
Idproject
LatestIndicatorTimeproject
NetworkSourceIPproject
OperationTypeproject
TI_ipEntityproject
ValidUntilproject

VIP Mailbox manipulation

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

'Alert if a cmdlet that can be translated to data exfiltration or mailbox access is executed on a VIP Mailbox.'

MITRE ATT&CK Coverage

Rule Body

id: 5170c3c4-b8c9-485c-910d-a21d965ee181
name: VIP Mailbox manipulation
description: |
  'Alert if a cmdlet that can be translated to data exfiltration or mailbox access is executed on a VIP Mailbox.'
requiredDataConnectors:
  - connectorId: ESI-ExchangeAdminAuditLogEvents
    dataTypes:
      - Event
severity: Medium
queryFrequency: 30m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Exfiltration
  - Persistence
  - Collection
relevantTechniques:
  - T1020
  - T1098
  - T1114
query: |
  let VIPRestriction = "on";
  ExchangeAdminAuditLogs
  | where IsVIP or VIPRestriction =~ "off"
  | where UserOriented =~ 'Yes' and IsSensitive and ((IsRestrictedCmdLet and IsSenstiveCmdletParameters) or IsRestrictedCmdLet == false)
  | extend Level = iif (Status == "Failure", "Medium", "High")
entityMappings:
- entityType: Mailbox
  fieldMappings:
    - identifier: MailboxPrimaryAddress
      columnName: TargetObject
- entityType: Host
  fieldMappings:
    - identifier: FullName
      columnName: Computer
- entityType: Account
  fieldMappings:
    - identifier: Sid
      columnName: TargetObject
    - identifier: ObjectGuid
      columnName: TargetObject
    - identifier: FullName
      columnName: TargetObject
- entityType: Account
  fieldMappings:
    - identifier: Name
      columnName: Caller
alertDetailsOverride:
  alertDisplayNameFormat: "{{CmdletName}} executed on {{TargetObject}}"
  alertDescriptionFormat: "Alert from Microsoft Exchange Security as {{CmdletName}} with parameters {{CmdletParameters}} was executed on {{TargetObject}}"
  alertSeverityColumnName: Level 
version: 1.2.0
kind: Scheduled

Stages and Predicates

Parameters

let VIPRestriction = "on";

Stage 1: source

ExchangeAdminAuditLogs

Stage 2: where

| where IsVIP or VIPRestriction =~ "off"

Stage 3: where

| where UserOriented =~ 'Yes' and IsSensitive and ((IsRestrictedCmdLet and IsSenstiveCmdletParameters) or IsRestrictedCmdLet == false)

Stage 4: extend

| extend Level = iif (Status == "Failure", "Medium", "High")

Indicators

These rows show field, operator, and value matches.

Output Fields

These fields are emitted when the rule matches.

FieldSource
Levelextend

Back to all Kusto rules