Protect yourself against future threats.
-----BEGIN PGP SIGNED MESSAGE----- =========================================================================== AUSCERT External Security Bulletin Redistribution ESB-97.112 -- FreeBSD Security Advisory FreeBSD-SA-97:04 Security Compromise via procfs 27 August 1997 =========================================================================== The FreeBSD Security Team has released the following advisory concerning a vulnerability in procfs kernel code. This vulnerability may allow any user with a local account to gain root access. The following security bulletin is provided as a service to AUSCERT's members. As AUSCERT did not write this document, AUSCERT has had no control over its content. As such, the decision to use any or all of this information is the responsibility of each user or organisation, and should be done so in accordance with site policies and procedures. Contact information for FreeBSD, Inc. is included in the Security Bulletin below. If you have any questions or need further information, please contact them directly. Previous advisories and external security bulletins can be retrieved from: http://www.auscert.org.au/information/advisories.html If you believe that your system has been compromised, contact AUSCERT or your representative in FIRST (Forum of Incident Response and Security Teams). Internet Email: auscert@auscert.org.au Facsimile: (07) 3365 4477 Telephone: (07) 3365 4417 (International: +61 7 3365 4417) AUSCERT personnel answer during Queensland business hours which are GMT+10:00 (AEST). On call after hours for emergencies. - --------------------------BEGIN INCLUDED TEXT-------------------- - -----BEGIN PGP SIGNED MESSAGE----- ============================================================================= FreeBSD-SA-97:04 Security Advisory FreeBSD, Inc. Topic: security compromise via procfs Category: core Module: procfs Announced: 1997-08-19 Affects: FreeBSD 2.1.*, FreeBSD 2.2.*, FreeBSD-stable and FreeBSD-current before 1997/08/12 suffer from this problem. Corrected: FreeBSD-current as of 1997/08/12 FreeBSD-stable as of 1997/08/12 FreeBSD 2.1-stable as of 1997/08/25 FreeBSD only: no (also other BSD systems may be affected) Patches: ftp://freebsd.org/pub/CERT/patches/SA-97:04/ ============================================================================= I. Background Procfs provides a filesystem interface to processes on a system. Among others it is used by ps(1) and gdb(1). II. Problem Description A problem exists in the procfs kernel code that allows processes to write memory of other processes where it should have been prohibited. III. Impact The hole can be used by any user on the system to gain root privileges. IV. Workaround A workaround is to disable the mounting of procfs. To achieve this, edit the file /etc/fstab and put a '#' in front of the line proc /proc procfs rw 0 0 Note that when you do that, some utilities may either not work anymore or have a limited functionality. V. Solution Apply one of the following patches in /usr/src/sys/miscfs/procfs, rebuild your kernel, install it and reboot your system. For 2.1 and 2.2 systems: Index: procfs_regs.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_regs.c,v retrieving revision 1.3 retrieving revision 1.3.4.1 diff -u -r1.3 -r1.3.4.1 --- procfs_regs.c 1996/01/24 18:41:25 1.3 +++ procfs_regs.c 1997/08/12 04:45:25 1.3.4.1 @@ -36,7 +36,7 @@ * * @(#)procfs_regs.c 8.3 (Berkeley) 1/27/94 * - * $Id: procfs_regs.c,v 1.3 1996/01/24 18:41:25 peter Exp $ + * $Id: procfs_regs.c,v 1.3.4.1 1997/08/12 04:45:25 sef Exp $ */ #include <sys/param.h> @@ -62,6 +62,8 @@ char *kv; int kl; + if (!CHECKIO(curp, p)) + return EPERM; kl = sizeof(r); kv = (char *) &r; Index: procfs.h =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs.h,v retrieving revision 1.12 retrieving revision 1.12.2.1 diff -u -r1.12 -r1.12.2.1 --- procfs.h 1996/07/02 13:38:07 1.12 +++ procfs.h 1997/08/12 04:45:20 1.12.2.1 @@ -36,7 +36,7 @@ * * @(#)procfs.h 8.6 (Berkeley) 2/3/94 * - * $Id: procfs.h,v 1.12 1996/07/02 13:38:07 dyson Exp $ + * $Id: procfs.h,v 1.12.2.1 1997/08/12 04:45:20 sef Exp $ */ /* @@ -83,6 +83,18 @@ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0)) #define KMEM_GROUP 2 + +/* + * Check to see whether access to target process is allowed + * Evaluates to 1 if access is allowed. + */ +#define CHECKIO(p1, p2) + ((((p1)->p_cred->pc_ucred->cr_uid == (p2)->p_cred->p_ruid) && + ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && + ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && + ((p2)->p_flag & P_SUGID) == 0) || + (suser((p1)->p_cred->pc_ucred, &(p1)->p_acflag) == 0)) + /* * Format of a directory entry in /proc, ... * This must map onto struct dirent (see <dirent.h>) Index: procfs_mem.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_mem.c,v retrieving revision 1.20 retrieving revision 1.20.2.1 diff -u -r1.20 -r1.20.2.1 --- procfs_mem.c 1996/10/24 02:47:05 1.20 +++ procfs_mem.c 1997/08/12 04:45:23 1.20.2.1 @@ -37,7 +37,7 @@ * * @(#)procfs_mem.c 8.4 (Berkeley) 1/21/94 * - * $Id: procfs_mem.c,v 1.20 1996/10/24 02:47:05 dyson Exp $ + * $Id: procfs_mem.c,v 1.20.2.1 1997/08/12 04:45:23 sef Exp $ */ /* @@ -300,6 +300,23 @@ if (uio->uio_resid == 0) return (0); + /* + * XXX + * We need to check for KMEM_GROUP because ps is sgid kmem; + * not allowing it here causes ps to not work properly. Arguably, + * this is a bug with what ps does. We only need to do this + * for Pmem nodes, and only if it's reading. This is still not + * good, as it may still be possible to grab illicit data if + * a process somehow gets to be KMEM_GROUP. Note that this also + * means that KMEM_GROUP can't change without editing procfs.h! + * All in all, quite yucky. + */ + + if (!CHECKIO(curp, p) && + !(curp->p_cred->pc_ucred->cr_gid == KMEM_GROUP && + uio->uio_rw == UIO_READ)) + return EPERM; + error = procfs_rwmem(p, uio); return (error); Index: procfs_vnops.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_vnops.c,v retrieving revision 1.24 retrieving revision 1.24.2.1 diff -u -r1.24 -r1.24.2.1 --- procfs_vnops.c 1996/09/03 14:23:10 1.24 +++ procfs_vnops.c 1997/08/12 04:45:27 1.24.2.1 @@ -36,7 +36,7 @@ * * @(#)procfs_vnops.c 8.6 (Berkeley) 2/7/94 * - * $Id: procfs_vnops.c,v 1.24 1996/09/03 14:23:10 bde Exp $ + * $Id: procfs_vnops.c,v 1.24.2.1 1997/08/12 04:45:27 sef Exp $ */ /* @@ -120,16 +120,21 @@ struct vop_open_args *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); + struct proc *p1 = ap->a_p, *p2 = PFIND(pfs->pfs_pid); + + if (p2 == NULL) + return ENOENT; switch (pfs->pfs_type) { case Pmem: - if (PFIND(pfs->pfs_pid) == 0) - return (ENOENT); /* was ESRCH, jsp */ - if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) || ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) return (EBUSY); + if (!CHECKIO(p1, p2) && + (p1->p_cred->pc_ucred->cr_gid != KMEM_GROUP)) + return EPERM; + if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); @@ -176,7 +181,6 @@ procfs_ioctl(ap) struct vop_ioctl_args *ap; { - return (ENOTTY); } Index: procfs_fpregs.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_fpregs.c,v retrieving revision 1.3 retrieving revision 1.3.4.1 diff -u -r1.3 -r1.3.4.1 --- procfs_fpregs.c 1996/01/24 18:40:56 1.3 +++ procfs_fpregs.c 1997/08/12 05:24:20 1.3.4.1 @@ -36,7 +36,7 @@ * * @(#)procfs_fpregs.c 8.1 (Berkeley) 1/27/94 * - * $Id: procfs_fpregs.c,v 1.3 1996/01/24 18:40:56 peter Exp $ + * $Id: procfs_fpregs.c,v 1.3.4.1 1997/08/12 05:24:20 sef Exp $ */ #include <sys/param.h> @@ -62,6 +62,8 @@ char *kv; int kl; + if (!CHECKIO(curp, p)) + return EPERM; kl = sizeof(r); kv = (char *) &r; For FreeBSd-current systems: Index: procfs_regs.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_regs.c,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- procfs_regs.c 1997/08/02 14:32:16 1.7 +++ procfs_regs.c 1997/08/12 04:34:29 1.8 @@ -37,7 +37,7 @@ * @(#)procfs_regs.c 8.4 (Berkeley) 6/15/94 * * From: - * $Id: procfs_regs.c,v 1.7 1997/08/02 14:32:16 bde Exp $ + * $Id: procfs_regs.c,v 1.8 1997/08/12 04:34:29 sef Exp $ */ #include <sys/param.h> @@ -60,6 +60,8 @@ char *kv; int kl; + if (!CHECKIO(curp, p)) + return EPERM; kl = sizeof(r); kv = (char *) &r; Index: procfs.h =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs.h,v retrieving revision 1.15 retrieving revision 1.16 diff -u -r1.15 -r1.16 --- procfs.h 1997/02/22 09:40:26 1.15 +++ procfs.h 1997/08/12 04:34:27 1.16 @@ -37,7 +37,7 @@ * @(#)procfs.h 8.9 (Berkeley) 5/14/95 * * From: - * $Id: procfs.h,v 1.15 1997/02/22 09:40:26 peter Exp $ + * $Id: procfs.h,v 1.16 1997/08/12 04:34:27 sef Exp $ */ /* @@ -85,6 +85,18 @@ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0)) #define KMEM_GROUP 2 + +/* + * Check to see whether access to target process is allowed + * Evaluates to 1 if access is allowed. + */ +#define CHECKIO(p1, p2) + ((((p1)->p_cred->pc_ucred->cr_uid == (p2)->p_cred->p_ruid) && + ((p1)->p_cred->p_ruid == (p2)->p_cred->p_ruid) && + ((p1)->p_cred->p_svuid == (p2)->p_cred->p_ruid) && + ((p2)->p_flag & P_SUGID) == 0) || + (suser((p1)->p_cred->pc_ucred, &(p1)->p_acflag) == 0)) + /* * Format of a directory entry in /proc, ... * This must map onto struct dirent (see <dirent.h>) Index: procfs_mem.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_mem.c,v retrieving revision 1.26 retrieving revision 1.27 diff -u -r1.26 -r1.27 --- procfs_mem.c 1997/08/02 14:32:14 1.26 +++ procfs_mem.c 1997/08/12 04:34:28 1.27 @@ -37,7 +37,7 @@ * * @(#)procfs_mem.c 8.5 (Berkeley) 6/15/94 * - * $Id: procfs_mem.c,v 1.26 1997/08/02 14:32:14 bde Exp $ + * $Id: procfs_mem.c,v 1.27 1997/08/12 04:34:28 sef Exp $ */ /* @@ -276,6 +276,23 @@ if (uio->uio_resid == 0) return (0); + + /* + * XXX + * We need to check for KMEM_GROUP because ps is sgid kmem; + * not allowing it here causes ps to not work properly. Arguably, + * this is a bug with what ps does. We only need to do this + * for Pmem nodes, and only if it's reading. This is still not + * good, as it may still be possible to grab illicit data if + * a process somehow gets to be KMEM_GROUP. Note that this also + * means that KMEM_GROUP can't change without editing procfs.h! + * All in all, quite yucky. + */ + + if (!CHECKIO(curp, p) && + !(curp->p_cred->pc_ucred->cr_gid == KMEM_GROUP && + uio->uio_rw == UIO_READ)) + return EPERM; return (procfs_rwmem(p, uio)); } Index: procfs_vnops.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_vnops.c,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- procfs_vnops.c 1997/08/02 14:32:20 1.30 +++ procfs_vnops.c 1997/08/12 04:34:30 1.31 @@ -36,7 +36,7 @@ * * @(#)procfs_vnops.c 8.18 (Berkeley) 5/21/95 * - * $Id: procfs_vnops.c,v 1.30 1997/08/02 14:32:20 bde Exp $ + * $Id: procfs_vnops.c,v 1.31 1997/08/12 04:34:30 sef Exp $ */ /* @@ -127,16 +127,21 @@ } */ *ap; { struct pfsnode *pfs = VTOPFS(ap->a_vp); + struct proc *p1 = ap->a_p, *p2 = PFIND(pfs->pfs_pid); + + if (p2 == NULL) + return ENOENT; switch (pfs->pfs_type) { case Pmem: - if (PFIND(pfs->pfs_pid) == 0) - return (ENOENT); /* was ESRCH, jsp */ - if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) || (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE)) return (EBUSY); + if (!CHECKIO(p1, p2) && + (p1->p_cred->pc_ucred->cr_gid != KMEM_GROUP)) + return EPERM; + if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); @@ -194,7 +199,6 @@ struct proc *a_p; } */ *ap; { - return (ENOTTY); } Index: procfs_fpregs.c =================================================================== RCS file: /home/cvsup/freebsd/CVS/src/sys/miscfs/procfs/procfs_fpregs.c,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- procfs_fpregs.c 1997/08/02 14:32:11 1.7 +++ procfs_fpregs.c 1997/08/12 05:23:51 1.8 @@ -37,7 +37,7 @@ * @(#)procfs_fpregs.c 8.2 (Berkeley) 6/15/94 * * From: - * $Id: procfs_fpregs.c,v 1.7 1997/08/02 14:32:11 bde Exp $ + * $Id: procfs_fpregs.c,v 1.8 1997/08/12 05:23:51 sef Exp $ */ #include <sys/param.h> @@ -60,6 +60,8 @@ char *kv; int kl; + if (!CHECKIO(curp, p)) + return EPERM; kl = sizeof(r); kv = (char *) &r; (These patches can also be found on ftp://freebsd.org/pub/CERT/patches/SA-97:04/) VI. Thanks This problem was brought to light by Brian Mitchell <brian@firehouse.net> ============================================================================= FreeBSD, Inc. Web Site: http://www.freebsd.org/ Confidential contacts: security-officer@freebsd.org PGP Key: ftp://freebsd.org/pub/CERT/public_key.asc Security notifications: security-notifications@freebsd.org Security public discussion: security@freebsd.org Notice: Any patches in this document may not apply cleanly due to modifications caused by digital signature or mailer software. Please reference the URL listed at the top of this document for original copies of all patches if necessary. ============================================================================= - -----BEGIN PGP SIGNATURE----- Version: 2.6.2 iQCVAwUBNAMWLFUuHi5z0oilAQHmrQQAoXR/BUliLCJgtDx/tG4lSNMpY2+wYWtw PNiPjLfHHbA2yOXoJxv5ANw0Z6zeovCP1rHTKbG0vGNQe45d34kC+qY1hSKhYxjV BGeEKzCUyfGn0ovrfWjmW6FL3n2Kq76yJbhR5tiev5vaM9+kvWKs8aK5c1maAEOv PxYm/nzJg04= =aC0v - -----END PGP SIGNATURE----- - --------------------------END INCLUDED TEXT-------------------- -----BEGIN PGP SIGNATURE----- Version: 2.6.3i Charset: noconv Comment: ftp://ftp.auscert.org.au/pub/auscert/AUSCERT_PGP.key iQCVAwUBNAQR/yh9+71yA2DNAQH+9QP+OlKFNmU+xG/kmIutYltiOVRSfKLDDxqU Co0PeIgji113U8JfOsTrcxacwuEVmII9cfkivvRs3eTQLaMZcyXxrIUDHdHcNCy6 +yrCqSeu5VqmxBmRIpbnxWX8J5tq7FnpHhsiIYADTm15oJ+oDUGYJQhdVh3EVYeF YiHXmVM76O0= =2P7f -----END PGP SIGNATURE-----