虎符网络安全大赛之零解

First Post:

Last Update:

Word Count:
5.4k

Read Time:
29 min

Hu Fu 2021 Online 之零解

Intro

这次比赛一个都没做出来,菜~,这篇文章主要重新复现一下没做出来的PWN题,这次比赛既然把两个区块链的题放入了PWN中,我们队伍也没人去研究那玩意儿,以后有时间可以去了解一下。。。这次主要还是WEB输出高,REVERSE也不错,我菜得抠脚QAQ。

Misc

你会分析日志吗

通过分析日志,发现请求长度有两个399和377,都是采用sql注入,将日志中sleep函数前面的一个数字提取出来即可。

1
192.168.52.156 - - [11/Mar/2021:18:04:27 +0000] "GET /index.php?id=1'%20and%20if(ord(substr((select%20flag%20from%20flllag),34,1))=83,sleep(2),1)--+ HTTP/1.1" 200 399 "-" "python-requests/2.21.0"

这里也就是83

通过提取长度为377请求中的数字,执行命令进行提取

1
cat access.log| grep "377" | grep "select%20flag%20from%20flllag" | tr -d "a-z()[]\"'/A-Z %.+?-" |  awk -F '=' '{print $3 }' | cut -d ',' -f 1 | tr "\n" ","

1
cat access.log| grep "377" | grep "select%20flag%20from%20flllag" | cut -d '=' -f 3 | cut -d ',' -f 1 | tr "\n" ","

输出如下

1
90,109,120,104,90,51,116,90,98,51,86,102,89,88,74,108,88,51,78,118,88,50,100,121,90,87,70,48,102,81,61,61,
1
2
3
a = [90,109,120,104,90,51,116,90,98,51,86,102,89,88,74,108,88,51,78,118,88,50,100,121,90,87,70,48,102,81,61,61]
for i in a:
print(chr(i), end='')

打印出该字符为: ZmxhZ3tZb3VfYXJlX3NvX2dyZWF0fQ==

base64进行解密:

1
2
echo "ZmxhZ3tZb3VfYXJlX3NvX2dyZWF0fQ==" | base64 -d 
flag{You_are_so_great}

PWN

PWN逆了半天,调试了半天。。。逻辑都没进入,一个字菜~。这次PWN题基本算是VM类型的,grira没办法把某些代码反编译,IDA虽然能反编译汇编,但是伪代码还是存在不能完全反编译。aarch64指令又难看,ememme~。另一个题我看了下,感觉也跟这个差不多,然后我也没很细的看那道题,继续分析apollo,下午太困了,搞不出来,睡觉~~~。这次比赛采用跳转表,只能看汇编动态分析,c伪代码根本不知道在干啥玩意儿!

apollo

hint

hint1: 附件补充aarch64库,同时此题的难点在于vm虚拟机的分析。 https://pan.baidu.com/s/1_RgBNGCBzJeBqlFKDBV0xg 提取码:GAME。

hint2: 漏洞点在于车辆前方的绿灯或红灯的转化,使车辆到达了地图外

各个函数伪c代码如下

main 函数
1
2
3
4
5
6
7
8
9
10
11
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // w0
const char **v4; // x1
const char **v5; // x2

