Home page logo

bugtraq logo Bugtraq mailing list archives

Re: Vulnerability in 4.4BSD rfork() implementation
From: presotto () PLAN9 BELL-LABS COM (presotto () PLAN9 BELL-LABS COM)
Date: Mon, 4 Aug 1997 21:46:07 -0400

To set the Plan 9 users' minds at ease, the above vulnerability doesn't
exist in Plan 9.  While rfork() comes from us, we don't have the
concept of SETUID precisely because it is such an attractor of
security holes.

------ forwarded message follows ------

From bell-labs.com!ches Sun Aug  3 10:41:09 EDT 1997
Received: from research.research.bell-labs.com ([]) by plan9; Sun Aug  3 10:41:09 EDT 1997
Received: from chesport.research.bell-labs.com ([]) by research; Sun Aug  3 10:40:41 EDT 1997
Message-Id: < () plan9 cs bell-labs com>
X-Sender: ches () plan9 cs bell-labs com
X-Mailer: Windows Eudora Light Version 1.5.4 (32)
Mime-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Date: Sun, 03 Aug 1997 10:38:54 -0400
To: research.bell-labs.com!presotto,research.bell-labs.com!philw
From: Bill Cheswick <bell-labs.com!ches>
Subject: note the plan9 reference

The Plan 9 reference looks bogus to me, esp. with the reference to setuid
programs.  But I forward this just in case.

To: research.bell-labs.com!ches
Subject: note the plan9 reference
Date: Sat, 02 Aug 1997 22:17:36 -0400
From: Steven Bellovin <research.att.com!smb>

------- Forwarded Message

Return-Path: owner-bugtraq () NETSPACE ORG
Return-Path: owner-bugtraq () NETSPACE ORG
Received: from research.att.com (research.research.att.com [])
by raptor.research.att.com (8.8.5/8.7) with SMTP id WAA04289 for
<smb () raptor research att com>; Sat, 2 Aug 1997 22:08:01 -0400 (EDT)
Received: from brimstone.netspace.org ([]) by research; Sat
Aug  2 22:06:31 EDT 1997
Received: from unknown () netspace org (port 49671 []) by
brimstone.netspace.org with ESMTP id <20198-10761>; Sat, 2 Aug 1997 21:28:52
Received: from NETSPACE.ORG by NETSPACE.ORG (LISTSERV-TCP/IP release 1.8c) with
         spool id 4576641 for BUGTRAQ () NETSPACE ORG; Sat, 2 Aug 1997 21:23:12
Received: from brimstone.netspace.org (brimstone []) by
         netspace.org (8.8.5/8.8.2) with ESMTP id VAA24394 for
         <BUGTRAQ () NETSPACE ORG>; Sat, 2 Aug 1997 21:22:23 -0400
Received: from unknown () netspace org (port 49671 []) by
         brimstone.netspace.org with ESMTP id <20092-10761>; Sat, 2 Aug 1997
         21:23:40 -0400
Approved-By: aleph1 () UNDERGROUND ORG
Received: from enteract.com (enteract.com []) by netspace.org
         (8.8.5/8.8.2) with ESMTP id UAA22689 for <bugtraq () netspace org>; Sat,
         2 Aug 1997 20:59:44 -0400
Received: (from tqbf () localhost) by enteract.com (8.8.5/8.7.6) id UAA19968 for
         bugtraq () netspace org; Sat, 2 Aug 1997 20:02:04 -0500 (CDT)
X-Mailer: ELM [version 2.4 PL24 ME8a]
Content-Type: text
Message-ID: <199708030102.UAA19968 () enteract com>
Date:  Sat, 2 Aug 1997 20:02:04 -0500
Reply-To: tqbf () enteract com
Sender: Bugtraq List <BUGTRAQ () NETSPACE ORG>
From: "Thomas H. Ptacek" <tqbf () enteract com>
Subject:      Vulnerability in 4.4BSD rfork() implementation

- ----------------------------------------------------------------------------

                       OpenBSD Security Advisory

                             August 2, 1997

                 Vulnerability in rfork() System Call

- ----------------------------------------------------------------------------


A vulnerability in certain 4.4BSD kernels allows processes to gain
access to restricted resources by manipulating the file descriptor
tables of SUID and SGID executables. Applications of this vulnerability
will allow users to gain root access.

- ----------------------------------------------------------------------------


It is believed that all 4.4BSD operating systems implementing the
rfork() system call are currently vulnerable to this problem. These
operating systems include OpenBSD 2.1 and FreeBSD 3.0. The OpenBSD
project has resolved this problem in OpenBSD-current.

