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 | |
| Persistence | |
| Credential Access |
Telemetry coverage
Rule body
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
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Destination_Port | eq |
| field:"Destination_Port" kind:eq |
Direction | eq |
| field:"Direction" kind:eq |
EventCode | eq |
| field:"EventID" kind:eq |
FailureReason | eq |
| field:"FailureReason" kind:eq |
Failure_Reason | eq |
| field:"Failure_Reason" kind:eq |
Logon_Type | eq |
| field:"LogonType" kind:eq |
Protocol | eq |
| field:"Protocol" kind:eq |
event_count | gt |
| field:"event_count" kind:gt value:"3" |
prefix | eq |
| field:"prefix" kind:eq |
signature_id | regex_match |
| field:"signature_id" kind:regex_match |
Search terms
These SPL tokens match against raw event text.
| Stage | Term |
|---|---|
| 1 | "<EventID>5156<" |
| 1 | "<EventID>4625<" |
| 11 | iplocation |
| 11 | src_ip |