|
COMMAND OpenBSD local priviledge escalation via select call SYSTEMS AFFECTED All versions of OpenBSD PROBLEM In OpenBSD security advisory by Niels Provos [provos@citi.umich.edu] : Insufficient boundary checks in the select call allow an attacker to overwrite kernel memory and execute arbitrary code in kernel context. Traditionally, the size parameter for the select system call is a signed integer. As a result, the kernel evaluates the upper boundary checks in a signed context, so that an attacker can circumvent when using certain negative values. When the kernel copies the data for the select system call from userland the size is used as an unsigned integer which causes kernel memory to be overwritten with arbitrary data. Local users can obtain complete system privileges and circumvent the extra security measures provided by the securelevel system. SOLUTION Apply one of the supplied kernel patches or update to 3.0-stable or 3.1-stable from 2002-08-11 17:00 EDT or later. Apply by doing: cd /usr/src patch -p0 < 014_scarg.patch And then rebuild your kernel. Index: sys/kern/sys_generic.c =================================================================== RCS file: /cvs/src/sys/kern/sys_generic.c,v retrieving revision 1.39 retrieving revision 1.39.2.1 diff -u -r1.39 -r1.39.2.1 --- sys/kern/sys_generic.c 14 Mar 2002 01:27:04 -0000 1.39 +++ sys/kern/sys_generic.c 11 Aug 2002 17:14:55 -0000 1.39.2.1 @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_generic.c,v 1.39 2002/03/14 01:27:04 millert Exp $ */ +/* $OpenBSD: sys_generic.c,v 1.39.2.1 2002/08/11 17:14:55 jason Exp $ */ /* $NetBSD: sys_generic.c,v 1.24 1996/03/29 00:25:32 cgd Exp $ */ /* @@ -644,12 +644,9 @@ * Select system call. */ int -sys_select(p, v, retval) - register struct proc *p; - void *v; - register_t *retval; +sys_select(struct proc *p, void *v, register_t *retval) { - register struct sys_select_args /* { + struct sys_select_args /* { syscallarg(int) nd; syscallarg(fd_set *) in; syscallarg(fd_set *) ou; @@ -659,14 +656,15 @@ fd_set bits[6], *pibits[3], *pobits[3]; struct timeval atv; int s, ncoll, error = 0, timo; - u_int ni; + u_int nd, ni; - if (SCARG(uap, nd) > p->p_fd->fd_nfiles) { + nd = SCARG(uap, nd); + if (nd > p->p_fd->fd_nfiles) { /* forgiving; slightly wrong */ - SCARG(uap, nd) = p->p_fd->fd_nfiles; + nd = p->p_fd->fd_nfiles; } - ni = howmany(SCARG(uap, nd), NFDBITS) * sizeof(fd_mask); - if (SCARG(uap, nd) > FD_SETSIZE) { + ni = howmany(nd, NFDBITS) * sizeof(fd_mask); + if (nd > FD_SETSIZE) { caddr_t mbits; mbits = malloc(ni * 6, M_TEMP, M_WAITOK); @@ -713,7 +711,7 @@ retry: ncoll = nselcoll; p->p_flag |= P_SELECT; - error = selscan(p, pibits[0], pobits[0], SCARG(uap, nd), retval); + error = selscan(p, pibits[0], pobits[0], nd, retval); if (error || *retval) goto done; if (SCARG(uap, tv)) { @@ -919,10 +917,7 @@ * differently. */ int -sys_poll(p, v, retval) - register struct proc *p; - void *v; - register_t *retval; +sys_poll(struct proc *p, void *v, register_t *retval) { struct sys_poll_args *uap = v; size_t sz; @@ -931,13 +926,13 @@ struct timeval atv; int timo, ncoll, i, s, error, error2; extern int nselcoll, selwait; + u_int nfds = SCARG(uap, nfds); /* Standards say no more than MAX_OPEN; this is possibly better. */ - if (SCARG(uap, nfds) > min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, - maxfiles)) + if (nfds > min((int)p->p_rlimit[RLIMIT_NOFILE].rlim_cur, maxfiles)) return (EINVAL); - sz = sizeof(struct pollfd) * SCARG(uap, nfds); + sz = sizeof(struct pollfd) * nfds; /* optimize for the default case, of a small nfds value */ if (sz > sizeof(pfds)) @@ -946,7 +941,7 @@ if ((error = copyin(SCARG(uap, fds), pl, sz)) != 0) goto bad; - for (i = 0; i < SCARG(uap, nfds); i++) + for (i = 0; i < nfds; i++) pl[i].revents = 0; if (msec != -1) { @@ -966,7 +961,7 @@ retry: ncoll = nselcoll; p->p_flag |= P_SELECT; - pollscan(p, pl, SCARG(uap, nfds), retval); + pollscan(p, pl, nfds, retval); if (*retval) goto done; if (msec != -1) {