Detection rules › Kusto
Kusto rules: 2c
Alsid DCShadow
#'Searches for DCShadow attacks'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
Codename | eq |
| field:"Codename" kind:eq value:"DCShadow" |
MessageType | eq |
| field:"MessageType" kind:eq value:"2" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
AWSCloudTrail - Suspicious AWS EC2 Compute Resource Deployments
#'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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
ErrorMessage | is_null | field:"aws::errorMessage" kind:is_null | |
EventName | eq |
| field:"aws::eventName" kind:eq value:"RunInstances" |
core | is_not_null | field:"core" kind:is_not_null | |
totalgpu | gt |
| field:"totalgpu" kind:gt value:"800" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountName | summarize |
AccountUPNSuffix | summarize |
RecipientAccountId | summarize |
SourceIpAddress | summarize |
Start | summarize |
UserAgent | summarize |
UserIdentityArn | summarize |
end | summarize |
totalgpu | summarize |
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.
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| 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.
"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
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Stealth | |
| Credential Access | |
| Command & Control |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
ConfidenceScore | ge |
| field:"ConfidenceScore" kind:ge value:"50" |
ConfidenceScore | lt |
| field:"ConfidenceScore" kind:lt value:"80" |
RecommendedActions | match |
| field:"RecommendedActions" kind:match value:"Block" |
Roles | match |
| field:"Roles" kind:match value:"Trojan" |
pattern | contains |
| field:"pattern" kind:contains value:"file:hashes" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Algo_MD5 | project |
Algo_SHA1 | project |
Algo_SHA256 | project |
ConfidenceScore | project |
Country | project |
Description | project |
IndicatorID | project |
MD5 | project |
ProductName | project |
ProviderName | project |
RecommendedActions | project |
Roles | project |
SHA1 | project |
SHA256 | project |
SecurityVendors | project |
Sources | project |
Tags | project |
ThreatActors | project |
ThreatType | project |
TimeGenerated | project |
created | project |
modified | project |
name | project |
valid_from | project |
Dev-0270 Registry IOC - September 2022
#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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | match |
| field:"CommandLine" kind:match |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
InitiatingProcessCommandLine | match |
| field:"ParentCommandLine" kind:match |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | extend |
AccountDomain | project |
Activity | project |
CommandLine | project |
Computer | extend |
EventID | project |
EventSourceName | project |
ProcessName | project |
ProcessNameFullPath | project |
TimeGenerated | project |
Type | project |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
AccountNTDomain | extend |
AccountName | extend |
Dynatrace Application Security - Attack detection
#'Dynatrace has detected an ongoing attack in your environment.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Privilege Escalation | |
| Impact |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
State | ne |
| field:"State" kind:ne value:"ALLOWLISTED" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AttackId | summarize |
Infoblox - Data Exfiltration Attack
#'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
| Tactic | Techniques |
|---|---|
| Impact |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceEventClassID | match |
| field:"DeviceEventClassID" kind:match value:"RPZ" |
InfobloxB1FeedName | eq |
| field:"InfobloxB1FeedName" kind:eq value:"Threat Insight - Data Exfiltration" |
count_ | gt |
| field:"count_" kind:gt value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SourceIP | summarize |
IP with multiple failed Microsoft Entra ID logins successfully logs in to Palo Alto VPN
#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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
DeviceEventClassID | eq |
| field:"DeviceEventClassID" kind:eq value:"globalprotect" |
DeviceVendor | eq |
| field:"DeviceVendor" kind:eq value:"Palo Alto Networks" |
Message | match |
| field:"Message" kind:match value:"GlobalProtect gateway user authentication succeeded" |
SourceIP | in |
| field:"SourceIP" kind:in value:"suspicious_signins" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientOS | project |
Computer | project |
DeviceEventClassID | project |
DeviceName | project |
DeviceVendor | project |
FileName | project |
Location | project |
Message | project |
Reason | project |
ReceiptTime | project |
SourceIP | project |
TimeGenerated | project |
User | project |
timestamp | extend |
New PA, PCA, or PCAS added to Azure DevOps
#'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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
Details | match |
| field:"Details" kind:match |
OperationName | eq |
| field:"OperationName" kind:eq value:"Group.UpdateGroupMembership.Add" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActionsWhenAdded | summarize |
ActorUPN | summarize |
AddingUser | summarize |
IpAddress | summarize |
PermissionGrantDetails | summarize |
TimeAdded | summarize |
UserAgent | summarize |
AccountName | extend |
AccountUPNSuffix | extend |
AddingUserAccountName | extend |
AddingUserAccountUPNSuffix | extend |
Pathlock TDnR - SAP HANA Parameter Changes
#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
| Tactic | Techniques |
|---|---|
| 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.
| Field | Kind | Values | Search |
|---|---|---|---|
DataSource | eq |
| field:"DataSource" kind:eq value:"HANA_PARAM_CHANGED" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AffectedUser | project |
Area | project |
Bname | project |
CentralTs | project |
DataSource | project |
DestIp | project |
Eventid | project |
Hostname | project |
Instance | project |
LogLine | project |
MessageV1 | project |
MessageV2 | project |
MessageV3 | project |
MessageV4 | project |
MsgId | project |
MsgNo | project |
MsgType | project |
Report | project |
SrcIp | project |
Subid | project |
Sysid | project |
Tcode | project |
TimeGenerated | project |
PROD (TM009.1) - ACL - Modified at Root of the Domain
#ACL Modification at the Root of the Domain.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 5136: A directory service object was modified. |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Activity | match |
| field:"Activity" kind:match value:"modified" |
EventData | match |
| field:"EventData" kind:match |
EventData | regex_match |
| field:"EventData" kind:regex_match value:"(?i)DC=" |
EventID | eq |
| field:"EventID" kind:eq value:"5136" |
ObjectName | starts_with |
| field:"ObjectName" kind:starts_with |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | project |
ActivityType | project |
AttributeLDAP | project |
Channel | project |
Computer | project |
Domain | project |
EventData | project |
EventID | project |
ObjectClass | project |
ObjectName | project |
ObjectTier | project |
OperationType | project |
TimeGenerated | project |
ProofpointPOD - Email sender IP in TI list
#'Email sender IP in TI list.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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 =isnotempty(NetworkIP)NetworkIPNetworkDestinationIPStage 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.
| Field | Kind | Values | Search |
|---|---|---|---|
Active | eq |
| field:"Active" kind:eq value:"true" |
EmailSourceIpAddress | is_not_null | field:"EmailSourceIpAddress" kind:is_not_null | |
NetworkDestinationIP | is_not_null | field:"NetworkDestinationIP" kind:is_not_null | |
NetworkIP | is_not_null | field:"NetworkIP" kind:is_not_null | |
NetworkSourceIP | is_not_null | field:"NetworkSourceIP" kind:is_not_null | |
SrcIpAddr | is_not_null | field:"SrcIpAddr" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientIP | project |
ConfidenceScore | project |
DstUserUpn | project |
ExpirationDateTime | project |
IndicatorId | project |
ProofpointPOD_TimeGenerated | project |
SrcIpAddr | project |
SrcUserUpn | project |
TI_ipEntity | project |
ThreatType | project |
timestamp | extend |
Quokka - Malicious Results Detected
#'Detects if there are any malicious results in the app events coming from organization devices.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation | |
| Defense Evasion | |
| Credential Access | |
| Discovery | |
| Collection | |
| Command and Control | |
| Impact |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
result_type | eq |
| field:"result_type" kind:eq value:"maliciousness" |
results.entries | is_not_null | field:"results.entries" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | project |
TimeGenerated | project |
app_id | project |
desription | project |
mdm_device_id | project |
mdm_source | project |
package | project |
platform | project |
rule_id | project |
threat_level | project |
threat_types | project |
version | project |
SecurityEvent - Multiple authentication failures followed by a success
#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
| Tactic | Techniques |
|---|---|
| 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 =EventID == 4624"Success""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.
| Field | Kind | Values | Search |
|---|---|---|---|
Account | is_not_null | field:"Account" kind:is_not_null | |
EventID | in |
| field:"EventID" kind:in |
FailureCountBeforeSuccess | ge |
| field:"FailureCountBeforeSuccess" kind:ge value:"5" |
IpAddress | ne |
| field:"SourceIp" kind:ne value:"-" |
list_Outcome | in |
| field:"list_Outcome" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Account | summarize |
EndTime | summarize |
FailureCountBeforeSuccess | summarize |
StartTime | summarize |
Computer | extend |
IpAddress | extend |
DnsDomain | extend |
HostName | extend |
NTDomain | extend |
Name | extend |
timestamp | extend |
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.
'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
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Privilege Escalation | |
| Defense Impairment | |
| Credential Access | |
| Discovery | |
| Lateral Movement | |
| Collection | |
| Command & Control | |
| Exfiltration | |
| Impact |
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.
| Field | Kind | Values | Search |
|---|---|---|---|
action_d | eq |
| field:"action_d" kind:eq value:"9" |
TI Map IP entity to GitHub_CL
#'Identifies a match in GitHub_CL table from any IP IOC from TI'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| 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 =isnotempty(NetworkSourceIP)NetworkSourceIPNetworkSourceIPStage 8: extend
| extend TI_ipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
TI_ipEntity =isempty(TI_ipEntity) and isnotempty(NetworkSourceIP)NetworkSourceIPTI_ipEntityStage 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.
| Field | Kind | Values | Search |
|---|---|---|---|
IndicatorType | in |
| field:"IndicatorType" kind:in |
IsActive | eq |
| field:"IsActive" kind:eq value:"true" |
ValidUntil | is_null | field:"ValidUntil" kind:is_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Action | project |
ActivityGroupNames | project |
Actor | project |
Confidence | project |
Country | project |
Description | project |
GitHubAudit_TimeGenerated | project |
IPaddress | project |
Id | project |
LatestIndicatorTime | project |
NetworkSourceIP | project |
OperationType | project |
TI_ipEntity | project |
ValidUntil | project |
VIP Mailbox manipulation
#'Alert if a cmdlet that can be translated to data exfiltration or mailbox access is executed on a VIP Mailbox.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Collection | |
| Exfiltration |
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.
| Field | Source |
|---|---|
Level | extend |