The rfork() system call originated in the Plan9 operating system,
and the 4.4BSD implementations of it share the original's semantics.
Therefore, it is believed that Plan9 may be vulnerable as well.

Code is provided at the end of this document that will allow system
operators to test their vulnerability.

- ----------------------------------------------------------------------------


Recent 4.4BSD operating systems added the rfork() system call as an
additional method of creating a new process. Unlike fork(), rfork()
allows the caller tighter control over which resources are shared
between the parent and child processes. These resources include
the per-process descriptor table.

The descriptor table of a process lists all open file descriptors
for that process. Input and output on files, sockets, and pipes is
done through these descriptors. Two processes sharing the same
descriptor table can read from any file either has open in read mode,
and write to any file either has in write mode.

Unfortunately, the 4.4BSD implementation of rfork() allows this to occur
with processes whose credentials have been altered via SUID/SGID programs.
A process can execute any SUID program on the system and gain access to
it's file descriptor table. This can be exploited to allow unprivileged
processes to access security-critical resources, such as the password

- ----------------------------------------------------------------------------


The default behavior of rfork() is to share the file descriptor table
between the child and parent processes. A process created with rfork() can
therefore, by default, be manipulated by it's parent.

An example of this problem occurs in passwd(1), an SUID program that
modifies the password database. A user on the system can rfork() a process
and use it to execute passwd(1). The child process will gain effective
superuser credentials as a result of executing the SUID program. The
parent can then wait for the temporary copy of the password database to be
opened, and inject a fake entry into it using the file descriptor it
now shares with passwd(1). When the password database is rebuilt, the fake
entry will be commited to it and system security will be compromised.

It should be noted that this is not the only avenue of exploitation for
this problem. The vulnerability allows complete control over the file
descriptor tables of privileged programs; this can be exploited in a
variety of ways with any SUID program.

Another possible attack allows an attacker to, among other things, steal
sockets from network programs; an attacker can execute an SUID networking
program such as "ping", duplicate the descriptor associated with a raw
socket, and close the original descriptor. The unprivileged attacker now
controls a raw socket.

Additionally, an attacker can close a descriptor opened by an SUID
program, and re-open it pointing elsewhere, causing the SUID program to
unwittingly alter any file accessible by the attacker.

- ----------------------------------------------------------------------------


Provided at the end of this document is a patch from OpenBSD-current that
resolves the problem in OpenBSD systems. The OpenBSD patch alters execve()
to cause it not to honor the SUID or SGID bit when executing from a
process that shares a file descriptor table with a different process.

Also provided is a modloadable workaround for FreeBSD. The provided module
will disable the rfork() system call from a running system that supports
loadable modules.

- ----------------------------------------------------------------------------


The following patch resolves the rfork() problem in OpenBSD systems.

- -- cut here --

- --- kern_exec.c 1997/06/05 08:05:54     1.11
+++ kern_exec.c 1997/08/01 22:54:50     1.12
@@ -1,4 +1,4 @@
- -/*     $OpenBSD: kern_exec.c,v 1.11 1997/06/05 08:05:54 deraadt Exp $  */
+/*     $OpenBSD: kern_exec.c,v 1.12 1997/08/01 22:54:50 deraadt Exp $  */
/*     $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $  */

@@ -124,7 +124,8 @@
               error = EACCES;
               goto bad1;
- -       if ((vp->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & P_TRACED))
+       if ((vp->v_mount->mnt_flag & MNT_NOSUID) ||
+           (p->p_flag & P_TRACED) || p->p_fd->fd_refcnt > 1)
               epp->ep_vap->va_mode &= ~(VSUID | VSGID);

       /* check access.  for root we have to see if any exec bit on */

- -- cut here --

- ----------------------------------------------------------------------------


The following patch is unsupported by the FreeBSD project.

- -- cut here --

- --- kern_exec.c Wed Apr 23 17:13:00 1997
+++ kern_exec_new.c     Sat Aug  2 19:18:34 1997
@@ -653,7 +653,9 @@
        * Disable setuid/setgid if the filesystem prohibits it or if
        * the process is being traced.
- -        if ((vp->v_mount->mnt_flag & MNT_NOSUID) || (p->p_flag & P_TRACED))
+        if ((vp->v_mount->mnt_flag & MNT_NOSUID)
+           || (p->p_flag & P_TRACED)
+           || p->p_fd->fd_refcnt > 1)
               attr->va_mode &= ~(VSUID | VSGID);

       return (0);

- -- cut here --

