RC RANDOM CHAOS

OpenSSH turns every authenticated session into a pivot

How SSH local, remote, and dynamic port forwarding becomes pivot infrastructure for lateral movement and exfiltration, and what it leaves in telemetry.

· 7 min read
OpenSSH turns every authenticated session into a pivot

OpenSSH ships with TCP forwarding enabled. AllowTcpForwarding defaults to yes in a stock sshd_config. That one directive turns every authenticated SSH session into a programmable network bridge. No exploit. No CVE. The protocol working exactly as RFC 4254 specifies, repurposed as pivot infrastructure.

SSH multiplexes independent logical channels over a single encrypted transport. The transport runs over one TCP connection, usually to port 22. Inside it the protocol opens channels on demand - an interactive shell, an SFTP subsystem, a forwarded socket - each negotiated separately. Local forwarding opens a direct-tcpip channel. Remote forwarding sends a tcpip-forward global request, then carries connections back inside forwarded-tcpip channels. The transport encrypts the whole stream. A firewall or proxy inspecting the flow sees one long-lived outbound session to a known port. It cannot read the channels inside, cannot see the forwarded destinations, and cannot distinguish an admin running a backup from an operator relaying SMB into a flat subnet.

Local forwarding, the -L flag, binds a listener on the client and relays each connection through the SSH server to a destination the server resolves. The client controls the bind address, the target host, and the target port. The destination is resolved server-side, after the encrypted session terminates. That detail is the entire primitive. A host with no route to an internal VLAN reaches it the moment the SSH server has a route. The tunnel inherits the server’s network position, its firewall exemptions, and its trust relationships. A jump box meant to broker RDP becomes a relay into every system the jump box can reach.

Remote forwarding, the -R flag, inverts the direction. The SSH server binds the listener and relays inbound connections back across the established session to the client. This is the reverse-tunnel primitive that defeats perimeter filtering. A compromised internal host opens an outbound SSH session to an attacker-controlled server, then requests a remote forward. The attacker connects to the listener on their own box, and the traffic lands inside the victim network. Egress became ingress. The corporate firewall logged one permitted outbound connection on 22. It never evaluated an inbound rule, because no inbound connection ever crossed the boundary. The session was dialed out from inside.

Dynamic forwarding, the -D flag, runs a SOCKS proxy on the client and resolves each requested destination server-side. One channel becomes a route into an entire subnet. Tooling chains a scanner or a C2 agent through the SOCKS port and reaches hosts the operator’s machine has no path to. The same mechanism backs the SOCKS pivots in Cobalt Strike, the portfwd command in Meterpreter, and purpose-built relays like ligolo-ng and chisel when SSH itself is unavailable.

The attacker controls less than the mechanism suggests, and that is what makes it durable. The client picks the bind point and the logical destination. The SSH server performs the resolution, the routing, and the final TCP connect under its own identity and ACLs. The operator never needs a route to the target, never needs a credential on the target at the tunnel layer, and never sends a packet the perimeter can attribute to an external source. The session authenticates once, with a key or a password the server already trusts, and every forwarded connection rides that single authorization. Compromise one host with outbound 22 and a foothold key, and the encrypted session becomes a stable, multiplexed, attributable-to-the-victim path into whatever that host can see.

Reach depends on bind behavior. By default a remote forward binds to the loopback interface of the SSH server unless GatewayPorts is set to yes or clientspecified. Operators who control the server flip that directive and expose the forwarded port to any host that can reach the server. AllowTcpForwarding governs whether forwarding is permitted at all, and its default of yes means a stock OpenSSH install grants every authenticated user a tunneling capability the administrator never explicitly granted. The capability is bundled with shell access.

There is no patch boundary here because there is no vulnerability in the bug-class sense. The distinction matters for triage. Terrapin, CVE-2023-48795, CVSS 5.9, was a real SSH protocol flaw: a prefix-truncation weakness in the binary packet protocol that let an active network attacker downgrade the handshake. Port forwarding is not that. It is intended functionality. It will not appear in a vulnerability scan, will not generate a CVSS vector, and will not be closed by an update. The exposure is configuration and trust, which is why it survives patch cycles that close memory-corruption bugs.

