Detection rules › Splunk
Scheduled Task with Potential SSH Tunnel - Windows (Windows Event Log)
Threat actors have been observed using scheduled tasks to create reverse SSH tunnels to facilitate persistence on a compromised machine as reported by DFIR. This use case detects scheduled tasks created with commands indicating potential SSH tunnels. The regex filtering matches on the patterns <user>@<IP address> and <port number>:<IP Address> :<port number>
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution | T1053 Scheduled Task/Job |
| Persistence | T1053 Scheduled Task/Job |
| Privilege Escalation | T1053 Scheduled Task/Job |
| Command & Control | T1572 Protocol Tunneling |
References
Event coverage
| Provider | Event | Title |
|---|---|---|
| Security-Auditing | Event ID 4688 | A new process has been created. |
| Security-Auditing | Event ID 4698 | A scheduled task was created. |
Rule body yaml
id: '25326.46988'
title: Scheduled Task with Potential SSH Tunnel - Windows
description: Threat actors have been observed using scheduled tasks to create reverse
SSH tunnels to facilitate persistence on a compromised machine as reported by DFIR.
This use case detects scheduled tasks created with commands indicating potential
SSH tunnels. The regex filtering matches on the patterns <user>@<IP address> and
<port number>:<IP Address> :<port number>. Living Off the Land Binary and Scripts
(LOLBAS) (LOLBIN)
logic_format: Splunk
logic: '`get_endpoint_data` `get_endpoint_data_winevent` (TERM(EventCode=4698) OR
"<EventID>4698<") OR (TERM(EventCode=4688) OR "<EventID>4688<" OR Type=Process)
"@" "/create" ("-L" OR "-R" OR "-N" OR "-D" OR "-C" OR "IdentitiesOnly=yes" OR "StrictHostKeyChecking=no"
OR "ssh") | where ((match(TaskContent, "\w+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")
and match(TaskContent, "\d{1,5}:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}") and
match(TaskContent, "-L|-R|-N|-D|-C|IdentitiesOnly=yes|StrictHostKeyChecking=no|ssh"))
OR (match(process, "\w+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") and match(process,
"\d{1,5}:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}") and match(process, "(?i)\-(L|R|N|D|C)|IdentitiesOnly=yes|StrictHostKeyChecking=no|ssh")))
| table _time, host, user, process, process_*, parent_process_name, parent_process_*,
signature_id | bin span=1s | stats values(*) as * by _time, host '
techniques:
- execution:scheduled task/job
- command-and-control:protocol tunneling
technique_id:
- T1053
- T1572
data_category:
- Process command-line parameters
- Windows event logs
references:
- https://thedfirreport.com/2023/10/30/netsupport-intrusion-results-in-domain-compromise/
Stages and Predicates
Stage 1: search
`get_endpoint_data` `get_endpoint_data_winevent` (TERM(EventCode=4698) OR "<EventID>4698<") OR (TERM(EventCode=4688) OR "<EventID>4688<" OR Type=Process) "@" "/create" ("-L" OR "-R" OR "-N" OR "-D" OR "-C" OR "IdentitiesOnly=yes" OR "StrictHostKeyChecking=no" OR "ssh")
Stage 2: where
| where ((match(TaskContent, "\w+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") and match(TaskContent, "\d{1,5}:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}") and match(TaskContent, "-L|-R|-N|-D|-C|IdentitiesOnly=yes|StrictHostKeyChecking=no|ssh")) OR (match(process, "\w+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") and match(process, "\d{1,5}:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}") and match(process, "(?i)\-(L|R|N|D|C)|IdentitiesOnly=yes|StrictHostKeyChecking=no|ssh")))
Stage 3: table
| table _time, host, user, process, process_*, parent_process_name, parent_process_*, signature_id
Stage 4: bucket
| bin span=1s
Stage 5: stats
| stats values(*) as * by _time, host
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 |
|---|---|---|
EventCode | eq |
|
TaskContent | match |
|
process | 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>4698<" |
| 1 | TERM |
| 1 | "<EventID>4688<" |
| 1 | "@" |
| 1 | "/create" |
| 1 | "-L" |
| 1 | "-R" |
| 1 | "-N" |
| 1 | "-D" |
| 1 | "-C" |
| 1 | "IdentitiesOnly=yes" |
| 1 | "StrictHostKeyChecking=no" |
| 1 | "ssh" |