- ----------------------------------------------------------------------------


The following module, when loaded on a FreeBSD system supporting
rfork(), will disable the system call as a temporary resolution
to the problem.

- -- cut here --

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
# This archive contains:
#       Makefile
#       unrfork_mod_load.c
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
XBINDIR=        .
XSRCS=  unrfork_mod_load.c
XKMOD=  disable_rfork
XNOMAN= none
X.include <bsd.kmod.mk>
echo x - unrfork_mod_load.c
sed 's/^X//' >unrfork_mod_load.c << 'END-of-unrfork_mod_load.c'
X#include <sys/param.h>
X#include <sys/ioctl.h>
X#include <sys/proc.h>
X#include <sys/systm.h>
X#include <sys/sysproto.h>
X#include <sys/conf.h>
X#include <sys/mount.h>
X#include <sys/exec.h>
X#include <sys/sysent.h>
X#include <sys/lkm.h>
X#include <a.out.h>
X#include <sys/file.h>
X#include <sys/errno.h>
X#include <sys/queue.h>
X#include <sys/mbuf.h>
X#include <sys/socket.h>
X#include <sys/socketvar.h>
X#include <sys/protosw.h>
X#include <sys/kernel.h>
X#include <sys/sockio.h>
Xint disable_rfork(struct lkm_table *lkp, int cmd, int ver);
Xstatic int
Xdisable_rfork_load(struct lkm_table *lkp, int cmd) {
X       struct sysent *sp = &sysent[RFORK_SYSCALL_NO];
X       int err = 0;
X       switch(cmd) {
X               case LKM_E_LOAD:
X                       sp->sy_call = (sy_call_t *) nosys;
X                       printf("rfork() call disabled\n");
X                       break;
X               case LKM_E_UNLOAD:
X                       sp->sy_call = (sy_call_t *) rfork;
X                       printf("rfork() call enabled\n");
X                       break;
X               default:
X                       err = EINVAL;
X                       break;
X       }
X       return(err);
Xint disable_rfork(struct lkm_table *lkp, int cmd, int ver) {
X       DISPATCH(lkp, cmd, ver, disable_rfork_load,
X                               disable_rfork_load, lkm_nullcmd);

- ----------------------------------------------------------------------------


The following code tests for the presence of the rfork() vulnerability on
4.4BSD systems. If, after running this program, a file is created in "/"
containing the word "VULNERABLE", the system is vulnerable to the problem.

To use this test, extract the following two C programs. Compile the first
("dummy-suid") and make it SUID root, world executable. Compile and run
the second in the same directory.

- -- cut here (dummy-suid.c) --

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

int main() {
       int fd;


       /* open a file in the root directory */

       if(fd = open("/VULNERABLE", O_RDWR|O_CREAT) < 0) {

       /* wait for something to happen */



- -- cut here (test.c) --

#include <stdio.h>
#include <unistd.h>

int main() {
       int p;

       /* UNPRIVILEGED */

       /* create a new process that shares it's parent's file
        * descriptor table

       if(!(p = rfork(RFPROC))) {

               /* wait for parent to open a file, write
                * to it.

               write(3, "VULNERABLE\n", 10);

       /* PRIVILEGED */

       /* execute 'p', an SUID program that opens a file and
        * hangs

       execl("./dummy-suid", "dummy-suid", NULL);


- -- cut here --

- ----------------------------------------------------------------------------


The OpenBSD development team would like to express gratitude to Danny
Dulai for his discovery of this problem, to Theo de Raadt for the OpenBSD
patch, and to Tim Newsham, for providing proof-of-concept code.

The developers at OpenBSD would also like to thank Perry Metzger for his
reliable and consistant support of their work.

OpenBSD would like to thank Crosswalk Network Security, Inc. for
extensive assistance in the discovery, testing, and documentation of this

- ---
       Crosswalk Network Security, Inc. is a full service computer
security consultancy, founded to address the growing need for
comprehensive data protection solutions.  By providing extensive security
auditing, intrusion detection and response, rigid policy design, and
implementation of cutting-edge encryption systems, Crosswalk ensures
robust, thorough, and uncompromising protection for organizations seeking
enterprise wide data security.

       For more information, mail info () crosswalk com 

Version: 2.6.2


------- End of Forwarded Message

  By Date           By Thread  

Current thread:
  • Re: Vulnerability in 4.4BSD rfork() implementation presotto () PLAN9 BELL-LABS COM (Aug 05)
[ Nmap | Sec Tools | Mailing Lists | Site News | About/Contact | Advertising | Privacy ]