2023 山东省大学生网络安全技能大赛 复盘

2023 网安省赛复盘

前言

  • AiDai👴🏻🐂🍺

Misc - 签到

下载附件得到游戏地址,随便玩了三四关发现一共 17 关,👴🏻懒的打了,网页源码中发现注释的 index.js,搜 flag 有个 flag 函数,里头就有 flag:

image-20231023221659202

1
2
3
4
5
6
7
let r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+=";
let cars = [25, 38, 49, 33, 25, 55, 45, 37, 12, 22, 24, 50, 12, 51, 24, 51, 13, 3, 16, 52, 13, 38, 25, 38, 13, 54, 4, 52, 13, 19, 20, 55, 12, 38, 8, 51, 12, 38, 16, 49, 14, 22, 8, 54, 13, 35, 37, 33, 12, 55, 52, 63];
let ff = "";
for (var iii = 0; iii < cars.length; iii++) {
ff = ff + r[cars[iii]];
}
/*this is flag*/

扔到控制台里头🏃🏻‍♀️:

image-20231023221716239

1
ZmxhZ3tlMWYyMzYzNDQ0NmZmN2E0NTU3MmIzMmQxOWI2NjlhM30=

base64 解码得 flag

Misc - 啊吧啊吧的数据包

通过筛选 POST 请求发现有 shell 交互:

image-20231023221757948

解码 base64 发现这是一个查看 flag 的操作

1
2
echo Y2F0IGZsYWdfMTI2NDQzMTI= | base64 -d   
cat flag_12644312

再结合后面的命令,这个 shell 在逐个字符爆破 flag,采用时间盲注,那么筛选这一段的 http 流数据(30147 - 35661),导出为 csv 便于 python 处理,删掉多余的列之后,保留 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
#!/usr/bin/python3

import re

with open("data_out.csv", "r") as f:
lines = f.readlines()
now_num = 1
now_index = "cut -c{:d}"
old_line = ""
attack_time = 0
response_time = 0
pattern = "cut -c\d+\) = \'(.)\'"
for line in lines:
try:
t, payload = line.strip().split(",")
if payload == "":
response_time = float(t)
# print(attack_time, response_time)
if response_time - attack_time >= 3:
xxx = re.findall(pattern=pattern, string=old_line)
print(xxx[0], end="")
else:
attack_time = float(t)
old_line = line.strip()
except Exception:
pass

Misc - 我应该去爱你

扔到 Audacity 里看频谱,这玩意考眼神:

image-20231023222109483

猜了好几遍才猜对,眼神不行了😅

1
flag{dfcba866efb361d89b7240c49653a782}

Misc - 简单编码(补)

  • 比赛日下午脑子一团浆糊,最后十几分钟才意识到这玩意是不是 二进制和八进制
  • 然后写脚本,寄在这个根据长度判断是二进制还是八进制(但凡判断条件里头加上个 = 就出 flag 了)
    • 出来的字符串先 base64 解码,然后 base32 解码,最后 hex2str
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
import binascii

with open("./e949a66de57a5411f5e9935442787a5b.txt", "r") as f:
data = f.read().split(" ")
flag = ""
for i in data:
if len(i) == 0:
continue
if len(i) <= 4:
flag += chr(int(i, 8))
else:
flag += chr(int(i, 2))

flag = base64.b64decode(flag)
flag = base64.b32decode(flag)
flag = binascii.a2b_hex(flag)
print(flag)

Misc - 神秘的 base(补)

  • 👴🏻之前没用过 base 换表,不晓得怎么改,找 b64decode 源码改了一通也不行
    • 原来只需要 translate 就行😅
  • 后续补的脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import base64
import itertools

def DecodeB64(text):
old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
l = "OYiv05"

all = itertools.permutations(l, 6)
for item in all:
item = "".join(item)
table = "xbQTZqjN8ERuwlzVfUIrPkeHd{}LK697o2pSsGD+ncgm3CBh/Xy1MF4JAWta".format(item)
decoded = base64.b64decode(text.translate(text.maketrans(old_table, table)))
if decoded.endswith(b"}") and decoded.isascii():
print(decoded)

if __name__ == '__main__':
encoded_flag = "EvAzEwo6E9RO4qSAHq42E9KvEv5zHDt34GtdHGJaHD7NHG42bwd="
DecodeB64(encoded_flag)

Crypto - 小试牛刀

给的密文:

1
ipfm\x82Kj]p~l?\x82ogw\x85mt[K\x8br\x97

猜测是根据 flag{} 进行变换的

那么找规律发现:

1
2
3
4
5
In [11]: ord('f') - ord('i')
Out[11]: -3

In [12]: ord('l') - ord('p')
Out[12]: -4

写解题脚本得到 flag:

1
2
3
4
5
d = b"ipfm\x82Kj]p~l?\x82ogw\x85mt[K\x8br\x97"
for i, v in enumerate(d):
print(chr(v-i-3), end="")

# flag{CaSer_1s_VerY_E4sY}

Reverse - 人生模拟(补)

  • 👴🏻好久不用 Ghidra 的 patch,给忘了导出要选 ELF(👴🏻选的Binary,👴🏻脑子瓦特了),就说怎么 patch 的程序直接执行不了

简单看了看程序逻辑,猜测 flag 会在 FUN_0040350f 函数中打印出来,想要执行到这里必须要活到 60 岁,在主函数中的 while 循环里的 FUN_00403914 函数应该是获取当前的年龄:

image-20231024160930613

这个函数长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
00403914 55              PUSH       RBP
00403915 48 89 e5 MOV RBP,RSP
00403918 48 89 7d f8 MOV qword ptr [RBP + local_10],RDI
0040391c 48 8b 45 f8 MOV RAX,qword ptr [RBP + local_10]
00403920 8b 40 04 MOV EAX,dword ptr [RAX + 0x4]
00403923 5d POP RBP
00403924 c3 RET
00403925 90 ?? 90h

undefined4 FUN_00403914(long param_1)

{
return *(param_1 + 4);
}

那直接 patch, 直接返回 60:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
00403914 55              PUSH       RBP
00403915 48 89 e5 MOV RBP,RSP
00403918 48 89 7d f8 MOV qword ptr [RBP + local_10],RDI
0040391c 48 c7 c0 MOV RAX,0x3c
3c 00 00 00
00403923 5d POP RBP
00403924 c3 RET
00403925 90 ?? 90h

undefined8 get_age(void)

{
return 0x3c;
}

然后 FUN_0040350f 函数里有这样的条件判断:

1
2
if ((((0 < param_2) && (0 < param_3)) && (0 < param_4)) &&
(((0 < param_5 && (param_1 == 0x3c)) && (local_f0 == 0x11120c94))))

直接把对应条件判断指令反过来写, 改完:

1
if ((((true) && (true)) && (true)) && (((true && (param_1 == 60)) && (local_f0 != 0x11120c94))))

Patch 完直接导出 ELF 文件, 运行之后崩溃在了 0x4035de 处(idiv rcx, 此时 rcx 是 0),直接 nop 掉,重新导出运行就能拿到flag:

1
2
3
4
5
6
7
8
9
10
11
12
 ./simulation2
==============================================================================
|| ||
|| It's another year of Shandong competition. ||
|| Welcome to the life simulator ||
|| ||
|| author: Mr_hello ||
|| ||
|| ||
==============================================================================
你出生了,你是一名男孩
flag{76bce138e9f529db4d684e1d5e7875e4}