fpbe(虎符2022-re1)

通过一道题目,学习ebpf相关的知识,并且了解Ghidra调试器的使用和ida的pip使用

HCTF-fpbe

用户使用 BPF 虚拟机的指令集(也称 BPF 字节码)定义过滤器表达式,然后传递给内核,由解释器执行。类似于vm逆向的概念

方法1:

首先ida分析原来的文件:根据main函数,找到关键位置

image-20220327141819319

这里百度得到往内存中加载字节码的关键函数,跟进这个函数,找到字节码

image-20220327142009846

可以看到字节码的长度就是1648字节,提取出来的字节码dump进一个新的文件,这及时ebp文件了,下面开始分析文件。

反汇编方法1

由dump出的字节码,然后通过Ghidra的ebpe插件(选用9.2版本的插件,然后不解压,直接修改里面的配置文件,将版本修改成对应Ghidra的版本),之后重新启动,进行反编译之后的伪代码。【这里不要使用360zip】

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
//方式1
undefined8 uprobe(longlong param_1)

{
ulonglong uVar1;
ulonglong uVar2;
undefined8 uVar3;
undefined8 local_40;
undefined8 local_38;
undefined8 local_30;
undefined8 local_28;
undefined local_20;
ulonglong local_18;
ulonglong local_10;
undefined local_8;

uVar2 = *(ulonglong *)(param_1 + 0x68) & 0xffffffff;
local_18 = *(ulonglong *)(param_1 + 0x70) & 0xffffffff;
local_10 = *(ulonglong *)(param_1 + 0x60) & 0xffffffff;
local_8 = 0;
uVar1 = *(ulonglong *)(param_1 + 0x58) & 0xffffffff;
uVar3 = 1;
if ((((uVar2 * 0xfb88 + local_18 * 0x6dc0 + local_10 * 0x71fb + uVar1 * 0xcc8e == -0x5e8ca66b) & &
(uVar2 * 0x6ae5 + local_18 * 0xf1bf + local_10 * 0xadd3 + uVar1 * 0x9284 == -0x1aabfcc0)) & &
(uVar2 * 0x8028 + local_18 * 0xdd85 + local_10 * 0x652d + uVar1 * 0xe712 == 0xa6f374484da3))
&& (uVar2 * 0xca43 + local_18 * 0x822c + local_10 * 0x7c8e + uVar1 * 0xf23a == 0xb99c485a7277 ))
{
local_10 = local_10 | uVar1 << 0x20;
local_18 = local_18 | uVar2 << 0x20;
local_28 = 0xa7d73257b465443;
local_30 = 0x4648203a47414c46;
local_38 = 0x2052554f59202145;
local_40 = 0x4e4f44204c4c4557;
uVar3 = 0;
local_20 = 0;
bpf_trace_printk((char *)&local_40,0x21);
}
return uVar3;
}

反汇编方法2

通过ida的插件进行反汇编

ida_pip

该插件来自 cylance/eBPF_processor: An IDA processor for eBPF bytecode (github.com)

  • place ebpf.py in your IDA_ROOT\procs folder.

然后找到ida——python的目录:F:\IDA\IDA7.5\IDA_Pro_v7.5_Portable\python38\Scripts

然后在这里下载库>pip3.8 install pyelftools,在外面下载的化,idapython是用不了的。

  • 最后一个脚本有一个bug,需要把del(sym)加一个缩进,到if判断里面才能执行。

反汇编

image-20220327142423849

这里选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
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
uprobe_func:0000000000000008 uprobe:
uprobe_func:0000000000000008 ldxdw r2, [r1+0x68]
uprobe_func:0000000000000010 lsh r2, 0x20
uprobe_func:0000000000000018 rsh r2, 0x20
uprobe_func:0000000000000020 ldxdw r3, [r1+0x70]
uprobe_func:0000000000000028 lsh r3, 0x20
uprobe_func:0000000000000030 rsh r3, 0x20
uprobe_func:0000000000000038 mov r4, r3
uprobe_func:0000000000000040 mul r4, 0x6DC0
uprobe_func:0000000000000048 mov r5, r2
uprobe_func:0000000000000050 mul r5, 0xFB88
uprobe_func:0000000000000058 add r5, r4
uprobe_func:0000000000000060 ldxdw r4, [r1+0x60]
uprobe_func:0000000000000068 lsh r4, 0x20
uprobe_func:0000000000000070 rsh r4, 0x20
uprobe_func:0000000000000078 mov r0, r4
uprobe_func:0000000000000080 mul r0, 0x71FB
uprobe_func:0000000000000088 add r5, r0
uprobe_func:0000000000000090 ldxdw r1, [r1+0x58]
uprobe_func:0000000000000098 mov r0, 0
uprobe_func:00000000000000A0 stxb [r10-8], r0
uprobe_func:00000000000000A8 stxdw [r10-0x10], r0
uprobe_func:00000000000000B0 stxdw [r10-0x18], r0
uprobe_func:00000000000000B8 lsh r1, 0x20
uprobe_func:00000000000000C0 rsh r1, 0x20
uprobe_func:00000000000000C8 mov r0, r1
uprobe_func:00000000000000D0 mul r0, 0xCC8E
uprobe_func:00000000000000D8 add r5, r0
uprobe_func:00000000000000E0 mov r6, 1
uprobe_func:00000000000000E8 lddw r0, 0xBE18A1735995
uprobe_func:00000000000000F8 jne r5, r0, LBB0_5
uprobe_func:0000000000000100 mov r5, r3
uprobe_func:0000000000000108 mul r5, 0xF1BF
uprobe_func:0000000000000110 mov r0, r2
uprobe_func:0000000000000118 mul r0, 0x6AE5
uprobe_func:0000000000000120 add r0, r5
uprobe_func:0000000000000128 mov r5, r4
uprobe_func:0000000000000130 mul r5, 0xADD3
uprobe_func:0000000000000138 add r0, r5
uprobe_func:0000000000000140 mov r5, r1
uprobe_func:0000000000000148 mul r5, 0x9284
uprobe_func:0000000000000150 add r0, r5
uprobe_func:0000000000000158 lddw r5, 0xA556E5540340
uprobe_func:0000000000000168 jne r0, r5, LBB0_5
uprobe_func:0000000000000170 mov r5, r3
uprobe_func:0000000000000178 mul r5, 0xDD85
uprobe_func:0000000000000180 mov r0, r2
uprobe_func:0000000000000188 mul r0, 0x8028
uprobe_func:0000000000000190 add r0, r5
uprobe_func:0000000000000198 mov r5, r4
uprobe_func:00000000000001A0 mul r5, 0x652D
uprobe_func:00000000000001A8 add r0, r5
uprobe_func:00000000000001B0 mov r5, r1
uprobe_func:00000000000001B8 mul r5, 0xE712
uprobe_func:00000000000001C0 add r0, r5
uprobe_func:00000000000001C8 lddw r5, 0xA6F374484DA3
uprobe_func:00000000000001D8 jne r0, r5, LBB0_5
uprobe_func:00000000000001E0 mov r5, r3
uprobe_func:00000000000001E8 mul r5, 0x822C
uprobe_func:00000000000001F0 mov r0, r2
uprobe_func:00000000000001F8 mul r0, 0xCA43
uprobe_func:0000000000000200 add r0, r5
uprobe_func:0000000000000208 mov r5, r4
uprobe_func:0000000000000210 mul r5, 0x7C8E
uprobe_func:0000000000000218 add r0, r5
uprobe_func:0000000000000220 mov r5, r1
uprobe_func:0000000000000228 mul r5, 0xF23A
uprobe_func:0000000000000230 add r0, r5
uprobe_func:0000000000000238 lddw r5, 0xB99C485A7277
uprobe_func:0000000000000248 jne r0, r5, LBB0_5
uprobe_func:0000000000000250 stxw [r10-0xC], r1
uprobe_func:0000000000000258 stxw [r10-0x10], r4
uprobe_func:0000000000000260 stxw [r10-0x14], r2
uprobe_func:0000000000000268 stxw [r10-0x18], r3
uprobe_func:0000000000000270 lddw r1, 0xA7D73257B465443
uprobe_func:0000000000000280 stxdw [r10-0x28], r1
uprobe_func:0000000000000288 lddw r1, 0x4648203A47414C46
uprobe_func:0000000000000298 stxdw [r10-0x30], r1
uprobe_func:00000000000002A0 lddw r1, 0x2052554F59202145
uprobe_func:00000000000002B0 stxdw [r10-0x38], r1
uprobe_func:00000000000002B8 lddw r1, 0x4E4F44204C4C4557
uprobe_func:00000000000002C8 stxdw [r10-0x40], r1
uprobe_func:00000000000002D0 mov r6, 0
uprobe_func:00000000000002D8 stxb [r10-0x20], r6
uprobe_func:00000000000002E0 mov r1, r10
uprobe_func:00000000000002E8 add r1, -0x40
uprobe_func:00000000000002F0 mov r3, r10
uprobe_func:00000000000002F8 add r3, -0x18
uprobe_func:0000000000000300 mov r2, 0x21
uprobe_func:0000000000000308 call 6 ; long bpf_trace_printk(const char *fmt, __u32 fmt_size, ...)
uprobe_func:0000000000000310
uprobe_func:0000000000000310 LBB0_5: ; CODE XREF: uprobe+F0↑j
uprobe_func:0000000000000310 ; uprobe+160↑j ...
uprobe_func:0000000000000310 mov r0, r6
uprobe_func:0000000000000318 ret
uprobe_func:0000000000000318 ; End of function uprobe
uprobe_func:0000000000000318
uprobe_func:0000000000000318 ; end of 'uprobe_func'
uprobe_func:0000000000000318
license:0000000000000320 ; ===========================================================================
license:0000000000000320
license:0000000000000320 ; Segment type: Pure data
license:0000000000000320 LICENSE: db "Dual BSD/GPL",0
license:0000000000000320 ; end of 'license'
license:0000000000000320
.rodata.str1.1:000000000000032D ; ===========================================================================
.rodata.str1.1:000000000000032D
.rodata.str1.1:000000000000032D ; Segment type: Pure data
.rodata.str1.1:000000000000032D aWellDoneYourFl:db "WELL DONE! YOUR FLAG: HFCTF{%s}",0xA,0
.rodata.str1.1:000000000000032D ; end of '.rodata.str1.1'

求解

根据Ghidra的方式,可以看到有一个if判断,这里直接用Z3进行约束求解,然后转字符串,最后连接就行(注意 大小端序)

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
from z3 import *
from Crypto.Util.number import *

r1 = Int('r1')
r2 = Int('r2')
r3 = Int('r3')
r4 = Int('r4')

s = Solver()

s.add(28096*r1+64392*r2+29179*r3+52366*r4 == 209012997183893)
s.add(61887*r1+27365*r2+44499*r3+37508*r4 == 181792633258816)
s.add(56709*r1+32808*r2+25901*r3+59154*r4 == 183564558159267)
s.add(33324*r1+51779*r2+31886*r3+62010*r4 == 204080879923831)

if s.check() == sat:
flag = b""

m = s.model()

for i in [r1,r2,r3,r4]:
flag += long_to_bytes(m[i].as_long())[::-1]


print(flag)
#0vR3sAlbs8pD2h53

方法2:

看到main函数里的:

image-20220327143733361

明显是在内核中调用了一个普通的sha-256加密,来判断输入的flag,而且分析这个算法,给出了密文,而且知道flag的长度,直接爆破也是可以的,但是时间比较长