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

", __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.