2024 年春秋杯网络安全联赛冬季赛

Misc

See anything in these pics?

题目内容:

TBH THERE ARE SO MANY PICS NOT ONLY JUST 2 PIC

附件下载 提取码(GAME)备用下载

附件是一张 png 图片 Aztec.png 和一个加密的 zip 压缩包 YVL.zip

Aztec

根据图片特征以及它的名字可以知道这是 Aztec 条码,找个免费的在线扫码网站扫一下得到了 5FIVE

2

5FIVE 成功解开压缩包,得到一张 jpg 图片 YVL.png

YVL

把图片丢到随波逐流,发现藏了东西,foremost 提取出了一张黑色的 png 图片,再丢回随波逐流发现图片宽度和高度被修改过,可能存在隐写,恢复后得到图片

4

1
flag{opium_00pium}

简单算术

题目内容:

想想异或

附件下载 提取码(GAME)备用下载

附件的内容是 ys~xdg/m@]mjkz@vl@z~lf>b,根据提示可以知道要对明文进行简单的异或解密操作

1
2
3
4
5
cipher_text = "ys~xdg/m@]mjkz@vl@z~lf>b"
for key in range(256):
plain_text = ''.join([chr(ord(c) ^ key) for c in cipher_text])
if "flag" in plain_text:
print(plain_text)

写个脚本就能得到 flag 了

1
flag{x0r_Brute_is_easy!}

简单镜像提取

题目内容:

RR_studio

附件下载 提取码(GAME)备用下载

附件是 data.pcapng,丢到随波逐流就发现文件头不对了

接着用 foremost 提取出一个压缩包,解压得到 disk-recovery.img,再次丢到随波逐流就发现文件头不对

还是用 foremost 提取,这次发现提取出了一个 Excel 文档

5

1
flag{E7A10C15E26AA5750070EF756AAA1F7C}

压力大,写个脚本吧

题目内容:

爆破

附件下载 提取码(GAME)备用下载

附件是一个带密码的 zip 压缩包 zip_99.zip 和一个 txt 文档 password_99.txt,文档的内容是 RkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZH

先试了一下发现不是伪加密,然后直接拿文档的内容作为密码也失败了,接着我还试了下重复的片段 RkdGR0ZH 发现也不对

没试出来我就看了下里面的文档的 CRC 校验值竟然和外面这个是一样的

6

又联想到题目的 “爆破”,我就天真地以为是明文爆破,结果就是爆破了六个小时😡

7

后来才发现不对劲,把 RkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZHRkdGR0ZH 用 base64 解码的结果当作密钥试了下就解开了😭

解开了发现是层层嵌套的,并且每一层的压缩包的密码就是和压缩包放在一起那个 txt 文档里的内容用 base64 解码后的结果,写个脚本就搞定了(压缩包后面没用就先删掉了,txt 还有用就先留着不删)

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
import zipfile
import os
import base64

# 设置初始参数
folder_path = r'E:\Downloads\zip_100' # 这里改成实际的地址

def get_password_from_txt(password_filename):
if not os.path.exists(password_filename):
print(f"警告: 密码文件 {password_filename} 不存在。")
return None

with open(password_filename, 'r') as file:
encoded_password = file.read().strip()

try:
# Base64解码
password = base64.b64decode(encoded_password).decode('utf-8')
return password
except Exception as e:
print(f"错误: 解码密码时出错 - {e}")
return None

def unzip_and_cleanup(zip_path, folder_path):
zip_filename = os.path.basename(zip_path)
password_num = zip_filename.split('.')[0].split('_')[1] # 提取数字部分如'99'
password_filename = os.path.join(folder_path, f"password_{password_num}.txt")

password = get_password_from_txt(password_filename)

if password is None:
return False

try:
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
# 尝试使用给定的密码解压
zip_ref.extractall(path=folder_path, pwd=password.encode())
print(f"成功解压: {zip_filename}")
return True
except RuntimeError as e:
print(f"解压失败: {zip_filename} - 错误信息: {e}")
return False

def process_nested_zips(folder_path, start_num):
current_num = start_num

while True:
zip_filename = f"zip_{current_num}.zip"
full_zip_path = os.path.join(folder_path, zip_filename)

# 检查文件是否存在
if not os.path.exists(full_zip_path):
print(f"文件 {zip_filename} 不存在,停止解压。")
break

if unzip_and_cleanup(full_zip_path, folder_path):
# 清理工作:删除原始ZIP文件和对应的TXT文件
password_filename = f"password_{current_num}.txt"
password_full_path = os.path.join(folder_path, password_filename)

# 删除文件
os.remove(full_zip_path)
# if os.path.exists(password_full_path):
# os.remove(password_full_path)

print(f"已删除: {zip_filename}{password_filename}")

# 继续处理下一个层级
next_zip_filename = f"zip_{current_num - 1}.zip"
next_full_zip_path = os.path.join(folder_path, next_zip_filename)
if os.path.exists(next_full_zip_path):
current_num -= 1
continue
else:
print("没有更多层级的压缩包了,解压完成。")
break
else:
print("解压过程中出现问题,停止进一步操作。")
break

# 解压缩
process_nested_zips(folder_path, 99)

解压到最后发现最后一层是 flag-hint.txt,内容是 PASSWORD+PASSWORD.png

