Detection rules › Kusto
Password Spray
Below queries detect password spray attacks using sliding window count plugin. Because of implementation of the sliding window, queries work better than the bin() usage, but may create duplicate alerts. Grouping can be used in such cases. Sentinel Query:
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Credential Access | No specific technique |
References
Telemetry coverage
Rule body
// Author : Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
//
// Link to original post:
// https://posts.bluraven.io/advanced-kql-for-threat-hunting-window-functions-part-2-dce3e321f54b
//
// Description : Detect if there are more than 2 distinct users seen from the same IP in a 3h window.
//
// Query parameters:
//
let start = ago(12h);
let end = now();
let lookbackWindow = 3h;
let bin = 1h;
let threshold = 2;
SecurityEvent
| where EventID in (4624, 4625)
| where IpAddress !in ("127.0.0.1", "::1", "-")
| evaluate sliding_window_counts(TargetUserName, TimeGenerated, start, end, lookbackWindow, bin, IpAddress)
| sort by IpAddress, TimeGenerated asc
| where Dcount >= 2
// Author : Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
//
// Link to original post:
// https://posts.bluraven.io/advanced-kql-for-threat-hunting-window-functions-part-2-dce3e321f54b
//
// Description : Detect if there are more than 2 distinct users seen from the same IP in a 3h window.
//
// Query parameters:
//
let start = ago(12h);
let end = now();
let lookbackWindow = 3h;
let bin = 1h;
let threshold = 2;
DeviceLogonEvents
| where Timestamp > ago(lookbackWindow)
| where RemoteIP !in ("127.0.0.1","::1","-") and isnotempty(RemoteIP)
| evaluate sliding_window_counts(AccountName, Timestamp, start, end, lookbackWindow, bin, RemoteIP)
| where Dcount > threshold
Stages and Predicates
Parameters
let start = ago(12h);
let end = now();
let lookbackWindow = 3h;
let bin = 1h;
let threshold = 2;
Stage 1: source
SecurityEvent
Stage 2: where
where EventID in~ (4624, 4625)
Stage 3: where
where not (IpAddress in~ ("-", "127.0.0.1", "::1"))
Stage 4: evaluate
evaluate
Stage 5: sort
sort by IpAddress, TimeGenerated
Stage 6: where
where Dcount >= 2
Stage 7: source
DeviceLogonEvents
Stage 8: where
where Timestamp > ago(10800s)
Stage 9: where
where not (RemoteIP in~ ("-", "127.0.0.1", "::1")) and isnotempty(RemoteIP)
Stage 10: evaluate
evaluate
Stage 11: where
where Dcount > 2
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
IpAddress | in | -, 127.0.0.1, ::1 | excludes:IpAddress field:"IpAddress" value:"-" field:"IpAddress" value:"127.0.0.1" field:"IpAddress" value:"::1" |
RemoteIP | in | -, 127.0.0.1, ::1 | excludes:RemoteIP field:"RemoteIP" value:"-" field:"RemoteIP" value:"127.0.0.1" field:"RemoteIP" value:"::1" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Dcount | ge |
| field:"Dcount" kind:ge value:"2" |
Dcount | gt |
| field:"Dcount" kind:gt value:"2" |
EventID | in |
| field:"EventID" kind:in |
RemoteIP | is_not_null | field:"dest_ip" kind:is_not_null |