Detection rules › Kusto
Kusto rules: 0u
1Password - Secret extraction post vault access change by administrator
#This will alert when a secret extraction has occurred after an administrator has changed their own vault access permissions within that same vault. Ref: https://1password.com/ Ref: https://github.com/securehats/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
Rule Body
id: 6711b747-16d7-4df4-9f61-8633617f45d7
name: 1Password - Secret extraction post vault access change by administrator
version: 1.0.0
kind: Scheduled
description: |-
This will alert when a secret extraction has occurred after an administrator has changed their own vault access permissions within that same vault.
Ref: https://1password.com/
Ref: https://github.com/securehats/
severity: High
requiredDataConnectors:
- connectorId: 1Password
dataTypes:
- OnePasswordEventLogs_CL
queryFrequency: 5m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1555
query: |-
let lookback = 1h;
let secretExtractionActivity =
OnePasswordEventLogs_CL
| where TimeGenerated between (ago(lookback) .. now())
| where log_source == "itemusages"
| where action has_any("server-fetch", "reveal", "secure-copy")
;
OnePasswordEventLogs_CL
| where log_source == "auditevents"
| where (action == "grant" and object_type == "uva") or (action == "update" and object_type == "uva")
| where tostring(actor_details.uuid) == tostring(aux_details.uuid)
| extend
userUuid = tostring(actor_details.uuid)
, vaultUuid = tostring(object_uuid)
| join (
secretExtractionActivity
| extend
userUuid = tostring(user.uuid)
, vaultUuid = tostring(vault_uuid)
)
on $left.userUuid == $right.userUuid and $left.vaultUuid == $right.vaultUuid
| extend
auditevents = bag_pack("action", action, "object_type", object_type)
, itemusages = bag_pack("action", action1, "object_type", object_type1)
, vault_details = bag_pack("vault_uuid", vault_uuid1, "item_uuid", item_uuid1)
| project
TimeGenerated
, actor_details
, target_details = aux_details
, location_details = location
, client_details = client1
, auditevents
, itemusages
, vault_details
, TargetUsername = tostring(user1.email)
, SrcIpAddr = tostring(client1.ip_address)
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
suppressionDuration: 1h
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUsername
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
Stages and Predicates
Parameters
let lookback = 1h;
Let Binding: secretExtractionActivity
let secretExtractionActivity = OnePasswordEventLogs_CL
| where TimeGenerated between (ago(lookback) .. now())
| where log_source == "itemusages"
| where action has_any("server-fetch", "reveal", "secure-copy");
Stage 1: source
OnePasswordEventLogs_CL
Stage 2: where
| where log_source == "auditevents"
Stage 3: where
| where (action == "grant" and object_type == "uva") or (action == "update" and object_type == "uva")
Stage 4: where
| where tostring(actor_details.uuid) == tostring(aux_details.uuid)
Stage 5: extend
| extend
userUuid = tostring(actor_details.uuid)
, vaultUuid = tostring(object_uuid)
Stage 6: join
| join (
secretExtractionActivity
| extend
userUuid = tostring(user.uuid)
, vaultUuid = tostring(vault_uuid)
)
on $left.userUuid == $right.userUuid and $left.vaultUuid == $right.vaultUuid
Stage 7: extend
extend auditevents, itemusages, vault_details
Stage 8: project
project SrcIpAddr, TargetUsername, TimeGenerated, actor_details, auditevents, client_details, itemusages, location_details, target_details, vault_details
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq |
action | match |
| field:"action" kind:match |
log_source | eq |
| field:"log_source" kind:eq |
object_type | eq |
| field:"object_type" kind:eq value:"uva" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SrcIpAddr | project |
TargetUsername | project |
TimeGenerated | project |
actor_details | project |
auditevents | project |
client_details | project |
itemusages | project |
location_details | project |
target_details | project |
vault_details | project |
Adding User or Group Failed
#Detects failed attempts to add a user or user group to Veeam Backup & Replication.
Rule Body
id: 6d1a5478-e613-44f4-a48f-12cc18568522
name: Adding User or Group Failed
description: Detects failed attempts to add a user or user group to Veeam Backup &
Replication.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: Syslog
dataTypes:
- Syslog
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 3h
queryPeriod: 3h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics: []
relevantTechniques: []
query: "Veeam_GetSecurityEvents\n| where instanceId == 31210\n| project\n Date\
\ = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),\n DataSource = original_host,\n\
\ EventId = instanceId,\n UserName = user,\n MessageDetails = Description,\n\
\ Severity = SeverityDescription"
version: 1.0.1
kind: Scheduled
customDetails:
Date: Date
VbrHostName: DataSource
EventId: EventId
MessageDetails: MessageDetails
Severity: Severity
Stages and Predicates
Stage 1: source
Veeam_GetSecurityEvents
Stage 2: where
| where instanceId == 31210
Stage 3: project
| project
Date = format_datetime(TimeGenerated, 'dd.MM.yyyy HH:mm'),
DataSource = original_host,
EventId = instanceId,
UserName = user,
MessageDetails = Description,
Severity = SeverityDescription
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
instanceId | eq |
| field:"instanceId" kind:eq value:"31210" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DataSource | project |
Date | project |
EventId | project |
MessageDetails | project |
Severity | project |
UserName | project |
AWSCloudTrail - Changes to AWS Security Group ingress and egress settings
#A Security Group acts as a virtual firewall for an AWS instance to control inbound and outbound traffic. This rule detects AWS CloudTrail events for changes to Security Group ingress and egress settings. Investigate to validate the legitimacy of the activity and identify potential malicious activity.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Telemetry Coverage
Rules Detecting the Same Action
These rules filter on the same operation.
- AWS EC2 Manual Security Group Change (Panther)
- EC2 Security Group Modified (Panther)
Rule Body
id: 4f19d4e3-ec5f-4abc-9e61-819eb131758c
name: AWSCloudTrail - Changes to AWS Security Group ingress and egress settings
description: |
A Security Group acts as a virtual firewall for an AWS instance to control inbound and outbound traffic. This rule detects AWS CloudTrail events for changes to Security Group ingress and egress settings.
Investigate to validate the legitimacy of the activity and identify potential malicious activity.
severity: Low
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
- connectorId: AWSS3
dataTypes:
- AWSCloudTrail
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562.007
query: |
let EventNameList = dynamic([ "AuthorizeSecurityGroupEgress", "AuthorizeSecurityGroupIngress", "RevokeSecurityGroupEgress", "RevokeSecurityGroupIngress"]);
AWSCloudTrail
| where EventName in~ (EventNameList)
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
| summarize EventCount=count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
by EventSource, EventName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, UserAgent, SessionMfaAuthenticated, AWSRegion,
AdditionalEventData, UserIdentityAccountId, UserIdentityPrincipalid, ResponseElements
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
customDetails:
UserIdentityArn: UserIdentityArn
UserIdentityUserName: UserIdentityUserName
UserIdentityType: UserIdentityType
EventName: EventName
AWSRegion: AWSRegion
UserAgent: UserAgent
alertDetailsOverride:
alertDisplayNameFormat: 'AWS Security Group ingress/egress change by {{AccountName}} from {{SourceIpAddress}}'
alertDescriptionFormat: 'AWS Security Group ingress/egress change activity {{EventName}} was detected for {{AccountName}} from {{SourceIpAddress}}.'
version: 1.0.5
kind: Scheduled
Stages and Predicates
Parameters
let EventNameList = dynamic([ "AuthorizeSecurityGroupEgress", "AuthorizeSecurityGroupIngress", "RevokeSecurityGroupEgress", "RevokeSecurityGroupIngress"]);
Stage 1: source
AWSCloudTrail
Stage 2: where
| where EventName in~ (EventNameList)
Stage 3: extend (4 consecutive steps)
| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)
| extend UserName = tostring(split(UserIdentityArn, '/')[-1])
| extend AccountName = case( UserIdentityPrincipalid == "Anonymous", "Anonymous", isempty(UserIdentityUserName), UserName, UserIdentityUserName)
| extend AccountName = iif(AccountName contains "@", tostring(split(AccountName, '@', 0)[0]), AccountName),
AccountUPNSuffix = iif(AccountName contains "@", tostring(split(AccountName, '@', 1)[0]), "")
Stage 4: summarize
| summarize EventCount=count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated)
by EventSource, EventName, UserIdentityType, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, UserAgent, SessionMfaAuthenticated, AWSRegion,
AdditionalEventData, UserIdentityAccountId, UserIdentityPrincipalid, ResponseElements
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventName | in |
| field:"aws::eventName" kind:in |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AWSRegion | summarize |
AccountName | summarize |
AccountUPNSuffix | summarize |
AdditionalEventData | summarize |
EndTimeUtc | summarize |
EventCount | summarize |
EventName | summarize |
EventSource | summarize |
RecipientAccountId | summarize |
ResponseElements | summarize |
SessionMfaAuthenticated | summarize |
SourceIpAddress | summarize |
StartTimeUtc | summarize |
UserAgent | summarize |
UserIdentityAccountId | summarize |
UserIdentityPrincipalid | summarize |
UserIdentityType | summarize |
BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based NTLM Relay to LDAP Attack
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based NTLM Relay to LDAP Attack
Rule Body
id: 1bc4eeaa-08d8-47b6-85ab-bbea5a062282
name: BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based
NTLM Relay to LDAP Attack
description: BloodHound Attack Path Finding - Tier Zero Computer Vulnerable to Coercion-Based
NTLM Relay to LDAP Attack
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "T0CoerceAndRelayNTLMToLDAP"
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: domain_name
customDetails:
Description: ShortDescription
Remediation: ShortRemediation
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
BHEAttackPathsData_CL
Stage 2: where
| where Finding == "T0CoerceAndRelayNTLMToLDAP"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"T0CoerceAndRelayNTLMToLDAP" |
COM Registry Key Modified to Point to File in Color Profile Folder
#This query looks for changes to COM registry keys to point to files in C:\Windows\System32\spool\drivers\color. This can be used to enable COM hijacking for persistence. Ref: https://www.microsoft.com/security/blog/2022/07/27/untangling-knotweed-european-private-sector-offensive-actor-using-0-day-exploits/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Stealth |
Telemetry Coverage
Rule Body
id: ed8c9153-6f7a-4602-97b4-48c336b299e1
name: COM Registry Key Modified to Point to File in Color Profile Folder
description: |
'This query looks for changes to COM registry keys to point to files in C:\Windows\System32\spool\drivers\color\.
This can be used to enable COM hijacking for persistence.
Ref: https://www.microsoft.com/security/blog/2022/07/27/untangling-knotweed-european-private-sector-offensive-actor-using-0-day-exploits/'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceRegistryEvents
- connectorId: SecurityEvents
dataTypes:
- SecurityEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1574
tags:
- KNOTWEED
query: |
let guids = dynamic(["{ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea}","{1f486a52-3cb1-48fd-8f50-b8dc300d9f9d}","{4590f811-1d3a-11d0-891f-00aa004b2e24}", "{4de225bf-cf59-4cfc-85f7-68b90f185355}", "{F56F6FDD-AA9D-4618-A949-C1B91AF43B1A}"]);
let mde_data = DeviceRegistryEvents
| where ActionType =~ "RegistryValueSet"
| where RegistryKey contains "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID"
| where RegistryKey has_any (guids)
| where RegistryValueData has "System32\\spool\\drivers\\color";
let event_data = SecurityEvent
| where EventID == 4657
| where ObjectName contains "HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\CLSID"
| where ObjectName has_any (guids)
| where NewValue has "System32\\spool\\drivers\\color"
| extend RegistryKey = ObjectName, RegistryValueData = NewValue, DeviceName=Computer, InitiatingProcessFileName = Process, InitiatingProcessAccountName=SubjectUserName, InitiatingProcessAccountDomain = SubjectDomainName;
union mde_data, event_data
| extend HostName = tostring(split(DeviceName, ".")[0]), DomainIndex = toint(indexof(DeviceName, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(DeviceName, DomainIndex + 1), DeviceName)
entityMappings:
- entityType: RegistryKey
fieldMappings:
- identifier: Key
columnName: RegistryKey
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: DeviceName
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
- entityType: Process
fieldMappings:
- identifier: ProcessId
columnName: InitiatingProcessFileName
- entityType: Account
fieldMappings:
- identifier: Name
columnName: InitiatingProcessAccountName
- identifier: NTDomain
columnName: InitiatingProcessAccountName
version: 1.1.1
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Others" ]
Stages and Predicates
Let Binding: guids
let guids = dynamic(["{ddc05a5a-351a-4e06-8eaf-54ec1bc2dcea}","{1f486a52-3cb1-48fd-8f50-b8dc300d9f9d}","{4590f811-1d3a-11d0-891f-00aa004b2e24}", "{4de225bf-cf59-4cfc-85f7-68b90f185355}", "{F56F6FDD-AA9D-4618-A949-C1B91AF43B1A}"]);
Union (2 Sources)
Each leg below queries one source; the rule matches if any leg does. Sources: mde_data, event_data
Leg 1: mde_data
Leg 2: event_data
Applied to the Combined Result
| extend HostName = tostring(split(DeviceName, ".")[0]), DomainIndex = toint(indexof(DeviceName, '.')) | extend HostNameDomain = iff(DomainIndex != -1, substring(DeviceName, DomainIndex + 1), DeviceName)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionType | eq |
| field:"ActionType" kind:eq value:"RegistryValueSet" |
EventID | eq |
| field:"EventID" kind:eq value:"4657" |
NewValue | match |
| field:"NewValue" kind:match value:"System32\spool\drivers\color" |
ObjectName | contains |
| field:"ObjectName" kind:contains value:"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID" |
ObjectName | match |
| field:"ObjectName" kind:match |
RegistryKey | contains |
| field:"RegistryKey" kind:contains value:"HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID" |
RegistryKey | match |
| field:"RegistryKey" kind:match |
RegistryValueData | match |
| field:"RegistryValueData" kind:match value:"System32\spool\drivers\color" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceName | extend |
InitiatingProcessAccountDomain | extend |
InitiatingProcessAccountName | extend |
InitiatingProcessFileName | extend |
RegistryKey | extend |
RegistryValueData | extend |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
CYFIRMA - Medium severity Trojan Network Indicators - Block Recommended Rule
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
"This analytics rule detects network-based indicators flagged by CYFIRMA threat intelligence as associated with Command & Control (C2) infrastructure. These indicators may represent attacker-controlled endpoints used for persistence, data exfiltration, or command delivery to compromised systems."
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence | |
| Credential Access | |
| Command & Control | |
| Impact |
Rule Body
id: baa63d52-285d-43bf-a34e-8ed2fa260f9e
name: CYFIRMA - Medium severity Trojan Network Indicators - Block Recommended Rule
description: |
"This analytics rule detects network-based indicators flagged by CYFIRMA threat intelligence as associated with Command & Control (C2) infrastructure.
These indicators may represent attacker-controlled endpoints used for persistence, data exfiltration, or command delivery to compromised systems."
version: 1.0.1
kind: Scheduled
severity: Medium
enabled: false
requiredDataConnectors:
- connectorId: CyfirmaCyberIntelligenceDC
dataTypes:
- CyfirmaIndicators_CL
query: |
//Trojan Network Indicators - Block Recommended
let timeFrame= 5m;
CyfirmaIndicators_CL
| where (ConfidenceScore < 80 and ConfidenceScore >= 50)
and TimeGenerated between (ago(timeFrame) .. now())
and pattern !contains 'file:hashes' and RecommendedActions has 'Block' and Roles has 'Trojan'
| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)
| mv-expand extensionKeys
| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend
extension_id = extensionKeyStr,
ASN_Owner = props.asn_owner,
ASN = props.asn,
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
| project
IPv4,
IPv6,
URL,
Domain,
ThreatActors,
RecommendedActions,
Sources,
Roles,
Country,
IPAbuse,
name,
Description,
ConfidenceScore,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
SecurityVendors,
ProductName,
ProviderName
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: GreaterThan
triggerThreshold: 0
suppressionDuration: 5m
suppressionEnabled: true
tactics:
- Impact
- Persistence
- DefenseEvasion
- CredentialAccess
- CommandAndControl
- Execution
- InitialAccess
relevantTechniques:
- T1496
- T1053
- T1555
- T1090
- T1071
- T1204
- T1189
- T1053.005
- T1555.003
- T1071.001
- T1204.001
alertDetailsOverride:
alertDisplayNameFormat: "High-Confidence Trojan Network Indicators - Block Recommended Rule - {{name}} "
alertDescriptionFormat: "{{Description}} - {{name}} "
alertDynamicProperties:
- alertProperty: ProductName
value: ProductName
- alertProperty: ProviderName
value: ProviderName
customDetails:
ThreatActors: ThreatActors
Sources: Sources
RecommendedActions: RecommendedActions
Roles: Roles
Country: Country
Description: Description
ConfidenceScore: ConfidenceScore
SecurityVendors: SecurityVendors
IndicatorID: IndicatorID
Created: created
Modified: modified
ValidFrom: valid_from
Tags: Tags
ThreatType: ThreatType
TimeGenerated: TimeGenerated
IPAbuse: IPAbuse
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPv4
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPv6
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: Domain
- entityType: URL
fieldMappings:
- identifier: Url
columnName: URL
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
Stages and Predicates
Parameters
let timeFrame = 5m;
Stage 1: source
CyfirmaIndicators_CL
Stage 2: where
| where (ConfidenceScore < 80 and ConfidenceScore >= 50)
and TimeGenerated between (ago(timeFrame) .. now())
and pattern !contains 'file:hashes' and RecommendedActions has 'Block' and Roles has 'Trojan'
Stage 3: extend (6 consecutive steps)
| extend IPv4 = extract(@"ipv4-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend IPv6 = extract(@"ipv6-addr:value\s*=\s*'([^']+)'", 1, pattern)
| extend URL = extract(@"url:value\s*=\s*'([^']+)'", 1, pattern)
| extend Domain = extract(@"domain-name:value\s*=\s*'([^']+)'", 1, pattern)
| extend parsed = parse_json(extensions)
| extend extensionKeys = bag_keys(parsed)
Stage 4: mv-expand
| mv-expand extensionKeys
Stage 5: extend (4 consecutive steps)
| extend extensionKeyStr = tostring(extensionKeys)
| extend ext = parsed[extensionKeyStr]
| extend props = ext.properties
| extend
extension_id = extensionKeyStr,
ASN_Owner = props.asn_owner,
ASN = props.asn,
ProviderName = 'CYFIRMA',
ProductName = 'DeCYFIR/DeTCT'
Stage 6: project
| project
IPv4,
IPv6,
URL,
Domain,
ThreatActors,
RecommendedActions,
Sources,
Roles,
Country,
IPAbuse,
name,
Description,
ConfidenceScore,
IndicatorID,
created,
modified,
valid_from,
Tags,
ThreatType,
TimeGenerated,
SecurityVendors,
ProductName,
ProviderName
Indicators
These rows show field, operator, and value matches.
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
pattern | contains | file:hashes | excludes:pattern field:"pattern" value:"file:hashes" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ConfidenceScore | project |
Country | project |
Description | project |
Domain | project |
IPAbuse | project |
IPv4 | project |
IPv6 | project |
IndicatorID | project |
ProductName | project |
ProviderName | project |
RecommendedActions | project |
Roles | project |
SecurityVendors | project |
Sources | project |
Tags | project |
ThreatActors | project |
ThreatType | project |
TimeGenerated | project |
URL | project |
created | project |
modified | project |
name | project |
valid_from | project |
EatonForeseer - Unauthorized Logins
#Detects Unauthorized Logins into Eaton Foreseer
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Telemetry Coverage
Rule Body
id: 5a7fccb8-3ed0-44f2-8477-540af3ef4d92
name: EatonForeseer - Unauthorized Logins
description: |
'Detects Unauthorized Logins into Eaton Foreseer'
severity: High
status: Available
requiredDataConnectors:
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1078
query: |
SecurityEvent
// Look for all events relating to user logins
| where EventID in (4624,4625,4648,4675,4634,4647)
| where AccountType == "User"
// Filter for data present
| where isnotempty(TargetUserName)
// Look for all events that relate to the Eaton Foreseer application
| where ProcessName has "Foreseer"
// Ignore known user accounts (please edit based on your allowed users)
| where TargetUserName !in ("janedoe", "johndoe")
// De-duplicate multiple entries for the same user accessing a particular device
| summarize TimeGenerated=arg_min(TimeGenerated, *) by TargetUserName, Computer
| project
TimeGenerated,
IPCustomEntity = IpAddress,
HostCustomEntity = Computer,
UserEntity = TargetUserName,
Process,
ProcessName
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: Account
fieldMappings:
- identifier: Name
columnName: UserEntity
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostCustomEntity
- entityType: Process
fieldMappings:
- identifier: CommandLine
columnName: Process
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityEvent
Stage 2: where
| where EventID in (4624,4625,4648,4675,4634,4647)
Stage 3: where
| where AccountType == "User"
Stage 4: where
| where isnotempty(TargetUserName)
Stage 5: where
| where ProcessName has "Foreseer"
Stage 6: where
| where TargetUserName !in ("janedoe", "johndoe")
Stage 7: summarize
| summarize TimeGenerated=arg_min(TimeGenerated, *) by TargetUserName, Computer
Stage 8: project
| project
TimeGenerated,
IPCustomEntity = IpAddress,
HostCustomEntity = Computer,
UserEntity = TargetUserName,
Process,
ProcessName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AccountType | eq |
| field:"AccountType" kind:eq value:"User" |
EventID | in |
| field:"EventID" kind:in |
ProcessName | match |
| field:"process_name" kind:match value:"Foreseer" |
TargetUserName | is_not_null | field:"TargetUserName" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
TargetUserName | in | janedoe, johndoe | excludes:TargetUserName field:"TargetUserName" value:"janedoe" field:"TargetUserName" value:"johndoe" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
HostCustomEntity | project |
IPCustomEntity | project |
Process | project |
ProcessName | project |
TimeGenerated | project |
UserEntity | project |
GSA - Detect Protocol Changes for Destination Ports
#Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline. This can indicate potential protocol misuse or configuration changes. Configurable Parameters: - Learning period: The time range to establish the baseline. Default is set to 7 days. - Run time: The time range for current analysis. Default is set to 1 day.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a
name: GSA - Detect Protocol Changes for Destination Ports
description: |
Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline.
This can indicate potential protocol misuse or configuration changes.
Configurable Parameters:
- Learning period: The time range to establish the baseline. Default is set to 7 days.
- Run time: The time range for current analysis. Default is set to 1 day.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- NetworkAccessTrafficLogs
queryFrequency: 1h
queryPeriod: 8d
triggerOperator: gt
triggerThreshold: 1
tactics:
- DefenseEvasion
- Exfiltration
- CommandAndControl
relevantTechniques:
- T1571
query: |
let LearningPeriod = 7d;
let RunTime = 1d;
let StartLearningPeriod = ago(LearningPeriod + RunTime);
let EndRunTime = ago(RunTime);
let LearningPortToProtocol =
NetworkAccessTraffic
| where TimeGenerated between (StartLearningPeriod .. EndRunTime)
| where isnotempty(DestinationPort)
| summarize LearningTimeCount = count() by LearningTimeDstPort = DestinationPort, LearningTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;
let AlertTimePortToProtocol =
NetworkAccessTraffic
| where TimeGenerated between (EndRunTime .. now())
| where isnotempty(DestinationPort)
| summarize AlertTimeCount = count() by AlertTimeDstPort = DestinationPort, AlertTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;
AlertTimePortToProtocol
| join kind=leftouter (LearningPortToProtocol) on $left.AlertTimeDstPort == $right.LearningTimeDstPort and $left.SourceIp == $right.SourceIp and $left.DestinationFqdn == $right.DestinationFqdn
| where isnotempty(LearningTimeProtocol) and isnotempty(AlertTimeProtocol) and LearningTimeProtocol != AlertTimeProtocol
| project AlertTimeDstPort, AlertTimeProtocol, LearningTimeProtocol, SourceIp, DestinationFqdn
| extend IPCustomEntity = SourceIp, FqdnCustomEntity = DestinationFqdn
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
- entityType: URL
fieldMappings:
- identifier: Url
columnName: FqdnCustomEntity
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let LearningPeriod = 7d;
let RunTime = 1d;
let StartLearningPeriod = ago(LearningPeriod + RunTime);
let EndRunTime = ago(RunTime);
let AlertTimePortToProtocol is inlined into the numbered stages below.
Let Binding: LearningPortToProtocol
let LearningPortToProtocol = NetworkAccessTraffic
| where TimeGenerated between (StartLearningPeriod .. EndRunTime)
| where isnotempty(DestinationPort)
| summarize LearningTimeCount = count() by LearningTimeDstPort = DestinationPort, LearningTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn;
Stages 1 to 4 define let AlertTimePortToProtocol as the rule's main pipeline source. Stages 5 to 8 use it.
Stage 1: source
NetworkAccessTraffic
Stage 2: where
| where TimeGenerated between (EndRunTime .. now())
Stage 3: where
| where isnotempty(DestinationPort)
Stage 4: summarize
| summarize AlertTimeCount = count() by AlertTimeDstPort = DestinationPort, AlertTimeProtocol = TransportProtocol, SourceIp, DestinationFqdn
Stage 5: join
AlertTimePortToProtocol
| join kind=leftouter (LearningPortToProtocol) on $left.AlertTimeDstPort == $right.LearningTimeDstPort and $left.SourceIp == $right.SourceIp and $left.DestinationFqdn == $right.DestinationFqdn
Stage 6: where
where isnotempty(AlertTimeProtocol) and LearningTimeProtocol != AlertTimeProtocol and isnotempty(LearningTimeProtocol)
Stage 7: project
project AlertTimeDstPort, AlertTimeProtocol, DestinationFqdn, LearningTimeProtocol, SourceIp
Stage 8: extend
extend FqdnCustomEntity, IPCustomEntity
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
AlertTimeProtocol | is_not_null | field:"AlertTimeProtocol" kind:is_not_null | |
DestinationPort | is_not_null | field:"DestinationPort" kind:is_not_null | |
LearningTimeProtocol | is_not_null | field:"LearningTimeProtocol" kind:is_not_null | |
TimeGenerated | ge |
| field:"TimeGenerated" kind:ge value:"StartLearningPeriod" |
TimeGenerated | le |
| field:"TimeGenerated" kind:le value:"EndRunTime" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AlertTimeDstPort | project |
AlertTimeProtocol | project |
DestinationFqdn | project |
LearningTimeProtocol | project |
SourceIp | project |
FqdnCustomEntity | extend |
IPCustomEntity | extend |
Guardian- Block Competitor Policy Violation Detection
#'This alert creates an incident when a Block Competitor Policy Violation is detected from the Guardian.'
Rule Body
id: e5d9eea1-3250-49ba-9e39-b5678ec7496b
name: Guardian- Block Competitor Policy Violation Detection
description: |
'This alert creates an incident when a Block Competitor Policy Violation is detected from the Guardian.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- Guardian
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
Guardian
| where PolicyViolatedControlFeature =~ 'Block Competitor'
| where Severity =~ 'Low'
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: NTDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: Guardian- Block Competitor Policy Violation detection
alertDescriptionFormat: |
This query detects Block Competitor Policy Violation detected from the Guardian generated at {{TimeGenerated}}.\n\nPlease check the source for more information and investigate further.
alertTacticsColumnName: null
alertSeverityColumnName: Severity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
Guardian
Stage 2: where
| where PolicyViolatedControlFeature =~ 'Block Competitor'
Stage 3: where
| where Severity =~ 'Low'
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
PolicyViolatedControlFeature | eq |
| field:"PolicyViolatedControlFeature" kind:eq value:"Block Competitor" |
Severity | eq |
| field:"Severity" kind:eq value:"Low" |
Imperva - Abnormal protocol usage
#'Detects abnormal protocol usage.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 363307f6-09ba-4926-ad52-03aadfd24b5e
name: Imperva - Abnormal protocol usage
description: |
'Detects abnormal protocol usage.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: ImpervaWAFCloudAPI
dataTypes:
- ImpervaWAFCloud
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
- T1133
query: |
ImpervaWAFCloud
| where NetworkApplicationProtocol in~ ('HTTP', 'HTTPs')
| where DstPortNumber !in ('80', '443')
| extend IPCustomEntity = SrcIpAddr
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
ImpervaWAFCloud
Stage 2: where
| where NetworkApplicationProtocol in~ ('HTTP', 'HTTPs')
Stage 3: where
| where DstPortNumber !in ('80', '443')
Stage 4: extend
| extend IPCustomEntity = SrcIpAddr
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
NetworkApplicationProtocol | in |
| field:"NetworkApplicationProtocol" kind:in |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DstPortNumber | in | 443, 80 | excludes:DstPortNumber field:"DstPortNumber" value:"443" field:"DstPortNumber" value:"80" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
IPCustomEntity | extend |
Infoblox - Many High Threat Level Queries From Single Host Detected
#'At least 200 high threat level queries generated by single host in 1 hour. Queries do not need to be the same. Customize query count, scheduling, responses and more. This rule depends on a parser based on a Kusto Function to work as expected called InfobloxCDC.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 3822b794-fa89-4420-aad6-0e1a2307f419
name: Infoblox - Many High Threat Level Queries From Single Host Detected
description: |
'At least 200 high threat level queries generated by single host in 1 hour. Queries do not need to be the same. Customize query count, scheduling, responses and more. This rule depends on a parser based on a Kusto Function to work as expected called [**InfobloxCDC**](https://aka.ms/sentinel-InfobloxCloudDataConnector-parser).'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1498
- T1565
query: |
let threshold = 200;
InfobloxCDC
| where DeviceEventClassID has_cs "RPZ"
| where ThreatLevel_Score >= 80
| summarize count() by SourceIP
| where count_ > threshold
| join kind=inner (InfobloxCDC
| where DeviceEventClassID has_cs "RPZ"
| where ThreatLevel_Score >= 80
) on SourceIP
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
- identifier: OSVersion
columnName: InfobloxB1SrcOSVersion
- identifier: FullName
columnName: SourceUserName
customDetails:
SourceMACAddress: SourceMACAddress
eventGroupingSettings:
aggregationKind: SingleAlert
incidentConfiguration:
createIncident: true
version: 1.0.3
kind: Scheduled
Stages and Predicates
Parameters
let threshold = 200;
Stage 1: source
InfobloxCDC
Stage 2: where
| where DeviceEventClassID has_cs "RPZ"
Stage 3: where
| where ThreatLevel_Score >= 80
Stage 4: summarize
| summarize count() by SourceIP
Stage 5: where
| where count_ > threshold
Stage 6: join
| join kind=inner (InfobloxCDC
| where DeviceEventClassID has_cs "RPZ"
| where ThreatLevel_Score >= 80
) on SourceIP
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SourceIP | summarize |
IP address of Windows host encoded in web request
#This detection will identify network requests in HTTP proxy data that contains Base64 encoded IP addresses. After identifying candidates the query joins with DeviceNetworkEvents to idnetify any machine within the network using that IP address. Alerts indicate that the IP address of a machine within your network was seen with it's IP address base64 encoded in an outbound web request. This method of egressing the IP was seen used in POLONIUM's RunningRAT tool, however the detection is generic.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | |
| Exfiltration |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceNetworkEvents action any: Network activity |
Rule Body
id: a4ce20ae-a2e4-4d50-b40d-d49f1353b6cc
name: IP address of Windows host encoded in web request
description: |
'This detection will identify network requests in HTTP proxy data that contains Base64 encoded IP addresses. After identifying candidates the query joins with DeviceNetworkEvents to idnetify any machine within the network using that IP address. Alerts indicate that the IP address of a machine within your network was seen with it's IP address base64 encoded in an outbound web request. This method of egressing the IP was seen used in POLONIUM's RunningRAT tool, however the detection is generic.'
severity: Medium
requiredDataConnectors:
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: Fortinet
dataTypes:
- CommonSecurityLog
- connectorId: CheckPoint
dataTypes:
- CommonSecurityLog
- connectorId: PaloAltoNetworks
dataTypes:
- CommonSecurityLog
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
- CommandAndControl
relevantTechniques:
- T1041
- T1071.001
tags:
- POLONIUM
query: |
// Extracts plaintext IPv4 addresses
let ipv4_plaintext_extraction_regex = @"((?:(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.)){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){1,3})";
// Identified base64 encoded IPv4 addresses
let ipv4_encoded_identification_regex = @"\=([a-zA-Z0-9\/\+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/\+]{2,4}){3}[a-zA-Z0-9\/\+\=]*)";
// Extractes IPv4 addresses as hex values
let ipv4_decoded_hex_extract = @"((?:(?:61|62|63|64|65|66|67|68|69|6a|6b|6c|6d|6e|6f|70|71|72|73|74|75|76|77|78|79|7a|41|42|43|44|45|46|47|48|49|4a|4b|4c|4d|4e|4f|50|51|52|53|54|55|56|57|58|59|5a|2f|2b|3d),){7,15})";
CommonSecurityLog
| where isnotempty(RequestURL)
// Identify requests with encoded IPv4 addresses
| where RequestURL matches regex ipv4_encoded_identification_regex
| project TimeGenerated, RequestURL
// Extract IP candidates in their base64 encoded format, significantly reducing the dataset
| extend extracted_encoded_ip_candidate = extract_all(ipv4_encoded_identification_regex, RequestURL)
// We could have more than one candidate, expand them out
| mv-expand extracted_encoded_ip_candidate to typeof(string)
| summarize Start=min(TimeGenerated), End=max(TimeGenerated), make_set(RequestURL) by extracted_encoded_ip_candidate
// Pad if we need to
| extend extracted_encoded_ip_candidate = iff(strlen(extracted_encoded_ip_candidate) % 2 == 0, extracted_encoded_ip_candidate, strcat(extracted_encoded_ip_candidate, "="))
// Now decode the candidate to a long array, we cannot go straight to string as it cannot handle non-UTF8, we need to strip that first
| extend extracted_encoded_ip_candidate = tostring(base64_decode_toarray(extracted_encoded_ip_candidate))
// Extract the IP candidates from the array
| extend hex_extracted = extract_all(ipv4_decoded_hex_extract, extracted_encoded_ip_candidate)
// Expand, it's still possible that we might have more than 1 IP
| mv-expand hex_extracted
// Now we should have a clean string. We need to put it back into a dynamic array to convert back to a string.
| extend hex_extracted = trim_end(",", tostring(hex_extracted))
| extend hex_extracted = strcat("[",hex_extracted,"]")
| extend hex_extracted = todynamic(hex_extracted)
| extend extracted_encoded_ip_candidate = todynamic(extracted_encoded_ip_candidate)
// Convert the array back into a string
| extend decoded_ip_candidate = make_string(hex_extracted)
| summarize by decoded_ip_candidate, tostring(set_RequestURL), Start, End
// Now the IP candidates will be in plaintext, extract the IPs using a regex
| extend ipmatch = extract_all(ipv4_plaintext_extraction_regex, decoded_ip_candidate)
// If it's not an IP, throw it out
| where isnotnull(ipmatch)
| mv-expand ipmatch to typeof(string)
// Join with DeviceNetworkEvents to find instances where an IP of a machine in our MDE estate sent it's IP in a base64 encoded string
| join (
DeviceNetworkEvents
| summarize make_set(DeviceId), make_set(DeviceName) by RemoteIP
) on $left.ipmatch == $right.RemoteIP
| project Start, End, IPmatch=ipmatch, RequestURL=set_RequestURL, DeviceNames=set_DeviceName, DeviceIds=set_DeviceId, RemoteIP
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPmatch
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceNames
- entityType: URL
fieldMappings:
- identifier: Url
columnName: RequestURL
version: 1.0.2
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Thomas McElroy
support:
tier: Community
categories:
domains: [ "Security - Others", "Networking" ]
Stages and Predicates
Let Binding: ipv4_plaintext_extraction_regex
let ipv4_plaintext_extraction_regex = @"((?:(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(?:\.)){3}(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]){1,3})";
Let Binding: ipv4_encoded_identification_regex
let ipv4_encoded_identification_regex = @"\=([a-zA-Z0-9\/\+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/\+]{2,4}){3}[a-zA-Z0-9\/\+\=]*)";
Let Binding: ipv4_decoded_hex_extract
let ipv4_decoded_hex_extract = @"((?:(?:61|62|63|64|65|66|67|68|69|6a|6b|6c|6d|6e|6f|70|71|72|73|74|75|76|77|78|79|7a|41|42|43|44|45|46|47|48|49|4a|4b|4c|4d|4e|4f|50|51|52|53|54|55|56|57|58|59|5a|2f|2b|3d),){7,15})";
Stage 1: source
CommonSecurityLog
Stage 2: where
| where isnotempty(RequestURL)
Stage 3: where
| where RequestURL matches regex ipv4_encoded_identification_regex
Stage 4: project
| project TimeGenerated, RequestURL
Stage 5: extend
| extend extracted_encoded_ip_candidate = extract_all(ipv4_encoded_identification_regex, RequestURL)
Stage 6: mv-expand
| mv-expand extracted_encoded_ip_candidate to typeof(string)
Stage 7: summarize
| summarize Start=min(TimeGenerated), End=max(TimeGenerated), make_set(RequestURL) by extracted_encoded_ip_candidate
Stage 8: extend (3 consecutive steps)
| extend extracted_encoded_ip_candidate = iff(strlen(extracted_encoded_ip_candidate) % 2 == 0, extracted_encoded_ip_candidate, strcat(extracted_encoded_ip_candidate, "="))
| extend extracted_encoded_ip_candidate = tostring(base64_decode_toarray(extracted_encoded_ip_candidate))
| extend hex_extracted = extract_all(ipv4_decoded_hex_extract, extracted_encoded_ip_candidate)
extracted_encoded_ip_candidate =/* macro: ((strlen(extracted_encoded_ip_candidate) % 2) == 0) */extracted_encoded_ip_candidatestrcat(extracted_encoded_ip_candidate, "=")Stage 9: mv-expand
| mv-expand hex_extracted
Stage 10: extend (5 consecutive steps)
| extend hex_extracted = trim_end(",", tostring(hex_extracted))
| extend hex_extracted = strcat("[",hex_extracted,"]")
| extend hex_extracted = todynamic(hex_extracted)
| extend extracted_encoded_ip_candidate = todynamic(extracted_encoded_ip_candidate)
| extend decoded_ip_candidate = make_string(hex_extracted)
Stage 11: summarize
| summarize by decoded_ip_candidate, tostring(set_RequestURL), Start, End
Stage 12: extend
| extend ipmatch = extract_all(ipv4_plaintext_extraction_regex, decoded_ip_candidate)
Stage 13: where
| where isnotnull(ipmatch)
Stage 14: mv-expand
| mv-expand ipmatch to typeof(string)
Stage 15: join
| join (
DeviceNetworkEvents
| summarize make_set(DeviceId), make_set(DeviceName) by RemoteIP
) on $left.ipmatch == $right.RemoteIP
Stage 16: project
project DeviceIds, DeviceNames, End, IPmatch, RemoteIP, RequestURL, Start
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
RequestURL | is_not_null | field:"RequestURL" kind:is_not_null | |
RequestURL | regex_match |
| field:"RequestURL" kind:regex_match value:"\=([a-zA-Z0-9\/+]*(?:(?:MC|Au|wL|MS|Eu|xL|Mi|Iu|yL|My|Mu|zL|NC|Qu|0L|NS|Uu|1L|Ni|Yu|2L|Ny|cu|3L|OC|gu|4L|OS|ku|5L){1}[a-zA-Z0-9\/+]{2,4}){3}[a-zA-Z0-9\/+\=]*)" |
ipmatch | is_not_null | field:"ipmatch" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DeviceIds | project |
DeviceNames | project |
End | project |
IPmatch | project |
RemoteIP | project |
RequestURL | project |
Start | project |
Malware Detected
#'Creates an incident when a Symantec Endpoint Proection agent detects malware and the malware was not cleaned.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution |
Rule Body
id: 072ee087-17e1-474d-b162-bbe38bcab9f9
name: Malware Detected
description: |
'Creates an incident when a Symantec Endpoint Proection agent detects malware and the malware was not cleaned.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1204.002
query: |
SymantecEndpointProtection
| where LogType == "Agent Risk Logs"
| where CategorySet == "Malware"
| where ActualAction !contains "Cleaned"
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by SrcIpAddr, SrcHostName, UserName, FilePath, ActualAction, CategorySet, CategoryType
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: SrcHostName
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
SymantecEndpointProtection
Stage 2: where
| where LogType == "Agent Risk Logs"
Stage 3: where
| where CategorySet == "Malware"
Stage 4: where
| where ActualAction !contains "Cleaned"
Stage 5: summarize
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by SrcIpAddr, SrcHostName, UserName, FilePath, ActualAction, CategorySet, CategoryType
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CategorySet | eq |
| field:"CategorySet" kind:eq value:"Malware" |
LogType | eq |
| field:"LogType" kind:eq value:"Agent Risk Logs" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
ActualAction | contains | Cleaned | excludes:ActualAction field:"ActualAction" value:"Cleaned" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActualAction | summarize |
CategorySet | summarize |
CategoryType | summarize |
EndTimeUtc | summarize |
FilePath | summarize |
SrcHostName | summarize |
SrcIpAddr | summarize |
StartTimeUtc | summarize |
UserName | summarize |
Mimecast Secure Email Gateway - AV
#'Detects threats from email anti virus scan.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution |
Rule Body
id: 33bf0cc9-e568-42bf-9571-c22adf7be66d
name: Mimecast Secure Email Gateway - AV
description: |
'Detects threats from email anti virus scan.'
severity: Informational
status: Available
requiredDataConnectors:
- connectorId: MimecastSEGAPI
dataTypes:
- MimecastCG
enabled: true
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1053
query: |
MimecastCG
| where Type == "email_antivirus"
| extend SenderEnvelope = ['Sender Envelope']
suppressionDuration: 5h
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: P7D
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: SenderEnvelope
- identifier: Recipient
columnName: Recipients
- identifier: Subject
columnName: Subject
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastCG
Stage 2: where
| where Type == "email_antivirus"
Stage 3: extend
| extend SenderEnvelope = ['Sender Envelope']
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Type | eq |
| field:"Type" kind:eq value:"email_antivirus" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
SenderEnvelope | extend |
Mimecast Secure Email Gateway - AV
#Detects threats from email anti virus scan
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution |
Rule Body
id: 0f0dc725-29dc-48c3-bf10-bd2f34fd1cbb
name: Mimecast Secure Email Gateway - AV
description: Detects threats from email anti virus scan
severity: Informational
requiredDataConnectors:
- connectorId: MimecastSIEMAPI
dataTypes:
- MimecastSIEM_CL
enabled: true
query: MimecastSIEM_CL| where mimecastEventId_s=="mail_av"
queryFrequency: 5m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
suppressionDuration: 5h
suppressionEnabled: false
tactics:
- Execution
relevantTechniques:
- T1053
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1d
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: SingleAlert
customDetails:
IP: IP_s
MsgId_s: MsgId_s
Route: Route_s
SenderDomain: SenderDomain_s
MimecastIP: MimecastIP_s
fileName: fileName_s
sha256: sha256_s
Size: Size_s
fileExt: fileExt_s
Virus: Virus_s
sha1: sha1_s
SenderDomainInternal: SenderDomainInternal_s
fileMime: fileMime_s
CustomerIP: CustomerIP_s
md5: md5_g
entityMappings:
- entityType: MailMessage
fieldMappings:
- identifier: Sender
columnName: Sender_s
- identifier: Recipient
columnName: Recipient_s
- identifier: Subject
columnName: Subject_s
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
MimecastSIEM_CL
Stage 2: where
| where mimecastEventId_s=="mail_av"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
mimecastEventId_s | eq |
| field:"mimecastEventId_s" kind:eq value:"mail_av" |
NordPass - Deleting items of deleted member
#This will alert you if the deleted user's items have been removed without being transferred to another active user, as this could result in the loss of access to critical tools or information.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 0068dca4-dea0-46a3-a970-655e067a145f
name: NordPass - Deleting items of deleted member
kind: Scheduled
version: 1.0.0
displayName: Deleting items of deleted member
description: |
This will alert you if the deleted user's items have been removed without being transferred to another active user, as this could result in the loss of access to critical tools or information.
severity: High
query: |
NordPassEventLogs_CL
| where event_type == "item_delete"
| where action == "item_reassignment_deleted"
| extend TargetEmail = user_email
requiredDataConnectors:
- connectorId: NordPass
dataTypes:
- NordPassEventLogs_CL
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
incidentConfiguration:
createIncident: false
entityMappings:
- entityType: Mailbox
fieldMappings:
- identifier: MailboxPrimaryAddress
columnName: TargetEmail
Stages and Predicates
Stage 1: source
NordPassEventLogs_CL
Stage 2: where
| where event_type == "item_delete"
Stage 3: where
| where action == "item_reassignment_deleted"
Stage 4: extend
| extend TargetEmail = user_email
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
action | eq |
| field:"action" kind:eq value:"item_reassignment_deleted" |
event_type | eq |
| field:"event_type" kind:eq value:"item_delete" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
TargetEmail | extend |
NRT Base64 Encoded Windows Process Command-lines
#This detection identifies instances of a base64 encoded PE file header seen in the process command line parameter.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Stealth |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Security-Auditing | Event ID 4688: A new process has been created. |
Rule Body
id: c3e5dbaa-a540-408c-8b36-68bdfb3df088
name: NRT Base64 Encoded Windows Process Command-lines
description: |
'This detection identifies instances of a base64 encoded PE file header seen in the process command line parameter.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
tactics:
- Execution
- DefenseEvasion
relevantTechniques:
- T1059
- T1027
- T1140
query: |
SecurityEvent
| where EventID == 4688
| where isnotempty(CommandLine)
| where CommandLine contains "TVqQAAMAAAAEAAA"
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SubjectAccount
- identifier: Name
columnName: SubjectUserName
- identifier: NTDomain
columnName: SubjectDomainName
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.0.2
kind: NRT
Stages and Predicates
Stage 1: source
SecurityEvent
Stage 2: where
| where EventID == 4688
Stage 3: where
| where isnotempty(CommandLine)
Stage 4: where
| where CommandLine contains "TVqQAAMAAAAEAAA"
Stage 5: extend
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CommandLine | contains |
| field:"CommandLine" kind:contains value:"TVqQAAMAAAAEAAA" |
CommandLine | is_not_null | field:"CommandLine" kind:is_not_null | |
EventID | eq |
| field:"EventID" kind:eq value:"4688" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
OracleDBAudit - User activity after long inactivity time
#'Detects when an action was made by a user which last activity was observed more than 30 days ago.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Persistence |
Rule Body
id: 5e93a535-036b-4570-9e58-d8992f30e1ae
name: OracleDBAudit - User activity after long inactivity time
description: |
'Detects when an action was made by a user which last activity was observed more than 30 days ago.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Persistence
relevantTechniques:
- T1078
query: |
let lbtime_14d = 14d;
let lbtime_7d = 7d;
let lbtime_24h = 24h;
let known_users_14d = OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_14d) .. ago(lbtime_14d))
| where isnotempty(DstUserName)
| where isnotempty(Action)
| summarize makeset(DstUserName);
let known_users_7d = OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_7d) .. ago(lbtime_24h))
| where isnotempty(DstUserName)
| where isnotempty(Action)
| summarize makeset(DstUserName);
OracleDatabaseAuditEvent
| where isnotempty(DstUserName)
| where isnotempty(Action)
| where DstUserName !in (known_users_7d)
| where DstUserName in (known_users_14d)
| project DstUserName
| extend AccountCustomEntity = DstUserName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let lbtime_14d = 14d;
let lbtime_7d = 7d;
let lbtime_24h = 24h;
Let Binding: known_users_14d
let known_users_14d = OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_14d) .. ago(lbtime_14d))
| where isnotempty(DstUserName)
| where isnotempty(Action)
| summarize makeset(DstUserName);
Let Binding: known_users_7d
let known_users_7d = OracleDatabaseAuditEvent
| where TimeGenerated between (ago(lbtime_7d) .. ago(lbtime_24h))
| where isnotempty(DstUserName)
| where isnotempty(Action)
| summarize makeset(DstUserName);
Stage 1: source
OracleDatabaseAuditEvent
Stage 2: where
| where isnotempty(DstUserName)
Stage 3: where
| where isnotempty(Action)
Stage 4: where
| where DstUserName !in (known_users_7d)
Stage 5: where
| where DstUserName in (known_users_14d)
Stage 6: project
| project DstUserName
Stage 7: extend
| extend AccountCustomEntity = DstUserName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action | is_not_null | field:"Action" kind:is_not_null | |
DstUserName | in |
| field:"DstUserName" kind:in value:"known_users_14d" |
DstUserName | is_not_null | field:"DstUserName" kind:is_not_null |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
DstUserName | eq | known_users_7d | excludes:DstUserName field:"DstUserName" value:"known_users_7d" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstUserName | project |
AccountCustomEntity | extend |
Pathlock TDnR - SAP System Log Events
#Detects security-relevant events from the SAP System Log (SM21), forwarded by Pathlock Threat Detection and Response. System log events capture low-level system activity including kernel errors, authorization violations, and system configuration events that may indicate attacks or misuse.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Discovery |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c63
name: Pathlock TDnR - SAP System Log Events
kind: Scheduled
description: >-
Detects security-relevant events from the SAP System Log (SM21), forwarded by Pathlock Threat
Detection and Response. System log events capture low-level system activity including kernel
errors, authorization violations, and system configuration events that may indicate
attacks or misuse.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Discovery
relevantTechniques:
- T1082
query: |
Pathlock_TDnR_CL
| where DataSource == "SYSLOG"
| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Bname
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: Hostname
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIp
eventGroupingSettings:
aggregationKind: SingleAlert
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 5h
matchingMethod: AnyAlert
groupByEntities: []
groupByAlertDetails: []
groupByCustomDetails: []
suppressionDuration: 5h
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Stage 1: source
Pathlock_TDnR_CL
Stage 2: where
| where DataSource == "SYSLOG"
Stage 3: project
| project TimeGenerated, Sysid, DataSource, Eventid, Instance, Hostname, Bname,
Tcode, Report, Area, Subid, SrcIp, DestIp, AffectedUser, LogLine,
MsgType, MsgId, MsgNo, MessageV1, MessageV2, MessageV3, MessageV4, CentralTs
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DataSource | eq |
| field:"DataSource" kind:eq value:"SYSLOG" |
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 |
Recorded Future Playbook Alerts Incident Creation
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
'Create Recorded Future Playbook Alerts in Microsoft Defender'
Rule Body
id: 0aded97e-4e2d-4e4d-93cf-4a7e45c7bab2
name: Recorded Future Playbook Alerts Incident Creation
description: |
'Create Recorded Future Playbook Alerts in Microsoft Defender'
severity: Medium
status: Available
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
RecordedFuturePlaybookAlerts_CL
| where TimeGenerated >= now(-1h)
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: 'Alert: {{rule_label_s}} - {{title_s}}'
alertDescriptionFormat: |
_Recorded Future Alert_
Evidence Summary:
{{alert_description}}
Priority: {{priority_s}}
Recorded Future Playbook Alert ID
{{id_s}}
alertDynamicProperties:
- alertProperty: AlertLink
value: link_s
customDetails:
RFLabel: SourceSystem
Category: Category
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: Selected
groupByCustomDetails:
- Category
version: 1.0.0
kind: NRT
Stages and Predicates
Stage 1: source
RecordedFuturePlaybookAlerts_CL
Stage 2: where
| where TimeGenerated >= now(-1h)
Semperis DSP Operations Critical Notifications
#Alerts when there are critical notifications fired in the DSP system.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Resource Development | |
| Initial Access | |
| Credential Access |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Semperis-DSP-Notifications | Event ID 30001: DSP critical notification |
Rule Body
id: "8f471e21-3bb2-466f-9bc2-0a0326a60788"
name: Semperis DSP Operations Critical Notifications
description: |
'Alerts when there are critical notifications fired in the DSP system.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SemperisDSP
dataTypes:
- dsp_parser
queryFrequency: 30m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CredentialAccess
- ResourceDevelopment
relevantTechniques:
- T1133
- T1110
- T1584
query: |
SecurityEvent
| where EventSourceName == 'Semperis-DSP-Notifications' and EventID == 30001
| extend p1Xml = parse_xml(EventData).EventData.Data
| mv-expand bagexpansion=array p1Xml
| evaluate bag_unpack(p1Xml)
| extend Name=column_ifexists('@Name', ''), Value=column_ifexists('#text', '')
| evaluate pivot(Name, any(Value), TimeGenerated, EventSourceName, Channel, Computer, Level, EventLevelName, EventID, Task, Type, _ResourceId)
| parse column_ifexists('objectDN', '') with * "CN=" cnName "," *
| where "Critical" == column_ifexists('severity', "")
| extend changedBy = column_ifexists('changedBy', "")
| extend NTDomain = tostring(split(changedBy, '\\', 0)[0]), LoginUser = tostring(split(changedBy, '\\', 1)[0])
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: LoginUser
- identifier: NTDomain
columnName: NTDomain
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
eventGroupingSettings:
aggregationKind: SingleAlert
alertDetailsOverride:
alertDisplayNameFormat: Critical Notification -- Alert from Semperis Directory Services Protector
alertDescriptionFormat: A critical notification was created in the DSP system.
version: 2.0.7
kind: Scheduled
Stages and Predicates
Stage 1: source
SecurityEvent
Stage 2: where
| where EventSourceName == 'Semperis-DSP-Notifications' and EventID == 30001
Stage 3: extend
| extend p1Xml = parse_xml(EventData).EventData.Data
Stage 4: mv-expand
| mv-expand bagexpansion=array p1Xml
Stage 5: evaluate
| evaluate bag_unpack(p1Xml)
Stage 6: extend
| extend Name=column_ifexists('@Name', ''), Value=column_ifexists('#text', '')
Stage 7: evaluate
| evaluate pivot(Name, any(Value), TimeGenerated, EventSourceName, Channel, Computer, Level, EventLevelName, EventID, Task, Type, _ResourceId)
Stage 8: parse
| parse column_ifexists('objectDN', '') with * "CN=" cnName "," *
Stage 9: where
| where "Critical" == column_ifexists('severity', "")
Stage 10: extend (3 consecutive steps)
| extend changedBy = column_ifexists('changedBy', "")
| extend NTDomain = tostring(split(changedBy, '\\', 0)[0]), LoginUser = tostring(split(changedBy, '\\', 1)[0])
| extend HostName = tostring(split(Computer, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Computer, '.'), 1, -1), '.'))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventID | eq |
| field:"EventID" kind:eq value:"30001" |
EventSourceName | eq |
| field:"Category" kind:eq value:"Semperis-DSP-Notifications" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
p1Xml | extend |
Name | extend |
Value | extend |
changedBy | extend |
LoginUser | extend |
NTDomain | extend |
DnsDomain | extend |
HostName | extend |
Sensitive Azure Key Vault operations
#'Identifies when sensitive Azure Key Vault operations are used. This includes: VaultDelete, KeyDelete, SecretDelete, SecretPurge, KeyPurge, SecretBackup, KeyBackup. Any Backup operations should match with expected scheduled backup activity.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: d6491be0-ab2d-439d-95d6-ad8ea39277c5
name: Sensitive Azure Key Vault operations
description: |
'Identifies when sensitive Azure Key Vault operations are used. This includes: VaultDelete, KeyDelete, SecretDelete, SecretPurge, KeyPurge, SecretBackup, KeyBackup.
Any Backup operations should match with expected scheduled backup activity.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: AzureKeyVault
dataTypes:
- KeyVaultData
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1485
query: |
let SensitiveOperationList = dynamic(
["VaultDelete", "KeyDelete", "SecretDelete", "SecretPurge", "KeyPurge", "SecretBackup", "KeyBackup"]);
AzureDiagnostics
| extend ResultType = column_ifexists("ResultType", "NoResultType"),
requestUri_s = column_ifexists("requestUri_s", "None"),
identity_claim_oid_g = column_ifexists("identity_claim_oid_g", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None"),
clientInfo_s = column_ifexists("clientInfo_s", "None"),
identity_claim_upn_s = column_ifexists("identity_claim_upn_s", "None"),
identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g = column_ifexists("identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g", "None")
| where ResourceType =~ "VAULTS" and ResultType =~ "Success"
| where OperationName in~ (SensitiveOperationList)
| summarize EventCount=count(), StartTimeUtc=min(TimeGenerated), EndTimeUtc=max(TimeGenerated), TimeTriggered=make_list(TimeGenerated),OperationNameList=make_set(OperationName), RequestURLList=make_set(requestUri_s), CallerIPList = make_set(CallerIPAddress), CallerIPMax= arg_max(CallerIPAddress,*) by ResourceType, ResultType, Resource, identity_claim_upn_s, clientInfo_s, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g
| extend timestamp = StartTimeUtc
| extend Name = tostring(split(identity_claim_upn_s,'@',0)[0]), UPNSuffix = tostring(split(identity_claim_upn_s,'@',1)[0]), AadUserId = identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g
entityMappings:
- entityType: Account
fieldMappings:
- identifier: AadUserId
columnName: AadUserId
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CallerIPMax
version: 1.0.4
kind: Scheduled
Stages and Predicates
Let Binding: SensitiveOperationList
let SensitiveOperationList = dynamic(
["VaultDelete", "KeyDelete", "SecretDelete", "SecretPurge", "KeyPurge", "SecretBackup", "KeyBackup"]);
Stage 1: source
AzureDiagnostics
Stage 2: extend
| extend ResultType = column_ifexists("ResultType", "NoResultType"),
requestUri_s = column_ifexists("requestUri_s", "None"),
identity_claim_oid_g = column_ifexists("identity_claim_oid_g", "None"), CallerIPAddress = column_ifexists("CallerIPAddress", "None"),
clientInfo_s = column_ifexists("clientInfo_s", "None"),
identity_claim_upn_s = column_ifexists("identity_claim_upn_s", "None"),
identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g = column_ifexists("identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g", "None")
Stage 3: where
| where ResourceType =~ "VAULTS" and ResultType =~ "Success"
Stage 4: where
| where OperationName in~ (SensitiveOperationList)
Stage 5: summarize
| summarize EventCount=count(), StartTimeUtc=min(TimeGenerated), EndTimeUtc=max(TimeGenerated), TimeTriggered=make_list(TimeGenerated),OperationNameList=make_set(OperationName), RequestURLList=make_set(requestUri_s), CallerIPList = make_set(CallerIPAddress), CallerIPMax= arg_max(CallerIPAddress,*) by ResourceType, ResultType, Resource, identity_claim_upn_s, clientInfo_s, identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g
Stage 6: extend
| extend timestamp = StartTimeUtc
Stage 7: extend
| extend Name = tostring(split(identity_claim_upn_s,'@',0)[0]), UPNSuffix = tostring(split(identity_claim_upn_s,'@',1)[0]), AadUserId = identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
OperationName | in |
| field:"OperationName" kind:in |
ResourceType | eq |
| field:"ResourceType" kind:eq value:"VAULTS" |
ResultType | eq |
| field:"ResultType" kind:eq value:"Success" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
CallerIPList | summarize |
CallerIPMax | summarize |
EndTimeUtc | summarize |
EventCount | summarize |
OperationNameList | summarize |
RequestURLList | summarize |
Resource | summarize |
ResourceType | summarize |
ResultType | summarize |
StartTimeUtc | summarize |
TimeTriggered | summarize |
clientInfo_s | summarize |
identity_claim_http_schemas_microsoft_com_identity_claims_objectidentifier_g | summarize |
identity_claim_upn_s | summarize |
timestamp | extend |
AadUserId | extend |
Name | extend |
UPNSuffix | extend |
Silk Typhoon Suspicious Exchange Request
#'This query looks for suspicious request patterns to Exchange servers that fit a pattern observed by Silk Typhoon actors. The same query can be run on HTTPProxy logs from on-premise hosted Exchange servers. Reference: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: 23005e87-2d3a-482b-b03d-edbebd1ae151
name: Silk Typhoon Suspicious Exchange Request
description: |
'This query looks for suspicious request patterns to Exchange servers that fit a pattern observed by Silk Typhoon actors.
The same query can be run on HTTPProxy logs from on-premise hosted Exchange servers.
Reference: https://www.microsoft.com/security/blog/2021/03/02/hafnium-targeting-exchange-servers/'
severity: Medium
requiredDataConnectors:
- connectorId: AzureMonitor(IIS)
dataTypes:
- W3CIISLog
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
query: |
let exchange_servers = (
W3CIISLog
| where TimeGenerated > ago(14d)
| where sSiteName =~ "Exchange Back End"
| summarize by Computer);
W3CIISLog
| where TimeGenerated > ago(1d)
| where Computer in (exchange_servers)
| where csUriQuery startswith "t="
| project-reorder TimeGenerated, Computer, csUriStem, csUriQuery, csUserName, csUserAgent, cIP
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(csUserName, "@")[0]), AccountUPNSuffix = tostring(split(csUserName, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: csUserName
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: NTDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: cIP
version: 1.0.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Microsoft Security Research
support:
tier: Community
categories:
domains: [ "Security - Threat Intelligence" ]
Stages and Predicates
Let Binding: exchange_servers
let exchange_servers = (
W3CIISLog
| where TimeGenerated > ago(14d)
| where sSiteName =~ "Exchange Back End"
| summarize by Computer);
Stage 1: source
W3CIISLog
Stage 2: where
| where TimeGenerated > ago(1d)
Stage 3: where
| where Computer in (exchange_servers)
Stage 4: where
| where csUriQuery startswith "t="
Stage 5: project-reorder
| project-reorder TimeGenerated, Computer, csUriStem, csUriQuery, csUserName, csUserAgent, cIP
Stage 6: extend (3 consecutive steps)
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| extend AccountName = tostring(split(csUserName, "@")[0]), AccountUPNSuffix = tostring(split(csUserName, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Computer | in |
| field:"Computer" kind:in value:"exchange_servers" |
csUriQuery | starts_with |
| field:"csUriQuery" kind:starts_with value:"t=" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
AccountName | extend |
AccountUPNSuffix | extend |
Tenable.ad LSASS Memory
#'Searches for OS Credentials dumping attacks.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: 6f7fa5f9-7d21-42c1-bc52-ac355b87c6cf
name: Tenable.ad LSASS Memory
description: |
'Searches for OS Credentials dumping attacks.'
severity: High
requiredDataConnectors:
- connectorId: Tenable.ad
dataTypes:
- Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1003.001
query: |
// For the query to work properly, make sure you have imported the afad_parser.kql parser into the workspace
// Retrieve the parser here: https://raw.githubusercontent.com/tenable/Azure-Sentinel/Tenable.ad-connector/Solutions/TenableAD/Parsers/afad_parser.kql
// Then, create the Kusto Function with alias afad_parser
afad_parser
| where MessageType == 2 and Codename == "OS Credential Dumping: LSASS Memory"
| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
afad_parser
Stage 2: where
| where MessageType == 2 and Codename == "OS Credential Dumping: LSASS Memory"
Stage 3: extend
| extend HostName = tostring(split(Host, '.', 0)[0]), DnsDomain = tostring(strcat_array(array_slice(split(Host, '.'), 1, -1), '.'))
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Codename | eq |
| field:"Codename" kind:eq value:"OS Credential Dumping: LSASS Memory" |
MessageType | eq |
| field:"MessageType" kind:eq value:"2" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |