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.