Before using a telnet server
It only makes sense in a closed test environment, or when you have to talk to equipment that speaks nothing but telnet. Besides the setup steps, this article uses a packet capture to show that the password really does travel in readable form.
| File to edit | Purpose |
|---|---|
/etc/inetd.conf | The telnet line (switching it on and off) |
/etc/hosts.deny | Restricting where connections may come from |
Checking what is installed
The client is installed from the start; the server is not.
kazulog@sv1:~$ apt list --installed 2>/dev/null | grep -E "telnet|inetd" | cat
inetutils-telnet/resolute-updates,resolute-security,now 2:2.7-2ubuntu1.1 amd64 [installed,automatic]
telnet/resolute-updates,resolute-security,now 0.17+2.7-2ubuntu1.1 all [installed,automatic]
kazulog@sv1:~$ apt-cache policy inetutils-telnetd | head -3
inetutils-telnetd:
Installed: (none)
Candidate: 2:2.7-2ubuntu1.1kazulog@sv2:~$ which telnet ssh
/usr/bin/telnet
/usr/bin/ssh| Package | Role |
|---|---|
inetutils-telnet | The client (the telnet command), installed by default |
inetutils-telnetd | The server. The package named telnetd is only a dummy that installs this one |
Installing the telnet server
sudo apt install -y inetutils-telnetdThe telnet server does not listen by itself; the super-server inetutils-inetd accepts the connection and starts it, so it is installed too. How that works is covered in Configuring inetd.
kazulog@sv1:~$ sudo apt install -y inetutils-telnetd | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
Installing:
inetutils-telnetd
Installing dependencies:
inetutils-inetd tcpd update-inetd
Summary:
Upgrading: 0, Installing: 4, Removing: 0, Not Upgrading: 96
Download size: 153 kB
Space needed: 546 kB / 63.1 GB available
Get:1 http://archive.ubuntu.com/ubuntu resolute/universe amd64 tcpd amd64 7.6.q-36build2 [24.6 kB]
Get:2 http://archive.ubuntu.com/ubuntu resolute/main amd64 update-inetd all 4.54build1 [23.0 kB]
Get:3 http://archive.ubuntu.com/ubuntu resolute-updates/universe amd64 inetutils-inetd amd64 2:2.7-2ubuntu1.1 [45.2 kB]
Get:4 http://archive.ubuntu.com/ubuntu resolute-updates/universe amd64 inetutils-telnetd amd64 2:2.7-2ubuntu1.1 [60.6 kB]
Preconfiguring packages ...
Fetched 153 kB in 2s (85.3 kB/s)
Selecting previously unselected package tcpd.
(Reading database ... 91400 files and directories currently installed.)
Preparing to unpack .../tcpd_7.6.q-36build2_amd64.deb ...
Unpacking tcpd (7.6.q-36build2) ...
Selecting previously unselected package update-inetd.
Preparing to unpack .../update-inetd_4.54build1_all.deb ...
Unpacking update-inetd (4.54build1) ...
Selecting previously unselected package inetutils-inetd.
Preparing to unpack .../inetutils-inetd_2%3a2.7-2ubuntu1.1_amd64.deb ...
Unpacking inetutils-inetd (2:2.7-2ubuntu1.1) ...
Selecting previously unselected package inetutils-telnetd.
Preparing to unpack .../inetutils-telnetd_2%3a2.7-2ubuntu1.1_amd64.deb ...
Unpacking inetutils-telnetd (2:2.7-2ubuntu1.1) ...
Setting up update-inetd (4.54build1) ...
Setting up tcpd (7.6.q-36build2) ...
Setting up inetutils-inetd (2:2.7-2ubuntu1.1) ...
Created symlink '/etc/systemd/system/inetd.service' → '/usr/lib/systemd/system/inetutils-inetd.service'.
Created symlink '/etc/systemd/system/multi-user.target.wants/inetutils-inetd.service' → '/usr/lib/systemd/system/inetutils-inetd.service'.
Setting up inetutils-telnetd (2:2.7-2ubuntu1.1) ...Installing it is not enough. Line 23 of /etc/inetd.conf gets the telnet entry, but it starts with #<off># and is inactive. With no active line, inetd skips starting, and port 23 stays closed.
kazulog@sv1:~$ grep -n telnet /etc/inetd.conf
23:#<off># telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd
kazulog@sv1:~$ systemctl status inetutils-inetd --no-pager | head -9 | cat
○ inetutils-inetd.service - GNU Network Utilities internet superserver
Loaded: loaded (/usr/lib/systemd/system/inetutils-inetd.service; enabled; preset: enabled)
Active: inactive (dead) (Result: exec-condition) since Sat 2026-09-12 09:08:41 JST; 7s ago
Invocation: 5197dfcb6b574992b9abab5f7049d3a7
Condition: start condition unmet at Sat 2026-09-12 09:08:41 JST; 7s ago
Docs: man:inetutils-inetd(8)
https://www.gnu.org/software/inetutils/manual/
Process: 2629 ExecCondition=grep -qr ^[0-9A-Za-z/] /etc/inetd.conf /etc/inetd.d/ (code=exited, status=1/FAILURE)
Mem peak: 1.7M
kazulog@sv1:~$ sudo ss -tlnp 'sport = :23'; echo "ss rc=$?"
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ss rc=0Enabling telnet (/etc/inetd.conf)
Remove the leading #<off># from line 23 of /etc/inetd.conf. Use the dedicated command update-inetd for this line (editing by hand also works, but update-inetd reloads inetd as well).
sudo update-inetd --enable telnettelnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetdWith an active line, inetd starts and listens on port 23. The protocol field says tcp, so it listens on IPv4 only.
kazulog@sv1:~$ sudo update-inetd --enable telnet
kazulog@sv1:~$ grep -n telnet /etc/inetd.conf
23:telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd
kazulog@sv1:~$ systemctl is-active inetutils-inetd
active
kazulog@sv1:~$ sudo ss -tlnp 'sport = :23'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 10 0.0.0.0:23 0.0.0.0:* users:(("inetutils-inetd",pid=3120,fd=4)) Connecting
Create a test user. This password exists to show that it travels in readable form; do not use it in production.
kazulog@sv1:~$ sudo useradd -m -s /bin/bash telnetdemo
kazulog@sv1:~$ echo 'telnetdemo:TelnetIsPlaintext' | sudo chpasswd
kazulog@sv1:~$ id telnetdemo
uid=1001(telnetdemo) gid=1001(telnetdemo) groups=1001(telnetdemo)Connect from another server, log in and run w.
kazulog@sv2:~$ telnet 192.168.100.10
Trying 192.168.100.10...
Connected to 192.168.100.10.
Escape character is '^]'.
Linux 7.0.0-30-generic (ubuntu) (pts/0)
sv1 login: telnetdemo
Password:
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-30-generic x86_64)
* Documentation: https://docs.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sat Sep 12 09:09:13 JST 2026
System load: 0.4 Processes: 108
Usage of /: 3.5% of 60.93GB Users logged in: 0
Memory usage: 12% IPv4 address for ens2: 10.19.12.11
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
101 updates can be applied immediately.
91 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
telnetdemo@sv1:~$ w
09:09:14 up 7 min, 1 user, load average: 0.40, 0.25, 0.14
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
telnetde pts/0 192.168.100.20 09:09 1.00s 0.04s ? w
telnetdemo@sv1:~$ exit
logout
Connection closed by foreign host.Confirming the password travels in the clear
The login above was done while capturing on the link between sv1 and the switch. Extracting what the client sent shows exactly what was typed.
$ tshark -r ubuntu-telnet.pcap -n -Y 'ip.src==192.168.100.20 && telnet.data' -T fields -e frame.number -e telnet.data
22 telnetdemo\r
27 TelnetIsPlaintext\r
40 w\r
45 exit\rThe user name (No.22), the password (No.27) and the commands (w in No.40, exit in No.45) are all readable. The password packet in hex contains the string as typed.
$ tshark -r ubuntu-telnet-no27.pcap -n -x
0000 52 54 00 9b cb ec 52 54 00 3b a6 5b 08 00 45 00 RT....RT.;.[..E.
0010 00 46 39 44 40 00 40 06 b7 fe c0 a8 64 14 c0 a8 .F9D@.@.....d...
0020 64 0a 8a e4 00 17 8d 63 fa f2 92 5d 74 f7 80 18 d......c...]t...
0030 00 fb 8d 50 00 00 01 01 08 0a 33 01 40 c2 2e be ...P......3.@...
0040 78 34 54 65 6c 6e 65 74 49 73 50 6c 61 69 6e 74 x4TelnetIsPlaint
0050 65 78 74 0d ext.Comparing with SSH
Do the same thing as the same user, over SSH with password authentication.
kazulog@sv2:~$ ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null telnetdemo@192.168.100.10
Warning: Permanently added '192.168.100.10' (ED25519) to the list of known hosts.
telnetdemo@192.168.100.10's password:
Welcome to Ubuntu 26.04 LTS (GNU/Linux 7.0.0-30-generic x86_64)
* Documentation: https://docs.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Sat Sep 12 09:11:17 JST 2026
System load: 0.13 Processes: 109
Usage of /: 3.5% of 60.93GB Users logged in: 0
Memory usage: 12% IPv4 address for ens2: 10.19.12.11
Swap usage: 0%
Expanded Security Maintenance for Applications is not enabled.
101 updates can be applied immediately.
91 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
telnetdemo@sv1:~$ w
09:11:17 up 9 min, 1 user, load average: 0.13, 0.18, 0.12
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
telnetde pts/0 192.168.100.20 09:11 0.00s 0.05s ? w
telnetdemo@sv1:~$ exit
logout
Connection to 192.168.100.10 closed.In the same capture, not a single SSH packet contains the password string.
$ tshark -r ubuntu-telnet.pcap -n -Y 'tcp.port==22 && frame contains "TelnetIsPlaintext"' | wc -l
0The only readable part is the version string exchanged at the start.
$ tshark -r ubuntu-telnet.pcap -n -Y 'ssh.protocol' -T fields -e frame.number -e ip.src -e ssh.protocol
62 192.168.100.20 SSH-2.0-OpenSSH_10.2p1 Ubuntu-2ubuntu3.5
64 192.168.100.10 SSH-2.0-OpenSSH_10.2p1 Ubuntu-2ubuntu3.5An encrypted packet from the client shows no readable string in hex either.
$ tshark -r ubuntu-telnet-no74.pcap -n -x
0000 52 54 00 9b cb ec 52 54 00 3b a6 5b 08 00 45 b8 RT....RT.;.[..E.
0010 00 88 97 ae 40 00 40 06 58 9a c0 a8 64 14 c0 a8 ....@.@.X...d...
0020 64 0a 80 bc 00 16 50 bf 95 d5 93 3b 05 b2 80 18 d.....P....;....
0030 01 01 94 85 00 00 01 01 08 0a 53 3a 79 f6 5c c6 ..........S:y.\.
0040 25 61 00 00 00 0c 0a 15 00 00 00 00 00 00 00 00 %a..............
0050 00 00 01 28 41 dc e0 7e 4d fe c6 7e 1a e2 a3 3a ...(A..~M..~...:
0060 d0 52 39 09 3e af 3c 1f 14 8e d1 bd 1c 2d 65 e4 .R9.>.<......-e.
0070 1f 90 c9 75 3f f2 fb b7 cc 2e 36 04 61 b9 37 6e ...u?.....6.a.7n
0080 45 25 45 53 ed 1c e9 76 9b 09 5f 15 90 ea 48 72 E%ES...v.._...Hr
0090 85 22 04 a7 dc 9b ."....| Item | telnet | SSH |
|---|---|---|
| User name and password | Readable | Encrypted |
| Commands typed | Readable | Encrypted |
| What is readable | Everything | Only the initial version string |
Restricting where connections come from (/etc/hosts.deny)
The registered line goes through /usr/sbin/tcpd, so /etc/hosts.deny can refuse a source. The daemon name is telnetd.
sudo vi /etc/hosts.denytelnetd: 192.168.100.20kazulog@sv1:~$ cat /etc/hosts.deny
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.
# See the manual pages hosts_access(5) and hosts_options(5).
#
# Example: ALL: some.host.name, .some.domain
# ALL EXCEPT in.fingerd: other.host.name, .other.domain
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# The PARANOID wildcard matches any host whose name does not match its
# address.
#
# You may wish to enable this to ensure any programs that don't
# validate looked up hostnames still leave understandable logs. In past
# versions of Debian this has been the default.
# ALL: PARANOID
telnetd: 192.168.100.20Only telnetd is named, so other services such as SSH are unaffected. The refused side is disconnected right after connecting.
kazulog@sv2:~$ timeout 8 telnet 192.168.100.10 < /dev/null
Trying 192.168.100.10...
Connected to 192.168.100.10.
Escape character is '^]'.
Connection closed by foreign host.kazulog@sv1:~$ sudo journalctl --since "-5 min" --no-pager | grep "refused connect" | cat
Sep 12 09:14:42 sv1 telnetd[4642]: refused connect from 192.168.100.20 (192.168.100.20)The evaluation order of hosts.allow and hosts.deny, and the warning about writing ALL: ALL, are covered in the inetd article.
Final state of the configuration files
This is the state with everything above applied. Only two files were touched: /etc/inetd.conf and /etc/hosts.deny.
kazulog@sv1:~$ grep -n telnet /etc/inetd.conf
23:telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd
kazulog@sv1:~$ cat /etc/hosts.deny
# /etc/hosts.deny: list of hosts that are _not_ allowed to access the system.
# See the manual pages hosts_access(5) and hosts_options(5).
#
# Example: ALL: some.host.name, .some.domain
# ALL EXCEPT in.fingerd: other.host.name, .other.domain
#
# If you're going to protect the portmapper use the name "rpcbind" for the
# daemon name. See rpcbind(8) and rpc.mountd(8) for further information.
#
# The PARANOID wildcard matches any host whose name does not match its
# address.
#
# You may wish to enable this to ensure any programs that don't
# validate looked up hostnames still leave understandable logs. In past
# versions of Debian this has been the default.
# ALL: PARANOID
telnetd: 192.168.100.20
kazulog@sv1:~$ systemctl is-enabled inetutils-inetd; systemctl is-active inetutils-inetd
enabled
active
kazulog@sv1:~$ sudo ss -tlnp 'sport = :23'
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 10 0.0.0.0:23 0.0.0.0:* users:(("inetutils-inetd",pid=3120,fd=4))
kazulog@sv1:~$ dpkg -l | grep -E "inetutils|tcpd|update-inetd" | cat
ii inetutils-inetd 2:2.7-2ubuntu1.1 amd64 internet super server
ii inetutils-telnet 2:2.7-2ubuntu1.1 amd64 telnet client
ii inetutils-telnetd 2:2.7-2ubuntu1.1 amd64 telnet server
ii tcpd 7.6.q-36build2 amd64 Wietse Venema's TCP wrapper utilities
ii tcpdump 4.99.6-1 amd64 command-line network traffic analyzer
ii telnet 0.17+2.7-2ubuntu1.1 all transitional dummy package for inetutils-telnet default switch
ii update-inetd 4.54build1 all inetd configuration file updater| File | Change |
|---|---|
/etc/inetd.conf | Remove #<off># from line 23 (update-inetd --enable telnet) |
/etc/hosts.deny | Append telnetd: 192.168.100.20 |
| Packages | inetutils-telnetd, plus inetutils-inetd / tcpd / update-inetd pulled in with it |
Stopping and removing
To stop it temporarily, switch the line back off. The #<off># prefix returns to line 23 and port 23 closes.
sudo update-inetd --disable telnetRemoving the package also removes the telnet line from /etc/inetd.conf. The packages pulled in with it go with autoremove, but inetutils-inetd and update-inetd stay in the rc state with their configuration files, and /etc/inetd.conf remains.
kazulog@sv1:~$ sudo update-inetd --disable telnet
kazulog@sv1:~$ grep -n telnet /etc/inetd.conf
23:#<off># telnet stream tcp nowait root /usr/sbin/tcpd /usr/sbin/telnetd
kazulog@sv1:~$ sudo ss -tlnp 'sport = :23'; echo "ss rc=$?"
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
ss rc=0
kazulog@sv1:~$ sudo apt purge -y inetutils-telnetd | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
The following packages were automatically installed and are no longer required:
inetutils-inetd tcpd update-inetd
Use 'sudo apt autoremove' to remove them.
REMOVING:
inetutils-telnetd*
Summary:
Upgrading: 0, Installing: 0, Removing: 1, Not Upgrading: 96
Freed space: 184 kB
(Reading database ... 91438 files and directories currently installed.)
Removing inetutils-telnetd (2:2.7-2ubuntu1.1) ...
Processing triggers for man-db (2.13.1-1build1) ...
(Reading database ... 91429 files and directories currently installed.)
Purging configuration files for inetutils-telnetd (2:2.7-2ubuntu1.1) ...
kazulog@sv1:~$ sudo apt autoremove -y | cat
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
Reading package lists...
Building dependency tree...
Reading state information...
Solving dependencies...
REMOVING:
inetutils-inetd tcpd update-inetd
Summary:
Upgrading: 0, Installing: 0, Removing: 3, Not Upgrading: 96
Freed space: 361 kB
(Reading database ... 91429 files and directories currently installed.)
Removing inetutils-inetd (2:2.7-2ubuntu1.1) ...
Removing tcpd (7.6.q-36build2) ...
Removing update-inetd (4.54build1) ...
Processing triggers for man-db (2.13.1-1build1) ...
kazulog@sv1:~$ dpkg -l | grep -E "inetd|telnet|tcpd" | cat
rc inetutils-inetd 2:2.7-2ubuntu1.1 amd64 internet super server
ii inetutils-telnet 2:2.7-2ubuntu1.1 amd64 telnet client
ii tcpdump 4.99.6-1 amd64 command-line network traffic analyzer
ii telnet 0.17+2.7-2ubuntu1.1 all transitional dummy package for inetutils-telnet default switch
rc update-inetd 4.54build1 all inetd configuration file updater
kazulog@sv1:~$ ls -l /etc/inetd.conf 2>&1
-rw-r--r-- 1 root root 1062 Sep 12 09:15 /etc/inetd.confTest environment and session logs
Two Ubuntu 26.04 LTS Servers on CML. The disks were wiped immediately beforehand, because packages left over from earlier testing would stop the procedure from reproducing.
sv1 sv2
ens3 192.168.100.10/24 ens3 192.168.100.20/24
inetutils-telnetd (23/tcp) telnet / ssh
| |
+----------- LAB-SW ------------+
^ captured on this link (ports 23 and 22)The full capture, with both the telnet and the SSH login in one file:
Capture of the telnet and SSH logins (ubuntu-telnet.pcap)| Step | sv1 | sv2 |
|---|---|---|
| Initial state (just wiped) | show / conf / log | show / conf / log |
| Installing inetutils-telnetd | show / conf / log | — |
| Enabling telnet | show / conf / log | — |
| Creating the test user | show / conf / log | — |
| Logging in over telnet | — | telnet |
| Logging in over SSH | — | ssh |
| Analysing the capture | tshark | — |
| Refusing a source | show / conf / log | — |
| Checking the refusal | show / conf / log | show / conf / log |
| Final state of the files | show / conf / log | — |
| Disabling and removing | show / conf / log | — |
Reference
Related articles
- Changing the Hostname on Ubuntu 26.04 LTS Server (hostnamectl)
- Updating Packages on Ubuntu 26.04 LTS Server (apt update / upgrade)
- Setting the Timezone and Time Synchronisation on Ubuntu 26.04 LTS Server
- Creating Users and Granting sudo Privileges on Ubuntu 26.04 LTS Server
- Configuring the SSH Server on Ubuntu 26.04 LTS Server
- Setting Up a telnet Server on Ubuntu 26.04 LTS Server
- Configuring inetd (the Super-Server) on Ubuntu 26.04 LTS Server
- Setting Up a TFTP Server on Ubuntu 26.04 LTS Server (tftpd-hpa)
- FTP Server Setup on Ubuntu 26.04 LTS Server (vsftpd)
- Setting Up a syslog Server on Ubuntu 26.04 LTS Server (rsyslog)
- Managing Services with systemctl and Reading Logs with journalctl on Ubuntu 26.04 LTS Server
- Configuring Automatic Updates on Ubuntu 26.04 LTS Server (unattended-upgrades)
- Automating the Initial Setup of Ubuntu 26.04 LTS Server with cloud-init
- Configuring Kernel Parameters on Ubuntu 26.04 LTS Server (sysctl)
- Configuring the Network on Ubuntu 26.04 LTS Server (Netplan)
- Configuring Name Resolution on Ubuntu 26.04 LTS Server (systemd-resolved)
- Changing the NTP Source on Ubuntu 26.04 LTS Server (chrony)
- Static Routes on Ubuntu 26.04 LTS Server (Netplan)
- How to Install the Latest neovim from the Official Site on Ubuntu 26.04 LTS Server