23
The plaintext remote-terminal port, still the default admin interface on switches, cameras and embedded boards — and the port people open by accident and get scanned on within minutes.
Run the line for your platform. Each one names the process holding the port and the address it bound to, which is usually the whole answer.
sudo lsof -nP -iTCP:23 -sTCP:LISTENOne row per listening socket. The columns are COMMAND, PID, USER, then NAME, which holds the bound address — `127.0.0.1:23` means only this machine can reach it and a remote client never will, while `*:23` means every interface. `-n` skips host-name lookup and `-P` skips port-name lookup, so you get numbers rather than `telnet`. `-sTCP:LISTEN` is the filter that hides established client connections; drop it to see who is currently connected as well. No output at all, with lsof exiting 1, means nothing holds the port. Without `sudo` you only see your own processes, so a daemon owned by root looks invisible.
sudo ss -tlnp 'sport = :23'`-t` limits it to TCP, `-l` to listening sockets, `-n` keeps ports numeric and `-p` adds the owning process. Read the Local Address:Port column: `0.0.0.0:23` is every IPv4 interface, `127.0.0.1:23` is loopback only, and `[::]:23` is the IPv6 wildcard, which on most Linux hosts also accepts IPv4. The process appears as `users:(("systemd",pid=1,fd=42))` when the port is socket-activated, which is normal for telnet and not a sign of anything wrong. Without `sudo` that column is blank for processes you do not own.
sudo netstat -tlnp | grep ':23 'The older net-tools spelling, for boxes without iproute2. The flags mean the same things and the last column is the PID/Program name pair — its own man page warns the value "is not trustworthy" and that superuser privileges are needed to see it for sockets you do not own. Keep the trailing space in the grep pattern: without it `:23` also matches `:2323` and `:23000`.
netstat -ano | findstr :23`-a` shows listening ports as well as live connections, `-n` keeps addresses and ports numeric, and `-o` appends the owning PID as the last column. Resolve that PID with `tasklist /FI "PID eq 1234"`. Mind the match: `findstr` compares substrings, so `:23` also hits `:2323` and any ephemeral source port containing 23 — confirm the Local Address column ends in exactly `:23` before you kill anything.
`nmap -Pn -p 23 192.0.2.10` answers the only question that matters from outside the host, and `-Pn` stops nmap skipping a device that ignores pings. `open` means something completed the TCP handshake — if this is a device you did not deliberately expose, treat it as an incident. `closed` means the host answered with a reset, so it is reachable and nothing is listening. `filtered` means nothing came back at all: a firewall or ACL is dropping the packet rather than refusing it. Add `--reason` to see which of the three it actually was, and `-sV` to let nmap read the telnet banner, which usually names the device vendor. Scan only hosts you are responsible for.
| What you see | What it means |
|---|---|
telnet: connect to address 192.0.2.10: Connection refused | The packet reached the host and its kernel replied with a reset because no process holds port 23. The daemon is stopped or was never installed, or it is bound to an address that does not include the one you dialled. Run the probe above on the target itself, not on your laptop. |
telnet: connect to address 192.0.2.10: Operation timed out | Nothing answered at all. A packet filter is dropping the SYN silently — an on-host firewall, a network ACL, or a VLAN you are not on. A refusal comes back instantly; a drop takes tens of seconds. The delay is itself the diagnosis. |
Connected to 192.0.2.10. ... Connection closed by foreign host. | The port is open and the handshake succeeded, then the server hung up. Usually a TCP wrappers or access-list rule that admits the connection and then rejects your source address, a device that allows one session at a time and already has one, or a daemon that crashed on start-up. This is not a network problem: everything up to the application layer worked. |
telnetd: bind: Address already in use | Something already holds port 23 on the address you asked for. Run the platform probe, look at the bound address in the output, and check whether the conflict is real: a process on `0.0.0.0:23` blocks a second bind to `192.0.2.10:23`, while two processes on different specific addresses coexist happily. |
bind: Permission denied when starting a listener on 23 as a normal user | Ports below 1024 are privileged on Unix-like systems, and the kernel refuses the bind with EACCES before anything else is checked. Start the service through its init system as root, grant the binary `CAP_NET_BIND_SERVICE` on Linux, or listen high and redirect. The port is free; you are simply not allowed to have it. |
IANA assigns port 23 to `telnet` on both TCP and UDP, citing RFC 854, but only the TCP half is ever used. Telnet is a character-stream terminal protocol with no encryption and no integrity check of any kind: the login name, the password and every keystroke afterwards travel as readable bytes. Nothing has run it as a serious login service on a general-purpose machine for two decades, yet it survives as the factory management interface on network switches, IP cameras, DVRs, set-top boxes, serial console servers, industrial gear and hobby boards, which is why it keeps appearing on networks nobody intended to expose. There is a second, entirely different reason engineers search this port: `telnet host 25` used as a bare TCP poke to see whether something answers. That use is a client, not a listener, and carries none of the risk — `nc -vz host 25` does the same job and is what most systems ship today, since macOS and several Linux distributions no longer install a telnet client by default. One thing to know before you go hunting for the process: telnetd is classically started on demand rather than run continuously, so on a systemd host with socket activation, or on an older box running inetd or xinetd, the process shown holding port 23 is `systemd` or `inetd` — the telnetd itself only exists once a connection arrives.
Do not expose port 23 to the internet, and prefer not to expose it to your office network either. Anything on the path between client and server — a switch, a compromised router, another machine on the same Wi-Fi — reads the password in clear text, and there is no server certificate, so there is nothing to detect an interceptor with. Public telnet listeners are found by internet-wide scanners continuously and immediately tried against lists of vendor default credentials; that pattern is how large botnets of consumer devices have historically been assembled, and an embedded device is usually running an old, unpatched daemon on top of it. The correct replacement is SSH on port 22, which gives you the same shell with host-key verification and encryption. When the device genuinely speaks nothing but telnet, keep it on a management VLAN reachable only through an SSH jump host or a VPN, change the factory password before it ever touches a network, and check what your router actually forwards — UPnP has a long history of publishing ports nobody asked it to. "It is behind NAT" is not a control you can verify; a port-forward rule or a UPnP mapping undoes it silently.
All of these run in your browser — nothing is uploaded.
Ports people usually end up checking in the same session as 23.
Almost always permissions. lsof and ss list the socket but hide the owning process when it belongs to another user, so a server started by a system account, by a container runtime or by launchd shows up as an anonymous listener until you re-run the command with sudo. On Windows the opposite happens: netstat shows nothing at all, yet a bind still fails, which means the port sits inside a range that Hyper-V, WSL2 or Docker Desktop reserved at boot.
Look at what it is first. An orphaned dev server is safe to kill; a database with a write in flight is not, and neither is a system service that something else depends on. If the owner is a container, stop the container instead — killing the process the host sees only makes the runtime restart it. When the process is something you need, moving your own service to another port is the faster fix.
The listener bound to 127.0.0.1 rather than 0.0.0.0. The probes on every page print the bound address next to the process for exactly this reason: a loopback bind is reachable from the machine itself and from nowhere else, no matter how the firewall is configured. Many frameworks made that the default deliberately, so it is usually a flag rather than a bug. If the address is already 0.0.0.0 and it still fails, the next suspect is a host firewall or a cloud security group.
Barely. Internet-wide scanners sweep the whole port range continuously, so an unusual number buys hours, not safety, and it costs you every colleague who now has to remember it. It does cut the noise in your logs from opportunistic scans of the default port, which is a real if modest benefit. Authentication, a firewall that restricts source addresses, and not exposing the service at all are the measures that actually change the outcome.
Still stuck on port 23? browse every port in the reference — or go back to the failure message you actually got, above.