255 First: A Deep Dive into CVE-2026-24061
How Telnet Negotiation Allows Pre-Login Injection
255 (IAC): Interpret As Command (Introduction)
I wrote this post because I wanted to understand exactly why the CVE-2026-24061 proof of concept (PoC) works. I could run it, but when I read the code the mechanism was not obvious, and I did not really understand what it was doing during the Telnet setup phase. That sent me down a rabbit hole into Telnet negotiation, NEW-ENVIRON, and the point where telnetd hands control to the system login process. This write-up is the result of that digging, with the aim of making the PoC readable and understandable rather than just something you can execute.
CVE-2026-24061 is triggered during Telnet session setup but the impact is realised later, when authentication begins. The issue occurs before any user interaction, when Telnet negotiation is used to establish state that is then inherited by the system login process. This is not a flaw in the Telnet protocol itself. It is an GNU InetUtils telnetd implementation issue, where user-controlled setup data is carried forward and affects how /usr/bin/login is started. This affects GNU InetUtils telnetd up to and including version 2.7. Upstream has published patches and distributors have issued updates.
The PoC shows how a Telnet client can supply structured data during connection setup which is later consumed when the login program is started. Telnet does not perform authentication, but it does determine the state that the login process starts with, and that is where this vulnerability sits.
Background: How Telnet Really Works
From a user perspective, Telnet appears simple. A connection is made, a banner appears, and the user is prompted for credentials. Behind the scenes there is an initial negotiation phase that occurs before the login program is invoked.
This negotiation phase allows the client and server to agree on capabilities such as terminal behaviour and environment variable handling. These exchanges are not visible to the user and do not involve typed input. They are carried out using control bytes embedded in the data stream.
A key concept is that Telnet is not purely text based. It combines human readable characters with protocol commands. These commands are introduced by a special byte value, 255, known as IAC or Interpret As Command.
Any data following this byte is treated as an instruction rather than text.
Root Cause
The root cause of CVE-2026-24061 is a misplaced trust boundary during the Telnet connection setup phase. Specifically, GNU InetUtils telnetd accepts and retains client-supplied session state before authentication has begun and before the login program is executed. In affected versions, that pre-auth state can influence how the daemon starts the system login process.
This is not a flaw in the Telnet RFCs. Telnet negotiation is just the delivery mechanism. The vulnerability is in telnetd’s implementation, where unauthenticated setup-time data is allowed to bleed into the login invocation path.
In the vulnerable code path, telnetd builds its login invocation from a template string (commonly referred to as login_invocation) and expands placeholders into a final command line. The important placeholder here is %U, which resolves to the daemon’s USER environment variable. In the vulnerable flow, unauthenticated Telnet session setup state can influence the environment telnetd later consults (including USER), meaning %U can expand to attacker-controlled content.
The following excerpt shows how telnetd maps %U to the daemon’s USER environment variable (formatting condensed for readability):
/* telnetd expands %U from the daemon environment */
case 'U':
return getenv ("USER") ? xstrdup (getenv ("USER")) : xstrdup ("");
The vulnerability becomes exploitable because the value substituted via %U is not guaranteed to be a “plain username”. If the substituted value begins with - and includes whitespace (for example -f root), it stops behaving like data and starts behaving like arguments from the perspective of /usr/bin/login. At that point the daemon is no longer passing “a username”; it is effectively allowing untrusted pre-auth state to participate in login’s option parsing.
The template below is representative of the login_invocation style used by GNU InetUtils telnetd and shows the design choice that matters for this issue: conditional expansion that can fall back to %U when no authenticated username is available. This snippet is illustrative (not a verbatim copy from upstream):
/* Illustrative example (not verbatim upstream code) */
const char *login_invocation =
PATH_LOGIN " -p -h %h %?u{-f %u}{%U}";
Once a daemon is constructing a command line from a template, the second ingredient that creates risk is executing the expanded string as a single command rather than invoking login with a strictly constructed argv[]. The snippet below is deliberately simplified pseudocode to show the general pattern; it is not real telnetd code:
/* Pseudocode – illustrates the risk pattern, not real telnetd code */
char *login_cmd = expand_template(login_invocation); /* expands %h, %u, %U etc */
char *argv[] = { "/bin/sh", "-c", login_cmd, NULL };
execv(argv[0], argv);
Telnet is only how the unauthenticated value arrives. The bug is that telnetd allows that value to cross the boundary into the way /usr/bin/login is started, where it can be interpreted as options rather than as a username.
Further reading: upstream GNU InetUtils fix/patch notes and the SafeBreach root cause analysis:
Why Typing the Payload Does Not Work
Typing the payload directly at the login prompt does not produce the same result because it occurs at an entirely different stage of execution. By the time the prompt is displayed, the login program is already running and is explicitly expecting a username as input. Any value entered at this point is handled as user data and processed according to normal authentication rules. Flags or control characters supplied here are not reinterpreted as execution parameters.
In contrast, the exploit succeeds because the payload is delivered earlier, during the setup phase, before the login program exists. The key difference is timing: this value is accepted during session setup and later influences how telnetd starts /usr/bin/login. When an unsafe implementation later consumes those variables, it does so under the assumption that they are trusted. The distinction is not the payload itself, but when and how it is introduced. Timing, rather than content, is what enables the vulnerability.
How the Proof of Concept Exploit Interacts with Telnet
The proof of concept does not rely on malformed packets or undefined behaviour. Instead, it implements a minimal but correct Telnet client that explicitly handles protocol negotiation. The exploit operates entirely within the bounds of the protocol as specified.
Telnet embeds control instructions directly into the data stream. These instructions are identified by a leading control byte and are processed separately from user visible text. The proof of concept monitors the incoming stream for these control sequences and responds to them manually, rather than delegating this behaviour to a standard Telnet client implementation.
This approach allows the client to make deliberate decisions about which options it accepts and how it responds when the server initiates negotiation.
To clarify where this vulnerability occurs within the lifecycle of a Telnet connection, the following diagram illustrates the execution flow from initial connection through to login initialisation. It highlights the distinction between protocol negotiation and authentication logic, and shows the point at which environment variables are supplied and accepted by the server. The key observation is that the NEW-ENVIRON exchange takes place entirely before the login process is started, allowing unauthenticated input to influence the execution context inherited by the authentication mechanism.



