Detection rules › Kusto
Idira - Multiple Failed Actions Followed by Success (15m)
Detects 3+ failed actions against an account followed by a success in a short window, indicating brute-force or credential guessing.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | No specific technique |
Rule body
id: bdf3cf98-d64f-4c55-97e4-43483d6d3237
kind: Scheduled
name: Idira - Multiple Failed Actions Followed by Success (15m)
description: >
Detects 3+ failed actions against an account followed by a success in a short window,
indicating brute-force or credential guessing.
severity: Medium
tactics:
- CredentialAccess
queryFrequency: 5M
queryPeriod: 15M
triggerOperator: GreaterThan
triggerThreshold: 0
query: |
let window = 15m;
let fails =
CyberArk_AuditEvents_CL
| where TimeGenerated > ago(window)
| where actionType has_any ("Fail","Failed","Denied","Error")
or message has_any ("fail","failed","denied","unauthorized")
| summarize Fails=count(), firstFail=min(TimeGenerated), lastFail=max(TimeGenerated) by username, targetAccount;
let success =
CyberArk_AuditEvents_CL
| where TimeGenerated > ago(window)
| where actionType has_any ("Success","Succeeded","Allow","Allowed")
or message has_any ("success","allowed")
| summarize Successes=count(), firstSuccess=min(TimeGenerated) by username, targetAccount;
fails
| where Fails >= 3
| join kind=inner success on username, targetAccount
| join kind=inner (
CyberArk_AuditEvents_CL
| extend cd = parse_json(customData)
| extend cd_client_ip = tostring(cd.client_ip_address),
cd_source_ip = tostring(cd.source_ip_address),
cd_device_os = tostring(cd.device_os),
cd_browser = tostring(cd.browser_name),
cd_geo_city = tostring(cd.geoip_city_name),
cd_geo_country = tostring(cd.geoip_country_name),
cd_target = coalesce(tostring(cd.target), tostring(cd.target_resource), tostring(cd.new_target))
| project username, targetAccount, any_cd_client_ip = cd_client_ip, any_cd_source_ip = cd_source_ip,
any_cd_device_os = cd_device_os, any_cd_browser = cd_browser,
any_cd_geo_city = cd_geo_city, any_cd_geo_country = cd_geo_country, any_cd_target = cd_target
) on username, targetAccount
| project username, targetAccount, Fails, Successes, firstFail, lastFail, firstSuccess,
any_cd_source_ip, any_cd_client_ip, any_cd_device_os, any_cd_browser,
any_cd_geo_city, any_cd_geo_country, any_cd_target
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: username
- entityType: Account
fieldMappings:
- identifier: Name
columnName: targetAccount
suppressionEnabled: false
version: 1.0.0
Stages and Predicates
Stage 0: let
let window = 15m;
let fails = CyberArk_AuditEvents_CL <inlined as stages below>;
let success =
CyberArk_AuditEvents_CL
| where TimeGenerated > ago(window)
| where actionType has_any ("Success","Succeeded","Allow","Allowed")
or message has_any ("success","allowed")
| summarize Successes=count(), firstSuccess=min(TimeGenerated) by username, targetAccount;
Stage 1: source
CyberArk_AuditEvents_CL
Stage 2: where
| where TimeGenerated > ago(window)
Stage 3: where
| where actionType has_any ("Fail","Failed","Denied","Error")
or message has_any ("fail","failed","denied","unauthorized")
Stage 4: summarize
| summarize Fails=count(), firstFail=min(TimeGenerated), lastFail=max(TimeGenerated) by username, targetAccount
Stage 5: where
| where Fails >= 3
Stage 6: join
| join kind=inner success on username, targetAccount
Stage 7: join
| join kind=inner (
CyberArk_AuditEvents_CL
| extend cd = parse_json(customData)
| extend cd_client_ip = tostring(cd.client_ip_address),
cd_source_ip = tostring(cd.source_ip_address),
cd_device_os = tostring(cd.device_os),
cd_browser = tostring(cd.browser_name),
cd_geo_city = tostring(cd.geoip_city_name),
cd_geo_country = tostring(cd.geoip_country_name),
cd_target = coalesce(tostring(cd.target), tostring(cd.target_resource), tostring(cd.new_target))
| project username, targetAccount, any_cd_client_ip = cd_client_ip, any_cd_source_ip = cd_source_ip,
any_cd_device_os = cd_device_os, any_cd_browser = cd_browser,
any_cd_geo_city = cd_geo_city, any_cd_geo_country = cd_geo_country, any_cd_target = cd_target
) on username, targetAccount
Stage 8: project
project Fails, Successes, any_cd_browser, any_cd_client_ip, any_cd_device_os, any_cd_geo_city, any_cd_geo_country, any_cd_source_ip, any_cd_target, firstFail, firstSuccess, lastFail, targetAccount, username
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Fails | ge |
| field:"Fails" kind:ge value:"3" |
actionType | match |
| field:"actionType" kind:match |
message | match |
| field:"message" kind:match |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Fails | project |
Successes | project |
any_cd_browser | project |
any_cd_client_ip | project |
any_cd_device_os | project |
any_cd_geo_city | project |
any_cd_geo_country | project |
any_cd_source_ip | project |
any_cd_target | project |
firstFail | project |
firstSuccess | project |
lastFail | project |
targetAccount | project |
username | project |