根据最后一层的解压密码 89504E470D0A1A0A0000000D494844520000019000000190 不难看出这题是要把这些密码按顺序拼起来凑出一个 png 图片(png 的文件头是 89 50 4e 47 0d 0a 1a 0a

于是在上面代码的基础上加上下面的代码,把字符串拼起来拼凑出一张 png 图片

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
# 根据提示把密码文件解码得到的字符串拼接起来并传换成png图片
def concatenate_base64_decoded_from_txt_files(folder_path, start_num, end_num):
decoded_string = ''

for num in range(start_num, end_num + 1):
password_filename = os.path.join(folder_path, f"password_{num}.txt")

if not os.path.exists(password_filename):
print(f"警告: 密码文件 {password_filename} 不存在")
continue

with open(password_filename, 'r') as file:
encoded_password = file.read().strip()

try:
# Base64解码
decoded_data = base64.b64decode(encoded_password)
# 将解码后的二进制数据转换为字符串并拼接
decoded_string += decoded_data.decode(errors='replace')
except Exception as e:
print(f"错误: 解码 {password_filename} 时出错 - {e}")

return decoded_string

# 获取所有密码并解码为字符串
hex_string = concatenate_base64_decoded_from_txt_files(folder_path, 0, 99)

# 如果字符串以 'FG' 结尾,则移除 'FG'
while hex_string.endswith('FG'):
hex_string = hex_string[:-2]

# 将十六进制字符串转换为字节对象
try:
byte_data = bytes.fromhex(hex_string)
except ValueError as e:
print(f"Error converting hex to bytes: {e}")
exit(1)

# 指定输出文件名
output_filename = os.path.join(folder_path, "output_image.png")

# 写入二进制数据到文件
with open(output_filename, 'wb') as image_file:
image_file.write(byte_data)

完整代码如下

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
111
112
113
114
115
116
117
118
119
120
121
122
123
import zipfile
import os
import base64

# 设置初始参数
folder_path = r'E:\Downloads\zip_100'

def get_password_from_txt(password_filename):
if not os.path.exists(password_filename):
print(f"警告: 密码文件 {password_filename} 不存在")
return None

with open(password_filename, 'r') as file:
encoded_password = file.read().strip()

try:
# Base64解码
password = base64.b64decode(encoded_password).decode('utf-8')
return password
except Exception as e:
print(f"错误: 解码密码时出错 - {e}")
return None

def unzip_and_cleanup(zip_path, folder_path):
zip_filename = os.path.basename(zip_path)
password_num = zip_filename.split('.')[0].split('_')[1] # 提取数字部分
password_filename = os.path.join(folder_path, f"password_{password_num}.txt")

password = get_password_from_txt(password_filename)

if password is None:
return False

try:
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
# 尝试使用给定的密码解压
zip_ref.extractall(path=folder_path, pwd=password.encode())
return True
except RuntimeError as e:
print(f"解压失败: {zip_filename} - 错误信息: {e}")
return False

def process_nested_zips(folder_path, start_num):
current_num = start_num

while True:
zip_filename = f"zip_{current_num}.zip"
full_zip_path = os.path.join(folder_path, zip_filename)

# 检查文件是否存在
if not os.path.exists(full_zip_path):
print(f"文件 {zip_filename} 不存在,停止解压")
break

if unzip_and_cleanup(full_zip_path, folder_path):
# 清理工作:删除原始ZIP文件和对应的TXT文件
password_filename = f"password_{current_num}.txt"
password_full_path = os.path.join(folder_path, password_filename)

# 删除文件
os.remove(full_zip_path)
# if os.path.exists(password_full_path):
# os.remove(password_full_path)

# 继续处理下一个层级
next_zip_filename = f"zip_{current_num - 1}.zip"
next_full_zip_path = os.path.join(folder_path, next_zip_filename)
if os.path.exists(next_full_zip_path):
current_num -= 1
continue
else:
break
else:
print("解压过程中出现问题,停止进一步操作")
break

# 解压缩
process_nested_zips(folder_path, 99)

# 根据提示把密码文件解码得到的字符串拼接起来并传换成png图片
def concatenate_base64_decoded_from_txt_files(folder_path, start_num, end_num):
decoded_string = ''

for num in range(start_num, end_num + 1):
password_filename = os.path.join(folder_path, f"password_{num}.txt")

if not os.path.exists(password_filename):
print(f"警告: 密码文件 {password_filename} 不存在")
continue

with open(password_filename, 'r') as file:
encoded_password = file.read().strip()

try:
# Base64解码
decoded_data = base64.b64decode(encoded_password)
# 将解码后的二进制数据转换为字符串并拼接
decoded_string += decoded_data.decode(errors='replace')
except Exception as e:
print(f"错误: 解码 {password_filename} 时出错 - {e}")

return decoded_string

# 获取所有密码并解码为字符串
hex_string = concatenate_base64_decoded_from_txt_files(folder_path, 0, 99)

# 如果字符串以 'FG' 结尾,则移除 'FG'
while hex_string.endswith('FG'):
hex_string = hex_string[:-2]

# 将十六进制字符串转换为字节对象
try:
byte_data = bytes.fromhex(hex_string)
except ValueError as e:
print(f"Error converting hex to bytes: {e}")
exit(1)

# 指定输出文件名
output_filename = os.path.join(folder_path, "output_image.png")

# 写入二进制数据到文件
with open(output_filename, 'wb') as image_file:
image_file.write(byte_data)

得到的图片是一张二维码,扫码就能得到 flag 了

8

1
flag{_PASSWORDs_is_fl@g!_}

NetHttP

题目内容:

在凌晨一两点,公司内网有一台私人服务器被入侵,攻击者非常挑衅的留下了明显的痕迹。

附件下载 提取码(GAME)备用下载

附件是 NetHttP.pcapng,那就先用 wireshark 打开分析一下,随便滑了两下就发现了这个(确实明显)

11

GET /rce?name=%7B%7Blipsum.__globals__.__builtins__.eval(%22__import__('os').popen('echo%20aWYgWyAkKGNhdCAvYXBwL3NlY3JldC9tdy9tNXxiYXNlNjQgLXcgMHwgYXdrIE5SPT0xIHwgY3V0IC1jIDEpID09ICdTJyBdOyB0aGVuIGVjaG8gInJjZSI7Zmk=%20%7C%20base64%20 这条恶意代码里面夹杂着一段 base64 编码后的字符串 aWYgWyAkKGNhdCAvYXBwL3NlY3JldC9tdy9tNXxiYXNlNjQgLXcgMHwgYXdrIE5SPT0xIHwgY3V0IC1jIDEpID09ICdTJyBdOyB0aGVuIGVjaG8gInJjZSI7Zmk=,解码得到 if [ $(cat /app/secret/mw/m5|base64 -w 0| awk NR==1 | cut -c 1) == 'S' ]; then echo "rce";fi

这段脚本的意思是,如果文件 /app/secret/mw/m5 的内容被 base64 编码后第一个字符是大写的 'S',那么就输出 "rce"。因此,只需要找查找包含 rce 的结果就能根据它对应的链接判断出这个文件经过 bese64 编码后某个位置所对应的字符,进而可以解出这个文件。

12

发现这样的记录都是 12 字节的

13

根据这点筛选一下 http.response && http.content_length == 12 就可以得到全部的结果了

14

全选保存到 txt 文档里

15

然后写个脚本把帧号提取出来

1
2
3
4
5
6
7
with open('log.txt', 'r') as file:
lines = file.readlines()

with open('frame_numbers.txt', 'w') as file:
for line in lines:
frame_number = line.split()[0]
file.write(frame_number + '\n')

这样就得到了

1
2
3
4
5
6
24311
24461
25890
26849
27760
...

丢给 ai 帮我写了一个 power shell 脚本,从特定帧号对应的 HTTP 响应中提取出准确的 GET 请求行,并且只输出完整的 GET 请求行而不包括其他头部信息

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
# FindHttpRequests.ps1

# 定义捕获文件路径(这里填附件的地址)
$CAPTURE_FILE = 'NetHttP.pcapng'

# 定义包含响应帧号的文件路径(这里填帧号的地址)
$FRAME_NUMBERS_FILE = 'frame_numbers.txt'

# 读取响应帧号列表
try {
$frameNumbers = Get-Content -Path $FRAME_NUMBERS_FILE
Write-Output "Read frame numbers: $($frameNumbers -join ', ')"
} catch {
Write-Error "Failed to read frame numbers from file: $_"
exit 1
}

# 检查是否读取到任何帧号
if ($frameNumbers.Count -eq 0) {
Write-Error "No frame numbers found in the file."
exit 1
}

# 定义tshark路径(这里填tshark的地址)
$TSHARK_PATH = 'tshark.exe'

# 确保tshark存在
if (-not (Test-Path $TSHARK_PATH)) {
Write-Error "tshark not found at path: $TSHARK_PATH"
exit 1
}

# 遍历每个帧号,查找对应的HTTP GET请求行
foreach ($frameNumber in $frameNumbers) {
Write-Output "Processing frame number: ${frameNumber}"

# 获取响应包的TCP流标识符
try {
$streamIndex = & $TSHARK_PATH -r $CAPTURE_FILE -Y "frame.number == ${frameNumber}" -T fields -e tcp.stream | Out-String
$streamIndex = $streamIndex.Trim()
if ([string]::IsNullOrEmpty($streamIndex)) {
Write-Warning "No stream index found for frame number ${frameNumber}"
continue
}
Write-Output "Stream index: ${streamIndex}"
} catch {
Write-Error "Failed to get stream index for frame number ${frameNumber}: $_"
continue
}

# 查找属于同一TCP流的GET请求行
try {
# 使用tcp.stream过滤条件查找GET请求行,并提取完整请求行
$getRequestLine = & $TSHARK_PATH -r $CAPTURE_FILE -Y "tcp.stream eq ${streamIndex} && http.request.method == ""GET""" -T fields -e http.request.method -e http.request.uri -e http.request.version | Out-String
if (-not [string]::IsNullOrEmpty($getRequestLine.Trim())) {
Write-Output "Found GET request line(s) for frame number ${frameNumber}:"
$getRequestLines = $getRequestLine -split "`n"
foreach ($line in $getRequestLines) {
if ($line -match "\S") { # 忽略空行
# 构建完整的GET请求行
$fields = $line.Split(' ')
$fullRequestLine = "$($fields[0]) $($fields[1]) $($fields[2])"
Write-Output $fullRequestLine
}
}
} else {
Write-Warning "No GET request line found in stream ${streamIndex}"
}
} catch {
Write-Error "Failed to find GET request line for frame number ${frameNumber}: $_"
}
}

在 power shell 运行.\FindHttpRequests.ps1 > output.txt 就可以得到所有符合条件的 GET 请求行的信息了,下面是输出结果的片段

1
2
3
4
Processing frame number: 239758
Stream index: 17673
Found GET request line(s) for frame number 239758:
GET /rce?name=%7B%7Blipsum.__globals__.__builtins__.eval(%22__import__('os').popen('echo%20aWYgWyAkKGNhdCAvZmxhZ3xiYXNlNjQgLXcgMHwgYXdrIE5SPT0xIHwgY3V0IC1jIDI0KSA9PSAnSycgXTsgdGhlbiBlY2hvICJyY2UiO2Zp%20%7C%20base64%20-d%7Cbash').read()%22)%7D%7D HTTP/1.1

写个脚本提取一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import re
import base64

filename = 'output.txt' # 文件路径

pattern = re.compile(r'echo%20([^%]+)%20')

with open(filename, 'r', encoding='utf-16-le') as file:
for line in file:
matches = pattern.findall(line)

for match in matches:
decoded_bytes = base64.b64decode(match)
decoded_string = decoded_bytes.decode('utf-8')
print(decoded_string)

这时能提取出来下面两种命令,经过尝试发现 /flag 里面的是假的,所以保留 /app/secret/mw/m5 里面的

1
2
if [ $(cat /app/secret/mw/m5|base64 -w 0| awk NR==1 | cut -c 1) == 'U' ]; then echo "rce";fi
if [ $(cat /flag|base64 -w 0| awk NR==1 | cut -c 1) == 'R' ]; then echo "rce";fi

根据这个再写一个脚本匹配并进行 base64 解码

1
2
3
4
5
6
7
8
9
10
11
logs = """
if [ $(cat /app/secret/mw/m5|base64 -w 0| awk NR==1 | cut -c 232) == '=' ]; then echo "rce";fi
if [ $(cat /app/secret/mw/m5|base64 -w 0| awk NR==1 | cut -c 231) == '=' ]; then echo "rce";fi
if [ $(cat /app/secret/mw/m5|base64 -w 0| awk NR==1 | cut -c 230) == 'Q' ]; then echo "rce";fi
...太长了下面省略
"""
import re
import base64
pattern = r"'(.*?)'"
matches = re.findall(pattern, logs)
print(base64.b64decode(''.join(matches[::-1])).decode('utf-8'))

运行得到 S0I3iWhvszKbOM/OalKTA0fpm5O5chVVnYGyKd5nV4erAzRbV6V6w8b/UiOfQEc3Ijh00hFjYFU1HaxNub9GnlPS/lcam5mATkf2sJS6JgpJo6AShVRxWDYKKrojeUeBZj5MEPI8/4DGGGuHFxmx2bxAahdDe1cGnjTZGWONpNI=

到这里没什么头绪了,回去翻一下流量发现攻击者在最开始的时候拿到了源码

16

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask,request,render_template_string,Response,session

app = Flask(__name__)
app.config['SECRET_KEY'] = 'gdkfksy05lx0nv8dl'
@app.route("/")
def index():
return open(__file__).read()

@app.route("/rce",methods=["GET"])
def rce():
data = request.args.get("name","Guest")
return render_template_string(f"Welcome {data}")

if __name__ == "__main__":
app.run(host="0.0.0.0",port=8989,debug=False)

接着又拿到了加密过后的私钥,把他保存到 encrypted_private.pem

17

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC1DBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIirzza4niI8QCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECEXSIcOIuwGaBIICgHLW3Qb39/+E
0uKiOi8yevcztF5toCOGsh6Fi23zSIwCjH8VPO1lbpFCkW9789ldbxBbSwtXwMmF
kTyFjOmymL/zktmt8PyExcWOGA481/IkpCPTmKAT8+67FJEdAf9BAZVPjqpu1Lla
Ohnp3JFZ8SStSUWwvjLZafi4Ucf7ajJexwCTkkvB7mF8kostYaBOsNJ1GORRdL3c
s73GxvX98MTLvF1DW5xujgdcl28msB3GHTxe7sSgScKfFUyfCViivW8FCqa6lfJo
Tj3JZtNlpPiOr1PXPfIWBt0wEQaF3+ovTEVu7x1r1Q3mq61GpO3s4n6kdeGg9Dkp
BYErmG76JdZtOWTZ88SrD7EDkh12EOdtM0ywR1DTYk4+fjKifkhPPrIGn8Nm07PE
yTAS7UG0Ut2Ut722rOBsgIZlnk2vF8qbIvKJj1JGzedMLabnafF5/L2N4wP8ZeL8
fO1Asxy0o/Hk89rl7ZI8Aocc1ZRMHKfxg/XV2bFHv2q1M1y3CI9wUrGnvk+8oX0H
T/5vFtfGb4QNiy+p6aTi+UEJOau5O0t4f2kAL6L/pgmLEMulKWVMK8u+p6os0cbt
KbVBmjNE/uA8SCv8E9XcL+/LWsSVInrYwJQzWbLIYx5FTRk4479taV3BGEN+hbmU
RqlIK8IwsVxWc4wC+oHoLMY4RllUZ9D2rBasMt6DOLA31Jjrabciv03zJPyqXcfi
DVTFu9JfT1fF7eOClQzTvIlTDVIDMPfAqR6B+/AbZDiQ2aK/54i10kohmXT2qWoT
pDYPWV2JGTXICaRyP8FYu26ZTdIKVB3PovfJEXR3yex14U5T8zFVpUQnoDJfNyPG
qUVmlGScmkU=
-----END ENCRYPTED PRIVATE KEY-----

查看源码拿到解密所需的短语 gdkfksy05lx0nv8dl

运行命令 openssl rsa -in encrypted_private.pem -out private.pem 得到解密后的私钥 private.pem

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGAaxYIU7D5lIndIBLu
bRRywJZiAQ90QiRjuHAIsyyka69Wl1n9K4W9/hjNDeI5BP14oADSmOqLKmj8nw2w
bk0mDZ0KbWfT3eCxttGoplMEoCqKizTMdHGe7MUaK9A2CKIHOsHQhkpAmwLcDzNr
bLg9nx0hjPUDefqwCn1q7B/IQPMCAwEAAQKBgEQaQ/ttrpwfvUhbodQvT/dY7ET+
XhJ+cAjo/y9r8bkmTmx853xZVwYVIbt1pouc46zmOQjVCOJU2GwS2aScXdkx8Fm1
YQJqzbxcZ4oEA/f66E99560um3RRsa7DWKwNdIcU4wukyfgx5fILoiuE8ThjG23V
b3oDOzaIhyCrcO65AkEApZJjxmMk0AB8ZUkhIqw+2gD4N5SPisae+aFfLgLt14H4
VwSZxl2kRs7yhZGl5spFlxdotym3YS/30aY3/+3GPQJBAKWSY8ZjJNAAfGVJISKs
PtoA+DeUj4rGnvmhXy4C7deB+FcEmcZdpEbO8oWRpebKRZcXaLcpt2Ev99GmN//t
xu8CQQCf2DInBvQ1MyLlDbLFrJCJGsKHtg7WJWa5DQe8fetsUPeV2sUycpj0Gzqb
pL8Ljl+cvGbF3apCU3LmnZgWplDpAkB+i1EYqmPTWdu5adgacP0kj4Mmr7O5xC5y
6kQdnX18rchJcam5843/1GGFdpkOuF/Rp8GP5CFU9V157Yl1YJ0fAkAvcGpACEWD
gZPSO8jGVr6XoVtA0tW2JMX/nPoxI1soLG38Kwaqc/+bepMmRQ50dlvZUA4uufmT
N3OWrL+BavU0
-----END PRIVATE KEY-----

写个脚本解密一下

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
from base64 import b64decode
from Crypto.PublicKey import RSA
from Crypto.Util.number import long_to_bytes, inverse

# 加载私钥
with open('private.pem', 'rb') as f:
private_key = RSA.import_key(f.read())

# 提取私钥参数
n = private_key.n
e = private_key.e
d = private_key.d
p = private_key.p
q = private_key.q

# 计算其他必要的参数
dp = d % (p - 1)
dq = d % (q - 1)
qinv = inverse(q, p)

# 解码Base64字符串
base64_encoded_ciphertext = "S0I3iWhvszKbOM/OalKTA0fpm5O5chVVnYGyKd5nV4erAzRbV6V6w8b/UiOfQEc3Ijh00hFjYFU1HaxNub9GnlPS/lcam5mATkf2sJS6JgpJo6AShVRxWDYKKrojeUeBZj5MEPI8/4DGGGuHFxmx2bxAahdDe1cGnjTZGWONpNI="
encrypted_data = b64decode(base64_encoded_ciphertext)

# 将加密数据转换为整数
ciphertext_int = int.from_bytes(encrypted_data, byteorder='big')

# 使用中国剩余定理(CRT)加速解密
mp = pow(ciphertext_int, dp, p)
mq = pow(ciphertext_int, dq, q)
h = (qinv * (mp - mq)) % p
plaintext_int = mq + h * q

# 将解密后的整数转换回字节串
plaintext = long_to_bytes(plaintext_int)

# 解码为UTF-8字符串
original_message = plaintext.decode('utf-8')
print(original_message)

运行得到 flag\{343907d2-35a3-4bfe-a5e1-5d6615157851\}

1
flag{343907d2-35a3-4bfe-a5e1-5d6615157851}

Infinity

题目内容:

Infinity comes from the Latin word infinitas, meaning “without boundaries”.

附件下载 提取码(GAME)备用下载

题目提示:BASE58-Ripple、SM4-ECB

附件是一张 png 图片 Infinity.png

Infinity

binwalk 提取出一个 tar 压缩包 zMjiQdMYLHK.tar,这是个套娃压缩包,我一开始无聊手动解开了(大概 130 层左右吧)😂解到最后是一个 txt 文档 SeCr3t.txt,内容是 Inf1nityIsS0CoOL,题目提示了有 SM4-ECB 那么这个大概就是解密时要用到的 Key 了

接着注意到这些文件的名字长度都差不多,又想到题目提示 BASE58-Ripple,就写个脚本把文件名给拼起来(这里的顺序要反转一下)

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
import os
import shutil
from pathlib import Path
import py7zr

def extract_zip(archive_path, output_dir):
shutil.unpack_archive(str(archive_path), output_dir)

def extract_tar(archive_path, output_dir):
shutil.unpack_archive(str(archive_path), output_dir)

def extract_7z(archive_path, output_dir):
with py7zr.SevenZipFile(str(archive_path), mode='r') as z:
z.extractall(path=output_dir)

def find_archives(directory):
return list(Path(directory).glob('*.zip')) + \
list(Path(directory).glob('*.7z')) + \
list(Path(directory).glob('*.tar'))

def recursive_extract(file_path, archive_list):
archive_name = file_path.stem
archive_list.append(archive_name)

extract_to = file_path.parent / ('extracted_' + archive_name)
os.makedirs(extract_to, exist_ok=True)

try:
if file_path.suffix == '.zip':
extract_zip(file_path, extract_to)
elif file_path.suffix == '.7z':
extract_7z(file_path, extract_to)
elif file_path.suffix == '.tar':
extract_tar(file_path, extract_to)
else:
raise ValueError(f"Unsupported file format: {file_path}")

archives = find_archives(extract_to)
if archives:
next_archive = archives[0]
recursive_extract(next_archive, archive_list)
except Exception as e:
print(f"{file_path}提取失败: {e}")
raise

if __name__ == '__main__':
start_file = Path('zMjiQdMYLHK.tar') # 换成实际的文件路径
archive_history = []
recursive_extract(start_file, archive_history)
print(''.join(archive_history)) # 反转合并输出

运行得到

1
zMjiQdMYLHK6c69b2nqwz2iYMtivbWMUHxi9d6pw4mLYYHtMsKZ9wuXKbwk4at4AHjdRPuEdHCG4d3gR1bg5U8YNJNoFPxJCSQVq5yn3gHbKg6Avpr7Kpj8sDam3p4WHR3fifpgyMHpeAgVacz8HEJyvgfdCVcJepagGRB7EsL11wcuvyXwZGh3ot37xqstfWn6LHtXEbjV2pKJeHtrjrMMjCFDZRPg6RNirwd11zXsXaQaq9euJcvgotRUjpYRYWbKB6A25qtQiwA4Gf8QjfHDyccqkRVjtoMp8SvPf9cR3iiRvwKyEypi7FVfi2FwxdUw3wh8torJsdXXeVLKMdzgiE7geiPvFtfA9qEAsqV7vqk7JncqDHoEDPrsUByKTpvypAjmuQxyaNF9GU22MxYLDZDrbqGyaLQrhaqP5Kn26CC44egaMVpYJSN4irp67f4KLv9LpD3WSHqKgJAtGV7KtUQ3MjJ8duxA8CwuadryMdkuasrZXj3c9YDQ1Nf7LbXKvzAXye64M1JNNUks3yrzaPJoUjXqkveanCgLHqaXutHiQMside1jYU2mNuBpjTuZb4mTzKcoamcw6qD7PeWL7qGBeBzzEKG4G51Q43EF4125LwYQeAS721ji7e9M8Lmf8CU315v3unatngTkGLFUGkFuhAmvnvaaCKPS7M3J8XSjdh9ofRFpX8xHdTUBoQutbHXE8uYLFaibrg5ohzAbrhPjhSdH2Aei1X3ztdQPxH1rm2PHhm2qQazwAuJDk9LLg3csdtRiTXb1qmeYoUmudRWkTTgxUf6gd53ckZZj9BigJrG5jQfNQ9RfRAhDFBreFU1nLXdyXLnmiVtD6s7Gtb4qXVaLtuRCtFyNW5yu2zyqaSdpXLjfPQxB3vzvqCzeDjFyaUMKQS4NsfBNmiYfRK95ErWieUL24eL28UzqnH65fhevYKzvN461Hq43Pw8vDDMcJwy4tjLhtFCkLvj8YUoZaWDDTKgRvTPzrK8bzxbsbUMg1mxYTZdt3KH2s5nzUHP1xBE9E8fknBQ5d5ezC5sRwk412eEEhKv2qohJZ3gW8JGczKZVqh6ks9aXhxDgNyJMANRqmLYZakCKVjv5fqdJRsy2NXJcXSmsbmaxoECWzoAaQrY8BAVZHSVbmFb5JACanfgDv3ExPNmMNCdoe4rN4Wg8hX2BxrNFhP27NxFSHUx57o7LHre1NDheyJvG8jiE7i5xmse7EY5oq1fyCcNkB9bFiXj8rb1CMtbBWUdTP3QifiuAtYdNH1bYWx4YRfH3JPFGHfGRaYX5jHQz5upm9f1qtnyM32bYLCXotjV6FSFajeaEG3RG6tsVNBZD8scRnEHTaP8qM67cH9SzHC6sNeuMqn1nAWUWY4X2BW7EUjDg1xPsJvpzLrucbCqdBnN9XKvC9oW3fdLYY96nFQwYN4vUki

接下来拿到 CyberChef 去解密。需要注意的是,Base58-Ripple 指的是 Ripple 网络中使用的 Base58 编码的一种特定实现或变种,Ripple 使用了一套稍微不同的字符集作为其 Base58 编码的字母表,具体如下:

1
rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz

按照提示的顺序,先 BASE58-Ripple,后 SM4-ECB

19

拿到了一个 Data Matrix 条码

Data Matrix

找个免费的在线扫码网站扫一下就拿到 flag 了

21

1
flag{a72dd260-f64d-4116-ab50-b26b40d69883}

Crypto

通往哈希的旅程

题目内容:

在数字城,大家都是通过是通过数字电话进行的通信,常见是以 188 开头的 11 位纯血号码组成,亚历山大抵在一个特殊的地方截获一串特殊的字符串 "ca12fd8250972ec363a16593356abb1f3cf3a16d",通过查阅发现这个跟以前散落的国度有点相似,可能是去往哈希国度的。年轻程序员亚力山大抵对这个国度充满好奇,决定破译这个哈希值。在经过一段时间的摸索后,亚力山大抵凭借强大的编程实力成功破解,在输入对应字符串后瞬间被传送到一个奇幻的数据世界,同时亚力山大抵也开始了他的进修之路。(提交格式:flag {11 位号码})

根据题意,开头 3 位数 188 已经知道了,那就只需要尝试后面的 8 位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import hashlib

# 给定的哈希值
target_hash = "ca12fd8250972ec363a16593356abb1f3cf3a16d"

# 尝试所有的11位数
for i in range(10**8): # 从0到99999999 (总共1亿个可能性)
phone_number = f"188{i:08d}" # 构建电话号码
hash_object = hashlib.sha1(phone_number.encode())
hex_dig = hash_object.hexdigest()

if hex_dig == target_hash:
print(f"Found match: {phone_number}") # Found match: 18876011645
break

1 分钟左右就试出来了

1
flag{18876011645}

你是小哈斯?

题目内容:

年轻黑客小符参加 CTF 大赛,他发现这个小哈斯文件的内容存在高度规律性,并且文件名中有隐藏信息,他成功找到了隐藏的信息,并破解了挑战。得意地说:“成功在于探索与质疑,碰撞是发现真相的关键!”

附件下载 提取码(GAME)备用下载

题目内容如下

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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
356a192b7913b04c54574d18c28d46e6395428ab
da4b9237bacccdf19c0760cab7aec4a8359010b0
77de68daecd823babbb58edb1c8e14d7106e83bb
1b6453892473a467d07372d45eb05abc2031647a
ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
c1dfd96eea8cc2b62785275bca38ac261256e278
902ba3cda1883801594b6e1b452790cc53948fda
fe5dbbcea5ce7e2988b8c69bcfdfde8904aabc1f
0ade7c2cf97f75d009975f4d720d1fa6c19f4897
b6589fc6ab0dc82cf12099d1c2d40ab994e8410c
3bc15c8aae3e4124dd409035f32ea2fd6835efc9
21606782c65e44cac7afbb90977d8b6f82140e76
22ea1c649c82946aa6e479e1ffd321e4a318b1b0
aff024fe4ab0fece4091de044c58c9ae4233383a
58e6b3a414a1e090dfc6029add0f3555ccba127f
4dc7c9ec434ed06502767136789763ec11d2c4b7
8efd86fb78a56a5145ed7739dcb00c78581c5375
95cb0bfd2977c761298d9624e4b4d4c72a39974a
51e69892ab49df85c6230ccc57f8e1d1606caccc
042dc4512fa3d391c5170cf3aa61e6a638f84342
7a81af3e591ac713f81ea1efe93dcf36157d8376
516b9783fca517eecbd1d064da2d165310b19759
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
60ba4b2daa4ed4d070fec06687e249e0e6f9ee45
d1854cae891ec7b29161ccaf79a24b00c274bdaa
7a81af3e591ac713f81ea1efe93dcf36157d8376
53a0acfad59379b3e050338bf9f23cfc172ee787
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
53a0acfad59379b3e050338bf9f23cfc172ee787
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
c2b7df6201fdd3362399091f0a29550df3505b6a
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
a0f1490a20d0211c997b44bc357e1972deab8ae3
3c363836cf4e16666669a25da280a1865c2d2874
4a0a19218e082a343a1b17e5333409af9d98f0f5
54fd1711209fb1c0781092374132c66e79e2241b
27d5482eebd075de44389774fce28c69f45c8a75
5c2dd944dde9e08881bef0894fe7b22a5c9c4b06
13fbd79c3d390e5d6585a21e11ff5ec1970cff0c
07c342be6e560e7f43842e2e21b774e61d85f047
395df8f7c51f007019cb30201c49e884b46b92fa
11f6ad8ec52a2984abaafd7c3b516503785c2072
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
7a38d8cbd20d9932ba948efaa364bb62651d5ad4
e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
d1854cae891ec7b29161ccaf79a24b00c274bdaa
6b0d31c0d563223024da45691584643ac78c96e8
5c10b5b2cd673a0616d529aa5234b12ee7153808
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
60ba4b2daa4ed4d070fec06687e249e0e6f9ee45
54fd1711209fb1c0781092374132c66e79e2241b
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
6b0d31c0d563223024da45691584643ac78c96e8
58e6b3a414a1e090dfc6029add0f3555ccba127f
53a0acfad59379b3e050338bf9f23cfc172ee787
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
22ea1c649c82946aa6e479e1ffd321e4a318b1b0
e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
53a0acfad59379b3e050338bf9f23cfc172ee787
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
53a0acfad59379b3e050338bf9f23cfc172ee787
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
11f6ad8ec52a2984abaafd7c3b516503785c2072
95cb0bfd2977c761298d9624e4b4d4c72a39974a
395df8f7c51f007019cb30201c49e884b46b92fa
c2b7df6201fdd3362399091f0a29550df3505b6a
3a52ce780950d4d969792a2559cd519d7ee8c727
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
a0f1490a20d0211c997b44bc357e1972deab8ae3
3c363836cf4e16666669a25da280a1865c2d2874
4a0a19218e082a343a1b17e5333409af9d98f0f5
54fd1711209fb1c0781092374132c66e79e2241b
27d5482eebd075de44389774fce28c69f45c8a75
5c2dd944dde9e08881bef0894fe7b22a5c9c4b06
13fbd79c3d390e5d6585a21e11ff5ec1970cff0c
07c342be6e560e7f43842e2e21b774e61d85f047
395df8f7c51f007019cb30201c49e884b46b92fa
11f6ad8ec52a2984abaafd7c3b516503785c2072
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
7a38d8cbd20d9932ba948efaa364bb62651d5ad4
e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
d1854cae891ec7b29161ccaf79a24b00c274bdaa
6b0d31c0d563223024da45691584643ac78c96e8
5c10b5b2cd673a0616d529aa5234b12ee7153808
3a52ce780950d4d969792a2559cd519d7ee8c727
22ea1c649c82946aa6e479e1ffd321e4a318b1b0
aff024fe4ab0fece4091de044c58c9ae4233383a
58e6b3a414a1e090dfc6029add0f3555ccba127f
4dc7c9ec434ed06502767136789763ec11d2c4b7
8efd86fb78a56a5145ed7739dcb00c78581c5375
95cb0bfd2977c761298d9624e4b4d4c72a39974a
51e69892ab49df85c6230ccc57f8e1d1606caccc
042dc4512fa3d391c5170cf3aa61e6a638f84342
7a81af3e591ac713f81ea1efe93dcf36157d8376
516b9783fca517eecbd1d064da2d165310b19759
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
60ba4b2daa4ed4d070fec06687e249e0e6f9ee45
d1854cae891ec7b29161ccaf79a24b00c274bdaa
7a81af3e591ac713f81ea1efe93dcf36157d8376
53a0acfad59379b3e050338bf9f23cfc172ee787
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
53a0acfad59379b3e050338bf9f23cfc172ee787
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
c2b7df6201fdd3362399091f0a29550df3505b6a
356a192b7913b04c54574d18c28d46e6395428ab
da4b9237bacccdf19c0760cab7aec4a8359010b0
77de68daecd823babbb58edb1c8e14d7106e83bb
1b6453892473a467d07372d45eb05abc2031647a
ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
c1dfd96eea8cc2b62785275bca38ac261256e278
902ba3cda1883801594b6e1b452790cc53948fda
fe5dbbcea5ce7e2988b8c69bcfdfde8904aabc1f
0ade7c2cf97f75d009975f4d720d1fa6c19f4897
b6589fc6ab0dc82cf12099d1c2d40ab994e8410c
3bc15c8aae3e4124dd409035f32ea2fd6835efc9
21606782c65e44cac7afbb90977d8b6f82140e76

看了下题目描述猜测这些是哈希值,搜了下 356a192b7913b04c54574d18c28d46e6395428ab 发现是 SHA-1,附上免费的转换网站

9

大胆猜测每一行就代表一个字符,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
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# 哈希值列表
hashes_string = """356a192b7913b04c54574d18c28d46e6395428ab
da4b9237bacccdf19c0760cab7aec4a8359010b0
77de68daecd823babbb58edb1c8e14d7106e83bb
1b6453892473a467d07372d45eb05abc2031647a
ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
c1dfd96eea8cc2b62785275bca38ac261256e278
902ba3cda1883801594b6e1b452790cc53948fda
fe5dbbcea5ce7e2988b8c69bcfdfde8904aabc1f
0ade7c2cf97f75d009975f4d720d1fa6c19f4897
b6589fc6ab0dc82cf12099d1c2d40ab994e8410c
3bc15c8aae3e4124dd409035f32ea2fd6835efc9
21606782c65e44cac7afbb90977d8b6f82140e76
22ea1c649c82946aa6e479e1ffd321e4a318b1b0
aff024fe4ab0fece4091de044c58c9ae4233383a
58e6b3a414a1e090dfc6029add0f3555ccba127f
4dc7c9ec434ed06502767136789763ec11d2c4b7
8efd86fb78a56a5145ed7739dcb00c78581c5375
95cb0bfd2977c761298d9624e4b4d4c72a39974a
51e69892ab49df85c6230ccc57f8e1d1606caccc
042dc4512fa3d391c5170cf3aa61e6a638f84342
7a81af3e591ac713f81ea1efe93dcf36157d8376
516b9783fca517eecbd1d064da2d165310b19759
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
60ba4b2daa4ed4d070fec06687e249e0e6f9ee45
d1854cae891ec7b29161ccaf79a24b00c274bdaa
7a81af3e591ac713f81ea1efe93dcf36157d8376
53a0acfad59379b3e050338bf9f23cfc172ee787
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
53a0acfad59379b3e050338bf9f23cfc172ee787
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
c2b7df6201fdd3362399091f0a29550df3505b6a
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
a0f1490a20d0211c997b44bc357e1972deab8ae3
3c363836cf4e16666669a25da280a1865c2d2874
4a0a19218e082a343a1b17e5333409af9d98f0f5
54fd1711209fb1c0781092374132c66e79e2241b
27d5482eebd075de44389774fce28c69f45c8a75
5c2dd944dde9e08881bef0894fe7b22a5c9c4b06
13fbd79c3d390e5d6585a21e11ff5ec1970cff0c
07c342be6e560e7f43842e2e21b774e61d85f047
395df8f7c51f007019cb30201c49e884b46b92fa
11f6ad8ec52a2984abaafd7c3b516503785c2072
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
7a38d8cbd20d9932ba948efaa364bb62651d5ad4
e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
d1854cae891ec7b29161ccaf79a24b00c274bdaa
6b0d31c0d563223024da45691584643ac78c96e8
5c10b5b2cd673a0616d529aa5234b12ee7153808
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
60ba4b2daa4ed4d070fec06687e249e0e6f9ee45
54fd1711209fb1c0781092374132c66e79e2241b
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
6b0d31c0d563223024da45691584643ac78c96e8
58e6b3a414a1e090dfc6029add0f3555ccba127f
53a0acfad59379b3e050338bf9f23cfc172ee787
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
22ea1c649c82946aa6e479e1ffd321e4a318b1b0
e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
53a0acfad59379b3e050338bf9f23cfc172ee787
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
53a0acfad59379b3e050338bf9f23cfc172ee787
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
11f6ad8ec52a2984abaafd7c3b516503785c2072
95cb0bfd2977c761298d9624e4b4d4c72a39974a
395df8f7c51f007019cb30201c49e884b46b92fa
c2b7df6201fdd3362399091f0a29550df3505b6a
3a52ce780950d4d969792a2559cd519d7ee8c727
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
a0f1490a20d0211c997b44bc357e1972deab8ae3
3c363836cf4e16666669a25da280a1865c2d2874
4a0a19218e082a343a1b17e5333409af9d98f0f5
54fd1711209fb1c0781092374132c66e79e2241b
27d5482eebd075de44389774fce28c69f45c8a75
5c2dd944dde9e08881bef0894fe7b22a5c9c4b06
13fbd79c3d390e5d6585a21e11ff5ec1970cff0c
07c342be6e560e7f43842e2e21b774e61d85f047
395df8f7c51f007019cb30201c49e884b46b92fa
11f6ad8ec52a2984abaafd7c3b516503785c2072
84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
7a38d8cbd20d9932ba948efaa364bb62651d5ad4
e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98
d1854cae891ec7b29161ccaf79a24b00c274bdaa
6b0d31c0d563223024da45691584643ac78c96e8
5c10b5b2cd673a0616d529aa5234b12ee7153808
3a52ce780950d4d969792a2559cd519d7ee8c727
22ea1c649c82946aa6e479e1ffd321e4a318b1b0
aff024fe4ab0fece4091de044c58c9ae4233383a
58e6b3a414a1e090dfc6029add0f3555ccba127f
4dc7c9ec434ed06502767136789763ec11d2c4b7
8efd86fb78a56a5145ed7739dcb00c78581c5375
95cb0bfd2977c761298d9624e4b4d4c72a39974a
51e69892ab49df85c6230ccc57f8e1d1606caccc
042dc4512fa3d391c5170cf3aa61e6a638f84342
7a81af3e591ac713f81ea1efe93dcf36157d8376
516b9783fca517eecbd1d064da2d165310b19759
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
60ba4b2daa4ed4d070fec06687e249e0e6f9ee45
d1854cae891ec7b29161ccaf79a24b00c274bdaa
7a81af3e591ac713f81ea1efe93dcf36157d8376
53a0acfad59379b3e050338bf9f23cfc172ee787
042dc4512fa3d391c5170cf3aa61e6a638f84342
a0f1490a20d0211c997b44bc357e1972deab8ae3
53a0acfad59379b3e050338bf9f23cfc172ee787
4a0a19218e082a343a1b17e5333409af9d98f0f5
07c342be6e560e7f43842e2e21b774e61d85f047
86f7e437faa5a7fce15d1ddcb9eaeaea377667b8
54fd1711209fb1c0781092374132c66e79e2241b
c2b7df6201fdd3362399091f0a29550df3505b6a
356a192b7913b04c54574d18c28d46e6395428ab
da4b9237bacccdf19c0760cab7aec4a8359010b0
77de68daecd823babbb58edb1c8e14d7106e83bb
1b6453892473a467d07372d45eb05abc2031647a
ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4
c1dfd96eea8cc2b62785275bca38ac261256e278
902ba3cda1883801594b6e1b452790cc53948fda
fe5dbbcea5ce7e2988b8c69bcfdfde8904aabc1f
0ade7c2cf97f75d009975f4d720d1fa6c19f4897
b6589fc6ab0dc82cf12099d1c2d40ab994e8410c
3bc15c8aae3e4124dd409035f32ea2fd6835efc9
21606782c65e44cac7afbb90977d8b6f82140e76"""

import hashlib

# 定义字符集
charset = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()-_=+[]{}\\|;:\'\",.<>/?'

# 创建字符到哈希值的映射
hash_to_char = {}
for char in charset:
# 计算每个字符的SHA-1哈希值
hash_object = hashlib.sha1(char.encode())
hex_dig = hash_object.hexdigest()
hash_to_char[hex_dig] = char

# 使用splitlines()方法将字符串按行分割成列表
provided_hashes = hashes_string.splitlines()

result = []
for hash_value in provided_hashes:
# 检查哈希值是否存在于映射中
if hash_value in hash_to_char:
result.append(hash_to_char[hash_value])
else:
result.append('⬛') # 不在映射中的哈希值用黑色方块占位

print("".join(result))

运行得到结果 1234567890-=qwertyuiopflag{no_is_flag}asdfghjklzxcvbnm,flag{game_cqb_isis_cxyz}.asdfghjklzxcvbnm,.qwertyuiopflag{no_is_flag}1234567890-=

1
flag{game_cqb_isis_cxyz}

Web

easy_flask

焚靖秒了

10

1
flag{48ad0cde8345c8b2608933ac4e85147e}