Detection rules › Splunk
RDP Brute-force Detection (Windows Event Log)
Detects possible Brute force attempt over Windows Remote Desktop Protocol (RDP)
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1133 External Remote Services |
| Persistence | T1133 External Remote Services |
| Credential Access | T1110.001 Brute Force: Password Guessing |
Event coverage
| Provider | Event | Title |
|---|---|---|
| Security-Auditing | Event ID 4625 | An account failed to log on. |
| Security-Auditing | Event ID 5156 | The Windows Filtering Platform has permitted a connection. |
Rule body yaml
id: '5455.5610'
title: RDP Brute-force Detection
description: 'Detects possible Brute force attempt over Windows Remote Desktop Protocol
(RDP) -- Software Association: BianLian, Conti, LockBit, SamSam, Snatch'
logic_format: Splunk
logic: ' `get_endpoint_data` `get_endpoint_data_winevent` AND ((TERM(EventCode=5156)
OR "<EventID>5156<") AND Destination_Port="3389" AND Direction="Inbound" AND Protocol="6")
OR ((TERM(EventCode=4625) OR "<EventID>4625<") AND (Failure_Reason="Unknown user
name or bad password." OR FailureReason="%%2313" AND Logon_Type="3")) | rex field=Message
(?<result>"(?i)An account failed to log on.")| rex field=Failure_Reason (?<result_reason>"(?i)Unknown
user name or bad password.")| eval note=mvappend(result, result_reason)| table _time,
host, user dest_port, note, process_name, process_path, signature_id, src_country,
src_dns, src_ip | bin span=20s | stats values(*) as * by _time, host | where match(signature_id,"5156")
AND match(signature_id,"4625")| where event_count > 3 | lookup dnslookup clientip
as src_ip OUTPUT clienthost as src_dns | iplocation prefix="src_" src_ip | rename
src_Country as src_country '
techniques:
- credential-access:brute force:password guessing
- initial-access:external remote services
technique_id:
- T1110.001
- T1133
data_category:
- Windows event logs
references: null
Stages and Predicates
Stage 1: search
`get_endpoint_data` `get_endpoint_data_winevent` AND ((TERM(EventCode=5156) OR "<EventID>5156<") AND Destination_Port="3389" AND Direction="Inbound" AND Protocol="6") OR ((TERM(EventCode=4625) OR "<EventID>4625<") AND (Failure_Reason="Unknown user name or bad password." OR FailureReason="%%2313" AND Logon_Type="3"))
Stage 2: rex
| rex field=Message (?<result>"(?i)An account failed to log on.")
Stage 3: rex
| rex field=Failure_Reason (?<result_reason>"(?i)Unknown user name or bad password.")
Stage 4: eval
| eval note=mvappend(result, result_reason)
Stage 5: table
| table _time, host, user dest_port, note, process_name, process_path, signature_id, src_country, src_dns, src_ip
Stage 6: bucket
| bin span=20s
Stage 7: stats
| stats values(*) as * by _time, host
Stage 8: where
| where match(signature_id,"5156") AND match(signature_id,"4625")
Stage 9: where
| where event_count > 3
Stage 10: lookup
| lookup dnslookup clientip as src_ip OUTPUT clienthost as src_dns
Stage 11: search
| iplocation prefix="src_" src_ip
Stage 12: rename
| rename src_Country as src_country
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
Destination_Port | eq |
|
Direction | eq |
|
EventCode | eq |
|
FailureReason | eq |
|
Failure_Reason | eq |
|
Logon_Type | eq |
|
Protocol | eq |
|
event_count | gt |
|
prefix | eq |
|
signature_id | match |
|
Search terms
Bare-string tokens in the SPL search body. Splunk matches each token against _raw (the untyped raw event text) anywhere it appears, not against a specific field. These don't surface in the Indicators table because they aren't predicates on a known field.
| Stage | Term |
|---|---|
| 1 | TERM |
| 1 | "<EventID>5156<" |
| 1 | TERM |
| 1 | "<EventID>4625<" |
| 11 | iplocation |
| 11 | src_ip |