27 May 2010 argp   » (Journeyer)

FreeBSD kernel NFS client local vulnerabilities

census ID: census-2010-0001
CVE ID: CVE-2010-2020
Affected Products: FreeBSD 8.0-RELEASE, 7.3-RELEASE, 7.2-RELEASE
Class: Improper Input Validation (CWE-20)
Remote: No
Discovered by: Patroklos Argyroudis

We have discovered two improper input validation vulnerabilities in the FreeBSD kernel’s NFS client-side implementation (FreeBSD 8.0-RELEASE, 7.3-RELEASE and 7.2-RELEASE) that allow local unprivileged users to escalate their privileges, or to crash the system by performing a denial of service attack.

Details

FreeBSD is an advanced operating system which focuses on reliability and performance. More information about its features can be found here.

FreeBSD 8.0-RELEASE, 7.3-RELEASE and 7.2-RELEASE employ an improper input validation method in the kernel’s NFS client-side implementation. Specifically, the first vulnerability is in function nfs_mount() (file src/sys/nfsclient/nfs_vfsops.c) which is reachable from the mount(2) and nmount(2) system calls. In order for them to be enabled for unprivileged users the sysctl(8) variable vfs.usermount must be set to a non-zero value.

The function nfs_mount() employs an insufficient input validation method for copying data passed in a structure of type nfs_args from userspace to kernel. Specifically, the file handle buffer to be mounted (args.fh) and its size (args.fhsize) are completely user-controllable. The unbounded copy operation is in file src/sys/nfsclient/nfs_vfsops.c (the excerpts are from 8.0-RELEASE):

  1094:      if (!has_fh_opt) {
1095:            error = copyin((caddr_t)args.fh, (caddr_t)nfh,
1096:                 args.fhsize);
1097:          if (error) {
1098:               goto out;
1099:            }

The declaration of the variables args and nfh is at:

  786: static int
787: nfs_mount(struct mount *mp)
788: {
789:         struct nfs_args args = {
790:             .version = NFS_ARGSVERSION,
791:             .addr = NULL,
792:             .addrlen = sizeof (struct sockaddr_in),
793:             .sotype = SOCK_STREAM,
794:             .proto = 0,
795:             .fh = NULL,
796:             .fhsize = 0,
797:             .flags = NFSMNT_RESVPORT,
798:             .wsize = NFS_WSIZE,
799:             .rsize = NFS_RSIZE,
800:             .readdirsize = NFS_READDIRSIZE,
801:             .timeo = 10,
802:             .retrans = NFS_RETRANS,
803:             .maxgrouplist = NFS_MAXGRPS,
804:             .readahead = NFS_DEFRAHEAD,
805:             .wcommitsize = 0,                   /* was: NQ_DEFLEASE */
806:             .deadthresh = NFS_MAXDEADTHRESH,    /* was: NQ_DEADTHRESH */
807:             .hostname = NULL,
808:             /* args version 4 */
809:             .acregmin = NFS_MINATTRTIMO,
810:             .acregmax = NFS_MAXATTRTIMO,
811:             .acdirmin = NFS_MINDIRATTRTIMO,
812:             .acdirmax = NFS_MAXDIRATTRTIMO,
813:         };
814:         int error, ret, has_nfs_args_opt;
815:         int has_addr_opt, has_fh_opt, has_hostname_opt;
816:         struct sockaddr *nam;
817:         struct vnode *vp;
818:         char hst[MNAMELEN];
819:         size_t len;
820:         u_char nfh[NFSX_V3FHMAX];

This vulnerability can cause a kernel stack overflow which leads to privilege escalation on FreeBSD 7.3-RELEASE and 7.2-RELEASE. On FreeBSD 8.0-RELEASE the result is a kernel crash/denial of service due to the SSP/ProPolice kernel stack-smashing protection which is enabled by default. Versions 7.1-RELEASE and earlier do not appear to be vulnerable since the bug was introduced in 7.2-RELEASE. In order to demonstrate the impact of the vulnerability we have developed a proof-of-concept privilege escalation exploit. A sample run of the exploit follows:

  [argp@julius ~]$ uname -rsi
FreeBSD 7.3-RELEASE GENERIC
[argp@julius ~]$ sysctl vfs.usermount
vfs.usermount: 1
[argp@julius ~]$ id
uid=1001(argp) gid=1001(argp) groups=1001(argp)
[argp@julius ~]$ gcc -Wall nfs_mount_ex.c -o nfs_mount_ex
[argp@julius ~]$ ./nfs_mount_ex
[*] calling nmount()
[!] nmount error: -1030740736
nmount: Unknown error: -1030740736
[argp@julius ~]$ id
uid=0(root) gid=0(wheel) egid=1001(argp) groups=1001(argp)

The second vulnerability exists in the function mountnfs() that is called from function nfs_mount():

  1119: error = mountnfs(&args, mp, nam, args.hostname, &vp,
1120:     curthread->td_ucred);

The function mountnfs() is reachable from the mount(2) and nmount(2) system calls by unprivileged users. As with the nfs_mount() case above, this requires the sysctl(8) variable vfs.usermount to be set to a non-zero value.

The file handle to be mounted (argp->fh) and its size (argp->fhsize) are passed to function mountnfs() from function nfs_mount() and are user-controllable. These are subsequently used in an unbounded bcopy() call (file src/sys/nfsclient/nfs_vfsops.c):

  1219: bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize);

The above can cause a kernel heap overflow when argp->fh is bigger than 128 bytes (the size of nmp->nm_fh) since nmp is an allocated item on the Universal Memory Allocator (UMA, the FreeBSD kernel’s heap allocator) zone nfsmount_zone (again from src/sys/nfsclient/nfs_vfsops.c):

  1160: static int
1161: mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
1162:     char *hst, struct vnode **vpp, struct ucred *cred)
1163: {
1164:         struct nfsmount *nmp;
1165:         struct nfsnode *np;
1166:         int error;
1167:         struct vattr attrs;
1168:
1169:         if (mp->mnt_flag &MNT_UPDATE) {
1170:                 nmp = VFSTONFS(mp);
1171:                 printf("%s: MNT_UPDATE is no longer handled here\n", 
                             __func__);
1172:                 free(nam, M_SONAME);
1173:                 return (0);
1174:         } else {
1175:                 nmp = uma_zalloc(nfsmount_zone, M_WAITOK);

This kernel heap overflow can lead on FreeBSD 8.0-RELEASE, 7.3-RELEASE and 7.2-RELEASE to privilege escalation and/or a kernel crash/denial of service attack. Similarly to the first vulnerability, FreeBSD 7.1-RELEASE and earlier versions do not appear to be vulnerable. We have developed a proof-of-concept DoS exploit to demonstrate the vulnerability. Furthermore, we have also developed a privilege escalation exploit for this second vulnerability which will not be released at this point.

FreeBSD has released an official advisory and a patch to address both vulnerabilities. All affected parties are advised to follow the upgrade instructions included in the advisory and patch their systems.

Syndicated 2010-05-23 20:18:51 (Updated 2010-05-28 17:16:17) from www.census-labs.com blog posts by author

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!