Detection rules › Kusto
Kusto rules: 19
AWSCloudTrail - Policy version set to default
#Detects SetDefaultPolicyVersion activity that makes a different IAM policy version active. Attackers can use this to activate previously created permissive versions and escalate access.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Rule Body
id: 874a1762-3fd7-4489-b411-6d4a9e9e8a59
name: AWSCloudTrail - Policy version set to default
description: |
Detects SetDefaultPolicyVersion activity that makes a different IAM policy version active.
Attackers can use this to activate previously created permissive versions and escalate access.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: AWS
dataTypes:
- AWSCloudTrail
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1098.003
query: |
AWSCloudTrail
| where EventName == "SetDefaultPolicyVersion" and isempty(ErrorCode) and isempty(ErrorMessage)
| 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]), "")
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- identifier: CloudAppAccountId
columnName: RecipientAccountId
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIpAddress
customDetails:
EventName: EventName
EventSource: EventSource
AWSRegion: AWSRegion
UserIdentityArn: UserIdentityArn
alertDetailsOverride:
alertDisplayNameFormat: 'AWS IAM default policy version changed by {{AccountName}}'
alertDescriptionFormat: 'Detected SetDefaultPolicyVersion from {{SourceIpAddress}} in account {{RecipientAccountId}}.'
version: 1.0.2
kind: Scheduled
Stages and Predicates
Stage 1: source
AWSCloudTrail
Stage 2: where
| where EventName == "SetDefaultPolicyVersion" and isempty(ErrorCode) and isempty(ErrorMessage)
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]), "")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ErrorCode | is_null | field:"aws::errorCode" kind:is_null | |
ErrorMessage | is_null | field:"aws::errorMessage" kind:is_null | |
EventName | eq |
| field:"aws::eventName" kind:eq value:"SetDefaultPolicyVersion" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
UserIdentityArn | extend |
UserName | extend |
AccountName | extend |
AccountUPNSuffix | extend |
blacklens Insights
#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.
'Creates incidents from blacklens.io Attack Surface Management alerts ingested into the blacklens_CL table. Alert severity is mapped dynamically from the source data.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance | |
| Resource Development | |
| Initial Access | |
| Defense Impairment | |
| Credential Access | |
| Collection | |
| Command & Control | |
| Exfiltration |
Rule Body
id: e261b70a-3005-4a1b-a7a2-2c8147fafed7
name: blacklens Insights
description: |
'Creates incidents from blacklens.io Attack Surface Management alerts ingested into the blacklens_CL table. Alert severity is mapped dynamically from the source data.'
severity: High
status: Available
tactics:
- Reconnaissance
- ResourceDevelopment
- InitialAccess
- CredentialAccess
- Collection
- Exfiltration
- DefenseEvasion
- CommandAndControl
relevantTechniques:
- T1595
- T1583
- T1190
- T1110
- T1005
- T1041
- T1562
- T1071
requiredDataConnectors:
- connectorId: blacklens_io
dataTypes:
- blacklens_CL
kind: NRT
query: |
blacklens_CL
| summarize arg_max(TimeGenerated, *) by id
| extend AlertSeverity = case(
tolower(severity) == "critical", "High",
tolower(severity) == "high", "High",
tolower(severity) == "medium", "Medium",
tolower(severity) == "low", "Low",
"Informational"
)
suppressionDuration: PT1H
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: false
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: "{{alert_title}}"
alertDescriptionFormat: "{{message}}"
alertSeverityColumnName: AlertSeverity
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: link
version: 1.0.0
Stages and Predicates
Stage 1: source
blacklens_CL
Stage 2: summarize
| summarize arg_max(TimeGenerated, *) by id
Stage 3: extend
| extend AlertSeverity = case(
tolower(severity) == "critical", "High",
tolower(severity) == "high", "High",
tolower(severity) == "medium", "Medium",
tolower(severity) == "low", "Low",
"Informational"
)
AlertSeverity =severity == "critical""High"severity == "high""High"severity == "medium""Medium"severity == "low""Low""Informational"Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
id | summarize |
AlertSeverity | extend |
BloodHound Attack Path Finding - Large Default Groups With All Extended Privileges
#This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.
BloodHound Attack Path Finding - Large Default Groups With All Extended Privileges
Rule Body
id: ab49f284-0b2a-42c9-b5e3-2c0fbf941d8e
name: BloodHound Attack Path Finding - Large Default Groups With All Extended Privileges
description: BloodHound Attack Path Finding - Large Default Groups With All Extended
Privileges
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: BloodHoundEnterprise
dataTypes:
- BHEAttackPathsData_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: BHEAttackPathsData_CL | where Finding == "LargeDefaultGroupsAllExtendedRights"
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 == "LargeDefaultGroupsAllExtendedRights"
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Finding | eq |
| field:"Finding" kind:eq value:"LargeDefaultGroupsAllExtendedRights" |
Cisco Cloud Security - Request Allowed to harmful/malicious URI category
#'It is reccomended that these Categories shoud be blocked by policies because they provide harmful/malicious content..'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | |
| Exfiltration |
Rule Body
id: d6bf1931-b1eb-448d-90b2-de118559c7ce
name: Cisco Cloud Security - Request Allowed to harmful/malicious URI category
description: |
'It is reccomended that these Categories shoud be blocked by policies because they provide harmful/malicious content..'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CiscoUmbrellaDataConnector
dataTypes:
- Cisco_Umbrella_proxy_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- Exfiltration
relevantTechniques:
- T1071.001
- T1041
query: |
Cisco_Umbrella
| where EventType == 'proxylogs'
| where DvcAction =~ 'Allowed'
| where UrlCategory contains 'Adult Themes' or
UrlCategory contains 'Adware' or
UrlCategory contains 'Alcohol' or
UrlCategory contains 'Illegal Downloads' or
UrlCategory contains 'Drugs' or
UrlCategory contains 'Child Abuse Content' or
UrlCategory contains 'Hate/Discrimination' or
UrlCategory contains 'Nudity' or
UrlCategory contains 'Pornography' or
UrlCategory contains 'Proxy/Anonymizer' or
UrlCategory contains 'Sexuality' or
UrlCategory contains 'Tasteless' or
UrlCategory contains 'Terrorism' or
UrlCategory contains 'Web Spam' or
UrlCategory contains 'German Youth Protection' or
UrlCategory contains 'Illegal Activities' or
UrlCategory contains 'Lingerie/Bikini' or
UrlCategory contains 'Weapons'
| project TimeGenerated, SrcIpAddr, DstIpAddr, UrlOriginal, Identities
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: UrlOriginal
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
version: 1.1.2
kind: Scheduled
Stages and Predicates
Stage 1: source
Cisco_Umbrella
Stage 2: where
| where EventType == 'proxylogs'
Stage 3: where
| where DvcAction =~ 'Allowed'
Stage 4: where
| where UrlCategory contains 'Adult Themes' or
UrlCategory contains 'Adware' or
UrlCategory contains 'Alcohol' or
UrlCategory contains 'Illegal Downloads' or
UrlCategory contains 'Drugs' or
UrlCategory contains 'Child Abuse Content' or
UrlCategory contains 'Hate/Discrimination' or
UrlCategory contains 'Nudity' or
UrlCategory contains 'Pornography' or
UrlCategory contains 'Proxy/Anonymizer' or
UrlCategory contains 'Sexuality' or
UrlCategory contains 'Tasteless' or
UrlCategory contains 'Terrorism' or
UrlCategory contains 'Web Spam' or
UrlCategory contains 'German Youth Protection' or
UrlCategory contains 'Illegal Activities' or
UrlCategory contains 'Lingerie/Bikini' or
UrlCategory contains 'Weapons'
Stage 5: project
| project TimeGenerated, SrcIpAddr, DstIpAddr, UrlOriginal, Identities
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DvcAction | eq |
| field:"DvcAction" kind:eq value:"Allowed" |
EventType | eq |
| field:"EventType" kind:eq value:"proxylogs" |
UrlCategory | contains |
| field:"UrlCategory" kind:contains |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | project |
Identities | project |
SrcIpAddr | project |
TimeGenerated | project |
UrlOriginal | project |
Cisco Cloud Security - URI contains IP address
#'Malware can use IP address to communicate with C2.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control | |
| Exfiltration |
Rule Body
id: ee1818ec-5f65-4991-b711-bcf2ab7e36c3
name: Cisco Cloud Security - URI contains IP address
description: |
'Malware can use IP address to communicate with C2.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CiscoUmbrellaDataConnector
dataTypes:
- Cisco_Umbrella_proxy_CL
queryFrequency: 10m
queryPeriod: 10m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
- Exfiltration
relevantTechniques:
- T1071
- T1567
query: |
Cisco_Umbrella
| where EventType == 'proxylogs'
| where DvcAction =~ 'Allowed'
| where UrlOriginal matches regex @'\Ahttp:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*'
| project TimeGenerated, SrcIpAddr, DstIpAddr, UrlOriginal, Identities
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: UrlOriginal
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
version: 1.1.2
kind: Scheduled
Stages and Predicates
Stage 1: source
Cisco_Umbrella
Stage 2: where
| where EventType == 'proxylogs'
Stage 3: where
| where DvcAction =~ 'Allowed'
Stage 4: where
| where UrlOriginal matches regex @'\Ahttp:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}.*'
Stage 5: project
| project TimeGenerated, SrcIpAddr, DstIpAddr, UrlOriginal, Identities
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DvcAction | eq |
| field:"DvcAction" kind:eq value:"Allowed" |
EventType | eq |
| field:"EventType" kind:eq value:"proxylogs" |
UrlOriginal | regex_match |
| field:"UrlOriginal" kind:regex_match value:"\Ahttp:\/\/\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}.*" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | project |
Identities | project |
SrcIpAddr | project |
TimeGenerated | project |
UrlOriginal | project |
Cisco Duo - Multiple users deleted
#'Detects when multiple users were deleted.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 6e4f9031-91d3-4fa1-8baf-624935f04ad8
name: Cisco Duo - Multiple users deleted
description: |
'Detects when multiple users were deleted.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: CiscoDuoActivityConnectorDefinition
dataTypes:
- DuoActivity_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Impact
relevantTechniques:
- T1531
query: |
DuoActivity_CL
| where ActionName =~ "user_delete"
| summarize count() by ActorName, bin(TimeGenerated, 10m)
| where count_ > 1
| extend AccountCustomEntity = ActorName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
DuoActivity_CL
Stage 2: where
| where ActionName =~ "user_delete"
Stage 3: summarize
| summarize count() by ActorName, bin(TimeGenerated, 10m)
Stage 4: where
| where count_ > 1
Stage 5: extend
| extend AccountCustomEntity = ActorName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionName | eq |
| field:"ActionName" kind:eq value:"user_delete" |
count_ | gt |
| field:"count_" kind:gt value:"1" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActorName | summarize |
AccountCustomEntity | extend |
Employee account deleted
#'This rule will monitor for any employee accounts being deleted. Deleting an employee account can have a big potential impact as all of the data for that user will be removed.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Impact |
Rule Body
id: 8a2cc466-342d-4ebb-8871-f9e1d83a24a5
name: Employee account deleted
description: |
'This rule will monitor for any employee accounts being deleted.
Deleting an employee account can have a big potential impact as all of the data for that user will be removed.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: LastPass
dataTypes:
- LastPassNativePoller_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
eventGroupingSettings:
aggregationKind: AlertPerResult
tactics:
- Impact
relevantTechniques:
- T1485
query: |
LastPassNativePoller_CL
| where Action_s == "Employee Account Deleted" or Action_s == "Remove User From Company"
| extend AccountCustomEntity = Username_s, IPCustomEntity = IP_Address_s
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
kind: Scheduled
Stages and Predicates
Stage 1: source
LastPassNativePoller_CL
Stage 2: where
| where Action_s == "Employee Account Deleted" or Action_s == "Remove User From Company"
Stage 3: extend
| extend AccountCustomEntity = Username_s, IPCustomEntity = IP_Address_s
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Action_s | eq |
| field:"Action_s" kind:eq |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountCustomEntity | extend |
IPCustomEntity | extend |
External Upstream Source Added to Azure DevOps Feed
#'The detection looks for new external sources added to an Azure DevOps feed. An allow list can be customized to explicitly allow known good sources. An attacker could look to add a malicious feed in order to inject malicious packages into a build pipeline.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rule Body
id: adc32a33-1cd6-46f5-8801-e3ed8337885f
name: External Upstream Source Added to Azure DevOps Feed
description: |
'The detection looks for new external sources added to an Azure DevOps feed. An allow list can be customized to explicitly allow known good sources.
An attacker could look to add a malicious feed in order to inject malicious packages into a build pipeline.'
severity: Medium
status: Available
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1199
query: |
// Add any known allowed sources and source locations to the filter below (the NuGet Gallery has been added here as an example).
let allowed_sources = dynamic(["NuGet Gallery"]);
let allowed_locations = dynamic(["https://api.nuget.org/v3/index.json"]);
ADOAuditLogs
// Look for feeds created or modified at either the organization or project level
| where OperationName matches regex "Artifacts.Feed.(Org|Project).Modify"
| where Details has "UpstreamSources, added"
| extend FeedName = tostring(Data.FeedName)
| extend FeedId = tostring(Data.FeedId)
| extend UpstreamsAdded = Data.UpstreamsAdded
// As multiple feeds may be added expand these out
| mv-expand UpstreamsAdded
// Only focus on external feeds
| where UpstreamsAdded.UpstreamSourceType !~ "internal"
| extend SourceLocation = tostring(UpstreamsAdded.Location)
| extend SourceName = tostring(UpstreamsAdded.Name)
// Exclude sources and locations in the allow list
| where SourceLocation !in (allowed_locations) and SourceName !in (allowed_sources)
| extend SourceProtocol = tostring(UpstreamsAdded.Protocol)
| extend SourceStatus = tostring(UpstreamsAdded.Status)
| project-reorder TimeGenerated, OperationName, ScopeDisplayName, ProjectName, FeedName, SourceName, SourceLocation, SourceProtocol, ActorUPN, UserAgent, IpAddress
| extend timestamp = TimeGenerated
| extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorUPN
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IpAddress
version: 1.0.4
kind: Scheduled
Stages and Predicates
Parameters
let allowed_sources = dynamic(["NuGet Gallery"]);
let allowed_locations = dynamic(["https://api.nuget.org/v3/index.json"]);
Stage 1: source
ADOAuditLogs
Stage 2: where
| where OperationName matches regex "Artifacts.Feed.(Org|Project).Modify"
Stage 3: where
| where Details has "UpstreamSources, added"
Stage 4: extend (3 consecutive steps)
| extend FeedName = tostring(Data.FeedName)
| extend FeedId = tostring(Data.FeedId)
| extend UpstreamsAdded = Data.UpstreamsAdded
Stage 5: mv-expand
| mv-expand UpstreamsAdded
Stage 6: where
| where UpstreamsAdded.UpstreamSourceType !~ "internal"
Stage 7: extend
| extend SourceLocation = tostring(UpstreamsAdded.Location)
Stage 8: extend
| extend SourceName = tostring(UpstreamsAdded.Name)
Stage 9: where
| where SourceLocation !in (allowed_locations) and SourceName !in (allowed_sources)
Stage 10: extend
| extend SourceProtocol = tostring(UpstreamsAdded.Protocol)
Stage 11: extend
| extend SourceStatus = tostring(UpstreamsAdded.Status)
Stage 12: project-reorder
| project-reorder TimeGenerated, OperationName, ScopeDisplayName, ProjectName, FeedName, SourceName, SourceLocation, SourceProtocol, ActorUPN, UserAgent, IpAddress
Stage 13: extend
| extend timestamp = TimeGenerated
Stage 14: extend
| extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Details | match |
| field:"Details" kind:match value:"UpstreamSources, added" |
OperationName | regex_match |
| field:"OperationName" kind:regex_match value:"Artifacts.Feed.(Org|Project).Modify" |
UpstreamsAdded.UpstreamSourceType | ne |
| field:"UpstreamsAdded.UpstreamSourceType" kind:ne value:"internal" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SourceLocation | eq | allowed_locations | excludes:SourceLocation field:"SourceLocation" value:"allowed_locations" |
SourceName | eq | allowed_sources | excludes:SourceName field:"SourceName" value:"allowed_sources" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
FeedName | extend |
FeedId | extend |
UpstreamsAdded | extend |
SourceLocation | extend |
SourceName | extend |
SourceProtocol | extend |
SourceStatus | extend |
timestamp | extend |
AccountName | extend |
AccountUPNSuffix | extend |
Guardian- Code Detection Policy Violation Detection
#'This alert creates an incident when Code Detection Policy Violation detected from the Guardian.'
Rule Body
id: 90f50a0b-6903-4ddc-adcc-c13ebb77ffa7
name: Guardian- Code Detection Policy Violation Detection
description: |
'This alert creates an incident when Code Detection Policy Violation detected from the Guardian.'
severity: Low
status: Available
requiredDataConnectors:
- connectorId: BoschAIShield
dataTypes:
- Guardian
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics: []
relevantTechniques: []
query: |
Guardian
| where PolicyViolatedControlFeature =~ 'Code Detection'
| 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- Code Detection Policy Violation detection
alertDescriptionFormat: |
This query detects Code Detection 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 =~ 'Code Detection'
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:"Code Detection" |
Severity | eq |
| field:"Severity" kind:eq value:"Low" |
Illumio VEN Clone Detection Rule
#'Create Microsoft Sentinel Incident When A Cloned Ven Is Detected'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Defense Impairment |
Rule Body
id: b3c4b8f4-c12c-471e-9999-023c05852276
name: Illumio VEN Clone Detection Rule
description: |
'Create Microsoft Sentinel Incident When A Cloned Ven Is Detected'
severity: High
status: Available
requiredDataConnectors:
- connectorId: IllumioSaaSDataConnector
dataTypes:
- Illumio_Auditable_Events_CL
- connectorId: SyslogAma
datatypes:
- Syslog
queryFrequency: 60m
queryPeriod: 60m
triggerOperator: gt
triggerThreshold: 0
tactics:
- DefenseEvasion
relevantTechniques:
- T1562
query: |
Illumio_Auditable_Events_CL
| union IllumioSyslogAuditEvents
| where event_type has 'agent.clone_detected'
| extend hostname = created_by.agent.hostname,
ven_href = created_by.ven.href
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: hostname
alertDetailsOverride:
alertDisplayNameFormat: |
Illumio VEN Clone Detection Incident for {{hostname}}
alertDescriptionFormat: |
Illumio VEN Clone Detection for {{hostname}} generated at {{TimeGenerated}}
version: 1.0.6
kind: Scheduled
Stages and Predicates
Stage 1: source
Illumio_Auditable_Events_CL
Stage 2: union
union of 1 branches
Stage 3: source
IllumioSyslogAuditEvents
Stage 4: where
where event_type has "agent.clone_detected"
Stage 5: extend
extend hostname, ven_href
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
event_type | match |
| field:"event_type" kind:match value:"agent.clone_detected" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
hostname | extend |
ven_href | extend |
Illusive Incidents Analytic Rule
#'Create a Microsoft Sentinel incident upon a new Illusive alert (incident) and associate all related Illusive events to the relevant Microsoft Sentinel alert. This is done by filtering and processing Illusive Syslog messages.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Privilege Escalation | |
| Stealth | |
| Lateral Movement |
Rule Body
id: 1a7dbcf6-21a2-4255-84b2-c8dbbdca4630
name: Illusive Incidents Analytic Rule
description: |
'Create a Microsoft Sentinel incident upon a new Illusive alert (incident) and associate all related Illusive events to the relevant Microsoft Sentinel alert. This is done by filtering and processing Illusive Syslog messages.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Illusive
dataTypes:
- CommonSecurityLog
- connectorId: illusiveAttackManagementSystemAma
dataTypes:
- CommonSecurityLog
- connectorId: CefAma
dataTypes:
- CommonSecurityLog
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- PrivilegeEscalation
- DefenseEvasion
- CredentialAccess
- LateralMovement
relevantTechniques:
- T1078
- T1098
- T1548
- T1021
query: |
CommonSecurityLog
| where DeviceProduct == "illusive"
| extend DeviceCustomNumber2 = coalesce(column_ifexists("FieldDeviceCustomNumber2", long(null)),DeviceCustomNumber2)
| summarize arg_max(TimeGenerated, *) by DeviceCustomNumber2, AdditionalExtensions, TimeGenerated
| extend Category = extract(@'cat=([^;]+)(\;|$)', 1, AdditionalExtensions), HasForensics = extract(@'cs7=([^;]+)(\;|$)', 1, AdditionalExtensions)
| extend Category = coalesce(column_ifexists("DeviceEventCategory",""),Category)
| where Category == "illusive:alerts"
| extend IncidentId = DeviceCustomNumber2, IncidentURL = DeviceCustomString5
| project TimeGenerated, SourceIP, SourceHostName,Computer , DeviceEventClassID ,HasForensics ,SourceUserName, Activity, DeviceAddress, DestinationHostName, DestinationUserName, IncidentId, IncidentURL
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: SourceHostName
- entityType: Host
fieldMappings:
- identifier: OMSAgentID
columnName: Computer
customDetails:
IllusiveIncidentId: IncidentId
HasForensics: HasForensics
Account: SourceUserName
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: |
Illusive Incident: {{IncidentId}}
alertDescriptionFormat: |
Illusive Incident {{IncidentId}} generated at {{TimeGenerated}}
version: 1.0.5
kind: Scheduled
Stages and Predicates
Stage 1: source
CommonSecurityLog
Stage 2: where
| where DeviceProduct == "illusive"
Stage 3: extend
| extend DeviceCustomNumber2 = coalesce(column_ifexists("FieldDeviceCustomNumber2", long(null)),DeviceCustomNumber2)
Stage 4: summarize
| summarize arg_max(TimeGenerated, *) by DeviceCustomNumber2, AdditionalExtensions, TimeGenerated
Stage 5: extend
| extend Category = extract(@'cat=([^;]+)(\;|$)', 1, AdditionalExtensions), HasForensics = extract(@'cs7=([^;]+)(\;|$)', 1, AdditionalExtensions)
Stage 6: extend
| extend Category = coalesce(column_ifexists("DeviceEventCategory",""),Category)
Stage 7: where
| where Category == "illusive:alerts"
Stage 8: extend
| extend IncidentId = DeviceCustomNumber2, IncidentURL = DeviceCustomString5
Stage 9: project
| project TimeGenerated, SourceIP, SourceHostName,Computer , DeviceEventClassID ,HasForensics ,SourceUserName, Activity, DeviceAddress, DestinationHostName, DestinationUserName, IncidentId, IncidentURL
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | eq |
| field:"Category" kind:eq value:"illusive:alerts" |
DeviceProduct | eq |
| field:"DeviceProduct" kind:eq value:"illusive" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Activity | project |
Computer | project |
DestinationHostName | project |
DestinationUserName | project |
DeviceAddress | project |
DeviceEventClassID | project |
HasForensics | project |
IncidentId | project |
IncidentURL | project |
SourceHostName | project |
SourceIP | project |
SourceUserName | project |
TimeGenerated | project |
Pathlock TDnR - GRC Access Control Change Documents
#Detects changes to SAP GRC (Governance, Risk, and Compliance) access control configuration, forwarded by Pathlock Threat Detection and Response. Modifications to GRC settings may indicate attempts to bypass segregation of duties controls or disable risk monitoring.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Privilege Escalation |
Rule Body
id: 2a3b4c5d-6e7f-4a0b-8c1d-2e3f4a5b6c10
name: Pathlock TDnR - GRC Access Control Change Documents
kind: Scheduled
description: >-
Detects changes to SAP GRC (Governance, Risk, and Compliance) access control configuration,
forwarded by Pathlock Threat Detection and Response. Modifications to GRC settings may indicate
attempts to bypass segregation of duties controls or disable risk monitoring.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Pathlock_TDnR
dataTypes:
- Pathlock_TDnR_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- PrivilegeEscalation
relevantTechniques:
- T1548
query: |
Pathlock_TDnR_CL
| where DataSource == "CHANGEDOC_GRAC"
| 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 == "CHANGEDOC_GRAC"
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:"CHANGEDOC_GRAC" |
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 |
Potential Password Spray Attack (Uses Authentication Normalization)
#This query searches for failed attempts to log in from more than 15 various users within a 5 minute timeframe from the same source. This is a potential indication of a password spray attack To use this analytics rule, make sure you have deployed the ASIM normalization parsers
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Telemetry Coverage
Rule Body
id: 6a2e2ff4-5568-475e-bef2-b95f12b9367b
name: Potential Password Spray Attack (Uses Authentication Normalization)
description: |
'This query searches for failed attempts to log in from more than 15 various users within a 5 minute timeframe from the same source. This is a potential indication of a password spray attack
To use this analytics rule, make sure you have deployed the [ASIM normalization parsers](https://aka.ms/ASimAuthentication)'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
tags:
- Id: e27dd7e5-4367-4c40-a2b7-fcd7e7a8a508
version: 1.0.0
- Schema: ASIMAuthentication
SchemaVersion: 0.1.0
query: |
let FailureThreshold = 15;
imAuthentication
| where EventType== 'Logon' and EventResult== 'Failure'
// reason: creds
| where EventResultDetails in ('No such user or password', 'Incorrect password')
| summarize UserCount=dcount(TargetUserId), Vendors=make_set(EventVendor), Products=make_set(EventVendor)
, Users = make_set(TargetUserId,100)
by SrcDvcIpAddr, SrcGeoCountry, bin(TimeGenerated, 5m)
| where UserCount > FailureThreshold
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcDvcIpAddr
version: 1.1.3
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Ofer Shezaf
support:
tier: Community
categories:
domains: [ "Security - Others", "Identity" ]
Stages and Predicates
Parameters
let FailureThreshold = 15;
Stage 1: source
imAuthentication
Stage 2: where
| where EventType== 'Logon' and EventResult== 'Failure'
Stage 3: where
| where EventResultDetails in ('No such user or password', 'Incorrect password')
Stage 4: summarize
| summarize UserCount=dcount(TargetUserId), Vendors=make_set(EventVendor), Products=make_set(EventVendor)
, Users = make_set(TargetUserId,100)
by SrcDvcIpAddr, SrcGeoCountry, bin(TimeGenerated, 5m)
Stage 5: where
| where UserCount > FailureThreshold
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventResult | eq |
| field:"EventResult" kind:eq value:"Failure" |
EventResultDetails | in |
| field:"Status" kind:in |
EventType | eq |
| field:"ElevatedToken" kind:eq value:"Logon" |
UserCount | gt |
| field:"UserCount" kind:gt value:"15" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Products | summarize |
SrcDvcIpAddr | summarize |
SrcGeoCountry | summarize |
UserCount | summarize |
Users | summarize |
Vendors | summarize |
Registry Persistence via AppInit DLLs Modification
#Adversaries may establish persistence and/or elevate privileges by executing malicious content triggered by AppInit DLLs loaded into processes. Dynamic-link libraries (DLLs) that are specified in the AppInit_DLLs value in the Registry keys HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows or HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows are loaded by user32.dll into every process that loads user32.dll. In practice this is nearly every program, since user32.dll is a very common library. Ref: https://attack.mitre.org/techniques/T1546/010/
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 13: RegistryEvent (Value Set) |
Rule Body
id: 9367dff0-941d-44e2-8875-cb48570c7add
name: Registry Persistence via AppInit DLLs Modification
description: |
'Adversaries may establish persistence and/or elevate privileges by executing malicious content triggered by AppInit DLLs loaded into processes.
Dynamic-link libraries (DLLs) that are specified in the AppInit_DLLs value in the Registry keys HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows or HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows are loaded by user32.dll into every process that loads user32.dll. In practice this is nearly every program, since user32.dll is a very common library.
Ref: https://attack.mitre.org/techniques/T1546/010/'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1546.010
query: |
Event
| where EventLog =~ "Microsoft-Windows-Sysmon/Operational" and EventID in (13)
| parse EventData with * 'TargetObject">' TargetObject "<" * 'Details">' Details "<" *
| where TargetObject has "\\Windows NT\\CurrentVersion\\Windows\\AppInit_DLLs"
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by EventID, Computer, TargetObject, Details
| extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
entityMappings:
- entityType: RegistryKey
fieldMappings:
- identifier: Key
columnName: TargetObject
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.0.3
kind: Scheduled
Stages and Predicates
Stage 1: source
Event
Stage 2: where
| where EventLog =~ "Microsoft-Windows-Sysmon/Operational" and EventID in (13)
Stage 3: parse
| parse EventData with * 'TargetObject">' TargetObject "<" * 'Details">' Details "<" *
Stage 4: where
| where TargetObject has "\\Windows NT\\CurrentVersion\\Windows\\AppInit_DLLs"
Stage 5: summarize
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by EventID, Computer, TargetObject, Details
Stage 6: extend
| extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventID | in |
| field:"EventID" kind:in value:"13" |
EventLog | eq |
| field:"EventLog" kind:eq value:"Microsoft-Windows-Sysmon/Operational" |
TargetObject | match |
| field:"TargetObject" kind:match value:"\Windows NT\CurrentVersion\Windows\AppInit_DLLs" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Computer | summarize |
Details | summarize |
EndTimeUtc | summarize |
EventID | summarize |
StartTimeUtc | summarize |
TargetObject | summarize |
DnsDomain | extend |
HostName | extend |
SAP BTP - Cloud Integration JDBC data source changes
#Identifies deployment and undeployment of JDBC data source configurations in SAP Cloud Integration. JDBC data sources contain database connection credentials and configuration that enable integration flows to access backend databases. Unauthorized JDBC data source manipulation could indicate: - Attacker adding rogue database connections for data exfiltration - Credential theft by accessing stored database passwords - Modification of connection strings to redirect traffic to attacker-controlled systems
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access | |
| Lateral Movement |
Rule Body
id: b2c3d4e5-6f7a-8b9c-0d1e-2f3a4b5c6d7e
kind: Scheduled
name: SAP BTP - Cloud Integration JDBC data source changes
description: |
Identifies deployment and undeployment of JDBC data source configurations in SAP Cloud Integration.
JDBC data sources contain database connection credentials and configuration that enable
integration flows to access backend databases.
Unauthorized JDBC data source manipulation could indicate:
- Attacker adding rogue database connections for data exfiltration
- Credential theft by accessing stored database passwords
- Modification of connection strings to redirect traffic to attacker-controlled systems
severity: High
status: Available
requiredDataConnectors:
- connectorId: SAPBTPAuditEvents
dataTypes:
- SAPBTPAuditLog_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
- LateralMovement
relevantTechniques:
- T1552
- T1021
query: |
SAPBTPAuditLog_CL
| where Category == "audit.security-events"
| extend data_s = tostring(Message.data),
ipAddress = tostring(Message.ip)
| extend parsedData = parse_json(data_s)
| extend action = tostring(parsedData.action),
objectType = tostring(parsedData.objectType),
objectId = tostring(parsedData.objectId)
| where objectType == "Data Source"
| where action in ("PasswordStore", "PasswordDelete")
| extend normalizedAction = case(
action == "PasswordStore", "deployed",
action == "PasswordDelete", "undeployed",
action
)
| extend MessageText = strcat("JDBC data source '", objectId, "' was ", normalizedAction)
| project
UpdatedOn,
UserName,
MessageText,
DataSourceName = objectId,
Action = action,
Tenant,
ipAddress,
CloudApp = "SAP Cloud Integration"
| extend AccountName = split(UserName, "@")[0], UPNSuffix = split(UserName, "@")[1]
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ipAddress
- entityType: CloudApplication
fieldMappings:
- identifier: Name
columnName: CloudApp
alertDetailsOverride:
alertDisplayNameFormat: 'SAP Cloud Integration: {{MessageText}}'
alertDescriptionFormat: |
{{MessageText}} by {{UserName}} from IP {{ipAddress}}.
JDBC data sources contain database connection credentials. Changes to these configurations
should be carefully reviewed.
This could indicate:
- Legitimate database configuration management
- Unauthorized database connection configuration
- Attacker establishing lateral movement paths to backend systems
customDetails:
DataSourceName: DataSourceName
Action: Action
SourceIP: ipAddress
version: 1.0.1
Stages and Predicates
Stage 1: source
SAPBTPAuditLog_CL
Stage 2: where
| where Category == "audit.security-events"
Stage 3: extend (3 consecutive steps)
| extend data_s = tostring(Message.data),
ipAddress = tostring(Message.ip)
| extend parsedData = parse_json(data_s)
| extend action = tostring(parsedData.action),
objectType = tostring(parsedData.objectType),
objectId = tostring(parsedData.objectId)
Stage 4: where
| where objectType == "Data Source"
Stage 5: where
| where action in ("PasswordStore", "PasswordDelete")
Stage 6: extend
| extend normalizedAction = case(
action == "PasswordStore", "deployed",
action == "PasswordDelete", "undeployed",
action
)
normalizedAction =action == "PasswordStore""deployed"action == "PasswordDelete""undeployed"actionStage 7: extend
| extend MessageText = strcat("JDBC data source '", objectId, "' was ", normalizedAction)
Stage 8: project
| project
UpdatedOn,
UserName,
MessageText,
DataSourceName = objectId,
Action = action,
Tenant,
ipAddress,
CloudApp = "SAP Cloud Integration"
Stage 9: extend
| extend AccountName = split(UserName, "@")[0], UPNSuffix = split(UserName, "@")[1]
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | eq |
| field:"Category" kind:eq value:"audit.security-events" |
action | in |
| field:"action" kind:in |
objectType | eq |
| field:"objectType" kind:eq value:"Data Source" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Action | project |
CloudApp | project |
DataSourceName | project |
MessageText | project |
Tenant | project |
UpdatedOn | project |
UserName | project |
ipAddress | project |
AccountName | extend |
UPNSuffix | extend |
SAP LogServ - HANA DB - Deactivation of Audit Trail
#Identifies deactivation of HANA DB audit log. Source Action: Deactivate Audit Log in HANA DB security defnitions. Data Sources: SAP LogServ - HANA DB (Syslog)
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Persistence | No specific technique |
| Stealth | No specific technique |
| Lateral Movement | No specific technique |
Rule Body
id: 8fb9fb88-693f-4906-8be2-4bb9771418fc
kind: Scheduled
name: SAP LogServ - HANA DB - Deactivation of Audit Trail
description: |
Identifies deactivation of HANA DB audit log.
Source Action: Deactivate Audit Log in HANA DB security defnitions.
*Data Sources: SAP LogServ - HANA DB (Syslog)*
severity: High
status: Available
requiredDataConnectors:
- connectorId: SAPLogServ
dataTypes:
- Syslog
queryFrequency: 10m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- LateralMovement
- DefenseEvasion
relevantTechniques: []
query: |
let AuditTimeAgo = 60m;
Syslog
| where TimeGenerated >= ago(AuditTimeAgo)
| where SourceSystem == "HANADB" and CollectorHostName == "SAPLogServ"
| extend raw_split = split(SyslogMessage, ";")
| extend
event_timestamp__col_0 = tostring(raw_split[0]),
service_name__col_1 = tostring(raw_split[1]),
hostname__col_2 = tostring(raw_split[2]),
sid__col_3 = tostring(raw_split[3]),
instance_number__col_4 = tostring(raw_split[4]),
port_number__col_5 = tostring(raw_split[5]),
database_name__col_6 = tostring(raw_split[6]),
client_ip_address__col_7 = tostring(raw_split[7]),
client_name__col_8 = tostring(raw_split[8]),
client_process_id__col_9 = tostring(raw_split[9]),
client_port_number__col_10 = tostring(raw_split[10]),
policy_name__col_11 = tostring(raw_split[11]),
audit_level__col_12 = tostring(raw_split[12]),
audit_action__col_13 = tostring(raw_split[13]),
session_user__col_14 = tostring(raw_split[14]),
target_schema__col_15 = tostring(raw_split[15]),
target_object__col_16 = tostring(raw_split[16]),
privilege_name__col_17 = tostring(raw_split[17]),
grantable__col_18 = tostring(raw_split[18]),
role_name__col_19 = tostring(raw_split[19]),
target_principal__col_20 = tostring(raw_split[20]),
action_status__col_21 = tostring(raw_split[21]),
component__col_22 = tostring(raw_split[22]),
section__col_23 = tostring(raw_split[23]),
parameter__col_24 = tostring(raw_split[24]),
old_value__col_25 = tostring(raw_split[25]),
new_value__col_26 = tostring(raw_split[26]),
comment__col_27 = tostring(raw_split[27]),
executed_statement__col_28 = tostring(raw_split[28]),
session_id__col_29 = tostring(raw_split[29]),
application_user_name__col_30 = tostring(raw_split[30]),
role_schema_name__col_31 = tostring(raw_split[31]),
grantee_schema_name__col_32 = tostring(raw_split[32]),
origin_database_name__col_33 = tostring(raw_split[33]),
origin_user_name__col_34 = tostring(raw_split[34]),
xs_application_user_name__col_35 = tostring(raw_split[35]),
application_name__col_36 = tostring(raw_split[36]),
statement_user_name__col_37 = tostring(raw_split[37]),
create_time__col_38 = tostring(raw_split[38]),
xsa_message_ip__col_39 = tostring(raw_split[39]),
xsa_tenant__col_40 = tostring(raw_split[40]),
xsa_uuid__col_41 = tostring(raw_split[41]),
xsa_channel__col_42 = tostring(raw_split[42]),
xsa_attachment_id__col_43 = tostring(raw_split[43]),
xsa_attachment_name__col_44 = tostring(raw_split[44]),
xsa_organization_id__col_45 = tostring(raw_split[45]),
xsa_space_id__col_46 = tostring(raw_split[46]),
xsa_instance_id__col_47 = tostring(raw_split[47]),
xsa_binding_id__col_48 = tostring(raw_split[48]),
xsa_object__col_49 = tostring(raw_split[49]),
xsa_data_subject__col_50 = tostring(raw_split[50])
| where
audit_action__col_13 contains "AUDIT CONFIGURATION" and
parameter__col_24 =~ "global_auditing_state" and
new_value__col_26 =~ "false"
| extend AlertRuleUniqueName = 'hanadb-deactivationofaudittrail-logserv'
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: CloudApplication
fieldMappings:
- identifier: AppId
columnName: sid__col_3
- identifier: InstanceName
columnName: database_name__col_6
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: hostname__col_2
- entityType: IP
fieldMappings:
- identifier: Address
columnName: client_ip_address__col_7
alertDetailsOverride:
alertDisplayNameFormat: 'SAP LogServ - HANA DB - Deactivation of Audit Trail'
alertDescriptionFormat: |
{{comment__col_27}}
customDetails:
SAP_User: session_user__col_14
version: 1.0.1
Stages and Predicates
Parameters
let AuditTimeAgo = 60m;
Stage 1: source
Syslog
Stage 2: where
| where TimeGenerated >= ago(AuditTimeAgo)
Stage 3: where
| where SourceSystem == "HANADB" and CollectorHostName == "SAPLogServ"
Stage 4: extend
| extend raw_split = split(SyslogMessage, ";")
Stage 5: extend
| extend
event_timestamp__col_0 = tostring(raw_split[0]),
service_name__col_1 = tostring(raw_split[1]),
hostname__col_2 = tostring(raw_split[2]),
sid__col_3 = tostring(raw_split[3]),
instance_number__col_4 = tostring(raw_split[4]),
port_number__col_5 = tostring(raw_split[5]),
database_name__col_6 = tostring(raw_split[6]),
client_ip_address__col_7 = tostring(raw_split[7]),
client_name__col_8 = tostring(raw_split[8]),
client_process_id__col_9 = tostring(raw_split[9]),
client_port_number__col_10 = tostring(raw_split[10]),
policy_name__col_11 = tostring(raw_split[11]),
audit_level__col_12 = tostring(raw_split[12]),
audit_action__col_13 = tostring(raw_split[13]),
session_user__col_14 = tostring(raw_split[14]),
target_schema__col_15 = tostring(raw_split[15]),
target_object__col_16 = tostring(raw_split[16]),
privilege_name__col_17 = tostring(raw_split[17]),
grantable__col_18 = tostring(raw_split[18]),
role_name__col_19 = tostring(raw_split[19]),
target_principal__col_20 = tostring(raw_split[20]),
action_status__col_21 = tostring(raw_split[21]),
component__col_22 = tostring(raw_split[22]),
section__col_23 = tostring(raw_split[23]),
parameter__col_24 = tostring(raw_split[24]),
old_value__col_25 = tostring(raw_split[25]),
new_value__col_26 = tostring(raw_split[26]),
comment__col_27 = tostring(raw_split[27]),
executed_statement__col_28 = tostring(raw_split[28]),
session_id__col_29 = tostring(raw_split[29]),
application_user_name__col_30 = tostring(raw_split[30]),
role_schema_name__col_31 = tostring(raw_split[31]),
grantee_schema_name__col_32 = tostring(raw_split[32]),
origin_database_name__col_33 = tostring(raw_split[33]),
origin_user_name__col_34 = tostring(raw_split[34]),
xs_application_user_name__col_35 = tostring(raw_split[35]),
application_name__col_36 = tostring(raw_split[36]),
statement_user_name__col_37 = tostring(raw_split[37]),
create_time__col_38 = tostring(raw_split[38]),
xsa_message_ip__col_39 = tostring(raw_split[39]),
xsa_tenant__col_40 = tostring(raw_split[40]),
xsa_uuid__col_41 = tostring(raw_split[41]),
xsa_channel__col_42 = tostring(raw_split[42]),
xsa_attachment_id__col_43 = tostring(raw_split[43]),
xsa_attachment_name__col_44 = tostring(raw_split[44]),
xsa_organization_id__col_45 = tostring(raw_split[45]),
xsa_space_id__col_46 = tostring(raw_split[46]),
xsa_instance_id__col_47 = tostring(raw_split[47]),
xsa_binding_id__col_48 = tostring(raw_split[48]),
xsa_object__col_49 = tostring(raw_split[49]),
xsa_data_subject__col_50 = tostring(raw_split[50])
Stage 6: where
| where
audit_action__col_13 contains "AUDIT CONFIGURATION" and
parameter__col_24 =~ "global_auditing_state" and
new_value__col_26 =~ "false"
Stage 7: extend
| extend AlertRuleUniqueName = 'hanadb-deactivationofaudittrail-logserv'
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
CollectorHostName | eq |
| field:"CollectorHostName" kind:eq value:"SAPLogServ" |
SourceSystem | eq |
| field:"SourceSystem" kind:eq value:"HANADB" |
audit_action__col_13 | contains |
| field:"audit_action__col_13" kind:contains value:"AUDIT CONFIGURATION" |
new_value__col_26 | eq |
| field:"new_value__col_26" kind:eq value:"false" |
parameter__col_24 | eq |
| field:"parameter__col_24" kind:eq value:"global_auditing_state" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
raw_split | extend |
action_status__col_21 | extend |
application_name__col_36 | extend |
application_user_name__col_30 | extend |
audit_action__col_13 | extend |
audit_level__col_12 | extend |
client_ip_address__col_7 | extend |
client_name__col_8 | extend |
client_port_number__col_10 | extend |
client_process_id__col_9 | extend |
comment__col_27 | extend |
component__col_22 | extend |
create_time__col_38 | extend |
database_name__col_6 | extend |
event_timestamp__col_0 | extend |
executed_statement__col_28 | extend |
grantable__col_18 | extend |
grantee_schema_name__col_32 | extend |
hostname__col_2 | extend |
instance_number__col_4 | extend |
new_value__col_26 | extend |
old_value__col_25 | extend |
origin_database_name__col_33 | extend |
origin_user_name__col_34 | extend |
parameter__col_24 | extend |
policy_name__col_11 | extend |
port_number__col_5 | extend |
privilege_name__col_17 | extend |
role_name__col_19 | extend |
role_schema_name__col_31 | extend |
section__col_23 | extend |
service_name__col_1 | extend |
session_id__col_29 | extend |
session_user__col_14 | extend |
sid__col_3 | extend |
statement_user_name__col_37 | extend |
target_object__col_16 | extend |
target_principal__col_20 | extend |
target_schema__col_15 | extend |
xs_application_user_name__col_35 | extend |
xsa_attachment_id__col_43 | extend |
xsa_attachment_name__col_44 | extend |
xsa_binding_id__col_48 | extend |
xsa_channel__col_42 | extend |
xsa_data_subject__col_50 | extend |
xsa_instance_id__col_47 | extend |
xsa_message_ip__col_39 | extend |
xsa_object__col_49 | extend |
xsa_organization_id__col_45 | extend |
xsa_space_id__col_46 | extend |
xsa_tenant__col_40 | extend |
xsa_uuid__col_41 | extend |
AlertRuleUniqueName | extend |
SUNBURST suspicious SolarWinds child processes (Normalized Process Events)
#Identifies suspicious child processes of SolarWinds.Orion.Core.BusinessLayer.dll that may be evidence of the SUNBURST backdoor References: - https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html - https://gist.github.com/olafhartong/71ffdd4cab4b6acd5cbcd1a0691ff82f To use this analytics rule, make sure you have deployed the ASIM normalization parsers'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution | |
| Persistence |
Telemetry Coverage
Rule Body
id: 631d02df-ab51-46c1-8d72-32d0cfec0720
name: SUNBURST suspicious SolarWinds child processes (Normalized Process Events)
description: |
Identifies suspicious child processes of SolarWinds.Orion.Core.BusinessLayer.dll that may be evidence of the SUNBURST backdoor
References:
- https://www.fireeye.com/blog/threat-research/2020/12/evasive-attacker-leverages-solarwinds-supply-chain-compromises-with-sunburst-backdoor.html
- https://gist.github.com/olafhartong/71ffdd4cab4b6acd5cbcd1a0691ff82f
To use this analytics rule, make sure you have deployed the [ASIM normalization parsers](https://aka.ms/ASimProcessEvent)'
severity: Medium
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- Persistence
relevantTechniques:
- T1059
- T1543
tags:
- Id: 4a3073ac-7383-48a9-90a8-eb6716183a54
version: 1.0.0
- Schema: ASIMProcessEvent
SchemaVersion: 0.1.0
- Solorigate
- NOBELIUM
query: |
let excludeProcs = dynamic([@"\SolarWinds\Orion\APM\APMServiceControl.exe", @"\SolarWinds\Orion\ExportToPDFCmd.Exe", @"\SolarWinds.Credentials\SolarWinds.Credentials.Orion.WebApi.exe", @"\SolarWinds\Orion\Topology\SolarWinds.Orion.Topology.Calculator.exe", @"\SolarWinds\Orion\Database-Maint.exe", @"\SolarWinds.Orion.ApiPoller.Service\SolarWinds.Orion.ApiPoller.Service.exe", @"\Windows\SysWOW64\WerFault.exe"]);
imProcessCreate
| where Process hassuffix 'solarwinds.businesslayerhost.exe'
| where not(Process has_any (excludeProcs))
| extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
| extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)
| project-away DomainIndex
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: ActorUsername
- identifier: Name
columnName: AccountName
- identifier: NTDomain
columnName: AccountNTDomain
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Dvc
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: FileHash
fieldMappings:
- identifier: Algorithm
columnName: AlgorithmType
- identifier: Value
columnName: TargetFileMD5
version: 1.1.6
kind: Scheduled
metadata:
source:
kind: Community
author:
name: Yuval Naor
support:
tier: Community
categories:
domains: [ "Security - 0-day Vulnerability" ]
Stages and Predicates
Let Binding: excludeProcs
let excludeProcs = dynamic([@"\SolarWinds\Orion\APM\APMServiceControl.exe", @"\SolarWinds\Orion\ExportToPDFCmd.Exe", @"\SolarWinds.Credentials\SolarWinds.Credentials.Orion.WebApi.exe", @"\SolarWinds\Orion\Topology\SolarWinds.Orion.Topology.Calculator.exe", @"\SolarWinds\Orion\Database-Maint.exe", @"\SolarWinds.Orion.ApiPoller.Service\SolarWinds.Orion.ApiPoller.Service.exe", @"\Windows\SysWOW64\WerFault.exe"]);
Stage 1: source
imProcessCreate
Stage 2: where
| where Process hassuffix 'solarwinds.businesslayerhost.exe'
Stage 3: where
| where not(Process has_any (excludeProcs))
Stage 4: extend (3 consecutive steps)
| extend AccountName = tostring(split(ActorUsername, @'\')[1]), AccountNTDomain = tostring(split(ActorUsername, @'\')[0])
| extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)
Stage 5: project-away
| project-away DomainIndex
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Process | ends_with |
| field:"Process" kind:ends_with value:"solarwinds.businesslayerhost.exe" |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Process | match | \SolarWinds\Orion\APM\APMServiceControl.exe, \SolarWinds\Orion\ExportToPDFCmd.Exe, \SolarWinds.Credentials\SolarWinds.Credentials.Orion.WebApi.exe, \SolarWinds\Orion\Topology\SolarWinds.Orion.Topology.Calculator.exe, \SolarWinds\Orion\Database-Maint.exe, \SolarWinds.Orion.ApiPoller.Service\SolarWinds.Orion.ApiPoller.Service.exe, \Windows\SysWOW64\WerFault.exe | excludes:Process |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AccountNTDomain | extend |
AccountName | extend |
HostName | extend |
HostNameDomain | extend |
Tenable.ad user accounts issues
#'Searches for triggered Indicators of Exposures related to user accounts issues.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Credential Access |
Rule Body
id: 4f8ed6f3-8815-437d-9462-f0def9dc70d6
name: Tenable.ad user accounts issues
description: |
'Searches for triggered Indicators of Exposures related to user accounts issues.'
severity: Low
requiredDataConnectors:
- connectorId: Tenable.ad
dataTypes:
- Tenable_ad_CL
queryFrequency: 2h
queryPeriod: 2h
triggerOperator: gt
triggerThreshold: 0
tactics:
- CredentialAccess
relevantTechniques:
- T1110
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
let SeverityTable=datatable(Severity:string,Level:int) [
"low", 1,
"medium", 2,
"high", 3,
"critical", 4
];
let codeNameList = datatable(Codename:string)["C-ACCOUNTS-DANG-SID-HISTORY", "C-PRE-WIN2000-ACCESS-MEMBERS", "C-PASSWORD-DONT-EXPIRE", "C-SLEEPING-ACCOUNTS", "C-DANG-PRIMGROUPID", "C-PASSWORD-NOT-REQUIRED", "C-USER-PASSWORD"];
afad_parser
| where MessageType == 0 and Codename in~ (codeNameList)
| lookup kind=leftouter SeverityTable on Severity
| order by Level
| 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
Let Binding: SeverityTable
let SeverityTable = datatable(Severity:string,Level:int) [
"low", 1,
"medium", 2,
"high", 3,
"critical", 4
];
Let Binding: codeNameList
let codeNameList = datatable(Codename:string)["C-ACCOUNTS-DANG-SID-HISTORY", "C-PRE-WIN2000-ACCESS-MEMBERS", "C-PASSWORD-DONT-EXPIRE", "C-SLEEPING-ACCOUNTS", "C-DANG-PRIMGROUPID", "C-PASSWORD-NOT-REQUIRED", "C-USER-PASSWORD"];
Stage 1: source
afad_parser
Stage 2: where
| where MessageType == 0 and Codename in~ (codeNameList)
Stage 3: kusto:lookup
lookup kind=leftouter (SeverityTable) on Severity
Stage 4: sort
sort by Level
Stage 5: extend
extend DnsDomain, HostName
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Codename | in |
| field:"Codename" kind:in value:"codeNameList" |
MessageType | eq |
| field:"MessageType" kind:eq value:"0" |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DnsDomain | extend |
HostName | extend |
TI Map Domain entity to Dns Events (ASIM DNS Schema)
#Identifies a match in DNS events from any Domain IOC from TI This analytic rule uses ASIM and supports any built-in or custom source that supports the ASIM DNS schema'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Provider | Record / event type |
|---|---|
| Sysmon | Event ID 22: DNSEvent (DNS query) |
Rule Body
id: 7c1ea2e6-6210-412c-92e4-180803a741b4
name: TI Map Domain entity to Dns Events (ASIM DNS Schema)
description: |
Identifies a match in DNS events from any Domain IOC from TI
This analytic rule uses [ASIM](https://aka.ms/AboutASIM) and supports any built-in or custom source that supports the ASIM DNS schema'
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelIndicators
- connectorId: DNS
dataTypes:
- DnsEvents
- connectorId: AzureFirewall
dataTypes:
- AzureDiagnostics
- connectorId: Zscaler
dataTypes:
- CommonSecurityLog
- connectorId: InfobloxNIOS
dataTypes:
- Syslog
- connectorId: GCPDNSDataConnector
dataTypes:
- GCP_DNS_CL
- connectorId: NXLogDnsLogs
dataTypes:
- NXLog_DNS_Server_CL
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
- connectorId: CiscoUmbrellaDataConnector
dataTypes:
- Cisco_Umbrella_dns_CL
- connectorId: Corelight
dataTypes:
- Corelight_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
tags:
- Id: 85aca4d1-5d15-4001-abd9-acb86ca1786a
version: 1.0.0
- Schema: ASIMDns
SchemaVersion: 0.1.1
query: |
let HAS_ANY_MAX = 10000;
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let DomainTIs= ThreatIntelIndicators
// Picking up only IOC's that contain the entities we want
//extract key part of kv pair
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
| extend IndicatorId = tostring(split(Id, "--")[2])
| where IndicatorType == "domain-name"
| extend DomainName = ObservableValue
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend ThreatList = tostring(parse_json(Data).indicator_types[0])
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil));
let Domains = DomainTIs |summarize NDomains=dcount(DomainName), DomainsList=make_set(DomainName)
| project DomainList = iff(NDomains > HAS_ANY_MAX, dynamic([]), DomainsList);
DomainTIs
| project-reorder *, ThreatList, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type
| join (
_Im_Dns(starttime=ago(dt_lookBack), domain_has_any=toscalar(Domains))
| extend DNS_TimeGenerated = TimeGenerated
) on $left.DomainName==$right.DnsQuery
| where DNS_TimeGenerated < ValidUntil
| extend Description = tostring(parse_json(Data).description)
| extend ActivityGroupNames = extract(@"ActivityGroup:(\S+)", 1, tostring(parse_json(Data).labels))
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ValidUntil, Confidence, DNS_TimeGenerated, Dvc, SrcIpAddr, Domain, DnsQuery, DnsQueryType, Url
| extend HostName = tostring(split(Dvc, ".")[0]), DomainIndex = toint(indexof(Dvc, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Dvc, DomainIndex + 1), Dvc)
entityMappings:
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Dvc
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: HostNameDomain
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: DNS
fieldMappings:
- identifier: DomainName
columnName: Domain
customDetails:
LatestIndicatorTime: LatestIndicatorTime
Description: Description
ActivityGroupNames: ActivityGroupNames
IndicatorId: IndicatorId
ExpirationDateTime: ValidUntil
ConfidenceScore: Confidence
DNSRequestTime: DNS_TimeGenerated
SourceIPAddress: SrcIpAddr
DnsQuery: DnsQuery
QueryType: DnsQueryType
version: 1.2.4
kind: Scheduled
Stages and Predicates
Parameters
let HAS_ANY_MAX = 10000;
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
let DomainTIs is inlined into the numbered stages below.
Let Binding: Domains
let Domains = DomainTIs |summarize NDomains=dcount(DomainName), DomainsList=make_set(DomainName)
| project DomainList = iff(NDomains > HAS_ANY_MAX, dynamic([]), DomainsList);
Stages 1 to 8 define let DomainTIs as the rule's main pipeline source. Stages 9 to 16 use it.
Stage 1: source
ThreatIntelIndicators
Stage 2: extend
| extend IndicatorType = replace(@"\[|\]|\""", "", tostring(split(ObservableKey, ":", 0)))
Stage 3: extend
| extend IndicatorId = tostring(split(Id, "--")[2])
Stage 4: where
| where IndicatorType == "domain-name"
Stage 5: extend (4 consecutive steps)
| extend DomainName = ObservableValue
| extend Url = iff(ObservableKey == "url:value", ObservableValue, "")
| extend TrafficLightProtocolLevel = tostring(parse_json(AdditionalFields).TLPLevel)
| extend ThreatList = tostring(parse_json(Data).indicator_types[0])
Stage 6: where
| where TimeGenerated >= ago(ioc_lookBack)
Stage 7: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId, ObservableValue
Stage 8: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 9: project-reorder
DomainTIs
| project-reorder *, ThreatList, IsActive, Tags, TrafficLightProtocolLevel, DomainName, Type
Stage 10: join
| join (
_Im_Dns(starttime=ago(dt_lookBack), domain_has_any=toscalar(Domains))
| extend DNS_TimeGenerated = TimeGenerated
) on $left.DomainName==$right.DnsQuery
Stage 11: where
where DNS_TimeGenerated < ValidUntil
Stage 12: extend
extend Description
Stage 13: extend
extend ActivityGroupNames
Stage 14: project
project ActivityGroupNames, Confidence, DNS_TimeGenerated, Description, DnsQuery, DnsQueryType, Domain, Dvc, IndicatorId, LatestIndicatorTime, SrcIpAddr, Url, ValidUntil
Stage 15: extend
extend DomainIndex, HostName
Stage 16: extend
extend HostNameDomain
HostNameDomain =DomainIndex != -1substring(Dvc, (DomainIndex + 1))DvcIndicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IndicatorType | eq |
| field:"IndicatorType" kind:eq value:"domain-name" |
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 |
|---|---|
ActivityGroupNames | project |
Confidence | project |
DNS_TimeGenerated | project |
Description | project |
DnsQuery | project |
DnsQueryType | project |
Domain | project |
Dvc | project |
IndicatorId | project |
LatestIndicatorTime | project |
SrcIpAddr | project |
Url | project |
ValidUntil | project |
DomainIndex | extend |
HostName | extend |
HostNameDomain | extend |
TI Map IP entity to Cloud App Events
#Identifies compromises and attacks and detect malicious activities in one's IP entity from TI
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | CloudAppEvents action any: Cloud app activity |
Rule Body
id: 16a45aee-5e39-4d1b-b508-40f847c99353
name: TI Map IP entity to Cloud App Events
description: |
'Identifies compromises and attacks and detect malicious activities in one's IP entity from TI'
severity: Medium
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- CloudAppEvents
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelIndicators
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1d;
let ioc_lookBack = 14d;
let IP_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)
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
| extend TIipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity);
// | extend TIipEntity = iff(isempty(TI_ipEntity) and isnotempty(EmailSourceIpAddress), EmailSourceIpAddress, TI_ipEntity);
IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
| join kind=innerunique (
CloudAppEvents
| where TimeGenerated >= ago(dt_lookBack)
| extend CloudAppEvents_TimeGenerated = TimeGenerated) on $left.TI_ipEntity == $right.IPAddress
| where CloudAppEvents_TimeGenerated < ValidUntil
| summarize CloudAppEventsTimeGenerated = argmax(CloudAppEvents_TimeGenerated, *) by Id, IPAddress
| extend
Description = column_ifexists("max_CloudAppEvents_TimeGenerated_Description", ""),
ActivityGroupNames = column_ifexists("max_CloudAppEvents_TimeGenerated_ActivityGroupNames", ""),
ThreatType = column_ifexists("max_CloudAppEvents_TimeGenerated_ThreatType", ""),
ExpirationDateTime = column_ifexists("max_CloudAppEvents_TimeGenerated_ExpirationDateTime", ""),
ConfidenceScore = column_ifexists("max_CloudAppEvents_TimeGenerated_ConfidenceScore", ""),
TI_ipEntity = column_ifexists("max_CloudAppEvents_TimeGenerated_TI_ipEntity", ""),
NetworkDestinationIP = column_ifexists("max_CloudAppEvents_TimeGenerated_NetworkDestinationIP", ""),
NetworkSourceIP = column_ifexists("max_CloudAppEvents_TimeGenerated_NetworkSourceIP", ""),
EmailSourceIPAddress = column_ifexists("max_CloudAppEvents_TimeGenerated_EmailSourceIpAddress", "")
| project CloudAppEventsTimeGenerated, Description, ActivityGroupNames, Id, ThreatType, ExpirationDateTime, ConfidenceScore, TI_ipEntity, NetworkDestinationIP, NetworkSourceIP, EmailSourceIPAddress
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: TI_ipEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: NetworkDestinationIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: NetworkSourceIP
- entityType: IP
fieldMappings:
- identifier: Address
columnName: EmailSourceIPAddress
version: 1.0.8
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1d;
let ioc_lookBack = 14d;
let IP_Indicators is inlined into the numbered stages below.
Stages 1 to 10 define let IP_Indicators as the rule's main pipeline source. Stages 11 to 16 use it.
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: summarize
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by Id, ObservableValue
Stage 8: where
| where IsActive and (ValidUntil > now() or isempty(ValidUntil))
Stage 9: extend
| extend TI_ipEntity = iff(isnotempty(NetworkSourceIP), NetworkSourceIP, NetworkSourceIP)
TI_ipEntity =isnotempty(NetworkSourceIP)NetworkSourceIPNetworkSourceIPStage 10: extend
| extend TIipEntity = iff(isempty(TI_ipEntity) and isnotempty(NetworkSourceIP), NetworkSourceIP, TI_ipEntity)
TIipEntity =isempty(TI_ipEntity) and isnotempty(NetworkSourceIP)NetworkSourceIPTI_ipEntityStage 11: project-reorder
IP_Indicators
| project-reorder *, Tags, TrafficLightProtocolLevel, NetworkSourceIP, Type, TI_ipEntity
Stage 12: join
| join kind=innerunique (
CloudAppEvents
| where TimeGenerated >= ago(dt_lookBack)
| extend CloudAppEvents_TimeGenerated = TimeGenerated) on $left.TI_ipEntity == $right.IPAddress
Stage 13: where
where CloudAppEvents_TimeGenerated < ValidUntil
Stage 14: summarize
summarize CloudAppEventsTimeGenerated by Id, IPAddress
Stage 15: extend
extend ActivityGroupNames, ConfidenceScore, Description, EmailSourceIPAddress, ExpirationDateTime, NetworkDestinationIP, NetworkSourceIP, TI_ipEntity, ThreatType
Stage 16: project
project ActivityGroupNames, CloudAppEventsTimeGenerated, ConfidenceScore, Description, EmailSourceIPAddress, ExpirationDateTime, Id, NetworkDestinationIP, NetworkSourceIP, TI_ipEntity, ThreatType
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 |
|---|---|
ActivityGroupNames | project |
CloudAppEventsTimeGenerated | project |
ConfidenceScore | project |
Description | project |
EmailSourceIPAddress | project |
ExpirationDateTime | project |
Id | project |
NetworkDestinationIP | project |
NetworkSourceIP | project |
TI_ipEntity | project |
ThreatType | project |
TI Map IP Entity to Duo Security
#'This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in DuoSecurity.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Command & Control |
Rule Body
id: d23ed927-5be3-4902-a9c1-85f841eb4fa1
name: TI Map IP Entity to Duo Security
description: |
'This query maps any IP indicators of compromise (IOCs) from threat intelligence (TI), by searching for matches in DuoSecurity.'
severity: Medium
requiredDataConnectors:
- connectorId: ThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: ThreatIntelligenceTaxii
dataTypes:
- ThreatIntelligenceIndicator
- connectorId: CiscoDuoSecurity
dataTypes:
- CiscoDuo
- connectorId: MicrosoftDefenderThreatIntelligence
dataTypes:
- ThreatIntelligenceIndicator
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- CommandAndControl
relevantTechniques:
- T1071
query: |
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
ThreatIntelligenceIndicator
// Picking up only IOC's that contain the entities we want
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
| where TimeGenerated >= ago(ioc_lookBack)
// As there is potentially more than 1 indicator type for matching IP, taking NetworkIP first, then others if that is empty.
// Taking the first non-empty value based on potential IOC match availability
| 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 and ExpirationDateTime > now()
| join (
DuoSecurityAuthentication_CL
| where TimeGenerated >= ago(dt_lookBack)
| where isnotempty(access_device_ip_s)
// renaming time column so it is clear the log this came from
| extend Duo_TimeGenerated = isotimestamp_t
)
on $left.TI_ipEntity == $right.access_device_ip_s
| where TimeGenerated >= ago(ioc_lookBack)
| summarize LatestIndicatorTime = arg_max(TimeGenerated, *) by IndicatorId
| where Active == true and ExpirationDateTime > now()
| project LatestIndicatorTime, Description, ActivityGroupNames, IndicatorId, ThreatType, Url, ExpirationDateTime, ConfidenceScore, Duo_TimeGenerated,
TI_ipEntity, user_name_s, factor_s, result_s, application_name_s, event_type_s, txid_g, user_key_s, access_device_ip_s, access_device_location_city_s, access_device_location_state_s, access_device_location_country_s
| extend timestamp = Duo_TimeGenerated, Name = tostring(split(user_name_s, '@', 0)[0]), UPNSuffix = tostring(split(user_name_s, '@', 1)[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: user_name_s
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: access_device_ip_s
version: 1.0.6
kind: Scheduled
Stages and Predicates
Parameters
let dt_lookBack = 1h;
let ioc_lookBack = 14d;
Stage 1: source
ThreatIntelligenceIndicator
Stage 2: where
| where isnotempty(NetworkIP) or isnotempty(EmailSourceIpAddress) or isnotempty(NetworkDestinationIP) or isnotempty(NetworkSourceIP)
Stage 3: where
| where TimeGenerated >= ago(ioc_lookBack)
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 and ExpirationDateTime > now()
Stage 7: join
| join (
DuoSecurityAuthentication_CL
| where TimeGenerated >= ago(dt_lookBack)
| where isnotempty(access_device_ip_s)
| extend Duo_TimeGenerated = isotimestamp_t
)
on $left.TI_ipEntity == $right.access_device_ip_s
Stage 8: where
where TimeGenerated >= ago(1209600s)
Stage 9: summarize
summarize LatestIndicatorTime by IndicatorId
Stage 10: where
where Active == true
Stage 11: project
project ActivityGroupNames, ConfidenceScore, Description, Duo_TimeGenerated, ExpirationDateTime, IndicatorId, LatestIndicatorTime, TI_ipEntity, ThreatType, Url, access_device_ip_s, access_device_location_city_s, access_device_location_country_s, access_device_location_state_s, application_name_s, event_type_s, factor_s, result_s, txid_g, user_key_s, user_name_s
Stage 12: extend
extend Name, UPNSuffix, timestamp
Indicators
These rows show field, operator, and value matches.
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ActivityGroupNames | project |
ConfidenceScore | project |
Description | project |
Duo_TimeGenerated | project |
ExpirationDateTime | project |
IndicatorId | project |
LatestIndicatorTime | project |
TI_ipEntity | project |
ThreatType | project |
Url | project |
access_device_ip_s | project |
access_device_location_city_s | project |
access_device_location_country_s | project |
access_device_location_state_s | project |
application_name_s | project |
event_type_s | project |
factor_s | project |
result_s | project |
txid_g | project |
user_key_s | project |
user_name_s | project |
Name | extend |
UPNSuffix | extend |
timestamp | extend |
VMware vCenter - Root login
#'Detects when root user login from uncommon IP address.'
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Privilege Escalation |
Rule Body
id: 03e8a895-b5ba-49a0-aed3-f9a997d92fbe
name: VMware vCenter - Root login
description: |
'Detects when root user login from uncommon IP address.'
severity: High
status: Available
requiredDataConnectors:
- connectorId: CustomLogsAma
dataTypes:
- vcenter_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- PrivilegeEscalation
relevantTechniques:
- T1078
query: |
let p_lookback = 14d;
let t_lookback = 1h;
let root_ips = vCenter
| where TimeGenerated between (ago(p_lookback) .. ago(t_lookback))
| where EventType has_all ('UserLoginSessionEvent', 'root', 'logged in')
| summarize make_set(SourceIP,128);
vCenter
| where TimeGenerated > ago(t_lookback)
| where EventType has_all ('UserLoginSessionEvent', 'root', 'logged in')
| where SourceIP !in (root_ips)
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SourceIP
version: 1.0.2
kind: Scheduled
Stages and Predicates
Parameters
let p_lookback = 14d;
let t_lookback = 1h;
Let Binding: root_ips
let root_ips = vCenter
| where TimeGenerated between (ago(p_lookback) .. ago(t_lookback))
| where EventType has_all ('UserLoginSessionEvent', 'root', 'logged in')
| summarize make_set(SourceIP,128);
Stage 1: source
vCenter
Stage 2: where
| where TimeGenerated > ago(t_lookback)
Stage 3: where
| where EventType has_all ('UserLoginSessionEvent', 'root', 'logged in')
Stage 4: where
| where SourceIP !in (root_ips)
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventType | match |
| field:"EventType" kind:match |
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
SourceIP | eq | root_ips | excludes:SourceIP field:"SourceIP" value:"root_ips" |
VTI - High Severity SHA1 Collision Detection
#This will alert when a collision is detected for DeviceFileEvents events with VTI high severity SHA1 IoCs
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Execution |
Telemetry Coverage
| Platform | Record / event type |
|---|---|
| Microsoft Defender for Endpoint | DeviceFileEvents action any: File activity |
Rule Body
id: dbd9e28f-973d-47f3-a8c3-9e18da846870
name: VTI - High Severity SHA1 Collision Detection
version: 1.0.0
kind: Scheduled
description: |-
This will alert when a collision is detected for DeviceFileEvents events with VTI high severity SHA1 IoCs
severity: High
requiredDataConnectors:
- connectorId: VisaThreatIntelligence
dataTypes:
- VisaThreatIntelligenceIOC_CL
- DeviceFileEvents
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
relevantTechniques:
- T1204
query: |-
DeviceFileEvents
| join kind=inner (VisaThreatIntelligenceIOC_CL | where IOCType == "sha1" and SourceSeverity == "High")
on $left.InitiatingProcessSHA1 == $right.Value
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 1h
matchingMethod: AllEntities
suppressionDuration: 1h
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DeviceName
- entityType: FileHash
fieldMappings:
- identifier: Algorithm
columnName: SHA1
- identifier: Value
columnName: InitiatingProcessSHA1
Stages and Predicates
Stage 1: source
DeviceFileEvents
Stage 2: join
| join kind=inner (VisaThreatIntelligenceIOC_CL | where IOCType == "sha1" and SourceSeverity == "High")
on $left.InitiatingProcessSHA1 == $right.Value
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
IOCType | eq |
| field:"IOCType" kind:eq value:"sha1" |
SourceSeverity | eq |
| field:"SourceSeverity" kind:eq value:"High" |
Whisper Security - BGP Route Anomaly with Traffic Spike
#Detects BGP origin AS changes for IP prefixes correlated with network traffic volume spikes. May indicate BGP hijacking or adversary-in-the-middle attacks.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Collection |
Rule Body
id: 1c08a7cb-7ff4-5a37-a961-39c29f8d07bd
name: Whisper Security - BGP Route Anomaly with Traffic Spike
description: |
Detects BGP origin AS changes for IP prefixes correlated with network traffic volume spikes. May indicate BGP hijacking or adversary-in-the-middle attacks.
severity: High
status: Available
requiredDataConnectors:
- connectorId: WhisperSecurityConnector
dataTypes:
- WhisperHistory_CL
queryFrequency: 15m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Collection
relevantTechniques:
- T1557
query: |
// MITRE ATT&CK: T1557 - Adversary-in-the-Middle
// Tactic: Collection
// Detects BGP route origin changes correlated with traffic volume anomalies
let lookbackPeriod = 1d;
let bgpChanges = WhisperHistory_CL
| where TimeGenerated > ago(lookbackPeriod)
| where isnotempty(bgpOrigin)
| serialize
| extend PreviousOrigin = prev(bgpOrigin), PreviousIndicator = prev(indicator)
| where indicator == PreviousIndicator
| where bgpOrigin != PreviousOrigin
| where isnotempty(PreviousOrigin)
| project indicator, bgpPrefix, OldOrigin = PreviousOrigin, NewOrigin = bgpOrigin, ChangeTime = snapshotDate;
let CommonSecurityLogSafe = union isfuzzy=true CommonSecurityLog, (datatable(TimeGenerated:datetime, DestinationIP:string, SentBytes:long, ReceivedBytes:long)[]);
let trafficBaseline = CommonSecurityLogSafe
| where TimeGenerated > ago(lookbackPeriod)
| where isnotempty(DestinationIP)
| summarize BaselineBytes = avg(SentBytes + ReceivedBytes), BaselineCount = count() by DestinationIP, bin(TimeGenerated, 1h)
| summarize AvgBaselineBytes = avg(BaselineBytes), AvgBaselineCount = avg(BaselineCount) by DestinationIP;
let recentTraffic = CommonSecurityLogSafe
| where TimeGenerated > ago(15m)
| where isnotempty(DestinationIP)
| summarize RecentBytes = sum(SentBytes + ReceivedBytes), RecentCount = count() by DestinationIP;
recentTraffic
| join kind=inner (trafficBaseline) on DestinationIP
| where RecentBytes > AvgBaselineBytes * 3 or RecentCount > AvgBaselineCount * 3
| extend TrafficChange = iff(AvgBaselineBytes > 0, round((RecentBytes - AvgBaselineBytes) / AvgBaselineBytes * 100, 2), 0.0)
| join kind=inner (bgpChanges) on $left.DestinationIP == $right.indicator
| project TimeGenerated = ChangeTime, IPAddress = DestinationIP, Prefix = bgpPrefix, OriginASN = NewOrigin, OldOriginASN = OldOrigin, TrafficChange, RecentBytes, AvgBaselineBytes
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPAddress
customDetails:
Prefix: Prefix
OriginASN: OriginASN
TrafficChange: TrafficChange
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let lookbackPeriod = 1d;
let CommonSecurityLogSafe and let recentTraffic are inlined into the numbered stages below.
Let Binding: bgpChanges
let bgpChanges = WhisperHistory_CL
| where TimeGenerated > ago(lookbackPeriod)
| where isnotempty(bgpOrigin)
| serialize
| extend PreviousOrigin = prev(bgpOrigin), PreviousIndicator = prev(indicator)
| where indicator == PreviousIndicator
| where bgpOrigin != PreviousOrigin
| where isnotempty(PreviousOrigin)
| project indicator, bgpPrefix, OldOrigin = PreviousOrigin, NewOrigin = bgpOrigin, ChangeTime = snapshotDate;
Let Binding: trafficBaseline
let trafficBaseline = CommonSecurityLogSafe
| where TimeGenerated > ago(lookbackPeriod)
| where isnotempty(DestinationIP)
| summarize BaselineBytes = avg(SentBytes + ReceivedBytes), BaselineCount = count() by DestinationIP, bin(TimeGenerated, 1h)
| summarize AvgBaselineBytes = avg(BaselineBytes), AvgBaselineCount = avg(BaselineCount) by DestinationIP;
Stage 1: union
union of 2 branches
Stage 2: source
CommonSecurityLog
Stage 3: source
datatable(...)
Stage 4: where
where TimeGenerated > ago(900s)
Stage 5: where
where isnotempty(DestinationIP)
Stage 6: summarize
summarize RecentBytes, RecentCount by DestinationIP
Stage 7: join
join kind=inner (trafficBaseline) on DestinationIP
Stage 8: where
where (RecentBytes > (AvgBaselineBytes * 3) or RecentCount > (AvgBaselineCount * 3))
Stage 9: extend
extend TrafficChange
TrafficChange =AvgBaselineBytes > 0round((((RecentBytes - AvgBaselineBytes) / AvgBaselineBytes) * 100), 2)0.0Stage 10: join
join kind=inner (bgpChanges) on $left.DestinationIP, $right.indicator
Stage 11: project
project AvgBaselineBytes, IPAddress, OldOriginASN, OriginASN, Prefix, RecentBytes, TimeGenerated, TrafficChange
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
DestinationIP | is_not_null | field:"DestinationIP" kind:is_not_null | |
PreviousOrigin | is_not_null | field:"PreviousOrigin" kind:is_not_null | |
bgpOrigin | is_not_null | field:"bgpOrigin" kind:is_not_null |
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AvgBaselineBytes | project |
IPAddress | project |
OldOriginASN | project |
OriginASN | project |
Prefix | project |
RecentBytes | project |
TimeGenerated | project |
TrafficChange | project |
XbowNewAssetDiscovered
#Alerts when a new asset is registered in XBOW for the first time. This is detected by matching assets whose CreatedAt timestamp falls within the current query window, indicating the asset was newly added rather than updated. This helps track shadow IT, new deployments, and any unexpected expansion of the external attack surface.
MITRE ATT&CK Coverage
| Tactic | Techniques |
|---|---|
| Reconnaissance |
Rule Body
id: e4c6a8b2-9d7f-4285-a1e3-6b9c2e4f1a85
name: XbowNewAssetDiscovered
description: |
Alerts when a new asset is registered in XBOW for the first time. This is detected by
matching assets whose CreatedAt timestamp falls within the current query window,
indicating the asset was newly added rather than updated. This helps track shadow IT,
new deployments, and any unexpected expansion of the external attack surface.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: XbowSecurityConnector
dataTypes:
- XbowAssets_CL
queryFrequency: 30m
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Reconnaissance
- Discovery
relevantTechniques:
- T1595
query: |
XbowAssets_CL
| where TimeGenerated > ago(1h)
// Only match assets created within the query window (new, not updated)
| where todatetime(CreatedAt) > ago(1h)
| summarize arg_max(TimeGenerated, *) by AssetId
| project
TimeGenerated,
AssetId,
AssetName,
StartUrl,
Lifecycle,
Sku,
OrganizationId,
CreatedAt,
UpdatedAt,
AssetReachableState,
AssetReachableMessage
eventGroupingSettings:
aggregationKind: AlertPerResult
alertDetailsOverride:
alertDisplayNameFormat: 'XBOW New Asset: {{AssetName}}'
alertDescriptionFormat: 'New asset registered in XBOW: {{AssetName}} ({{StartUrl}}). Reachability: {{AssetReachableState}}'
customDetails:
AssetID: AssetId
AssetName: AssetName
StartUrl: StartUrl
Lifecycle: Lifecycle
Sku: Sku
OrganizationID: OrganizationId
CreatedAt: CreatedAt
AssetReachableState: AssetReachableState
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: 6h
matchingMethod: Selected
groupByCustomDetails:
- AssetID
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: StartUrl
version: 1.0.1
kind: Scheduled
Stages and Predicates
Stage 1: source
XbowAssets_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: where
| where todatetime(CreatedAt) > ago(1h)
Stage 4: summarize
| summarize arg_max(TimeGenerated, *) by AssetId
Stage 5: project
| project
TimeGenerated,
AssetId,
AssetName,
StartUrl,
Lifecycle,
Sku,
OrganizationId,
CreatedAt,
UpdatedAt,
AssetReachableState,
AssetReachableMessage
Output Fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AssetId | project |
AssetName | project |
AssetReachableMessage | project |
AssetReachableState | project |
CreatedAt | project |
Lifecycle | project |
OrganizationId | project |
Sku | project |
StartUrl | project |
TimeGenerated | project |
UpdatedAt | project |