SSH Login, Terminal Types, and SSH Control Message Flow
In this post let us understand in detail what happens when use SSH to log into a remote device
1. Connecting with SSH
We can access a device or server remotely using SSH as long as the SSH server service (sshd) is running on the server.
By default, sshd listens on TCP port 22, but it can be configured to listen on a custom port. If that is the case, the SSH client must explicitly connect to that custom port.
# Default port
ssh user@FQDN_or_IP
# Custom port 2222
ssh -p 2222 user@FQDN_or_IPwhere:
- user = the username to log in with.
- FQDN_or_IP= Fully Qualified Domain Name or IP address of the remote system.
2. The Default Interactive Session Flow
By default, when the SSH client connects without specifying a command, it requests an interactive session. After authentication, the SSH server allocates a pseudo-terminal (often shown as /dev/[ts/N, where N is a number) and starts the user’s default shell.
[Client] [Server:sshd]
| TCP handshake |
|--------------------->|
| SSH handshake + auth |
|--------------------->|
| CHANNEL_OPEN "session" (SSH_MSG_CHANNEL_OPEN)
|--------------------->|
| CHANNEL_REQUEST "pty-req"
|--------------------->|
| CHANNEL_REQUEST "shell"
|--------------------->|
| <---- Allocates PTY (/dev/pts/N), starts shell
| <==== Encrypted data exchange ===> 3. Shell vs Terminal
The shell is the command interpreter, while the terminal is the interface that connects your keyboard input and program output, allowing you to interact with the shell.
You can confirm which terminal you are using with:
Examples
1. SSH session:
boss:~$ tty
/dev/pts/0 ===========> a pseudo-terminal slave device.
2.Virtual console
boss:~$ tty
/dev/tty1 ===========> virtual console or iDRAC console
As shown above
- In an SSH session, you might see /dev/pts/0 — this is a pseudo-terminal slave device.
- In a virtual console (accessed with CTRL+ALT+F3 on Ubuntu), you might see /dev/tty1, /dev/tty2, which are virtual terminals tied directly to the system’s display hardware.
It’s not only SSH that uses /dev/pts . Terminal multiplexers like tmux and screen also allocate their own pseudo-terminals for each session, window, or pane, attaching your shell process to them in the same way.
4. TTY vs PTY
TTY device:
is a real terminal interface.
Examples: Your physical console on a local machine (/dev/tty1..) A serial port terminal (/dev/ttyS0). These are hardware-backed or direct virtual console devices.
PTY device:
is A pseudo-terminal (PTY).
is a software-emulated terminal device.
A PTY comes in pairs:
1. Master side — controlled by a program (like SSH, screen, tmux).
2. Slave side — acts like a real TTY device to the program running inside it (like your shell).
Example: When you SSH into a server, the SSH server process creates a PTY slave for your shell, and keeps the master side so it can send/receive your keystrokes and output
5. PTY in SSH Sessions
In SSH, the PTY lets the remote shell behave as if you were physically logged in at a terminal:
- It handles line editing (backspace, arrow keys).
- It supports interactive programs like vim, top, nano
- It translates terminal control sequences.
- Without a PTY, the SSH connection is just a raw data pipe to a process.
- the SSH client sends a “please allocate a PTY” request to the server.
The SSH server:
- Allocates a pseudo-terminal on the remote side.
- Hooks it up to the shell (bash, zsh etc.).
- Sends all the terminal I/O back and forth through the SSH tunnel.
Why you might turn it off using the -T switch
- Automation: When running scripts remotely that don’t need user interaction.
- Security: Some restricted SSH setups disable PTY to prevent users from spawning an interactive shell.
- Tunneling only: If SSH is only being used for port forwarding (-L, -R, -D), there’s no need for a PTY.
TTY (Physical)
[Keyboard/Screen] <---> /dev/tty1 <---> shell
PTY (Pseudo-terminal over SSH)
[SSH Client] <--network--> SSHD master <---> /dev/pts/3 (slave) <---> shellRule of thumb:
- TTY = real or virtual hardware terminal.
- PTY = software-emulated terminal that acts like a TTY to programs.
- SSH uses PTY when you want interactive remote terminals.
6. SSH Control Messages for Different Scenarios
When an SSH connection is established, the client and server exchange several important control messages that determine how the session will behave. These happen after authentication and before actual data starts flowing.
i. PTY Allocation
Purpose: Client requests a pseudo-terminal so it can run interactive applications.
Message (SSH protocol):
SSH_MSG_CHANNEL_REQUEST
type: "pty-req"
TERM: "xterm"
dimensions: cols, rows- Sent from client to server.
- If server accepts, it allocates a PTY (/dev/pty/N) for the session.
- Without this, programs won’t think they’re running in a terminal (no line editing, colored output, etc.).
ii. Shell Start
Purpose: Client asks server to start the user’s default shell attached to the PTY.
Message:
SSH_MSG_CHANNEL_REQUEST
type: "shell"- The server starts a login shell (e.g.,
/bin/bash) and ties it to the PTY allocated in step 1.
iii. Command Execution
Purpose: Instead of a shell, the client can request execution of a specific command.
Message:
SSH_MSG_CHANNEL_REQUEST
type: "exec"
command: "uptime"- Server runs the given command and returns its stdout/stderr over the channel.
iv. Local Port Forwarding
Purpose: Client asks server to connect to a fixed host/port on behalf of the client when local port is hit.
Get pash4stud2’s stories in your inbox
Join Medium for free to get updates from this writer.
Message (per connection to local listener):
SSH_MSG_CHANNEL_OPEN
type: "direct-tcpip"
target_host: "internal_host"
target_port: 3389
originator_host: "127.0.0.1"
originator_port: 54321- Sent by client when a connection is made to the locally bound port in
-L. - Server then opens the outbound TCP connection to the target.
v. Remote Port Forwarding
Purpose: Client tells server to listen on a port, and when connections arrive, forward them back to the client.
Initial request:
SSH_MSG_GLOBAL_REQUEST
request: "tcpip-forward"
bind_address: "127.0.0.1"
bind_port: 8080When a remote client connects:
SSH_MSG_CHANNEL_OPEN
type: "forwarded-tcpip"
originator_host: "remote_client_IP"
originator_port: 56789
connected_host: "target_host"
connected_port: target_portvi. Dynamic Port Forwarding (SOCKS)
- This is just multiple
direct-tcpiprequests, one per SOCKS connection, but the target host/port come from the SOCKS handshake rather than being fixed in the SSH command.
| Scenario | Protocol Message(s) |
| --------------------------- | -------------------------------------------- |
| Interactive shell | `pty-req` → `shell` |
| Run a single command | `exec` |
| Local port forwarding (-L) | `direct-tcpip` |
| Remote port forwarding (-R) | `tcpip-forward` → `forwarded-tcpip` |
| Dynamic forwarding (-D) | Multiple `direct-tcpip` from SOCKS handshake |7. Important Note on ssh -v Output
- OpenSSH ssh -v (verbose mode) does not display “pty-req”, “shell”, “exec” or direct-tcp-ip by name.
- Instead, it summarizes them in human-readable lines such as:
- The actual SSH_MSG_* protocol messages happen under the hood.
8. Why You Don’t See These in Packet Captures
- After SSH key exchange, all channel requests and data are encrypted.
- In a normal pcap, you cannot see pty-req”, “shell”, “exec” or direct-tcp-ip in plaintext.
- To see them, you would need: SSH session keys (and configure Wireshark to decrypt), or Run SSH without encryption (unsafe, only for lab testing), or Capture logs from the SSH daemon in debug mode.
Without decryption, the pcap will show:
- TCP handshakes.
- SSH handshake and key exchange.
- Encrypted payload blobs for all later control and data messages.
9. Example Flows with ssh -v Output
Interactive Shell
ssh user@serverProtocol Flow:
CHANNEL_OPEN "session"
CHANNEL_REQUEST "pty-req"
CHANNEL_REQUEST "shell"ssh -v Output:
Authenticated to server using "password".
debug1: channel 0: new [client-session]
debug1: Requesting no-more-sessions@openssh.com
debug1: Entering interactive session.
debug1: Sending environment.
debug1: channel 0: setting env LANG = "C.UTF-8"Local Port Forwarding (-L)
sh -L 1234:internal_host:80 user@sshserverProtocol Flow:
(no PTY request, no shell request)
Local port 1234 bound
When traffic arrives:
CHANNEL_OPEN "direct-tcpip"
target_host=internal_host
target_port=80ssh -v Output (start):
debug1: Local connections to LOCALHOST:1234 forwarded to remote address internal_host:80
debug1: Local forwarding listening on 127.0.0.1 port 1234.ssh -v Output (when a connection is made):
debug1: Connection to port 1234 forwarding to internal_host port 80 requested.
debug1: channel 1: new [direct-tcpip]Remote Port Forwarding (-R)
ssh -R 8080:internal_client_host:22 user@sshserverProtocol Flow:
(no PTY request, no shell request)
Server binds port 8080
When traffic arrives:
CHANNEL_OPEN "forwarded-tcpip"
connected_host=internal_client_host
connected_port=22ssh -v Output (start):
debug1: Remote connections from LOCALHOST:8080 forwarded to local address internal_client_host:22
debug1: remote forward success for: listen 127.0.0.1 port 8080ssh -v Output (when a connection is made):
debug1: Connection to port 8080 forwarding to internal_client_host port 22 requested.
debug1: channel 1: new [forwarded-tcpip]Dynamic Port Forwarding (-D) — SOCKS Proxy
ssh -D 1080 user@sshserverProtocol Flow:
(no PTY request, no shell request)
Local port 1080 bound for SOCKS
When SOCKS connection arrives:
SOCKS handshake determines target host:port
CHANNEL_OPEN "direct-tcpip"
target_host = (SOCKS-requested host)
target_port = (SOCKS-requested port)ssh -v Output (start):
debug1: Local connections to LOCALHOST:1080 forwarded to remote address dynamic
debug1: Local forwarding listening on 127.0.0.1 port 1080.ssh -v Output (when a SOCKS connection is made):
debug1: Connection to port 1080 forwarding to dynamic requested.
debug1: channel 1: new [dynamic-tcpip]Check out the post below which expands more on how port forwarding/tunneling works and how adversaries use it to their benefit
TTY are part of /dev A.K.A device files in linux and if you would like to know more about them checkout the below article.






