oss-sec mailing list archives

Re: Telnetd Vulnerability Report


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

On Tue, Feb 24, 2026 at 06:29:43AM +0100, Solar Designer wrote:
On Tue, Feb 24, 2026 at 03:17:02AM +0200, Justin Swartz wrote:
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.

I'm now looking at telnet-0.17-85.el9.src.rpm from Rocky Linux 9.  The
telnet server part of it is still based on NetKit 0.17, where the latest
ChangeLog entry is:

22-Jul-2000:
        Bug fixes for environment processing from Olaf Kirch. Also fixes
          privacy issue noticed by Steve Bellovin. Also fix a wrong
          assert().

and the code is:

/* check that variable is safe to pass to login or shell */
#if 0  /* insecure version */
static int envvarok(char *varp) {
    if (strncmp(varp, "LD_", strlen("LD_")) &&
        strncmp(varp, "ELF_LD_", strlen("ELF_LD_")) &&
        strncmp(varp, "AOUT_LD_", strlen("AOUT_LD_")) &&
        strncmp(varp, "_RLD_", strlen("_RLD_")) &&
        strcmp(varp, "LIBPATH") &&
        strcmp(varp, "ENV") &&
        strcmp(varp, "IFS"))
    {
        return 1;
    }
    else {
        /* optionally syslog(LOG_INFO) here */
        return 0;
    }
}

#else
static int envvarok(char *varp) {
    /*
     * Allow only these variables.
     */
    if (!strcmp(varp, "TERM")) return 1;
    if (!strcmp(varp, "DISPLAY")) return 1;
    if (!strcmp(varp, "USER")) return 1;
    if (!strcmp(varp, "LOGNAME")) return 1;
    if (!strcmp(varp, "POSIXLY_CORRECT")) return 1;

    /* optionally syslog(LOG_INFO) here */
    return 0;
}

In my patch against OpenBSD's it is:

+/* This list comes from Linux NetKit telnetd, version 0.17 */
+static char *goodenv_table[] = {
+       "TERM",
+       "DISPLAY",
+       "USER",
+       "LOGNAME",
+       "POSIXLY_CORRECT",
+       NULL
 };

[...]

+envvarok(varp, valp)
+       char *varp, *valp;
 {
[...]
+       for (i = 0; goodenv_table[i]; i++) {
+               if (strcmp(goodenv_table[i], varp))
+                       continue;
+               if (strchr(valp, '/') || strlen(valp) >= 0x100) {
+                       syslog(LOG_NOTICE, "Rejected attempt to set the "
+                               "environment variable \"%s\" to an "
+                               "invalid value", varp);
+                       return (0);
+               }
+               return (1);
+       }
[...]
+       return (0);

So it looks like in the Linux world non-use of an allow list is specific
to InetUtils, which means primarily Debian and derived distros.

Alexander


Current thread: