COMMAND
crt0.c
SYSTEMS AFFECTED
FreeBSD 2.1.5, 2.1.6
PROBLEM
Thomas Ptacek and Michael Scher reported that there is a
critically important security problem in FreeBSD 2.1.5's C
runtime support library that will enable anyone with control of
the environment of a process to cause it to execute arbitrary
code. All executable SUID programs on the system are vulnerable
to this problem.
The issue is that FreeBSD 2.1.5's crt0.c start() routine, which
calls the "main()" entry point function in the program that is
starting, will under some circumstances call routines that set
the "locale" of the program. The routines that do this are
heavily dependant on environment variables, which are in some
circumstances copied directly into local character buffers on the
stack of the locale routines.
An immediately exploitable problem is evident in
"startup_setrunelocale()", which, if certain environment variables
are set, will copy the value of "PATH_LOCALE" directly into a 1024
byte buffer on the routine's stack. An attacker simply needs to
insert machine code and virtual memory addresses into the
"PATH_LOCALE" variable, enable startup locale processing, and run
an SUID program.
On FreeBSD 2.1.5, startup locale processing is enabled by
setting the environment variable "ENABLE_STARTUP_LOCALE".
"startup_setrunelocale()" is called if the environment variable
"LC_CTYPE" is set as well.
SOLUTION
FreeBSD 2.2-BETA, as well as OpenBSD, seem to have this problem
resolved. FreeBSD's crt0 start() function does not process
locales and is thus not vulnerable to this problem.
Dan Cross gave untested patch which SHOULD fix the problem, though:
----- Begin startup_setlocale.diff
*** startup_setlocale.c 1997/02/03 07:40:46 1.1
--- startup_setlocale.c 1997/02/03 07:41:47
***************
*** 174,183 ****
return(0);
}
! (void) strcpy(name, PathLocale);
! (void) strcat(name, "/");
! (void) strcat(name, encoding);
! (void) strcat(name, "/LC_CTYPE");
if ((fp = fopen(name, "r")) == NULL)
return(ENOENT);
--- 174,181 ----
return(0);
}
! (void) snprintf(name,
! PATH_MAX, "%s/%s/LC_CTYPE", PathLocale, encoding);
if ((fp = fopen(name, "r")) == NULL)
return(ENOENT);
----- End of startup_setlocale.diff