SSH’s built-in tunneling capabilities are frequently abused by adversaries to create covert channels through firewalls, route malicious traffic through compromised hosts, or exfiltrate data via encrypted proxies. Because the traffic looks like legitimate SSH, it passes through most network controls. MITRE ATT&CK tracks this as T1572 (Protocol Tunneling).

There are three tunneling modes to hunt for, each with distinct detection signatures:

  • Local port forwarding (-L): routes traffic arriving at a local port through the SSH session to a remote destination
  • Remote port forwarding (-R): binds a port on the remote SSH server and tunnels incoming connections back to the attacker’s machine
  • Dynamic port forwarding (-D): creates a SOCKS proxy on the local machine, routing arbitrary TCP traffic through the SSH session

Remote port forwarding and dynamic forwarding are the most common in offensive operations: remote forwarding enables C2 callbacks through outbound SSH, dynamic forwarding creates a full SOCKS5 proxy that tools like proxychains can route through.

Detection: Command-Line Arguments

The most direct detection is watching for SSH invocations containing tunneling flags. These can be caught from process creation events on Linux (auditd, Sysmon for Linux) and Windows (Sysmon Event ID 1, Security Event ID 4688).

Sigma rule — suspicious SSH port forwarding arguments:

title: SSH Tunneling or Port Forwarding via Command-Line
id: 8f4d2c1a-3b7e-4f9d-a2c6-1e8b5d4f7c3a
status: test
description: Detects SSH invocations with port forwarding or dynamic SOCKS proxy flags
references:
  - https://attack.mitre.org/techniques/T1572/
author: SOC Analyst Hub
date: 2026-06-26
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    Image|endswith: '/ssh'
  tunneling_flags:
    CommandLine|contains:
      - ' -L '
      - ' -R '
      - ' -D '
      - ' -w '
      - '-NTR'
      - '-NTL'
      - '-NTD'
  condition: selection and tunneling_flags
falsepositives:
  - Legitimate developer tunnels (e.g., db port forwarding to localhost)
  - SSH jumphost configurations using ProxyJump
level: medium
tags:
  - attack.lateral_movement
  - attack.t1572
  - attack.command_and_control

KQL for Microsoft Sentinel (Sysmon via MMA or AMA):

DeviceProcessEvents
| where FileName == "ssh" or FileName == "ssh.exe"
| where ProcessCommandLine matches regex @'-[LRDNW]\s+\d{1,5}:'
    or ProcessCommandLine has_any ("-D ", "-R ", "-L ", " -NTD ", " -NTR ")
| project TimeGenerated, DeviceName, InitiatingProcessFileName,
    InitiatingProcessCommandLine, ProcessCommandLine, AccountName
| sort by TimeGenerated desc

Detection: Unusual SSH Parent Processes

Legitimate SSH tunnels are usually initiated interactively by a user or from a known automation tool. When SSH is launched by a web server process, a script interpreter, or an implant, the parent-child process relationship is anomalous.

Sigma rule — SSH launched by unusual parent:

title: SSH Spawned by Suspicious Parent Process
id: 3c9e1b2f-8d4a-4e7c-b5f3-2a7d9c6e8b1f
status: test
description: Detects SSH tunneling initiated from web server or script interpreter parents
logsource:
  category: process_creation
  product: linux
detection:
  selection:
    Image|endswith: '/ssh'
    CommandLine|contains:
      - ' -L '
      - ' -R '
      - ' -D '
  parent_anomaly:
    ParentImage|endswith:
      - '/apache2'
      - '/nginx'
      - '/httpd'
      - '/php'
      - '/php-fpm'
      - '/python'
      - '/python3'
      - '/ruby'
      - '/node'
  condition: selection and parent_anomaly
level: high
tags:
  - attack.t1572
  - attack.initial_access

Detection: Network Telemetry

At the network level, SSH tunneling looks like normal SSH traffic on port 22 (or 443/80 if the adversary is using non-standard SSH ports to blend in). Key anomalies to hunt:

Long-duration SSH connections with high data volume: Normal interactive SSH sessions are short; tunnels persisting for hours or days with steady data flow are suspicious. Hunt for SSH connections exceeding 30 minutes duration with >100MB transferred.

SSH from non-standard processes: On a server that should only receive SSH, outbound SSH from a non-admin user to an external IP is anomalous. On workstations, any SSH connection to an external IP should be scrutinised.

Sysmon Network Event (Event ID 3) — SSH to external IP from unexpected process:

title: SSH Connection to External IP from Unexpected Process
id: 7a2e5d8b-1c4f-4a9e-d3b7-6f9c2a5e8d1b
status: test
logsource:
  product: windows
  service: sysmon
detection:
  selection:
    EventID: 3
    DestinationPort: 22
  filter_known:
    Image|endswith:
      - '\ssh.exe'
      - '\putty.exe'
      - '\winscp.exe'
  condition: selection and not filter_known
level: medium
tags:
  - attack.t1572

Detection: Listening Ports Created by SSH

Dynamic port forwarding (-D) and local port forwarding (-L) cause SSH to bind a local listening port. On Linux, ss -tlnp or auditd socket calls reveal this. Monitoring for new listening ports created by SSH processes is a reliable hunt signal.

Linux auditd rule to detect socket binding by SSH:

-a always,exit -F arch=b64 -S bind -F uid!=0 -F exe=/usr/bin/ssh -k ssh_bind

Search auditd logs for:

ausearch -k ssh_bind --start today | grep -E "bind|port"

Reducing False Positives

SSH tunneling is common in legitimate DevOps workflows: developers forward database ports to localhost, CI/CD systems use SSH as a transport, and jumphost configurations use ProxyJump internally. To tune detections:

  1. Maintain an allowlist of known automation accounts and their source IPs
  2. Suppress alerts for connections to internal RFC1918 destinations (internal port forwarding is lower risk than external)
  3. For dynamic forwarding (-D), correlate with network proxy traffic — a legitimate SOCKS proxy should have a known requestor

Broader Hunt

Combine SSH tunneling detection with adjacent T1572 techniques: autossh (persistence-based SSH tunnel manager), netsh portproxy on Windows, and chisel or ligolo-ng — purpose-built tunneling tools that adversaries use when native SSH isn’t available. Hunting for chisel, ligolo, gost, or ssht binary names covers the explicit tooling variants.