argp         posts     research     bugs

Advisory: FreeBSD kernel NFS client local vulnerabilities

Note: The original advisory can be found here.

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.