SSH vs Telnet – Securing Remote Access
1. Remote Access to Network Devices — Why It Matters
Network administrators must be able to manage routers and switches remotely — applying configuration changes, monitoring status, and troubleshooting faults — without requiring physical access to every device. Two protocols have historically been used for this: Telnet (TCP port 23) and SSH — Secure Shell (TCP port 22).
The choice between them has profound security implications. Telnet transmits everything — usernames, passwords, configuration commands, and all session data — in plaintext. Anyone with the ability to capture packets on the path between the administrator and the device (a man-in-the-middle attacker, a rogue employee, or a compromised network device) can read every keystroke. SSH eliminates this risk by encrypting the entire session using public-key cryptography.
| Feature | Telnet | SSH |
|---|---|---|
| TCP port | 23 | 22 |
| Encryption | None — plaintext transmission | Full session encryption (AES, 3DES, ChaCha20) |
| Authentication | Username/password sent in cleartext | Password or public-key authentication, encrypted |
| Integrity checking | None — data can be tampered in transit | HMAC ensures data integrity — tampering is detectable |
| Host verification | None — no way to confirm device identity | Server presents RSA/ECDSA host key — client can verify |
| Man-in-the-middle risk | High — attacker can read and inject session data | Low — encrypted + host-key verification prevents MITM |
| Recommended for production | No — considered insecure; deprecated in most networks | Yes — industry standard for remote device management |
| RFC | RFC 854 | RFC 4251–4254 (SSHv2) |
Related pages: SSH – Full Guide | ACL Overview | Named ACLs | AAA Overview | Common Port Numbers | TCP/UDP Port Reference | SSH Configuration Lab | Login Security & Brute-Force Protection Lab
2. Why Telnet Is Insecure — The Cleartext Problem
Telnet was designed in 1969 when the Internet was a small, trusted academic network. Security was not a design consideration. Every byte exchanged in a Telnet session — including the login password, enable password, and every configuration command typed — travels across the network as unencrypted ASCII text.
Telnet Packet Capture — What an Attacker Sees
An attacker running Wireshark (or tcpdump) on the same network segment captures a Telnet session to a Cisco router: Packet captures (raw Telnet data, plaintext): → "U" "s" "e" "r" "n" "a" "m" "e" ":" " " "a" "d" "m" "i" "n" → "P" "a" "s" "s" "w" "o" "r" "d" ":" " " "C" "i" "s" "c" "o" "1" "2" "3" → "R" "o" "u" "t" "e" "r" "#" → "e" "n" "a" "b" "l" "e" → "P" "a" "s" "s" "w" "o" "r" "d" ":" " " "E" "n" "a" "b" "l" "e" "P" "w" → "c" "o" "n" "f" " " "t" → "i" "n" "t" " " "g" "0" "/" "0" → "i" "p" " " "a" "d" "d" " " "1" "9" "2" "." "1" "6" "8" "." "1" "." "1" The attacker now has: - Username: admin - User password: Cisco123 - Enable password: EnablePw - Every command typed during the session - Full network topology from the configuration All of this from a single packet capture — zero effort required.
Attack Vectors for Telnet
| Attack Type | How Telnet Enables It |
|---|---|
| Passive eavesdropping | Any device on the path (hub, compromised switch, SPAN port) can capture the full Telnet session including all credentials |
| Man-in-the-middle (MITM) | Attacker intercepts and relays Telnet session — can read and modify commands in real time without either party knowing |
| Credential replay | Captured username/password can be replayed to log in to the same or other devices that share credentials |
| Session hijacking | Attacker injects commands into an active Telnet session by predicting TCP sequence numbers |
3. How SSH Secures the Session
SSH (Secure Shell) uses a combination of asymmetric (public-key) cryptography for initial key exchange and authentication, and symmetric cryptography for bulk session encryption. The result is a channel where all data — including credentials — is encrypted and integrity-protected from end to end.
SSH Session Establishment — Step by Step
SSH connection setup between admin workstation and Cisco router:
Step 1 — TCP connection established (port 22)
Admin ──[TCP SYN]──► Router :22
Admin ◄─[TCP SYN-ACK]── Router
Admin ──[TCP ACK]──► Router
Step 2 — Protocol version negotiation
Admin ◄──► Router: "SSH-2.0" exchanged — both agree on SSHv2
Step 3 — Algorithm negotiation (key exchange, encryption, MAC, compression)
Admin ◄──► Router: exchange supported cipher/MAC lists
Both agree on: key exchange = diffie-hellman-group14-sha256
encryption = aes256-ctr
MAC = hmac-sha2-256
Step 4 — Key exchange (Diffie-Hellman)
Admin and Router independently compute a shared secret
WITHOUT transmitting the secret across the network.
Result: both sides have the same symmetric session key
that no eavesdropper can derive from the exchange.
Step 5 — Router host key verification
Router sends its RSA/ECDSA public host key to the admin.
Admin workstation checks against known_hosts:
- First connection: "The authenticity of host cannot be established.
Fingerprint: SHA256:xxxx. Are you sure you want to continue? yes"
- Subsequent connections: fingerprint is verified automatically.
If fingerprint changes unexpectedly → MITM warning.
Step 6 — User authentication (over the encrypted channel)
Admin sends username and password (or public key) — ALL ENCRYPTED.
Even if captured, the encrypted credential is useless to an attacker.
Step 7 — Session active
All subsequent data (commands, output) is encrypted + integrity-checked.
An attacker capturing packets sees only random ciphertext.
SSH v1 vs SSH v2
| Feature | SSH Version 1 | SSH Version 2 |
|---|---|---|
| Key exchange | RSA only — known vulnerabilities | Diffie-Hellman, ECDH — more secure |
| Integrity | CRC-32 — susceptible to insertion attacks | HMAC-SHA — cryptographically strong |
| Authentication methods | Password, RSA host-based | Password, public-key, GSSAPI, keyboard-interactive |
| Known vulnerabilities | Session insertion, man-in-the-middle, CRC-32 weakness | No known fundamental protocol weaknesses |
| Recommended | No — deprecated; disable on all devices | Yes — always use SSHv2 in production |
ip ssh version 2 on Cisco devices.
SSH v1 has known cryptographic weaknesses and should never be
used. If a question asks about SSH configuration, assume SSHv2
is required unless stated otherwise.
4. SSH Configuration Prerequisites on Cisco IOS
Before SSH can be enabled on a Cisco router or switch, four prerequisites must be met. Missing any one of them prevents SSH from functioning.
| Prerequisite | Command | Why Required |
|---|---|---|
| IP domain name | ip domain-name <name> |
Used together with the hostname to generate the RSA key pair.
The fully qualified domain name (hostname + domain) forms the
key label. Without a domain name, the
crypto key generate rsa command is rejected. |
| Hostname (not default) | hostname <name> |
Must not be the default "Router" or "Switch" — the hostname is part of the RSA key label. A meaningful hostname is also required by best practice. |
| RSA key pair | crypto key generate rsa modulus <bits> |
SSH uses RSA asymmetric keys for host authentication and key exchange. The key pair must exist before SSH sessions can be established. Minimum 768 bits for SSHv2; recommended 2048 bits. |
| Local username and password | username <name> privilege <level> secret <pass> |
SSH requires username-based authentication
(login local). A VTY password alone
(password + login) is not sufficient
for SSH — a local username database or AAA server is required. |
Prerequisite check — what happens without each: Missing ip domain-name: Router(config)# crypto key generate rsa % Please define a domain-name first. Missing hostname (still "Router"): Router(config)# crypto key generate rsa Key will be generated using the label "Router.domain.com" (works but key is labelled with generic hostname — not best practice) Missing RSA key: Router# show ip ssh SSH Disabled - version 2.0 %Please create RSA keys (of at least 768 bits size) to enable SSH v2. Missing local username (only vty password set): SSH session attempt: % No username set. SSH requires that a local username be configured.
5. Full SSH v2 Configuration — Step by Step
The following is the complete SSH version 2 configuration for a Cisco IOS router or switch, including all best-practice hardening options.
Step 1 — Set Hostname and Domain Name
Router(config)# hostname R1 R1(config)# ip domain-name netstuts.com
Step 2 — Create Local User Account
! privilege 15 = full administrative access (level 0-15; 15 = highest) ! secret = stores password as SHA-256 hash (not reversible) ! Never use "password" instead of "secret" — "password" stores in MD5 or cleartext R1(config)# username admin privilege 15 secret Str0ngP@ssw0rd ! For multiple privilege levels (separation of duties): R1(config)# username netops privilege 10 secret Oper@t0rPw R1(config)# username readonly privilege 1 secret V1ewOnly!
Step 3 — Generate RSA Key Pair
R1(config)# crypto key generate rsa modulus 2048
The name for the keys will be: R1.netstuts.com
% Generating 2048 bit RSA keys, keys will be non-exportable...
[OK] (elapsed time was 3 seconds)
Key size guidelines:
768 bits → minimum for SSHv2 (weak — do not use in production)
1024 bits → acceptable but being phased out
2048 bits → recommended minimum for production
4096 bits → high-security environments
! Verify the key was generated:
R1# show crypto key mypubkey rsa
% Key pair was generated at: 12:00:00 UTC Jan 1 2025
Key name: R1.netstuts.com
Key type: RSA KEYS
Storage Device: not specified
Usage: General Purpose Key
Key is not exportable.
Key Data:
30820122 ... (public key in hex)
Step 4 — Specify SSH Version 2
R1(config)# ip ssh version 2 ! Verify: R1# show ip ssh SSH Enabled - version 2.0 Authentication timeout: 120 secs; Authentication retries: 3
Step 5 — Configure VTY Lines for SSH Only
R1(config)# line vty 0 4 R1(config-line)# login local ! use local username database R1(config-line)# transport input ssh ! ONLY SSH — Telnet is rejected R1(config-line)# exec-timeout 10 0 ! disconnect idle sessions after 10 minutes ! "transport input ssh" replaces the default "transport input all" which ! allows both Telnet and SSH. With "ssh" only, any Telnet attempt to port 23 ! receives a "connection refused" response. ! For routers with more than 5 VTY lines (supports up to 15 simultaneous sessions): R1(config)# line vty 5 15 R1(config-line)# login local R1(config-line)# transport input ssh R1(config-line)# exec-timeout 10 0
Step 6 — Enable Password Encryption (Global)
! Encrypt all plaintext passwords currently in running-config (Type 7 — Vigenere): R1(config)# service password-encryption ! Note: "service password-encryption" uses weak Type 7 encoding — easily reversed. ! Always use "secret" instead of "password" for user accounts (uses Type 9 / SHA-256). ! "service password-encryption" protects against casual shoulder-surfing but is ! NOT a substitute for strong hashing algorithms.
Step 7 — Set SSH Timeout and Retry Parameters
! Authentication timeout: maximum seconds to complete login (default 120): R1(config)# ip ssh time-out 60 ! Maximum failed authentication attempts before disconnecting (default 3): R1(config)# ip ssh authentication-retries 3 ! These settings limit brute-force login attempts. ! Combined with login block-for (see Section 8), they provide strong protection.
Complete Configuration — All Steps Together
! ── Hostname and domain ────────────────────────────────────────────────── Router(config)# hostname R1 R1(config)# ip domain-name netstuts.com ! ── Local user ─────────────────────────────────────────────────────────── R1(config)# username admin privilege 15 secret Str0ngP@ssw0rd ! ── RSA key pair ───────────────────────────────────────────────────────── R1(config)# crypto key generate rsa modulus 2048 ! ── SSH version ────────────────────────────────────────────────────────── R1(config)# ip ssh version 2 R1(config)# ip ssh time-out 60 R1(config)# ip ssh authentication-retries 3 ! ── VTY lines ──────────────────────────────────────────────────────────── R1(config)# line vty 0 4 R1(config-line)# login local R1(config-line)# transport input ssh R1(config-line)# exec-timeout 10 0 R1(config-line)# exit ! ── Password encryption ────────────────────────────────────────────────── R1(config)# service password-encryption
6. Restricting VTY Access with an ACL
Disabling Telnet and enabling SSH is a significant improvement, but it still allows any IP address to attempt an SSH login. Adding an ACL to the VTY lines further limits which management workstations can even reach the SSH service, providing defence-in-depth.
Goal: Allow SSH only from the management workstation at 192.168.99.10
and the management subnet 10.99.0.0/24. Block all other sources.
! ── Step 1: Create the ACL (standard ACL — matches source IP) ───────────
R1(config)# ip access-list standard MGMT_ACCESS
R1(config-std-nacl)# remark === Permit management workstation ===
R1(config-std-nacl)# permit host 192.168.99.10
R1(config-std-nacl)# remark === Permit management subnet ===
R1(config-std-nacl)# permit 10.99.0.0 0.0.255.255
R1(config-std-nacl)# remark === Deny all other sources ===
R1(config-std-nacl)# deny any log
R1(config-std-nacl)# exit
! ── Step 2: Apply to VTY lines with access-class ─────────────────────────
R1(config)# line vty 0 4
R1(config-line)# access-class MGMT_ACCESS in
R1(config-line)# exit
! access-class (not ip access-group) is used for VTY lines.
! "in" filters connection ATTEMPTS to the VTY lines (source of the SSH client).
! ── Result ────────────────────────────────────────────────────────────────
! SSH from 192.168.99.10 → permitted → reaches login prompt
! SSH from 10.99.1.5 → permitted → reaches login prompt
! SSH from 172.16.5.10 → denied by ACL → connection refused (logged)
! Telnet from anywhere → rejected by "transport input ssh" (not by ACL)
access-class is used under line vty to filter
remote management connections. ip access-group is used
under interface configurations to filter routed traffic.
They use the same ACL syntax but are applied with different commands
in different contexts. Using ip access-group on a VTY
line will produce an error.
See: ACL Overview | Named ACLs | Applying ACLs
7. The Console Port — Securing Physical Access
While SSH secures remote (VTY) access, the console port (the direct physical management connection via a rollover cable) must also be secured. An attacker with physical access and an unsecured console port can log in without any credentials.
Console port security best practices: R1(config)# line console 0 R1(config-line)# login local ! require username/password R1(config-line)# exec-timeout 5 0 ! disconnect after 5 minutes idle R1(config-line)# logging synchronous ! prevent log messages disrupting input R1(config-line)# exit ! Do NOT leave console with no authentication: ! (default — allows anyone with physical access to log in without credentials) ! line console 0 ! login ← requires only the line password ! password cisco ← weak — same password for everyone, cleartext ! Best practice — use local username database: ! line console 0 ! login local ← requires username AND password from the local database ! exec-timeout 5 0 ← auto-logout on idle
Console vs VTY — Key Differences
| Feature | Console (line con 0) | VTY (line vty 0 4) |
|---|---|---|
| Connection method | Physical rollover cable from PC serial/USB port | Remote network connection (SSH or Telnet) |
| Number of simultaneous sessions | 1 (only one console port) | 5 (lines 0–4) or up to 16 (lines 0–15) |
| Transport control | Not applicable — direct serial | transport input ssh restricts protocols |
| ACL control | Cannot use access-class (no network source) |
access-class <acl> in filters by source IP |
| Default authentication | None (open) — must configure login |
None (open) unless login or
login local is set |
8. Login Security — Brute-Force Protection
Even with SSH enforced, a determined attacker can attempt to brute-force the SSH login by trying thousands of username/password combinations. Cisco IOS provides the login block-for command to automatically block login attempts from an IP address after a configurable number of failures within a time window.
! Block login for 120 seconds if 5 failures occur within 60 seconds: R1(config)# login block-for 120 attempts 5 within 60 ! During the block period, ALL logins are refused (except from trusted IPs). ! A syslog message is generated when the block is triggered. ! Whitelist trusted management IPs (exempt from blocking): R1(config)# ip access-list standard TRUSTED_MGMT R1(config-std-nacl)# permit host 192.168.99.10 R1(config-std-nacl)# exit R1(config)# login quiet-mode access-class TRUSTED_MGMT ! "quiet-mode" is the state during the block period. ! Hosts in TRUSTED_MGMT can still log in during quiet-mode. ! Additional login delay (slow down brute-force even before blocking): R1(config)# login delay 3 ! enforce 3-second delay between login attempts ! Verify login security status: R1# show login Login Block-for period is 120 secs Login Delay is 3 sec Login Max-fail is 5 Login Failure Count: 0 R1# show login failures ! Shows recent failed login attempts with source IP and timestamp
login block-for
command requires that at least one VTY line has
login local or AAA authentication configured. If VTY lines
use only login (line password only), the block-for command
may not activate.
9. Privilege Levels and Role-Based Access
Cisco IOS supports 16 privilege levels (0–15). Level 1 is the default user EXEC mode (limited read-only commands). Level 15 is full privileged EXEC (all commands). Levels 2–14 are customisable — specific commands can be assigned to specific levels, enabling role-based access control where different administrators have different capabilities.
| Level | Name | Default Access |
|---|---|---|
| 0 | Minimum | Only: disable, enable, exit, help, logout |
| 1 | User EXEC | Basic show commands, ping, traceroute — no configuration |
| 2–14 | Custom | Defined by administrator — assign specific commands to levels |
| 15 | Privileged EXEC | All commands including global configuration mode |
Assigning privilege levels to users: ! Level 15 — full admin: R1(config)# username admin privilege 15 secret AdminP@ss ! Level 7 — network operator (show commands + interface resets, no config changes): R1(config)# username netops privilege 7 secret Oper@torP@ss ! Assign specific commands to level 7: R1(config)# privilege exec level 7 show running-config R1(config)# privilege exec level 7 debug ip packet R1(config)# privilege exec level 7 clear interface ! User at level 7 can use those commands without reaching level 15. ! They cannot enter global config mode (configure terminal = level 15). ! Verify current privilege level: R1# show privilege Current privilege level is 15 ! Escalate privilege (if level 15 enable password is set): R1> enable 15 Password: <enable secret>
See: AAA Overview | AAA Local vs RADIUS
10. Enable Password vs Enable Secret
When an SSH user connects to a device and reaches user EXEC mode,
they typically need to enter enable to reach privileged
EXEC mode and make configuration changes. The enable password
and enable secret both protect this transition — but they are
not equivalent.
| Command | Storage in Config | Algorithm | Security Level |
|---|---|---|---|
enable password <pw> |
Plaintext (or Type 7 if service password-encryption active) | None / Vigenere (Type 7) — easily reversed | Weak — do not use |
enable secret <pw> |
Hashed — never stored as plaintext | MD5 (Type 5) by default; SHA-256 (Type 9) in modern IOS | Strong — always use this |
enable algorithm-type sha256 secret <pw> |
SHA-256 hash (Type 8) | PBKDF2 with SHA-256 | Strongest — recommended for modern IOS |
! What an enable password looks like in the config (INSECURE): enable password Cisco123 ↓ After service password-encryption (still weak): enable password 7 0822455D0A16 ! What an enable secret looks like in the config (SECURE): enable secret Cisco123 ↓ Stored as: enable secret 5 $1$mERr$hx5rVt7rPNoS4wqbXKX7m0 ! Type 9 (SHA-256, recommended): enable algorithm-type sha256 secret Cisco123 ↓ Stored as: enable secret 9 $9$H/vbHKTvdEZSSx$f8... Rule: If both enable password and enable secret are configured, enable secret ALWAYS takes precedence — enable password is ignored. Remove enable password if enable secret is configured: R1(config)# no enable password
11. Disabling Telnet — Removing the Insecure Protocol
Even if SSH is configured, Telnet remains available by default on VTY
lines unless explicitly disabled. The transport input
command controls which protocols are accepted on VTY lines.
VTY transport input options:
transport input all ! accept Telnet AND SSH (default — insecure)
transport input telnet ! accept Telnet only (NEVER use in production)
transport input ssh ! accept SSH only (CORRECT — recommended)
transport input none ! accept no connections (disables VTY entirely)
transport input telnet ssh ! accept both (not recommended)
! Disable Telnet and enforce SSH on all VTY lines:
R1(config)# line vty 0 4
R1(config-line)# transport input ssh
! Verify:
R1# show line vty 0
...
Allowed input transports are ssh.
! What happens when Telnet is attempted after "transport input ssh":
$ telnet 192.168.1.1
Trying 192.168.1.1...
% Connection refused by remote host
! SSH connection succeeds normally:
$ ssh -l admin 192.168.1.1
Password: ***
R1#
Completely Disabling Telnet on the Device
! Some hardening guides also recommend disabling Telnet globally, ! though "transport input ssh" on VTY lines is the primary control. ! To additionally prevent outbound Telnet FROM the router: R1(config)# line vty 0 4 R1(config-line)# transport output ssh ! restrict outbound sessions too ! Verify Telnet is truly blocked — from an external host: $ telnet 192.168.1.1 23 telnet: connect to address 192.168.1.1: Connection refused
12. Verification and Troubleshooting Commands
! Show SSH status, version, timeout, and retries:
R1# show ip ssh
SSH Enabled - version 2.0
Authentication timeout: 60 secs; Authentication retries: 3
Minimum expected Diffie Hellman key size : 2048 bits
IOS Keys in SECSH format(ssh-rsa, base64 encoded):
ssh-rsa AAAAB3NzaC1...
! Show all active VTY sessions (SSH and Telnet):
R1# show users
Line User Host(s) Idle Location
* 0 con 0 admin idle 00:00:00
2 vty 0 netops 192.168.99.10 00:02:14 SSH
! Show detailed information about active SSH sessions:
R1# show ssh
Connection Version Mode Encryption Hmac State Username
0 2.0 IN aes256-ctr hmac-sha2-256 Session started admin
0 2.0 OUT aes256-ctr hmac-sha2-256 Session started admin
! Show VTY line configuration including transport settings:
R1# show line vty 0 4
R1# show running-config | section line vty
! Show the RSA key pair:
R1# show crypto key mypubkey rsa
! Show login security status and recent failures:
R1# show login
R1# show login failures
! Terminate a specific active session (disconnect an admin):
R1# clear line vty 2 ! disconnects session on VTY line 2
Common SSH Troubleshooting Issues
| Symptom | Likely Cause | Fix |
|---|---|---|
| SSH disabled — cannot connect | RSA key not generated, or missing domain name | Verify with show ip ssh; generate key with
crypto key generate rsa modulus 2048 |
| Connection refused on port 22 | VTY lines not configured for SSH transport, or ACL blocking the source IP | Check transport input ssh on VTY lines;
check access-class ACL |
| Authentication failure | Username not in local database, wrong password,
or login local not set on VTY |
Verify with show running-config | section line vty;
confirm username exists with show running-config | section username |
| SSH v1 only available | ip ssh version 2 not configured, or
RSA key too small (<768 bits) |
Configure ip ssh version 2; regenerate RSA key
with crypto key generate rsa modulus 2048 |
| Session times out immediately | exec-timeout set too low on VTY lines |
Adjust with exec-timeout <min> <sec>
— use exec-timeout 0 0 for no timeout (lab only) |
| All logins blocked during quiet-mode | login block-for triggered by failed attempts |
Wait for block period to expire, or use console port to
enter clear login quiet-mode |
13. SSH and Telnet Summary — Key Facts
| Topic | Key Fact |
|---|---|
| Telnet port | TCP 23 — cleartext; never use in production |
| SSH port | TCP 22 — encrypted session; industry standard |
| SSH version | Always use SSHv2 (ip ssh version 2);
SSHv1 has known weaknesses |
| Prerequisites for SSH | Hostname, IP domain name, RSA key pair, local username |
| RSA key size | Minimum 768 bits for SSHv2; 2048 bits recommended for production |
| VTY transport command | transport input ssh — blocks Telnet; allows SSH only |
| VTY authentication | login local — uses local username database;
required for SSH |
| VTY ACL command | access-class <acl> in — filters by source IP;
not ip access-group |
| Enable secret vs password | Always use enable secret (hashed);
never enable password (reversible) |
| Brute-force protection | login block-for <sec> attempts <n> within <sec> |
| Idle session timeout | exec-timeout <min> <sec> on VTY lines |
| Privilege levels | 0–15; privilege 15 = full access; use username ... privilege
to control access per user |