init_();
v3 = logo();
run(v3, v4, v5);
return 0;
}
run 函数
1
2
3
4
5
6
7
8
9
10
11
12
int __cdecl run(int argc, const char **argv, const char **envp)
{
int v4; // [xsp+18h] [xbp+18h]

input_ptr = (__int64)malloc(0x1000uLL);
if ( !input_ptr )
puts("Init fail!");
printf("cmd> ");
read(0, (void *)input_ptr, 0x1000uLL);
parse();
return v4 ^ _stack_chk_guard;
}
parse函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void sub_E14()
{
_QWORD v0[12]; // [xsp+58h] [xbp+58h]

v0[0] = func_0;
v0[1] = func_1[0];
v0[2] = func_2[0];
v0[3] = func_3[0];
v0[4] = func_4[0];
v0[5] = func_5[0];
v0[6] = func_6[0];
v0[7] = func_7[0];
v0[8] = func_8[0];
v0[9] = func_9;
v0[10] = func_a;
v0[11] = func_b;
__asm { BR X0 }
}
mul_1018函数
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
void mul_1018()
{
__int64 v0; // x29
int v1; // w19

if ( enter_flag )
{
*(_DWORD *)(v0 + 48) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 1LL);// var_1 = *(opcode_ptr + 1)
*(_DWORD *)(v0 + 52) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 2LL);// var_2 = *(opcode_ptr + 2)
*(_DWORD *)(v0 + 60) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 3LL);// var_3 = *(opcode_ptr + 3)
*(_DWORD *)(v0 + 64) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 4LL);// var_4 = *(opcode_ptr + 4)
*(_DWORD *)(v0 + 68) = *(_DWORD *)(v0 + 60) + (*(_DWORD *)(v0 + 64) << 8);// size = var_4 * 0x10 + var_3
if ( *(_DWORD *)(v0 + 48) < size_l // check (var_1 < size_l && var_2 < size_r)
&& *(_DWORD *)(v0 + 52) < size_r
&& !*(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52))// *(buf_1 + size_r * var_1 + var_2) == 0
&& *(int *)(v0 + 68) > 0 // check (size > 0 && size <= 0x600)
&& *(int *)(v0 + 68) <= 0x600 )
{
*(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) = 1;// *(buf_1 + size_r * var_1 + var_2) = 1, malloc_flag
v1 = size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52);// v1 = size_r * var_1 + var_2
*((_QWORD *)&ptr_ + v1) = malloc(*(int *)(v0 + 68));// *(ptr + size_r * var_1 + var_2) = malloc(size)
read(0, *((void **)&ptr_ + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)), *(int *)(v0 + 68));// read(0, *(ptr + size_r * var_1 + var_2), size)
*(_QWORD *)(v0 + 72) += 5LL; // opcode ptr += 5
JUMPOUT(0xED0LL); // jump table
}
JUMPOUT(0x256CLL); // abort
}
puts("Abort");
exit(255);
}
div_11f4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void div_11F4()
{
__int64 v0; // x29

if ( enter_flag )
{ // just tack two param
*(_DWORD *)(v0 + 48) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 1LL);// var_1 = *(opcode_ptr + 1)
*(_DWORD *)(v0 + 52) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 2LL);// var_2 = *(opcode_ptr + 2)
if ( *(_DWORD *)(v0 + 48) < size_l // check (var_1 < size_l && var_2 < size_r)
&& *(_DWORD *)(v0 + 52) < size_r
&& *(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) == 1// check malloc flag
&& *((_QWORD *)&ptr_ + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) )// check buf is not null
{
free(*((void **)&ptr_ + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)));// free(buf);
*((_QWORD *)&ptr_ + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) = 0LL;// buf=0;
*(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) = 0;// set malloc flag as 0
*(_QWORD *)(v0 + 72) += 3LL; // opcode_ptr +=3
JUMPOUT(0xED0LL);
}
JUMPOUT(0x256CLL);
}
puts("Abort");
exit(255);
}
plus_1394
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void plus_1394()
{
__int64 v0; // x29

if ( enter_flag )
{
*(_DWORD *)(v0 + 48) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 1LL);// var_1 = *(opcode_ptr + 1)
*(_DWORD *)(v0 + 52) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 2LL);// var_2 = *(opcode_ptr + 2)
*(_DWORD *)(v0 + 56) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 3LL);// var_3 = *(opcode_ptr + 3)
if ( *(_DWORD *)(v0 + 48) < size_l // check (var_1 < size_l && var_2 < size_r)
&& *(_DWORD *)(v0 + 52) < size_r
&& !*(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52))// *(buf_1 + size_r * var_1 + var_2) != 0
&& *(int *)(v0 + 56) > 1 // check (var_1 > 1 && var_2 <= 4)
&& *(int *)(v0 + 56) <= 4 )
{
*(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) = *(_DWORD *)(v0 + 56);// *(buf_1 + size_r * var_1 + var_2) = var_3
*(_QWORD *)(v0 + 72) += 4LL; // opcode ptr += 4
JUMPOUT(0xED0LL);
}
JUMPOUT(0x256CLL);
}
puts("Abort");
exit(255);
}
_sub_14d4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void sub_14D4()
{
__int64 v0; // x29

if ( enter_flag )
{
*(_DWORD *)(v0 + 48) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 1LL);// var_1 = *(opcode_ptr + 1)
*(_DWORD *)(v0 + 52) = *(unsigned __int8 *)(*(_QWORD *)(v0 + 72) + 2LL);// var_2 = *(opcode_ptr + 2)
if ( *(_DWORD *)(v0 + 48) < size_l // check (var_1 < size_l && var_2 < size_r)
&& *(_DWORD *)(v0 + 52) < size_r
&& *(unsigned __int8 *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) > 1u
&& *(unsigned __int8 *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) <= 4u )// check (var_1 > 1 && var_2 <= 4)
{
*(_BYTE *)(buf_1 + size_r * *(_DWORD *)(v0 + 48) + *(_DWORD *)(v0 + 52)) = 0;// *(buf_1 + size_r * var_1 + var_2) = 0
*(_QWORD *)(v0 + 72) += 3LL; // opcode ptr += 3
JUMPOUT(0xED0LL);
}
JUMPOUT(0x256CLL);
}
puts("Abort");
exit(255);
}
w_1620
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
void w_1620()
{
__int64 v0; // x29
char v1; // w0

if ( enter_flag )
{
if ( vertical > 0
&& *(_BYTE *)(base_ + (vertical - 1) * size_r + horizontal) != 1
&& *(_BYTE *)(base_ + (vertical - 1) * size_r + horizontal) != 4 )
{
*(_BYTE *)(base_ + vertical * size_r + horizontal) = 0;
if ( *(_BYTE *)(base_ + (vertical - 1) * size_r + horizontal) )
{
if ( *(_BYTE *)(base_ + (vertical - 1) * size_r + horizontal) == 2
|| *(_BYTE *)(base_ + (vertical - 1) * size_r + horizontal) == 3 )
{
*(_BYTE *)(base_ + (vertical - 2) * size_r + horizontal) = 5;
vertical -= 2;
}
}
else
{
*(_BYTE *)(base_ + --vertical * size_r + horizontal) = 5;
}
}
v1 = dword_14080++;
*(_BYTE *)(qword_14090 + vertical * size_r + horizontal) = v1;
++*(_QWORD *)(v0 + 72); // opcode_ptr ++
JUMPOUT(0xED0LL);
}
puts("Abort");
exit(255);
}
s_1990
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
void s_1990()
{
__int64 v0; // x29
char v1; // w0

if ( enter_flag )
{
if ( size_l - 1 > vertical
&& *(_BYTE *)(base_ + (vertical + 1) * size_r + horizontal) != 1
&& *(_BYTE *)(base_ + (vertical + 1) * size_r + horizontal) != 4 )
{
*(_BYTE *)(base_ + vertical * size_r + horizontal) = 0;
if ( *(_BYTE *)(base_ + (vertical + 1) * size_r + horizontal) )
{
if ( *(_BYTE *)(base_ + (vertical + 1) * size_r + horizontal) == 2
|| *(_BYTE *)(base_ + (vertical + 1) * size_r + horizontal) == 3 )
{
*(_BYTE *)(base_ + (vertical + 2) * size_r + horizontal) = 5;
vertical += 2;
}
}
else
{
*(_BYTE *)(base_ + ++vertical * size_r + horizontal) = 5;
}
}
v1 = dword_14080++;
*(_BYTE *)(qword_14090 + vertical * size_r + horizontal) = v1;
++*(_QWORD *)(v0 + 72); // opcode_ptr ++
JUMPOUT(0xED0LL);
}
puts("Abort");
exit(255);
}
a_1d10
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
void a_1d10()
{
__int64 v0; // x29
char v1; // w0

if ( enter_flag )
{
if ( horizontal > 0
&& *(_BYTE *)(base_ + vertical * size_r + horizontal - 1LL) != 1
&& *(_BYTE *)(base_ + vertical * size_r + horizontal - 1LL) != 4 )
{
*(_BYTE *)(base_ + vertical * size_r + horizontal) = 0;
if ( *(_BYTE *)(base_ + vertical * size_r + horizontal - 1LL) )
{
if ( *(_BYTE *)(base_ + vertical * size_r + horizontal - 1LL) == 2
|| *(_BYTE *)(base_ + vertical * size_r + horizontal - 1LL) == 3 )
{
*(_BYTE *)(base_ + vertical * size_r + horizontal - 2LL) = 5;
horizontal -= 2;
}
}
else
{
*(_BYTE *)(base_ + vertical * size_r + horizontal-- - 1LL) = 5;
}
}
v1 = dword_14080++;
*(_BYTE *)(qword_14090 + vertical * size_r + horizontal) = v1;
++*(_QWORD *)(v0 + 72); // opcode_ptr ++
JUMPOUT(0xED0LL); // loop jump
}
puts("Abort");
exit(255);
}
d_2080
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
void d_2080()
{
__int64 v0; // x29
char v1; // w0

if ( enter_flag )
{
if ( size_r - 1 > horizontal
&& *(_BYTE *)(base_ + vertical * size_r + horizontal + 1LL) != 1
&& *(_BYTE *)(base_ + vertical * size_r + horizontal + 1LL) != 4 )
{
*(_BYTE *)(base_ + vertical * size_r + horizontal) = 0;
if ( *(_BYTE *)(base_ + vertical * size_r + horizontal + 1LL) )
{
if ( *(_BYTE *)(base_ + vertical * size_r + horizontal + 1LL) == 2
|| *(_BYTE *)(base_ + vertical * size_r + horizontal + 1LL) == 3 )
{
*(_BYTE *)(base_ + vertical * size_r + horizontal + 2LL) = 5;
horizontal += 2;
}
}
else
{
*(_BYTE *)(base_ + vertical * size_r + horizontal++ + 1LL) = 5;
}
}
v1 = dword_14080++;
*(_BYTE *)(qword_14090 + vertical * size_r + horizontal) = v1;
++*(_QWORD *)(v0 + 72); // opcode_ptr ++
JUMPOUT(0xED0LL); // abort
}
puts("Abort");
exit(255);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void print_2400()
{
__int64 v0; // x29

if ( enter_flag )
{
*(_DWORD *)(v0 + 36) = 0;
for ( *(_DWORD *)(v0 + 36) = 0; *(_DWORD *)(v0 + 36) < size_l * size_r - 1; ++*(_DWORD *)(v0 + 36) )
{
*(_DWORD *)(v0 + 40) = *(_DWORD *)(v0 + 36) / size_r;
*(_DWORD *)(v0 + 44) = *(_DWORD *)(v0 + 36) - size_r * *(_DWORD *)(v0 + 40);
if ( *(_BYTE *)(base_ + *(int *)(v0 + 36)) == 1 )
{
printf("pos:%d,%d\n", *(unsigned int *)(v0 + 40), *(unsigned int *)(v0 + 44));
puts(*((const char **)&ptr_ + *(int *)(v0 + 36)));
}
}
++*(_QWORD *)(v0 + 72); // opcode_ptr ++
JUMPOUT(0xED0LL);
}
JUMPOUT(0x25B8LL);
}
get_next
1
2
3
4
5
6
7
void sub_2514()
{
__int64 v0; // x29

++*(_QWORD *)(v0 + 0x48); // opcode_ptr ++
JUMPOUT(0xED0LL);
}
exit_
1
2
3
4
5
void __noreturn exit_()
{
puts("Finish");
exit(1);
}

I dubugging this program, I found just input ‘awdsp+-*/‘ making the program aborted.

So input map followings:

set breaking pinter: b *(0x5500000000 + 0xED0 )

a: 0x5500001d10

w:0x5500001620

d: 0x5500002080

s: 0x5500001990

+: 0x5500001394

-: 0x55000014d4

*: 0x5500001018

/: 0x55000011f4

p: 0x5500002400

enter_flag: 0x5500014098

基本上能翻译为c伪装代码的就这几个函数,但是,enter_flag不知道该怎么设置为0。逻辑都进不去@_@。上面这个是在比赛那天分析的一部分。

复现

该程序存在一些函数跳转表,在parse函数下,ida反编译如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void sub_E14()
{
_QWORD v0[12]; // [xsp+58h] [xbp+58h]

v0[0] = func_0;
v0[1] = func_1[0];
v0[2] = func_2[0];
v0[3] = func_3[0];
v0[4] = func_4[0];
v0[5] = func_5[0];
v0[6] = func_6[0];
v0[7] = func_7[0];
v0[8] = func_8[0];
v0[9] = func_9;
v0[10] = func_a;
v0[11] = func_b;
__asm { BR X0 }
}

当时我还以为是寄存器的初始化,一直搞不明白它是怎么跳的。通过看汇编代码,通过下断点进行动态分析:

b *(0x5500000000 + 0xe14) 如下:

sub_E14

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
.text:0000000000000E14                 STP             X29, X30, [SP,#var_C0]!
.text:0000000000000E18 MOV X29, SP
.text:0000000000000E1C STR X19, [SP,#0xC0+var_B0]
.text:0000000000000E20 ADRP X0, #__stack_chk_guard_ptr@PAGE
.text:0000000000000E24 LDR X0, [X0,#__stack_chk_guard_ptr@PAGEOFF]; 获取只读页基址,
.text:0000000000000E28 LDR X1, [X0] ; 获取cannary值
.text:0000000000000E2C STR X1, [X29,#0xC0+var_8]; 储存cannary值在堆栈里
.text:0000000000000E30 MOV X1, #0
.text:0000000000000E34 ADRP X0, #reg_1@PAGE ; 获取data段基址,这里存放的是函数跳转表
.text:0000000000000E38 ADD X1, X0, #reg_1@PAGEOFF ; x1, x0, #0x10
.text:0000000000000E3C ADD X0, X29, #0x58 ; 开始从虚函数表中赋值到堆栈中
.text:0000000000000E40 LDP X2, X3, [X1]; 加载第一个函数地址, 0x5500000eb8
.text:0000000000000E44 STP X2, X3, [X0]; 赋值到堆栈中
.text:0000000000000E48 LDP X2, X3, [X1,#0x10] ; 加载第二个函数地址, 0x55000011f4
.text:0000000000000E4C STP X2, X3, [X0,#0x10]
.text:0000000000000E50 LDP X2, X3, [X1,#0x20] ; 加载第三个函数地址, 0x55000014d4
.text:0000000000000E54 STP X2, X3, [X0,#0x20]
.text:0000000000000E58 LDP X2, X3, [X1,#0x30] ; 加载第三个函数地址, 0x5500001990
.text:0000000000000E5C STP X2, X3, [X0,#0x30]
.text:0000000000000E60 LDP X2, X3, [X1,#0x40] ; 加载第四个函数地址, 0x5500002080
.text:0000000000000E64 STP X2, X3, [X0,#0x40]
.text:0000000000000E68 LDP X1, X2, [X1,#0x50] ; 加载第四个函数地址, 0x5500002514
.text:0000000000000E6C STP X1, X2, [X0,#0x50]
.text:0000000000000E70 ADRP X0, #off_13F98@PAGE ; 获取堆栈基址
.text:0000000000000E74 LDR X0, [X0,#off_13F98@PAGEOFF] ; 通过堆栈某变量获取在bss段储存输入的指针变量
.text:0000000000000E78 LDR X0, [X0] ; 通过该变量获取储存输入的堆指针
.text:0000000000000E7C STR X0, [X29,#0xC0+var_78] ; 将该指针储存在堆栈 var_78 处
.text:0000000000000E80 LDR X0, [X29,#0xC0+var_78] ;
.text:0000000000000E84 STR X0, [X29,#0xC0+var_70] ; 将该指针储存在堆栈 var_78 处
.text:0000000000000E88 LDR X0, [X29,#0xC0+var_78] ;
.text:0000000000000E8C LDRB W0, [X0] ; 取出输入的地一个字节
.text:0000000000000E90 MOV W1, W0 ;将第一个字节放在w1中
.text:0000000000000E94 ADRP X0, #off_13FE8@PAGE ; 获取只读页的base address
.text:0000000000000E98 LDR X0, [X0,#off_13FE8@PAGEOFF] ;opcode跳转表 0x5500003770 ◂— 0xb0000000a
.text:0000000000000E9C SXTW X1, W1 ;拓展为64位, 这里还是输入数据的第一个字节
.text:0000000000000EA0 LDR W0, [X0,X1,LSL#2]; ;x0 + x1*4计算出来的地址读取一字节到w0中,这里x1是我们输入的一字节, x0就是opcode跳转表地址
.text:0000000000000EA4 SXTW X0, W0 # 拓展为64位
.text:0000000000000EA8 LSL X0, X0, #3 ;左移3位
.text:0000000000000EAC ADD X1, X29, #0x58 ; 获取之前加载的函数跳转表
.text:0000000000000EB0 LDR X0, [X1,X0] ; 计算通过获取[x1 + x0]得到真正的函数地址
.text:0000000000000EB4 B loc_ED0

loc_ED0

1
2
3
.text:0000000000000ED0 loc_ED0                                 ; CODE XREF: parse+A0↑j
.text:0000000000000ED0 ; parse+200↓j ...
.text:0000000000000ED0 BR X0 ; 跳到x0指向的地址

函数跳转表如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
.data:0000000000014010 func_0          DCQ init_program        ; DATA XREF: parse+20↑o
.data:0000000000014010 ; parse+24↑o
.data:0000000000014018 func_1 DCQ mul_1018
.data:0000000000014020 func_2 DCQ div_11F4
.data:0000000000014028 func_3 DCQ plus_1394
.data:0000000000014030 func_4 DCQ _sub_14d4
.data:0000000000014038 func_5 DCQ w_1620
.data:0000000000014040 func_6 DCQ s_1990
.data:0000000000014048 func_7 DCQ a_1d10
.data:0000000000014050 func_8 DCQ d_2080
.data:0000000000014058 func_9 DCQ print_2400
.data:0000000000014060 func_a DCQ exit_
.data:0000000000014068 func_b DCQ get_next

opcode 跳转表如下: 0x5500003770

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
0A 00 00 00 0B 00 00 00  0B 00 00 00 0B 00 00 00 ; 00   [00 -> exit_]
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 08
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 10
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 18
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 20
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 01 00 00 00 03 00 00 00 ; 28 [2a -> mul_1018, 2b -> plus_1394]
0B 00 00 00 04 00 00 00 0B 00 00 00 02 00 00 00 ; [2d -> _sub_14d4, 2f -> div_11F4]
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 30
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 38
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 40
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 48
0B 00 00 00 00 00 00 00 0B 00 00 00 0B 00 00 00 [4d -> init_program]
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 50
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 58
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 07 00 00 00 0B 00 00 00 0B 00 00 00 ; 60 [61 -> a_1d10]
08 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 [64 -> d_2080]
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 68
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
09 00 00 00 0B 00 00 00 0B 00 00 00 06 00 00 00 ; 70 [70 -> print_2400, 73 -> s_1990]
0B 00 00 00 0B 00 00 00 0B 00 00 00 05 00 00 00 [77 -> w_1620]
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 78
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 80
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 88
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 90
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; 98
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; a0
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; a8
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; b0
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; b8
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; c0
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; c8
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; d0
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; d8
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; e0
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; e8
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; f0
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00 ; f8
0B 00 00 00 0B 00 00 00 0B 00 00 00 0B 00 00 00

经过调试, 对应opcode * 4之后跳转如下:

通过分析,程序采用 function_table[opcode_table[opcode * 4]]

所以对应查找出opcode对应函数如下:

00 -> exit_

2a -> mul_1018

2b -> plus_1394

2d -> _sub_14d4

2f -> div_11F4

4d -> init_program (EB8)

61 -> a_1d10

64 -> d_2080

70 -> print_2400

73 -> s_1990

77 -> w_1620

而其他的opcode在opcode_table[opcode * 4]之后,都会去执行 get_next (2514) 函数

通过逆向分析,程序进行逻辑交互,则必须要的使enter_flag(14098)变量赋值为1才能进行交互,不然都是执行abort,然而init_program这个分支是不能伪代码反编译的,应该加了花指令,在ghidra下直接不能反编译为汇编。这只能得看汇编了。

1
2
3
4
5
6
7
.text:0000000000000EB8 init_program                            ; DATA XREF: .data:func_0↓o
.text:0000000000000EB8 ADRP X0, #off_13F90@PAGE ; 获取bss段base adreess
.text:0000000000000EBC LDR X0, [X0,#off_13F90@PAGEOFF] ; 获取enter_flag变量地址
.text:0000000000000EC0 LDR W0, [X0] ; 获取enter_flag变量的值
.text:0000000000000EC4 CMP W0, #0 ; 与0做对比,若等于0跳到 loc_ED4处,初始化的时候,enter_flag值是为0的
.text:0000000000000EC8 B.NE loc_2564
.text:0000000000000ECC B loc_ED4

跳到loc_ED4,这里也没办法反编译,又只能看汇编QAQ。。。

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
.text:0000000000000ED4 loc_ED4                                 ; CODE XREF: parse+B8↑j
.text:0000000000000ED4 LDR X0, [X29,#0xC0+var_78] ; 获取输入数据的指针 opcode_ptr = buf_ptr
.text:0000000000000ED8 ADD X0, X0, #1 ; opcode_ptr += 1
.text:0000000000000EDC LDRB W0, [X0]; 获取opcode_ptr指向的该值
.text:0000000000000EE0 MOV W1, W0 ; 赋值给w1
.text:0000000000000EE4 ADRP X0, #off_13FD8@PAGE
.text:0000000000000EE8 LDR X0, [X0,#off_13FD8@PAGEOFF] ; 获取对应变量size_l (1409C)
.text:0000000000000EEC STR W1, [X0] ; 将opcde_ptr对应的一字节赋值给size_l
.text:0000000000000EF0 LDR X0, [X29,#0xC0+var_78] ; 重新获取输入数据指针, 相当于 opcode_ptr = buf_ptr
.text:0000000000000EF4 ADD X0, X0, #2 ; opcode_ptr += 2
.text:0000000000000EF8 LDRB W0, [X0] ; 获取输入数据的第3个值
.text:0000000000000EFC MOV W1, W0 ; 赋值给w1
.text:0000000000000F00 ADRP X0, #off_13F68@PAGE
.text:0000000000000F04 LDR X0, [X0,#off_13F68@PAGEOFF] ; 获取对应变量size_r (140A0)
.text:0000000000000F08 STR W1, [X0] ; 将输入数据的第3个值赋值给该变量
.text:0000000000000F0C ADRP X0, #off_13FD8@PAGE
.text:0000000000000F10 LDR X0, [X0,#off_13FD8@PAGEOFF] ; 获取变量size_l地址
.text:0000000000000F14 LDR W0, [X0] ; 获取size_l中的单字节数据
.text:0000000000000F18 CMP W0, #0x10 ; 判断size_l == 0x10
.text:0000000000000F1C B.GT loc_256C ; 大于的话,退出
.text:0000000000000F20 ADRP X0, #off_13F68@PAGE
.text:0000000000000F24 LDR X0, [X0,#off_13F68@PAGEOFF] ; 获取size_r地址
.text:0000000000000F28 LDR W0, [X0] ; 获取size_r中的单字节数据
.text:0000000000000F2C CMP W0, #0x10 ; 判断size_r == 0x10
.text:0000000000000F30 B.GT loc_256C ; 大于的话,退出
.text:0000000000000F34 ADRP X0, #off_13FD8@PAGE
.text:0000000000000F38 LDR X0, [X0,#off_13FD8@PAGEOFF] ; 获取变量size_l地址
.text:0000000000000F3C LDR W0, [X0] ; 获取size_l中的单字节数据
.text:0000000000000F40 CMP W0, #3 ; size_l与3做比较,若小于等于3,退出
.text:0000000000000F44 B.LE loc_256C
.text:0000000000000F48 ADRP X0, #off_13F68@PAGE
.text:0000000000000F4C LDR X0, [X0,#off_13F68@PAGEOFF] ; 获取变量size_r地址
.text:0000000000000F50 LDR W0, [X0]
.text:0000000000000F54 CMP W0, #3 ; size_r与3做比较,若小于等于3,退出
.text:0000000000000F58 B.LE loc_256C
.text:0000000000000F5C ADRP X0, #off_13FD8@PAGE
.text:0000000000000F60 LDR X0, [X0,#off_13FD8@PAGEOFF] ; 获取变量size_l地址
.text:0000000000000F64 LDR W1, [X0]
.text:0000000000000F68 ADRP X0, #off_13F68@PAGE
.text:0000000000000F6C LDR X0, [X0,#off_13F68@PAGEOFF] ; 获取变量size_r地址
.text:0000000000000F70 LDR W0, [X0]
.text:0000000000000F74 MUL W0, W1, W0 ; w0 = size_l * size_r
.text:0000000000000F78 SXTW X0, W0 ; nmemb
.text:0000000000000F7C MOV X1, #1 ; size
.text:0000000000000F80 BL .calloc ; 调用calloc(size_l * size_r)
.text:0000000000000F84 MOV X1, X0 ; 将返回值赋给x1
.text:0000000000000F88 ADRP X0, #off_13F80@PAGE
.text:0000000000000F8C LDR X0, [X0,#off_13F80@PAGEOFF] ; 获取变量 buf_1 (14088) 地址
.text:0000000000000F90 STR X1, [X0] ; 开辟的内存指针 放在变量 buf_1中
.text:0000000000000F94 ADRP X0, #off_13FD8@PAGE
.text:0000000000000F98 LDR X0, [X0,#off_13FD8@PAGEOFF] ; 获取变量size_l地址
.text:0000000000000F9C LDR W1, [X0]
.text:0000000000000FA0 ADRP X0, #off_13F68@PAGE
.text:0000000000000FA4 LDR X0, [X0,#off_13F68@PAGEOFF] ; 获取变量size_r地址
.text:0000000000000FA8 LDR W0, [X0]
.text:0000000000000FAC MUL W0, W1, W0 ; w0 = size_l * size_r
.text:0000000000000FB0 SXTW X0, W0 ; nmemb
.text:0000000000000FB4 MOV X1, #1 ; size
.text:0000000000000FB8 BL .calloc ; 再开辟一次 calloc(size_l * size_r)
.text:0000000000000FBC MOV X1, X0
.text:0000000000000FC0 ADRP X0, #off_13FF0@PAGE
.text:0000000000000FC4 LDR X0, [X0,#off_13FF0@PAGEOFF] ; 获取变量 buf_2 (14090) 地址
.text:0000000000000FC8 STR X1, [X0] ; 开辟的内存指针 放在变量 buf_2中
.text:0000000000000FCC ADRP X0, #off_13F90@PAGE
.text:0000000000000FD0 LDR X0, [X0,#off_13F90@PAGEOFF] ; 获取enter_flag变量的地址
.text:0000000000000FD4 MOV W1, #1
.text:0000000000000FD8 STR W1, [X0] ; 给enter_flag变量赋值为1
.text:0000000000000FDC LDR X0, [X29,#0xC0+var_78] ; 获取输入数据的指针 opcode_ptr = buf_ptr
.text:0000000000000FE0 ADD X0, X0, #3 ; opcode += 3
.text:0000000000000FE4 STR X0, [X29,#0xC0+var_78] ; 设置buf_ptr = opcode_ptr
.text:0000000000000FE8 LDR X0, [X29,#0xC0+var_78] ; 获取该opcode_ptr
.text:0000000000000FEC LDRB W0, [X0] ; 获取该opcode_ptr指向的值
.text:0000000000000FF0 MOV W1, W0
.text:0000000000000FF4 ADRP X0, #off_13FE8@PAGE
.text:0000000000000FF8 LDR X0, [X0,#off_13FE8@PAGEOFF] ; opcode_table的地址
.text:0000000000000FFC SXTW X1, W1
.text:0000000000001000 LDR W0, [X0,X1,LSL#2] ;通过opcode查找函数跳转表的索引
.text:0000000000001004 SXTW X0, W0
.text:0000000000001008 LSL X0, X0, #3
.text:000000000000100C ADD X1, X29, #0x58 ; 获取函数跳转表地址
.text:0000000000001010 LDR X0, [X1,X0] ; 获取真正要跳转的地址
.text:0000000000001014 B loc_ED0
.text:0000000000001014 ; End of function parse

通过以上我大致写一下伪c代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void init_program(char **opcode_ptr_ptr) {
char *opcode_ptr = *opcode_ptr_ptr;
opcode_ptr += 1;
size_l = *opcode_ptr;
opcode_ptr += 1;
size_r = *opcode_ptr;
if(size_r > 0x10 || size_l > 0x10 || size_r <= 3 || size_l <= 3) {
exit_();
}
buf_1 = calloc(size_l * size_r);
buf_2 = calloc(size_l * size_r);
enter_flag = 1;
*opcode_ptr_ptr += 3;
jump function_table[opcode_table(**opcode_ptr_ptr)] // 这里的opcode_table()代表是通过一系列操作得到的函数表索引
}

进入该函数 opcode = ‘4d’ + ‘0x00’ + ‘0x01’, 然后这里的’0x00’是要赋值给size_l,’0x01’赋值给size_r。

想要进入其他函数,不abort的情况下就的使enter_flag不为0了,调用init_program这个分支是第一步,这样才能操作其他函数。

好了基本所有函数逻辑已经清楚明白了,我没该怎么利用?漏洞在哪?

hint2提示: 漏洞点在于车辆前方的绿灯或红灯的转化,使车辆到达了地图外

车是什么玩意? 红绿灯?地图?

通过分析mul_1018, div_11f4plus_1394_sub_14d4 都没发现漏洞QAQ。

WEB

签到

师傅们常说,要善于学习,细致入微;师傅们也常说,要善于分享,总结归纳。

hint:

2021年3月28日,PHP维护的官方Git服务器 git.php.net 被袭击,其Git仓库遭到恶意篡改。如果开发者使用这些遭到篡改的源代码进行网页的开发的话,网站就会在不知情的情况下被感染。

不知道什么东西

1

是一个个人博客

打赏点小钱
支付宝 | Alipay
微信 | WeChat