In 2005, researchers found a textbook buffer overflow in the telnet client's SLC handler — CVE-2005-0469. It got patched. Everyone moved on. Twenty-one years later, the DREAM Security Research Team looked at the exact same function on the telnet server side and found the exact same bug. CVE-2026-32746 carries a CVSS 9.8, gives unauthenticated attackers root on any system running GNU inetutils telnetd, and as of today most distributions still haven't shipped a fix.
The Bug: 108 Bytes, Zero Bounds Checking
The vulnerable code lives in telnetd/slc.c, specifically the add_slc() function. During Telnet option negotiation — before any login prompt appears — the client and server negotiate terminal behavior via LINEMODE suboptions. One of those suboptions is SLC (Set Local Characters), which lets the remote side define special characters for things like interrupt, erase, and kill signals.
The daemon stores these SLC triplets (function code, flags, character value) in a global buffer called slcbuf. Each triplet is 3 bytes. The buffer is 108 bytes. That means it fits exactly 36 triplets.
The protocol allows sending roughly 65 triplets in a single subnegotiation packet.
There is no bounds check.
When an attacker sends more than 36 triplets, writes blow past slcbuf and into whatever sits adjacent in the BSS segment. And what sits next to it is slcptr — the pointer tracking where the next write goes — and def_slcbuf, another pointer used for deferred processing. Corrupting slcptr gives control of where subsequent writes land. Corrupting def_slcbuf turns a later free() call into an arbitrary-free primitive. Either path leads to code execution. And because the daemon typically runs as root under inetd or xinetd, that means full system compromise.
Pre-Auth Means Pre-Everything
SLC negotiation happens during the very first phase of the Telnet option exchange, well before the service prompts for a username. Connect to port 23, send crafted option bytes, overflow triggers. No credentials. No interaction.
800,000 Exposed Instances and Counting
You might think running Telnet in 2026 is a joke. The Shadowserver Foundation tracked roughly 800,000 IP addresses exposing the service globally as of early this year — heavy clusters across Asia, South America, and parts of Europe.
The protocol persists in places where SSH feels like a luxury:
| Environment | Why Telnet survives |
|---|---|
| CNC machines, factory PLCs | Firmware too constrained for SSH stacks |
| Legacy network gear | Management interfaces hardcoded to port 23 |
| Embedded medical devices | Vendor locked, no update path |
| University lab equipment | "It works, don't touch it" |
| Serial-over-IP / KVM consoles | Fallback management channel |
These aren't hypothetical. They're the machines cutting metal, routing hospital networks, and managing power distribution. Many can't be patched — the vendor is gone, the firmware is frozen, or the downtime cost is prohibitive. For these systems, a pre-auth root RCE on port 23 is about as bad as it gets.
The Real Story: Same Bug Class, Ignored for Two Decades
This is the part that should bother every security team. CVE-2005-0469 was the client-side mirror image of this flaw — slc_add_reply() in the telnet client had the same fixed-size buffer, the same missing bounds check, the same BSS memory layout. It was found, assigned a CVE, and patched across every major distribution in 2005.
Nobody walked across the hall to telnetd/slc.c and checked whether the server's add_slc() had the identical problem.
WatchTowr Labs' writeup calls it what it is: a pattern that got treated as a one-off. Security audits followed the path of the disclosure. The client had a bug, so people audited client code. The server code — doing essentially the same thing in mirror — sat untouched for another 21 years.
This pattern repeats constantly: find a vulnerability in one component, fix that component, never check siblings. If you discovered a SQL injection in your user registration endpoint, would you check the password reset endpoint that builds queries the same way? You'd hope so. In practice, teams often ship the targeted fix and move on.
Practical Response
GNU inetutils 2.7, the latest official release, is still vulnerable. Patched commits exist in the project's Git repository, but most distribution packages haven't incorporated them. Only Debian sid had a fix at the time of the initial disclosure.
Find your exposure. Scan for TCP/23 across your network. Check inetd.conf and xinetd.conf for telnet entries. Don't forget container base images — several minimal Linux images ship GNU inetutils, and a misconfigured Dockerfile can leave port 23 listening.
Block external access now. If anything on your perimeter exposes port 23, close it today. This vulnerability is being actively scanned for in the wild.
Kill the service where possible. If the system supports SSH, disable the Telnet daemon. Comment out the inetd line or set disable = yes in xinetd.
For systems that genuinely require it, restrict access to known management IPs via firewall rules and monitor connections for anomalous SLC suboption lengths. WatchTowr published a detection method: vulnerable servers silently accept oversized SLC bursts, while patched ones truncate excess data. That behavioral difference is your canary.
Audit your container images. Run dpkg -l | grep inetutils or the equivalent across your fleet. The package might be installed as a dependency even if nobody configured it to listen.
Stop Treating Bug Fixes as Singletons
We've known since the 1990s that fixed-size buffers without bounds checks kill systems. We've had automated tools to catch them for over a decade. And yet — a 32-year-old missing bounds check in code that writes into a 108-byte global, discoverable by reading a few hundred lines of C, sitting in systems that control physical infrastructure.
The vulnerability isn't in add_slc(). It's in the assumption that fixing a bug means you've fixed the class of bug.