Who uses telnet anyway? It's deprecated. Everyone uses ssh for any kind of remote access.
On Tuesday, February 24th, 2026 at 7:51 AM, Justin Swartz <[email protected]> wrote: > Greetings, > > 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. > > 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: > > [email protected]:~$ 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 > > > [email protected]:~$ 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. > > > [email protected]:~$ 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 > > > [email protected]:~$ .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. > > 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. > > 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 >
