# SSH Port Forwarding Cheatsheet (Graham Helton)

Graham Helton's reference page for working through SSH options he learned the hard way during red-team engagements. Three hosts recur throughout: `campfire.int` (the operator's machine), `vuln-server.int` (the foothold), `internal-web.int` (the loopback-only target). Everything is demonstrated against an HTTP service but applies to any TCP service.

The mnemonic running through the whole page: `-L` keeps the local port on the **L**eft of the address pair, `-R` keeps the local port on the **R**ight. Once that sticks, the rest is bookkeeping.

## The five forwarding shapes

**Local (-L)**: `ssh -N -f -L 1337:127.0.0.1:80 root@internal-web.int`. Operator binds local port 1337; traffic comes out as port 80 on the SSH server. Used when the target speaks only on its loopback and the operator has SSH there.

**Remote (-R)**: `ssh -N -f -R 3000:127.0.0.1:80 root@vuln-server.int`. The SSH server (`vuln-server.int`) binds port 3000; traffic comes out as port 80 on the operator's host. Used to expose a service from a network the operator is inside but the SSH server's network can't directly reach.

**Dynamic (-D)**: `ssh -N -f -D 8080 root@vuln-server.int`. Operator binds a local SOCKS5 listener; the SSH server is the egress. Pair with `/etc/proxychains.conf`'s `socks5 127.0.0.1 8080`, or Firefox's "Manual proxy configuration → SOCKS v5". DNS-over-SOCKS is unreliable enough that Helton uses raw IPs in his curl examples.

**Jumphost (-J)**: `ssh -J a@hop1,b@hop2 c@target`. Comma-separated chain; auth happens at each hop. The path in the post is `campfire.int → vuln-server.int → internal-web.int → dns.int`.

**Agent forwarding (-A)**: `ssh -A -J root@vuln-server.int root@internal-web.int`. Pushes the local ssh-agent socket along the hop chain so the destination can use the operator's loaded keys without a copy. Carries real risk — see [[ssh-agent-forwarding-risk]].

The triple `-N -f` shows up in nearly every example. `-N` says no command, `-f` backgrounds the session; together they create a forward without a shell tying up the terminal.

## The other flags

**-g (gateway mode)**: `ssh -N -f -g -L 2222:localhost:22 root@internal-web.int`. By default a `-L` listener binds 127.0.0.1; `-g` makes it bind 0.0.0.0 so other hosts can hit it. Turns the SSH server into a pivot for anyone on its LAN.

**-t (force TTY)**: `ssh user@host -t top`. Runs an interactive command on the remote with a real TTY allocated. Without it, curses-style programs fail.

## The ~? runtime console

Once inside an SSH session, `~?` (tilde-question on a fresh line) opens the escape menu. Two practical uses:

- `~.` — kill the session immediately. Useful when the shell is wedged.
- `~C` — open the SSH command-line inside the running session. From there `-D 8080` retroactively adds a dynamic forward, so a session opened as a normal shell can become a SOCKS pivot mid-session without disconnecting.

This works because tilde-prefixed sequences are intercepted by the local SSH client before they reach the remote side.

## SSH config file

`~/.ssh/config` lets the operator pre-register hosts with their non-default options. Helton's example:

```
host internal-web.int
    User root
    IdentityFile /home/smores/ssh_agent/internal-web-no-pw
    Port 2222
```

Lookup order: command-line flags win over config, config wins over defaults. Running `ssh graham@internal-web.int` overrides the config's `User root`.

The non-obvious keywords:

- `ProxyJump` — same as `-J`.
- `ForwardAgent` — same as `-A`.
- `IdentityFile` — same as `-i`.
- `Match` — conditional config blocks. Helton's example evaluates `export | grep PROXYME=TRUE` and only applies the block when the grep exits 0. Lets a single host entry behave differently based on environment variables, network, hostname, or any exec test.

`scp` and most SSH-derived utilities read this file by default; if not, `-F ~/.ssh/config` forces it.

## Key management one-liners

- `ssh-copy-id -i internal-web user@host` uploads the matching `.pub` to the remote's `~/.ssh/authorized_keys`.
- `ssh-keygen -t ecdsa -b 521` generates an ECDSA-P521 keypair (RSA is the default; longer/non-RSA is the usual recommendation).
- `ssh-keygen -lf <keyfile>` prints fingerprint and bit length.

## Why it's a red-team reference

The framing is offensive but the techniques are vendor-neutral OpenSSH behavior. SSH is the canonical [[living-off-the-land]] tool — every Unix-style target has the client, sshd is on most servers, every packet looks like normal admin traffic. The forwarding modes turn it into a layer-4 pivot, a SOCKS proxy, and a credential-replay surface without ever installing anything.

For the agent-forwarding security writeup Helton mentions, see [[ssh-agent-forwarding-risk]].

## See also

- [[ssh-port-forwarding]] — the four-flag taxonomy in one place
- [[ssh-agent-forwarding-risk]] — why `-A` and `ForwardAgent` need scrutiny
- [[living-off-the-land]] — pre-installed-tool attack patterns
- [[graham-helton-blog]] — the author's site