The pattern is not unique to OpenSSH. Cloudflare Tunnel via cloudflared, ngrok, and the reverse-connection logic inside most modern C2 frameworks implement the same outbound-dialed ingress, and detection teams that wrote a rule for ssh.exe forwarding flags often have no equivalent coverage for those clients. The transport differs. The trust violation is identical: a host inside the boundary initiates the connection, so inbound filtering never applies, and the channel terminates on infrastructure the defender does not control. Identity context is where this surfaces if anywhere: an Okta or equivalent log showing a service account authenticating SSH from an unexpected host, at an unusual hour, immediately before a spike in internal east-west traffic, is the correlation that survives encryption.

MITRE catalogues the behavior directly. T1572, Protocol Tunneling, covers the SSH channel carrying other protocols inside the encrypted session. T1090, Proxy, and its sub-technique T1090.001, Internal Proxy, cover dynamic forwarding used to relay between internal hosts. T1021.004, Remote Services: SSH, covers the lateral authentication itself. When the tunnel carries stolen data outbound, it maps to T1041, Exfiltration Over C2 Channel, or T1048 when the SSH session is a dedicated alternative-protocol path. Reverse SSH tunneling appears across ransomware affiliate intrusions and state-aligned espionage operations precisely because it collapses initial access, lateral movement, and exfiltration into one encrypted, allow-listed flow.

Telemetry is where the technique earns its place. The encrypted payload is opaque. The behavior around it is not. On Windows, Sysmon Event ID 1 records process creation, and the command line is the highest-value field. ssh.exe or plink.exe invoked with -R, -L, -D, -N, or -f exposes the intent in plaintext. -N requests no shell and -f backgrounds the client, the signature of a session that exists only to forward. Sysmon Event ID 3 records the network connection: a long-lived flow to port 22, or to a non-standard port chosen to blend with HTTPS, originating from a process that has no business speaking SSH. On Linux, auditd execve records capture the same invocation, and sshd at LogLevel VERBOSE emits channel-open events that name the forwarding requests. EDR platforms flag the parent-child anomaly: sshd spawning a forwarder, or an ssh client launched by a non-interactive service account, a scheduled task, or a web server’s worker process.

The gaps are specific. Content inspection yields nothing; the channels are encrypted end to end, so DPI and TLS-style interception both fail. Netflow shows a session but not its purpose, though byte-ratio asymmetry and session duration are correlatable when a baseline exists. Lateral connections emerging from the forward originate on the SSH server itself, so an internal sensor sees the jump host opening SMB to a file server and reads it as routine administration. The forwarded traffic carries the source identity of the relay, not the attacker. Persistence tools like autossh re-establish a dropped reverse tunnel automatically, and a session running only -N never spawns a child process, which removes the process-tree signal that many detections depend on. A correlation rule keyed on ssh.exe with forwarding flags catches the careless operator. It misses the one who renamed the binary, moved the listener to 443, and ran it from a host where SSH is expected.

Constraining the capability is a configuration problem with a known surface. AllowTcpForwarding set to no disables -L, -R, and -D for the affected users and is appropriate on bastions whose only job is interactive shell or SFTP. PermitOpen restricts local forwards to an explicit host-and-port allow-list. PermitListen restricts where remote forwards may bind. GatewayPorts left at no keeps remote-forward listeners on loopback. Match blocks scope these directives per user, per group, or per source address, so a backup account gets exactly the forward it needs and nothing adjacent. AllowAgentForwarding set to no removes the agent-socket relay that lets a compromised intermediate hop reuse a private key it never sees. ForceCommand pins a session to a single program and denies the shell that forwarding abuse depends on.

The residual exposure is the part worth stating plainly. Disabling SSH forwarding does not remove tunneling from a host. Any user with shell access and the ability to run a binary can build the same relay in userspace. socat, a few lines of Python, or a static Go binary moves bytes between two sockets without touching sshd at all. The control closes the SSH-native path; it does not close the capability. The real boundary is whoever holds valid credentials and a network position. SSH forwarding is the cleanest expression of that capability because it is signed, encrypted, allow-listed, and indistinguishable from the administration it imitates. The defense is not closing port 22. It is knowing which accounts can forward, to where, and watching the process that asks. Everything else is already inside the encrypted channel, where the inspection stops.

See also: NordVPN for tunneled traffic when operating outside controlled networks.


#ad Contains an affiliate link.

Share

Keep Reading

Stay in the loop

New writing delivered when it's ready. No schedule, no spam.