COMMAND
execve
SYSTEMS AFFECTED
linux-2.2.12
PROBLEM
'ben' found following. He discovered a really nasty stack smash
bug in linux-2.2.12. Previous versions of kernel were not
checked. The implications of this bug could be very dire. It may
be possible to easily obtain root privilege on any box running
this kernel.
Basically the problem is that the execve system call checks that
argv is a valid pointer but it doesn't check that all of the
pointers in argv array are valid pointers. If you pass bad
pointers into the execve system call you can corrupt the processes
stack before it returns to user space. Then when the kernel hands
off the process to the elf loader code and which begins to setup
the processes it can be made to execute some malicious code in
place of the program's main function.
This is particularly scary because all of this occurs BEFORE the
program begins executing its main function and AFTER the program
returns to user space with privilege. Therefore no matter how
well audited the program may be it can be used as to gain
privilege.
Alan Cox does not share same opinion as Ben. However, Ben sent
additional info. Here is one ltrace fragment where my program
only corrupts one of the parameters:
[pid 578] execv("/bin/grep", 0x7ffffcdc <unfinished ...>
[pid 578] __libc_start_main(0x0804a4e0, 200, 0x7fffb3a4, 0x08048bf4, 0x080516dc <unfinished ...>
[pid 578] --- SIGSEGV (Segmentation fault) ---
[pid 578] +++ killed by SIGSEGV +++
--- SIGCHLD (Child exited) ---
Here is some information from gdb:
(gdb) core-file /tmp/core
Core was generated by `=>> -#/g_v6Ej18HRtU {d:7&a:ɳ^Qt:XA:W'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /lib/ld-linux.so.2...done.
#0 0x2aae60f6 in getenv (name=0x2aba8562 "LLOC_TRIM_THRESHOLD_")
at ../sysdeps/generic/getenv.c:88
../sysdeps/generic/getenv.c:88: No such file or directory.
(gdb) bt
#0 0x2aae60f6 in getenv (name=0x2aba8562 "LLOC_TRIM_THRESHOLD_")
at ../sysdeps/generic/getenv.c:88
#1 0x2aae689b in __secure_getenv (name=0x2aba8560 "MALLOC_TRIM_THRESHOLD_")
at secure-getenv.c:29
#2 0x2ab1e2e0 in ptmalloc_init () at malloc.c:1689
#3 0x2aade211 in __libc_preinit (argc=200, argv=0x7fffb3a4, envp=0x7fffb6c8)
at set-init.c:26
#4 0x2aade030 in __libc_start_main (main=0x804a4e0 <strcpy+5500>, argc=200,
argv=0x7fffb3a4, init=0x8048bf4, fini=0x80516dc <strcpy+34680>,
rtld_fini=0x2aab5ad4 <_dl_fini>, stack_end=0x7fffb39c)
at ../sysdeps/generic/libc-start.c:68
(gdb)
This was just one run. There were other runs where more
interesting things happened. There was one in particular where
the pointer to init was corrupted. Ben putted the source code
for the program I was debugging at the time when he stumbled into
this at:
ftp://ftp.bastille-linux.org/bastille/broken-fuzz.c.gz
Note: this is not a working program!!! Do not take this as a
release. Its its current form its only purpose is to demonstrate
the problem. To trigger the problem simply run the program with
the -ba option and the name of your favorite exectuable. e.g.
"./fuzz -ba grep"
Matt Chapman sent a demo program. This program (on his system at
least 5 bad arguments are needed) reproducibly dies with SIGSEGV
on 2.2.12. A similarly configured system with kernel 2.0.36
correctly reports EFAULT. This would not normally be a problem,
however... the below program will not dump core for an ordinary
user, only root, which makes us believe that the fault occurs
after the process has gained the root euid from /bin/su. A gdb
trace suggests the usual heap corruption in glibc, which does not
seem to be related to the arguments passed to execve (as long as
they are bad), so there's doubt if it is exploitable. However it
is most likely a bug somewhere.
#include <unistd.h>
#include <errno.h>
#define BADPTR (char *)0x10 /* for example */
int main(int argc, char **argv, char **envp)
{
char *args[7];
int i;
args[0] = "su";
for (i = 1; i < 6; i++) {
args[i] = BADPTR;
}
args[6] = NULL;
execve("/bin/su", args, envp);
printf("%s\n", strerror(errno));
return 1;
}
Apparently the Linux execve( ) bug is caused by the failure of
copy_strings( ) in /usr/src/linux/fs/exec.c to check the return
value of strlen_user( ). On error, strlen_user( ) returns zero,
and copy_strings( ) blithely copies zero bytes to the argument
table of the programme to be execve( )'d, thus effectively
removing any bad pointers from argv. The problem is that argc is
not updated, so the missing arguments are taken from the
environment string table, and the missing environment strings
from whatever happens to be at the top of memory after the string
table. When that is exhausted, the rest of the environment
pointers point to the end of memory, which causes the SEGV's
people have been observing. There's no possible way this bug
could affect security, however.
SOLUTION
2.2.13 is however now out officially and including the fix. It
fixes several security holes.