2025蓝桥杯全国总决赛部分WP

xxtea

下载附件打开是cyberchef的一个加密步骤(当时半决也有一个差不多的题)

image-20250623181326912

按F12拿到输出

image-20250623181612105

1
2
3
4
5
6
7
8
00000000 9e 45 02 82 ea 25 d2 62 0d 06 e7 b4 5f dc 62 bd |.E...%.b...._.b.|
00000010 39 68 47 26 54 50 9e 26 4c 86 06 2e 2b af 3b 8b |9hG&TP.&L...+.;.|
00000020 7c e4 99 0d ad 12 e2 7e 46 c9 00 29 a6 ea 1f b0 ||......~F..)....|
00000030 ae c1 da 30 f3 98 94 82 33 fd 99 d3 d3 b9 a0 12 |...0....3.......|
00000040 d9 de a5 d2 0e 95 f3 a5 bb f2 f9 91 b2 a3 c2 94 |................|
00000050 b1 1c b7 eb 87 65 d7 0e 0c 6b d5 65 4d e1 43 1e |.....e...k.eM.C.|
00000060 be b4 34 71 b5 53 d4 ea 62 4e b9 80 f3 6f f0 5c |..4q.S..bN...o.\|
00000070 59 75 58 52 9e a4 ec e2 35 b7 d8 68 27 ee c0 94 |YuXR....5..h'...|

用cyberchef(本地)反解密就可以了

image-20250623181954669

flag{4eb88a16-be48-4de2-ab2a-ed09a09ed386}

server_logs

题目是让我们提交:

  • 攻击者的登录靶机所用的账号
  • 攻击者的ip
  • 攻击者留下的后面服务
  • 靶机泄露敏感信息时的DNS域名(只写关键部分)

flag格式是flag{账号_ip_服务_DNS域名}

(蓝桥杯连一个应急环境都给不了,直接给附件…….)附件给了三个文件夹,/var/logdnsmasq.log下可以看到有加密信息

image-20250623182701257

base64解密一下

image-20250623182821275

拿到账号:attacker,DNS域名固定部分:data.leak.ev,攻击者ip:192.168.42.77

etc/systemd/system下就有一个hidden_backdoor.service(文件名提示不用多说了)

image-20250623183130954

查看文件内容,可以看到是一个nc反弹shell

image-20250623183120828

拼接flag

flag{attacker_192.168.42.77_hidden_backdoor_data.leak.ev}

弱口令

题目环境打开:

image-20250623183900131

使用提示的账号密码登录环境,查看用户的个人笔记发现提示信息

image-20250623183955561

告诉我们密码提示是用户名+4位数字

那直接上bp爆破,第一次爆破没有结果

猜测使用0进行左填充

image-20250623184519805

根据回显长度筛选成功拿到密码

image-20250623184554071

登录admin即可拿到flag

image-20250623184612170

flag{8486fe92-2be4-4150-a481-74688803c872}

Fastcoll

使用下发的工具制造出符合规则的md5碰撞文件即可

image-20250623185318495

先使用fastcoll的-h选项查看如何使用

image-20250623185417459

  • -h [ –help ]
    • 显示帮助信息。
  • -q [ –quiet ]
    • 安静模式,减少输出信息(即不显示详细日志)。
  • -i [ –ihv ] arg
    • 使用指定的初始向量(Initial Hash Value,简称 IHV)。默认为 MD5 的标准初始值。
      (此项用于高级攻击,如不同IHV起点的碰撞,普通用户通常不使用。)
  • -p [ –prefixfile ] arg
    • ​ 指定一个前缀文件(prefixfile),程序将从这个文件开始构造碰撞。
      ​ 同时,prefixfile 的内容会被拷贝到输出文件的开头。
  • -o [ –out ] arg
    • 指定两个输出文件的文件名。必须放在最后,并且要指定两个文件名

直接构造符合规则的两个md5碰撞文件即可

fastcoll_v1.0.0.5.exe -p 1.txt -o msg1.bin msg2.bin

base64编码一下

image-20250623185943149

image-20250623185953249

验证成功之后,即可拿到flag

image-20250623190048610

flag{ccef1105-4048-4f10-8c69-edf68d93a1ec}

flowzip2

附件是一个pcapng流量包

binwalk分析一波

image-20250623190858656

发现有一堆zip压缩包,而且还有提示”\d{3}”

使用binwalk -e提取出来,尝试解压发现有密码

image-20250623191023768

根据前面的提示”\d{3}”,猜测是三个数字组合(正则的表达式)

先用ARCHPR爆破一下

image-20250623191253401

成功拿到密码,猜测正确

写一个爆破解压脚本

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
import os
import subprocess

def unzip_file(zip_file_path, output_dir, password):
"""调用7z解压单个zip包到指定目录"""
# 构造7z命令
cmd = [
"D:/7-Zip/7z.exe", "x", zip_file_path, # x 表示解压,这里的7z是我的7z命令行路径
f"-p{password}", # 设置密码
f"-o{output_dir}", # 设置解压路径
"-y" # 自动回答所有提示(如覆盖文件)
]
try:
result = subprocess.run(cmd, check=True,capture_output=True,text=True)
print(f"解压成功:{result.stdout}")
except Exception as e:
print(f"解压失败:{e}")

