COMMAND
kernel
SYSTEMS AFFECTED
FreeBSD 3.2 and earlier, FreeBSD-current before the correction
date, FreeBSD 3.2-stable before the correction date, FreeBSD
2.2.8-stable before the correction date.
PROBLEM
Following is based on FreeBSD Security Advisory. As a diagnostic
aid to help programmers find bugs in their programs, the system
creates core files when an illegal instruction or other fatal
error happens. A flaw in the kernel allowed it to follow symbolic
links when creating core files. The fts library functions had a
flaw in them where which would lead to a core dump when periodic
ran the security checking scripts (or other scripts which
traverse trees that can be controlled by users). periodic(3)
should limit core size to zero to disable core dumps while it is
executing commands, but does not do so. In addition, the kernel
should not follow symbolic links.
All three of these problems caused a situation where it was
possible for an attacker could create or overwrite an arbitrary
file on the system with a moderate degree of controll of its
contents to cause a problem. Local users could gain root access.
SOLUTION
Corrected in:
FreeBSD-3.3 RELEASE
FreeBSD-current as of 1999/08/26
FreeBSD-3.2-stable as of 1999/08/26
FreeBSD-2.2.8-stable as of 1999/08/29
The FreeBSD-3.3-RC series of releases are not affected.
One can workaround this problem by preventing core dumps for
periodic. This solution is less than completely satisfying,
since it only plugs the known exploit hole. None the less, this
may provide a short term stopgap solution until a new kernel
and/or userland can be installed.
# mv /usr/sbin/periodic /usr/sbin/periodic.bin
# cat > /usr/sbin/periodic
#!/bin/sh
ulimit -c 0
/usr/sbin/periodic.bin $*
^D
# chmod 555 /usr/sbin/periodic
Another alternative would be to update the fts routines to a
version newer than 1999/09/02 (for -current or 3.3-stable) or
1999/09/04 (for 2.2.8-stable). However, this requires that you
rebuild via "make world" to take effect. Please note: there is a
separate advisory describing the fts problem and solution. Please
see:
http://oliver.efri.hr/~crv/security/bugs/BSD/libc7.html
for additional information about the fts patch.
Apply the following patches to your kernel. They will disallow
following symbolic links when creating core files. This will stop
this attack, and all similar such attacks. Here's the patch for
freebsd-current:
*** kern/imgact_elf.c 1999/07/09 19:10:14 1.61
--- kern/imgact_elf.c 1999/08/26 17:32:48 1.62
***************
*** 722,729 ****
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
--- 722,729 ----
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
*** kern/imgact_aout.c 1999/05/17 00:53:36 1.52
--- kern/imgact_aout.c 1999/08/26 17:32:48 1.53
***************
*** 264,271 ****
name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
--- 264,271 ----
name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
Here's the patch for freebsd-3.2-stable:
*** kern/imgact_elf.c 1999/07/15 13:01:54 1.44.2.4
--- kern/imgact_elf.c 1999/08/26 17:35:03 1.44.2.5
***************
*** 699,706 ****
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
--- 699,706 ----
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
*** kern/imgact_aout.c 1999/04/14 04:55:22 1.44.2.1
--- kern/imgact_aout.c 1999/08/26 17:35:02 1.44.2.2
***************
*** 259,266 ****
name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
--- 259,266 ----
name = expand_name(p->p_comm, p->p_ucred->cr_uid, p->p_pid);
if (name == NULL)
return (EFAULT); /* XXX -- not the best error */
! NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
! error = vn_open(&nd, O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR);
free(name, M_TEMP);
if (error)
return (error);
Here's the patch for FreeBSD-2.2.8-stable
*** sys/LINK/fcntl.h Wed Dec 18 05:08:08 1996
--- sys/fcntl.h Fri Aug 27 14:39:26 1999
***************
*** 84,89 ****
--- 84,90 ----
#define O_EXLOCK 0x0020 /* open with exclusive file lock */
#define O_ASYNC 0x0040 /* signal pgrp when data ready */
#define O_FSYNC 0x0080 /* synchronous writes */
+ #define O_NOFOLLOW 0x0100 /* don't follow symlinks */
#endif
#define O_CREAT 0x0200 /* create if nonexistent */
#define O_TRUNC 0x0400 /* truncate to zero length */
*** kern/LINK/kern_sig.c Sat Dec 21 10:57:24 1996
--- kern/kern_sig.c Fri Aug 27 14:38:25 1999
***************
*** 1241,1249 ****
p->p_rlimit[RLIMIT_CORE].rlim_cur)
return (EFAULT);
sprintf(name, "%s.core", p->p_comm);
! NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p);
if ((error = vn_open(&nd,
! O_CREAT | FWRITE, S_IRUSR | S_IWUSR)))
return (error);
vp = nd.ni_vp;
--- 1241,1249 ----
p->p_rlimit[RLIMIT_CORE].rlim_cur)
return (EFAULT);
sprintf(name, "%s.core", p->p_comm);
! NDINIT(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, name, p);
if ((error = vn_open(&nd,
! O_CREAT | FWRITE | O_NOFOLLOW, S_IRUSR | S_IWUSR)))
return (error);
vp = nd.ni_vp;
*** kern/LINK/vfs_vnops.c Sat Mar 8 07:16:18 1997
--- kern/vfs_vnops.c Fri Aug 27 14:37:01 1999
***************
*** 87,93 ****
if (fmode & O_CREAT) {
ndp->ni_cnd.cn_nameiop = CREATE;
ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
! if ((fmode & O_EXCL) == 0)
ndp->ni_cnd.cn_flags |= FOLLOW;
error = namei(ndp);
if (error)
--- 87,93 ----
if (fmode & O_CREAT) {
ndp->ni_cnd.cn_nameiop = CREATE;
ndp->ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
! if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0)
ndp->ni_cnd.cn_flags |= FOLLOW;
error = namei(ndp);
if (error)
***************
*** 119,125 ****
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
! ndp->ni_cnd.cn_flags = FOLLOW | LOCKLEAF;
error = namei(ndp);
if (error)
return (error);
--- 119,126 ----
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
! ndp->ni_cnd.cn_flags =
! ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF;
error = namei(ndp);
if (error)
return (error);
*** kern/LINK/vfs_syscalls.c Wed Aug 4 12:44:30 1999
--- kern/vfs_syscalls.c Sat Aug 28 10:48:51 1999
***************
*** 694,699 ****
--- 694,701 ----
flags = FFLAGS(uap->flags);
if ((flags & FREAD + FWRITE) == 0)
return (EINVAL);
+ if (flags & O_NOFOLLOW)
+ flags &= ~O_NOFOLLOW;
error = falloc(p, &nfp, &indx);
if (error)
return (error);