Detection rules › Splunk

Scheduled Task with Potential SSH Tunnel - Windows (Windows Event Log)

Group by
_time, host
Source
github.com/anvilogic-forge/armory

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

TacticTechniques
ExecutionT1053 Scheduled Task/Job
PersistenceT1053 Scheduled Task/Job
Privilege EscalationT1053 Scheduled Task/Job
Command & ControlT1572 Protocol Tunneling

References

Event coverage

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.

FieldKindValues
EventCodeeq
  • 4688 corpus 313 (splunk 283, kusto 30)
  • 4698 corpus 14 (splunk 14)
TaskContentmatch
  • "-L|-R|-N|-D|-C|IdentitiesOnly=yes|StrictHostKeyChecking=no|ssh"
  • "\d{1,5}:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}"
  • "\w+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
processmatch
  • "(?i)\-(L|R|N|D|C)|IdentitiesOnly=yes|StrictHostKeyChecking=no|ssh" corpus 3 (splunk 3)
  • "\d{1,5}:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}" corpus 3 (splunk 3)
  • "\w+@\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}" corpus 3 (splunk 3)

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.

StageTerm
1TERM
1"<EventID>4698<"
1TERM
1"<EventID>4688<"
1"@"
1"/create"
1"-L"
1"-R"
1"-N"
1"-D"
1"-C"
1"IdentitiesOnly=yes"
1"StrictHostKeyChecking=no"
1"ssh"