KSR[T] Advisory #002 Date: Jul 16, 1997 ID #: lin-ldso-002 Operating System(s): Linux Affected Program: ld.so / ld-linux.so Problem Description: ld.so is the run-time linker used by dynamically linked executables(a.out). Inside the error reporting function there is a call to vsprintf, which doesn''t check the size of the string it is storing in an automatic buffer. The ELF version of run-time linker(ld-linux.so) is vulnerable to an almost identical stack overwrite. Compromise: A local user that can execute any dynamically linked setuid binary and can force ld.so to error, can execute arbitrary code as root. Patch/Fix: Upgrade your ld.so to the latest version, or apply the patch below provided by Alan Cox. [mod: Caldera, LST, Red Hat, Suse, yggdrasil, Debian, unifix, wgs? and delix? are stressing to make a patch available in their native format. If your vendor is not on this list, please tell me in private mail. -- R.E.Wolff@BitWizard.nl] ---- cut here ---- --- ld.so-1.7.14/d-link/boot1.c Thu Dec 14 19:08:19 1995 +++ ld.so-1.7.14-fixed/d-link/boot1.c Wed Jul 16 15:37:11 1997 @@ -108,10 +108,12 @@ #include "syscall.h" #include "string.h" +#define ELF_LDSO_IMAGE "/lib/ld-linux.so.1" + static char * _dl_malloc_addr, *_dl_mmap_zero; char * _dl_library_path = 0; /* Where we look for libraries */ char *_dl_preload = 0; /* Things to be loaded before the libs. */ -char *_dl_progname = "/lib/ld-linux.so.1"; +char *_dl_progname = ELF_LDSO_IMAGE; static char * _dl_not_lazy = 0; static char * _dl_warn = 0; /* Used by ldd */ static char * _dl_trace_loaded_objects = 0; @@ -165,6 +167,45 @@ #endif /* + * Stop argv0 overflowing vsprintf, but also try to stop false positives + * We obey the following rule + * + * If namesize < 256 keep + * If name from last / < 256 use that + * else use ELF_LDSO_IMAGE + * + * This ensures /very/long/stupid/nfs/path/we/often/get/foobarcmd + * comes out at least as. + * + * foobarcmd: someerror + * + * Even if we fix vsprintf to be vsnprintf (which we should), this + * ought to be kept to help make real size limited errors clearer. + */ + +static char *argv_remap(char *ptr) +{ + char *tmp; + if(strlen(ptr)<256) + return ptr; + if(!*ptr) + return ptr; + tmp=ptr+strlen(ptr)-1; + /* + * Walk back down the chain until we find a slash + */ + while(tmp>=ptr && *tmp!=''/'') + tmp--; + /* + * No slash, or too long after slash and Im not playing so nyah + */ + if(*tmp!=''/'') + return ELF_LDSO_IMAGE; + if(strlen(tmp)>256) /* Not off by 1 .. strlen includes the / */ + return ELF_LDSO_IMAGE; + return tmp+1; +} +/* * This stub function is used by some debuggers. The idea is that they * can set an internal breakpoint on it, so that we are notified when the * address mapping is changed in some way. @@ -487,7 +528,7 @@ } if (argv[0]) - _dl_progname = argv[0]; + _dl_progname = argv_remap(argv[0]); /* Now we need to figure out what kind of options are selected. Note that for SUID programs we ignore the settings in LD_LIBRARY_PATH */ --- ld.so-1.7.14/ld-so/ld.so.c Tue Nov 14 19:15:02 1995 +++ ld.so-1.7.14-fixed/ld-so/ld.so.c Tue Jun 24 10:55:54 1997 @@ -151,6 +151,46 @@ } #endif +/* + * Stop argv0 overflowing vsprintf, but also try to stop false positives + * We obey the following rule + * + * If namesize < 256 keep + * If name from last / < 256 use that + * else use LDSO_NAME + * + * This ensures /very/long/stupid/nfs/path/we/often/get/foobarcmd + * comes out at least as. + * + * foobarcmd: someerror + * + * Even if we fix vsprintf to be vsnprintf (which we should), this + * ought to be kept to help make real size limited errors clearer. + */ + +static char *argv_remap(char *ptr) +{ + char *tmp; + if(strlen(ptr)<256) + return ptr; + if(!*ptr) + return ptr; + tmp=ptr+strlen(ptr)-1; + /* + * Walk back down the chain until we find a slash + */ + while(tmp>=ptr && *tmp!=''/'') + tmp--; + /* + * No slash, or too long after slash and Im not playing so nyah + */ + if(*tmp!=''/'') + return LDSO_IMAGE; + if(strlen(tmp)>256) /* Not off by 1 .. strlen includes the / */ + return LDSO_IMAGE; + return tmp+1; +} + void shared_loader(int func, ...) { @@ -207,12 +247,14 @@ save_mapinfo(mapinfo); #endif argv0 = va_arg(ap, char *); + argv0 = arg_remap(argv0); __environ = va_arg(ap, char **); __SHARED_LIBRARIES__ = va_arg(ap, struct libentry **); _SHARABLE_CONFLICTS__ = va_arg(ap, struct fixuplist *); if (func == FUNC_LINK_AND_CALLBACK) callback = va_arg(ap, callbackptr); va_end(ap); + break; default: /* you want me to do what? */ @@ -228,7 +270,8 @@ /* find out who we are, in case somebody wants to know */ if (!argv0 && !(argv0 = getenv(LDD_ARGV0))) argv0 = LDSO_IMAGE; - + argv0=argv_remap(argv0); + /* hmm, you want your own configuration, do you? */ if (getuid() == geteuid() && getgid() == getegid()) { @@ -328,6 +371,11 @@ .text section. This is passed to ldpreload() below */ if (preload || callback) { + if(nlibs==11) + { + fdprintf(2, "%s: too many preloads\n",argv[0]); + exit(EXIT_FATAL); + } libs[nlibs] = alloca(strlen(buffer)+1); strcpy(libs[nlibs], buffer); nlibs++; ---- cut here ---- ----- KSR[T] Website : http://www.dec.net/ksrt E-mail: ksrt@dec.net