def unzip_folder(folder_path):
"""
调用7z解压整个文件夹下的所有zip包到指定目录
密码采用字典爆破
解压路径为folder_path
解压文件夹为zip包名
"""
for root, dirs, files in os.walk(folder_path):
for file in files:
if file.endswith(".zip"):
zip_file_path = os.path.join(root, file)
for password in range(1000):
# 密码填充为4位,用0左填充
password = str(password).zfill(3)
# file.split(".")[0]获取分隔符后的第一个字符串
unzip_file(zip_file_path, os.path.join(root, file.split(".")[0]), password.strip())


# unzip_file("D:/python_learn/testZIP/1A960.zip", "D:/python_learn/testZIP/1A960", "221")
unzip_folder("D:\\zuoye\\杂项\\蓝桥杯\\flowzip2_fe9d6d2cf5a6fe83f1d7f0fe3c60fa25\\_flowzip2.pcapng.extracted")

再用dnGrep查找flag{即可

image-20250624172929529

成功找到flag在579F0下的dcsxr.txt

flag{5f5491b6-fddf-4be8-ab44-5a18831cc45b}

rand_pyc_obf

题目都提示我们是pyc文件,那我们直接使用pyinstxtractor进行打包得到pyc文件

image-20250623193038990

然后使用uncompyle6进行反编译,成功拿到python源码

image-20250623193254079

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
import sys, random, base64
Ii = input("Please input the flag: ").strip()
if not (Ii.startswith("flag{") and Ii.endswith("}") and len(Ii) == 42):
print("Length incorrect")
sys.exit(-999)
oo0O000ooO = base64.b64encode(Ii.encode()).decode() + "_easyctf"
ii = []
for iiI in oo0O000ooO:
random.seed(ord(iiI))
ii.append(random.randint(1000000, 9999999))
else:
iii111 = [
4417023, 5690625, 9639225, 1327718, 4417023, 5085550, 5752075,
9556690, 5240080, 6431679, 3428007, 3189766, 3438336, 5757818,
3189766, 5690625, 4148389, 2254831, 6292433, 2122126, 5240080,
6431679, 9488271, 2464675, 7216908, 5757818, 3189766, 5690625,
3438336, 6431679, 2360475, 6002055, 5240080, 9040261, 8655414,
9347278, 3438336, 2254831, 2122126, 5135281, 2360475, 9347278,
4417023, 1327718, 3438336, 3448715, 9488271, 5501611, 5240080,
5757818, 9488271, 5501611, 5240080, 9347278, 4148389, 1714134,
9923116, 4267438, 4263793, 5752075, 2464675, 7777627, 6002055,
3485900]
Iio0 = []
for iiI in oo0O000ooO:
random.seed(ord(iiI))
Iio0.append(random.randint(1000000, 9999999))
else:
if Iio0 != iii111:
print("Wrong flag")
sys.exit(-1)
print("Correct!")

这个脚本验证 flag 的方式是:

  1. 检查格式是否是:flag{...},总长度必须为 42 字符。
  2. 将 flag 进行 base64 编码,并拼接字符串 _easyctf,变成一个新字符串。
  3. 用这个新字符串的每个字符做随机种子,调用 random.seed(ord(c)),然后生成 random.randint(1000000, 9999999),得到一个整数列表。
  4. 这个整数列表与预定义的 iii111 比较,完全相同则验证通过。

一个字符唯一对应一个整数。我们可以暴力穷举所有可能字符(ASCII 32~126),找出每一个 randint 对应哪个字符。

从而还原出整个 oo0O000ooO,然后去除结尾 _easyctf,base64 解码得到原始 flag。

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
import random
import base64

# 目标随机数序列
target = [
4417023, 5690625, 9639225, 1327718, 4417023, 5085550, 5752075,
9556690, 5240080, 6431679, 3428007, 3189766, 3438336, 5757818,
3189766, 5690625, 4148389, 2254831, 6292433, 2122126, 5240080,
6431679, 9488271, 2464675, 7216908, 5757818, 3189766, 5690625,
3438336, 6431679, 2360475, 6002055, 5240080, 9040261, 8655414,
9347278, 3438336, 2254831, 2122126, 5135281, 2360475, 9347278,
4417023, 1327718, 3438336, 3448715, 9488271, 5501611, 5240080,
5757818, 9488271, 5501611, 5240080, 9347278, 4148389, 1714134,
9923116, 4267438, 4263793, 5752075, 2464675, 7777627, 6002055,
3485900
]

# 建立:randint → 字符 的反查字典
def build_reverse_map():
rev = {}
for c in range(32, 127): # 所有可打印ASCII
random.seed(c)
val = random.randint(1000000, 9999999)
rev[val] = chr(c)
return rev

reverse_map = build_reverse_map()

# 还原出 base64(flag) + '_easyctf'
recovered = ""
for val in target:
if val not in reverse_map:
print(f"[!] 未知值: {val}")
exit(1)
recovered += reverse_map[val]

print("[+] Recovered string (base64 + _easyctf):", recovered)

# 去掉 _easyctf
if not recovered.endswith("_easyctf"):
print("[!] 末尾不正确")
exit(1)

b64_part = recovered[:-8]
flag = base64.b64decode(b64_part).decode()
print("[🎯] Flag =", flag)

image-20250623193427990

flag{30de99f4-50d2-9f8f-2868-dcfa9d81483c}