Bugku Misc Write Up (31-60)

31. 听首音乐

用 Audacity 看频谱图得到摩斯密码..... -... -.-. ----. ..--- ..... -.... ....- ----. -.-. -... ----- .---- ---.. ---.. ..-. ..... ..--- . -.... .---- --... -.. --... ----- ----. ..--- ----. .---- ----. .---- -.-.

解码得到 5BC925649CB0188F52E617D70929191C

flag
1
flag{5BC925649CB0188F52E617D70929191C}

32. 好多数值

打开 1.txt 后发现全部都是 *,*,* 这种形式的数据,且每个数最大为 255,很明显是 RGB 值,那么这题就很明显是需要将这些 RGB 像素点还原成一张图片了

再看文件的行数,除去空行以外共有 61366 行,分解质因数得到 2*61*503

排列组合一下得到下面几种组合方式:122*50361*10062*30683

最后两种显然不合适,那么就先尝试 122*503 这个长宽组合

附上源码

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
from PIL import Image

def create_image_from_rgb_file(file_path, width, height):
with open(file_path, 'r') as file:
lines = file.readlines()

rgb_values = [tuple(map(int, line.strip().split(','))) for line in lines]

image = Image.new('RGB', (width, height))

pixels = []
for i in range(height):
for j in range(width):
index = i * width + j
if index < len(rgb_values):
pixels.append(rgb_values[index])
else:
pixels.append((0, 0, 0))

image.putdata(pixels)

image.save('output.png')
print("图像已保存为 output.png")

if __name__ == "__main__":
width = int(input("请输入图像边长1: "))
height = int(input("请输入图像边长2: "))

with open('1.txt', 'r') as file:
line_count = sum(1 for _ in file)

create_image_from_rgb_file('1.txt', width, height)

解出来的图片向右旋转 90 度,水平翻转就得到 flag 了

flag
1
flag{youc@n'tseeme}

33. 很普通的数独 (ISCCCTF)

刚解压看缩略图能隐约看出 1.png 有点像二维码定位角,再看图片数量不但猜到这是一张二维码,有数字的地方涂黑就好了

先按 5×5 把图片拼起来,发现 1.png5.png21.png 都很像二维码定位角,更加确定这就是一张二维码了,然而它们的位置明显不对

21.png 放在原来 1.png 的位置,5.png 放在原来 21.png 的位置,21.png 放在原来 5.png 的位置就搞定了

总之前面大致的思路就是这样,但是由于这个工作量太大了我懒得画,于是我就用了网友们的数据 (心虚),附上还原出来的二维码

33.png

扫码得到 Vm0xd1NtUXlWa1pPVldoVFlUSlNjRlJVVGtOamJGWnlWMjFHVlUxV1ZqTldNakZIWVcxS1IxTnNhRmhoTVZweVdWUkdXbVZHWkhOWGJGcHBWa1paZWxaclpEUmhNVXBYVW14V2FHVnFRVGs9,base64 解码 8 次就能得到 flag 了

flag
1
flag{y0ud1any1s1}

34. PEN_AND_APPLE

没做出来

网上搜了下原题,是 NTFS 数据流隐藏,按理来说用 NtfsStreamsEditor 就可以解出来了

然而似乎是题目出问题了,无法复现

下面贴上网上找到的 flag

flag
1
SYC{Hei_hei_hei}

35. color

每张图片都拖进随波逐流修复高宽,发现下面多了一行黑白块

按照黑白分别对应 1 和 0 的逻辑并按图片名字顺序排列可以得到这些数:11111111010111101111111110111111101111110000110010101011000101001010010000001101110100110111010101111001101101101011011000111001101101111101

直接二进制转 ascii 发现是乱码的,于是转换思路看是不是要竖着看,写个 python 脚本看看

1
2
3
4
5
6
7
flags = ['11111111010111101111', '11111011111110111111', '00001100101010110001', '01001010010000001101', '11010011011101010111', '10011011011010110110', '00111001101101111101'] # 按顺序存到列表里

for i in range(0, 20):
flag = ''
for j in range(0, 7):
flag += flags[j][i] # 提取每一列的二进制位,将其组合成一个新的二进制字符串
print(chr(int(flag, 2)), end='') # 将该二进制字符串转换为对应的 ASCII 字符并输出
flag
1
flag{Png1n7erEs7iof}

36. 怀疑人生

ctf3.jpg 扫码的结果是 12580}

ctf2.jpgbinwalk 一下发现一个压缩包,解压得到一个文本,内容是

1
2
3
4
5
..... ..... ....! ?!!.? ..... ..... ....? .?!.? ....! .?... ..... .....
..!?! !.?.. ..... ..... ..?.? !.?.. ..... ..... ..... ..... !.?.. .....
..... .!?!! .?!!! !!!!! !!!!? .?!.? !!!!! !!!!! !!!!! .?... ....! ?!!.?
!!!!! !?.?! .?!!! !!!!! !!!!! .!!!. ?.... ..... ..... .!?!! .?... .....
..... .?.?! .?!.? .

丢到随波逐流 BrainFuck 解码得到 3oD54e,再用 base58 解码得到 misc

ctf1.jpg 试了发现不是伪加密,题目也没别的线索,那就爆破,解除密码是 password,文本内容是 XHU2Nlx1NmNcdTYxXHU2N1x1N2JcdTY4XHU2MVx1NjNcdTZiXHU2NVx1NzI=,经过两次 base64 解码和一次十六进制转字符得到 flag{hacker

连起来就是 flag 了

flag
1
flag{hackermisc12580}

37. 红绿灯

丢到随波逐流用 gif 动图分解帧给拆出来 (怎么会有 1168 张那么多🫠)

扫了一眼发现除了红绿黄灯以外还有很多不知道是什么玩意儿的灰灯

写个脚本把这些灰灯删一下看看先

删了灰灯在文件夹看到每隔 8 盏灯就有 1 盏黄灯,猜测是二进制转 ascii,每个数长度为 8 且每个数之间隔 1 盏黄灯,于是把脚本改改标注出这三盏灯,红绿黄分别对应 10/

在脚本输出的结果前面补上漏掉的 1(因为第一张图片和其他的格格不入)得到了 01100110/01101100/01100001/01100111/01111011/01010000/01101100/00110011/00110100/01110011/00110011/01011111/01110000/00110100/01111001/01011111/00110100/01110100/01110100/00110011/01101110/01110100/00110001/00110000/01101110/01011111/01110100/00110000/01011111/01110100/01110010/00110100/01100110/01100110/00110001/01100011/01011111/01110011/00110100/01100110/00110011/01110100/01111001/01011111/01110111/01101000/00110011/01101110/01011111/01111001/00110000/01110101/01011111/00110100/01110010/00110011/01011111/00110000/01110101/01110100/01110011/00110001/01100100/00110011/01111101

下面附上检测、删除和分类的代码:

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
import os
from PIL import Image

def find_duplicate_images(template_image_name, directory):
duplicates = []
template_path = os.path.join(directory, f'{template_image_name}.png')
with Image.open(template_path) as template_img:
template_hash = hash(template_img.tobytes())

for filename in sorted(os.listdir(directory)):
if filename.endswith('.png'):
try:
file_num = int(filename.rsplit('.', 1)[0])
if 0 <= file_num < 1168: # 有1168张图片
file_path = os.path.join(directory, filename)
with Image.open(file_path) as img:
if hash(img.tobytes()) == template_hash:
duplicates.append(file_num)
except ValueError:
continue
return duplicates

def delete_images(image_numbers, directory):
for image_num in image_numbers:
image_name = f"{image_num}.png"
image_path = os.path.join(directory, image_name)
try:
os.remove(image_path)
except Exception as e:
print(f"Failed to delete {image_name}: {e}")

def create_traffic_light_string(red_lights, yellow_lights, green_lights):
traffic_dict = {}
for num in red_lights:
traffic_dict[num] = '1'
for num in yellow_lights:
traffic_dict[num] = '/'
for num in green_lights:
traffic_dict[num] = '0'

result = ''.join([traffic_dict.get(num, '') for num in sorted(traffic_dict)])
return result

if __name__ == "__main__":
gif_frame_directory = '/path/gifframe' # 图片所在文件夹路径

# # 删除灰色的图片
# delete_images(find_duplicate_images('1', gif_frame_directory), gif_frame_directory)
# delete_images(find_duplicate_images('3', gif_frame_directory), gif_frame_directory)
# delete_images(find_duplicate_images('17', gif_frame_directory), gif_frame_directory)

# 检测红灯
red_lights = find_duplicate_images('2', gif_frame_directory)
red_lights.sort()

# 检测黄灯
yellow_lights = find_duplicate_images('16', gif_frame_directory)
yellow_lights.sort()

# 检测绿灯
green_lights = find_duplicate_images('18', gif_frame_directory)
green_lights.sort()

# 创建并打印交通灯字符串
traffic_light_str = create_traffic_light_string(red_lights, yellow_lights, green_lights)
print(traffic_light_str)

接下来只需要把那一堆二进制的数转成 ascii 就搞定了,附上源码:

1
2
flag = '01100110/01101100/01100001/01100111/01111011/01010000/01101100/00110011/00110100/01110011/00110011/01011111/01110000/00110100/01111001/01011111/00110100/01110100/01110100/00110011/01101110/01110100/00110001/00110000/01101110/01011111/01110100/00110000/01011111/01110100/01110010/00110100/01100110/01100110/00110001/01100011/01011111/01110011/00110100/01100110/00110011/01110100/01111001/01011111/01110111/01101000/00110011/01101110/01011111/01111001/00110000/01110101/01011111/00110100/01110010/00110011/01011111/00110000/01110101/01110100/01110011/00110001/01100100/00110011/01111101'
print(''.join([chr(int(i, 2)) for i in flag.split('/')]))

输出的结果就是 flag

flag
1
flag{Pl34s3_p4y_4tt3nt10n_t0_tr4ff1c_s4f3ty_wh3n_y0u_4r3_0uts1d3}

38. 不简单的压缩包

binwalk 发现隐藏了两个压缩包,先提取出来

尝试伪加密修复失败,于是开始字典爆破

爆破得到 tingshuo.txt 的密码是 0,文本内容是パスワードは50桁だそうです,翻译一下得到听说密码是 50 位数

穷举所有可能不切实际,于是猜测它们是重复的,先写个字典先

1
2
3
4
5
passwords = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

with open('dict.txt', 'w') as dictionary:
for i in range(len(passwords)):
dictionary.write(f"{passwords[i]*50}\n")

然后把压缩文件和字典一起丢到 ARCHPR 跑一下,结果发现密码就是 50 个小写 a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,解压拿到 flag.swf

这个后缀好眼熟但是又不知道是什么,一查才知道是 flash 动画(😂好老的东西)

整了个浏览器插件 Ruffle 来打开,发现是按键盘左右键控制人物赛跑的游戏,而且必须交替按,否则人物还会摔跤

实在跑不过那群人机,于是去下了个 swf 的逆向工具 JPEXS Decompiler

上面都是一些游戏资源,直接略过来看 scripts 目录,一个个翻

功夫不负有心人🥲我在 scripts-Define Sprite(142)-frame 1-DoAction 下面发现了这样的代码:

1
2
user_spd_min = 7;
user_spd_max = 17;

凭借我敏锐的直觉,我判断出这俩大概率是玩家的跑步速度😎把这两个数值都改成了 9999 回去再跑一遍,随便敲几下就轻松通关了

然而,通关了之后 flag 没出来🙃这是一道逆向题实锤了

我又回去吭哧吭哧地找,终于在 scripts-Define Sprite(171)-frame 1-DoAction 下面发现了这串神秘代码 3F3F666C61677B6A7065787337726565666C6173687D2121

丢到随波逐流看看是怎么个事儿,然后发现 flag 就这么出来了,base16 解码就好了

flag
1
flag{jpexs7reeflash}

39. 一枝独秀

binwalk 发现藏了压缩包,用 foremost 解出来发现有密码

尝试伪加密修复,失败了

丢到 ARCHPR 暴力破解,找到密码 12345678

解压出来 123 张图片,发现 flower (81).jpg 大小和修改时间和其他的图片都不一样(还真是一枝独秀呢)

考虑到是 jpg 文件,于是又用 stegdetect 扫了一下,结果却输出了 error: Quantization table 0x00 was not defined

实在没想法了,于是去看了网上大佬的 wp,才知道要用到一个 jpg 的隐写工具 JPHS(真是奇怪,按理来说 jphide 用 stegdetect 应该是能扫出来的才对啊,想不明白为什么扫不出来)

这个工具我找了半天,最后在德国数据处理服务中心 GWDG 给我找到了, 下载链接

用工具 seek 需要密码,经过多次尝试发现密码就是图片属性里的主题 flowers

提出来的文件丢到随波逐流发现是 zip 文件,改一下后缀然后解压,发现了一个名为参悟佛法.txt 的文本

1
2
3
4
从一朵花中看到一个世界,那从一段佛文中能看到什么呢?
你站在4楼的栏杆上眺望远方,如果参悟不出来就打算跳下去

佛曰:阿罰豆缽娑提諳竟諳迦亦侄栗侄大梵尼朋梵彌哆耨除怛奢般是諳爍悉哆爍冥參特參怯涅皤吉缽阿藝耶諳勝侄竟離諳諸尼缽曰。梵究呐耨盧他姪明漫究呐得哆藐集能冥盡滅知俱朋怯室神奢羅姪豆罰帝遠蘇明梵苦奢密侄曰缽者特哆呼勝蘇不冥死等那阿冥悉奢薩豆涅缽波罰。罰摩侄故罰夢缽恐皤寫諳闍舍哆得波苦奢即罰恐冥道一哆究梵呼冥闍哆上罰南訶諳寫冥依皤者哆諦故死哆夷菩侄曰呐逝至皤佛諳耶

一眼看出是佛曰加密,再丢到随波逐流,解开得到 H-hDs10OZL3lhIZZbeRSbbbVRZNm32W2X33mGm3Txt999RdV9hx0

做到这里好像又没什么思路了,突然想起题目的提示翻过四个栅栏即可得到flag,不就是栅栏加密吗,于是又丢回随波逐流,分为 4 栏时,解密结果为:HINT-ZmxhZ3tDb29seW91R290SXROb3dZb3VLbm93VGhlRmxhZ30,把 ZmxhZ3tDb29seW91R290SXROb3dZb3VLbm93VGhlRmxhZ30 用 base64 解码就能拿到 flag 了

flag
1
flag{CoolyouGotItNowYouKnowTheFlag}

40. 小猪佩奇

一点思路都没,看了评论区才知道出题人是给了提示的😭(可恶啊怎么不把链接在提示那栏贴出来呢👿出题人的提示

好吧,单看提示只知道和加密有关但还是没什么思路,看了评论区才知道是 LSB 隐写加密

问了下 AI,下面是简易的判断方式:

正常的图像中,像素值的分布应该是比较随机的。如果图像被用来做 LSB 隐写,那么在最低几位上可能会显示出非随机性,因为这些位被用来存储隐藏信息。你可以使用专门的工具来分析图像中每个颜色通道的最低有效位的频率分布。如果分布异常(例如某些值过于频繁或不频繁),这可能意味着图像中存在隐写信息。

既然如此,就先把出题人提到的字典先下载下来。

这里使用 github 上的一个工具处理 livz/cloacked-pixel: LSB steganography and detection(注意:该项目不支持 Python3,作者疑似放弃了该项目)

上面这个仓库的代码不支持 Python3,于是我就把这个问题修复了,顺手还把 README 加上了中文译文。要是对你有帮助的话欢迎给个 star⭐🥰aristorechina/cloacked-pixel: LSB steganography and detection

下载下来之后写个脚本进行爆破,代码参考了大佬的 wp

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
import re
import os
import filetype # pip install filetype
from lsb import extract # 把github上下载的lsb.py放到当前目录下

path = '/path' # 文件存放目录
dict_path = f'{path}/darkweb2017-top1000.txt'
filename=f'{path}/小猪佩奇.png'

# 反转字典
with open(dict_path, "r", encoding='utf-8') as file:
lines = file.readlines()
lines.reverse()
with open(dict_path, "w", encoding='utf-8') as file:
file.writelines(lines)

# 读取字典
with open(f'{path}/darkweb2017-top1000.txt','r+') as file:
file = file.readlines()

# 提取
for i in file:
i=i.replace('\n','')
if len(i)==7 and re.search('[0-9!?]',i)==None:
out_file= f'{path}/output/{i}'
extract(filename,out_file,i)

# 当前路径下所有子文件
def get_files_names(file_dir):
for _, _, files in os.walk(file_dir):
return files

files_names = get_files_names(f'{path}/output/')

# 判断文件类型并输出
for i in range(0, len(files_names)):
try:
kind = filetype.guess(f'{path}/output/{files_names[i]}')
if kind is None:
pass
else:
print(f"File: {files_names[i]}, MIME Type: {kind.mime}")
except Exception as e:
print(f"Error processing file {files_names[i]}: {e}")

输出了 File: raiders, MIME Type: image/png

给文件 output 目录下生成的 raiders 文件加上.png 后缀,打开扫码就得到 flag 了

flag
1
flag{37d9704c-9752-434c-8891-ee15e1800490}

好多压缩包

买了才发现这题要 8 个币(好贵

解压了发现有好多压缩包(还真是

打开几个发现都是 4 字节的 data.txt,遂打开随波逐流,用多 zip 文件 CRC32 爆破

因为是 4 字节所以位数就是 4,开始爆破

等了好久好久,多 zip 文件 crc 碰撞结束,结果为:z5BzAAANAAAAAAAAAKo+egCAIwBJAAAAVAAAAAKGNKv+a2MdSR0zAwABAAAAQ01UCRUUy91BT5UkSNPoj5hFEVFBRvefHSBCfG0ruGnKnygsMyj8SBaZHxsYHY84LEZ24cXtZ01y3k1K1YJ0vpK9HwqUzb6u9z8igEr3dCCQLQAdAAAAHQAAAAJi0efVT2MdSR0wCAAgAAAAZmxhZy50eHQAsDRpZmZpeCB0aGUgZmlsZSBhbmQgZ2V0IHRoZSBmbGFnxD17AEAHAA==

CyberChef 用 base64 解码,发现里面夹杂着提示 fix the file and get the flag

把它保存之后丢到随波逐流,文件头信息:cf907300,未知文件类型,结合上面的信息大概就知道是要修复文件头了

010Editor 打开,看到了 RAR 的文件尾 C43D7B00400700,补上 RAR 的文件头 526172211A0700,改文件后缀为.rar,然后找个解压工具打开,在注释那里就看到了

flag
1
flag{nev3r_enc0de_t00_sm4ll_fil3_w1th_zip}

一个普通的压缩包 (xp0intCTF)

解压下来发现是 rar 文件,,用 WinRAR 再解压发现诊断信息文件头已损坏:secret.png

然而我不知道怎么修复,看了大佬的 wp 才知道

修复好之后把 secret.png 丢到随波逐流发现文件类型是 gif,分解后发现只有两帧

分别用 StegSolve 打开,在 Red plane 0 发现二维码的下半部分和残缺的上半部分

补全上半部分,把它和下半部分拼起来就搞定了

42.jpg

flag
1
flag{yanji4n_bu_we1shi}

43. QAQ

解压后丢到随波逐流,文件头是 330d0d0a,没看出来是什么

又用记事本打开,看到了一些貌似是代码的东西,还在里面发现了 QAQ.py

再在搜索引擎找了下 330d0d0a,虽然没找到这个文件头,但是搜出来的清一色都是 pyc,那就当作是 pyc 逆向一下看看吧 uncompyle6.exe QAQ.pyc >QAQ.py

得到以下代码:

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
# uncompyle6 version 3.9.2
# Python bytecode version base 3.6 (3379)
# Decompiled from: Python 3.12.4 (tags/v3.12.4:8e8a4ba, Jun 6 2024, 19:30:16) [MSC v.1940 64 bit (AMD64)]
# Embedded file name: QAQ.py
# Compiled at: 2018-08-15 14:38:31
# Size of source mod 2**32: 620 bytes


def encryt(key, plain):
cipher = ""
for i in range(len(plain)):
cipher += chr(ord(key[i % len(key)]) ^ ord(plain[i]))

return cipher


def getPlainText():
plain = ""
with open("plain.txt") as f:
while True:
line = f.readline()
if line:
plain += line
else:
break

return plain


def main():
key = "LordCasser"
plain = getPlainText()
cipher = encryt(key, plain)
with open("cipher.txt", "w") as f:
f.write(cipher.encode("base_64"))


if __name__ == "__main__":
main()

# okay decompiling QAQ.pyc

根据这个写了个解密的代码,cipher.txt 应该就是描述里给的内容

1
2
3
4
5
6
7
8
9
10
11
12
import base64
def decrypt(key, cipher):
plain = ""
for i in range(len(cipher)):
plain += chr(ord(key[i % len(key)]) ^ ord(cipher[i]))
return plain

key = "LordCasser"
cipher = base64.b64decode("FSAnRAIzNlMjPQMjNyBJNTs6NlIFPFIqDDVTJy0zGE8rKxZBJDIrJkYoPUQML1M3MDYJZTElFyI7 UzE6DTtSNxckNDw2Mxk9Jzc=").decode()
plain = decrypt(key, cipher)
with open("plain.txt", "w") as f:
f.write(plain)

结果解出了下面这个(被耍了)

1
2
3
4
YOU ARE FOOLED
THIS IS NOT THAT YOU WANT
GO ON DUDE
CATCH THAT STEGOSAURUS

根据这个提示用 stegosaurus 解就行了 python stegosaurus.py -x QAQ.pyc(注意这里要用 Python 3.6,版本太高会报错)

flag
1
flag{fin4lly_z3r0_d34d}

44. 妹子的陌陌

binwalk 提取出来一个被加密的 rar 文件,试出来密码是图片里的字喜欢我吗.

解压得到下面的文本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
嘟嘟嘟嘟
士兵:报告首长!已截获纳粹的加密电报!
首长:拿来看看

电报内容:
..../-/-/.--./---.../-..-./-..-././-./-.-./---/-.././.-.-.-/-.-./..../.-/..../..-/---/.-.-.-/-.-./---/--/-..-.

首长:我操你在逗我吗?你确定是他们纳粹发的吗?
士兵:难道我弄错了?哦。。。等等是这一条

内容:http://c.bugku.com/U2FsdGVkX18tl8Yi7FaGiv6jK1SBxKD30eYb52onYe0=
AES Key:@#@#¥%……¥¥%%……&¥

士兵:二维码真的扫不出来吗??肯定可以扫出来

先解开摩斯密码得到 HTTP://ENCODE.CHAHUO.COM/,打开了是一个在线加密解密网站

在这个网站进行 AES 解密(用别的工具也行,既然出题人给了就顺手用了。至于为什么是 AES 解密那是因为上面说了 AES Key。)

密文是 U2FsdGVkX18tl8Yi7FaGiv6jK1SBxKD30eYb52onYe0=,密钥是 @#@#¥%……¥¥%%……&¥,解密结果是 momoj2j.png

到这一步就没思路了,看了大佬的 wp 才知道这里是要访问 http://c.bugku.com/momoj2j.png,但是链接挂了,这里就直接把我找到的图片贴出来了。直接扫码就是 flag 了

44.png

flag
1
KEY{nitmzhen6}

45. 就五层你能解开吗

先看描述:

链接: http://pan.baidu.com/s/1i4TQoz7 密码: w65m 提示:第一层:CRC32 碰撞 第二层:维吉尼亚密码 第三层:sha1 碰撞 第四层:md5 相同文件不同 第五层:RSA

(这题不会 Crpyto 真的寸步难行😢)

第一层:

下载来的 7z 压缩包用 CRC32 tools 爆破

三个 txt 文档的 CRC 分别是 7C2DF918A58A19264DAD5967

然后在这个工具目录下执行 python crc32.py reverse [CRC],这里的 CRC 例如 0x7C2DF918

一条条查看不难得到_CRC32_i5_n0t_s4f3,解压密码到手了

第二层:

写代码解:

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
def letter_to_index(letter):
if 'A' <= letter <= 'Z':
return ord(letter) - ord('A')
elif 'a' <= letter <= 'z':
return ord(letter) - ord('a')
else:
return None

def index_to_letter(index, is_upper):
if is_upper:
return chr(index + ord('A'))
else:
return chr(index + ord('a'))

def prepare_keyword(keyword, length):
return (keyword * (length // len(keyword) + 1))[:length]

def vigenere_decrypt(ciphertext, keyword):
decrypted_text = []
keyword = keyword.upper()
extended_keyword = prepare_keyword(keyword, len(ciphertext))
keyword_index = 0

for char in ciphertext:
if 'A' <= char <= 'Z' or 'a' <= char <= 'z':
is_upper = char.isupper()
char_index = letter_to_index(char)
key_index = letter_to_index(extended_keyword[keyword_index])
dec_idx = (char_index - key_index) % 26
decrypted_text.append(index_to_letter(dec_idx, is_upper))
keyword_index += 1
else:
decrypted_text.append(char)

return ''.join(decrypted_text)

def read_file(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
return file.read()

def read_keywords(file_path):
with open(file_path, 'r', encoding='utf-8') as file:
return [line.strip() for line in file]

def main():
path = "/path" # 密文和密钥所在目录
ciphertext = read_file(f'{path}/ciphertext.txt') # 密文
keywords = read_keywords(f'{path}/keys.txt') # 密钥

for keyword in keywords:
plaintext = vigenere_decrypt(ciphertext, keyword)
if 'password' in plaintext.lower(): # password也可以是别的词,猜出来的
print(plaintext)

if __name__ == "__main__":
main()

最后就得到了 the vigenere cipher is a method of encrypting alphabetic text by using a series of different caesar ciphers based on the letters of a keyword it is a simple form of polyalphabetic substitution so password is vigenere cipher funny,所以解压密码就是 vigenere cipher funny

第三层:

这层不会解,代码来自大佬的 wp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 不完整密码: *7*5-*4*3?
# 不完整sha-1: 619c20c*a4de755*9be9a8b*b7cbfa5*e8b4365*

import string
import hashlib
import itertools


# 1.先定义几个字符集到时候使用
str_digits = string.digits
str_upper = string.ascii_uppercase
str_lower = string.ascii_lowercase
str_printable = string.printable

password = "%s7%s5-%s4%s3?"
for chrs in itertools.product(str_printable, repeat=4):
pwd = password % chrs
if (hash_str := hashlib.sha1(pwd.encode()).hexdigest()).startswith("619c20c"):
print(pwd, hash_str)

运行结果是

1
2
s7v5-T4`3? 619c20c33dbeff190fab0d5498f0789e3ec1519a
I7~5-s4F3? 619c20c4a4de75519be9a8b7b7cbfa54e8b4365b

解压密码是 I7~5-s4F3?

第四层:

搜索文本内容后

Hello World 😉
MD5 校验真的安全吗?有没有两个不同的程序 MD5 却相同呢?如果有的话另一个程序输出是什么呢?解压密码为单行输出结果。

Hello World 😉
MD5 check is really safe?
There are two different procedures MD5 is the same?
If so what is the output of another program?
The decompression password is a single-line output.

发现来源于下面两个程序,这两个程序会在屏幕上打印出不同的字符 ,而它们的 md5 相同,都为 18fcc4334f44fed60718e7dacd82dddf

GoodbyeWorld-colliding.exe
HelloWorld-colliding.exe

因此解压密码是 Goodbye World :-(

第五层:

解压得到了 flag.encrsa_public_key.pem

然后又不会了(Crypto 是真的不会😭😭😭),下面贴上更改后的刚刚引用过的大佬的 wp 的代码(大佬的代码直接运行报错了,要将 gmpy2.mpz 类型转换为 int 类型才行):

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
import sympy
import libnum
from Crypto.PublicKey import RSA


# 1.read flag.enc
with open("flag.enc", "rb") as f:
flag = f.read()
c = libnum.s2n(flag)

# 2.read pub_key
with open("rsa_public_key.pem") as f:
pub_key = RSA.import_key(f.read())

n = pub_key.n
e = pub_key.e

# 3.使用factordb查询的
p = 15991846970993213322072626901560749932686325766403404864023341810735319249066370916090640926219079368845510444031400322229147771682961132420481897362843199
q = 28805791771260259486856902729020438686670354441296247148207862836064657849735343618207098163901787287368569768472521344635567334299356760080507454640207003

phi_n = (p - 1) * (q - 1)
d = sympy.mod_inverse(e, phi_n)

m = pow(c, d, n)
m = int(m) # 将 gmpy2.mpz 类型转换为 int 类型
print(libnum.n2s(m))

运行得到
b'\x02\xb3\xf3\xc6W8\xb5\x81S/cwr\xe8\xd3\xb5Cf\xe4\xe5w\x81h\t\x82\x8cd\x85D}\xec7\xec!\xe4;\x89\xb3w\xa4Uf\xf5\xd9%\xcb\x96\x85\x10\x11B\x9a<"QS\x05\x84\x80{\xb1.\x82\xcc\x1c\xf6\x87z@\x91\x9e\xf6h\xe7\xa1\x8f\x96\x9d%&\xa4\xcd\xf0\'\x16J\xf4!\x9c\'h8!Y\xa1o(H\xea}\x00flag{W0rld_Of_Crypt0gr@phy}'

flag
1
flag{W0rld_Of_Crypt0gr@phy}

46. /.-

…-./.-…/.-/–./----.–/-…/…–/…-./-.-./-…/…-./.----/–…/…-./----./…–/----./----./…/-----/…-/-----.-

随波逐流秒了,摩斯密码(要转小写)

flag
1
flag{d3fcbf17f9399504}

47. 聪明的小羊

一只小羊翻过了 2 个栅栏 fa

随波逐流秒了,栅栏密码分两栏

flag
1
flag{6fde4163df05d900}

48. ok

Brain Fuck 解码

flag
1
flag{0a394df55312c51a}

49. [±<>]

+++++ +++++ [->++ +++++ +<] >.+ +++++ .<+++ [->-- -<]>- -.+++ +++.< [ ->+ +<]>+ +.< +++++ +[- >---- ----< ]>— ----- —.< +++++ [-> +++++ <]> +.< +++++ +[->- ----- <]>-- ----- -.–. ----. --. +++++ +.< [ ->+ +<] > +.. < [-> ----- -<]>- ----- ----. -.< +++++ [-> +++++ <]>+. ----. . < +++[- >---- —<] >---- .+.<+ +++++ [-> +++++ +<] > +++++ ++.<

随波逐流秒了,Brain Fuck 解码

flag
1
flag{0d86208ac54fbf12}

50. easy_crypto

0010 0100 01 110 1111011 11 11111 010 000 0 001101 1010 111 100 0 001101 01111 000 001101 00 10 1 0 010 0 000 1 01111 10 11110 101011 1111101

随波逐流秒了,摩斯密码(要转小写)

flag
1
flag{m0rse_code_1s_interest1n9!}

51. 简单加密

这是基于一个简单的凯撒密码变种,其中每个字符都向后或向前移动了一定数量的位置。每个字符根据固定的 ASCII 值偏移进行了编码,根据字符串末尾的 AA 猜测恢复后是一个经过 base64 编码的字符串,所以 AA 恢复后是 ==。由于’A’的 ASCII 码是 65,而’=‘的 ASCII 码是 61,这意味着从’A’到’=' 需要减少 4 个位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def decode_string(encoded_str, shift=4):
decoded_str = ""
for char in encoded_str:
# Shift the ASCII value of each character backwards by 'shift'
new_ascii = ord(char) - shift
# Append the shifted character to the result string
decoded_str += chr(new_ascii)
return decoded_str

# Given encoded string
encoded_str = "e6Z9i~]8R~U~QHE{RnY{QXg~QnQ{^XVlRXlp^XI5Q6Q6SKY8jUAA"
# Decode the string
decoded_str = decode_string(encoded_str)
print("Decoded string:", decoded_str)

decoded_provided_string = base64.b64decode(decoded_str).decode('utf-8')
print("Decoded Provided String:", decoded_provided_string)

运行代码得到 flag

flag
1
key{68743000650173230e4a58ee153c68e8}

52. 散乱的密文

lf5 {ag024c483549d7fd@@1} 一张纸条上凌乱的写着 2 1 6 5 3 4

行置换密码(RowRermutationCipher)解密即可,密钥是 216534,解密得到 flag{52048c453d794df1}@@

flag
1
flag{52048c453d794df1}

53. 一段 Base64

Base64 解码 -> 八进制转字符 -> 十六进制转字符 -> Unicode 转义解码 -> 替换首尾元素 -> 第一次 HTML 实体解码 -> 第二次 HTML 实体解码 -> URL 解码

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
import base64
from html import unescape
import urllib.parse as urlparse

def decode_data(encoded_string):
# Step 1: Base64 decode
base64_decoded = base64.b64decode(encoded_string).decode('utf-8')

# Step 2: Split by backslash and convert octal to characters
octal_parts = base64_decoded.split('\\')
del octal_parts[0] # Remove the first element if it's empty or not an octal sequence
oct_to_text = ''.join(chr(int(part, 8)) for part in octal_parts if part) # Convert octal to char

# Step 3: Split by '\x' and convert hex to characters
hex_parts = oct_to_text.split('\\x')
del hex_parts[0] # Remove the first element if it's empty or not a hex sequence
hex_to_text = ''.join(chr(int(part, 16)) for part in hex_parts if part) # Convert hex to char

# Step 4: Decode unicode escape sequences
unicode_decoded = hex_to_text.encode().decode('unicode-escape')

# Step 5: Split by comma and replace first and last elements
decimal_parts = unicode_decoded.split(',')
if len(decimal_parts) > 1: # Ensure there are enough elements to modify
decimal_parts[0] = '38'
decimal_parts[-1] = '59'
dec_to_text = ''.join(chr(int(part)) for part in decimal_parts if part.isdigit()) # Convert decimal to char

# Step 6 & 7: HTML entity decode twice
html_decoded_first_pass = unescape(dec_to_text)
html_decoded_second_pass = unescape(html_decoded_first_pass)

# Step 8: URL decode
url_decoded = urlparse.unquote(html_decoded_second_pass)

return url_decoded

encoded_string = "" #这里填入密文
print(decode_data(encoded_string))

运行就能得出结果

flag
1
flag{ctf_tfc201717qwe}

54. .!?

随波逐流秒了,Brain Fuck 解码

flag
1
flag{bugku_jiami}

55. 奇怪的密码

突然天上一道雷电 gndk€rlqhmtkwwp} z

随波逐流秒了,凯撒密码 mode4 #1 得到 flag₧lei_ci_jiami,把删掉,补上括号就好了

flag
1
flag{lei_ci_jiami}

56. 托马斯。杰斐逊

Jefferson Disk Cipher(杰斐逊圆盘密码)是一种多表替换加密方法,它使用一组旋转的圆盘来对信息进行加密和解密。每个圆盘都有一个字母表的随机排列,这些圆盘可以独立旋转。加密时,选择一系列圆盘的位置形成密钥,然后将明文中的每一个字母替换成当前选定位置下的相应字母。

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
# 定义圆盘和密钥
disks = [
"ZWAXJGDLUBVIQHKYPNTCRMOSFE",
"KPBELNACZDTRXMJQOYHGVSFUWI",
"BDMAIZVRNSJUWFHTEQGYXPLOCK",
"RPLNDVHGFCUKTEBSXQYIZMJWAO",
"IHFRLABEUOTSGJVDKCPMNZQWXY",
"AMKGHIWPNYCJBFZDRUSLOQXVET",
"GWTHSPYBXIZULVKMRAFDCEONJQ",
"NOZUTWDCVRJLXKISEFAPMYGHBQ",
"QWATDSRFHENYVUBMCOIKZGJXPL",
"WABMCXPLTDSRJQZGOIKFHENYVU",
"XPLTDAOIKFZGHENYSRUBMCQWVJ",
"TDSWAYXPLVUBOIKZGJRFHENMCQ",
"BMCSRFHLTDENQWAOXPYVUIKZGJ",
"XPHKZGJTDSENYVUBMLAOIRFCQW"
]

key = [2, 5, 1, 3, 6, 4, 9, 7, 8, 14, 10, 13, 11, 12]
ciphertext = "HCBTSXWCRQGLES"

# 根据密钥重新排序圆盘
ordered_disks = [disks[i-1] for i in key]

# 找到每个密文字符在对应圆盘上的位置,并构建所有可能的解码结果
positions = []
for i, char in enumerate(ciphertext):
pos = ordered_disks[i].index(char)
positions.append(pos)

# 输出所有可能的解码结果(即每一列)
for i in range(len(disks[0])):
plaintext = ''
for j, disk in enumerate(ordered_disks):
plaintext += disk[(i + positions[j]) % len(disk)]
print(plaintext)

运行得到

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
HCBTSXWCRQGLES
GPVELQAEJWOTNW
VMIQOYTOLXIDYA
SNQGQIDNXPKESY
FZHYXZSJKHFNRX
UQKXVMRQIKHQUP
WWYPEJFGSZEWBL
IXPLTWHWEGNAMV
KYNOAAETFJYOCU
PITCMONHATVXQB
BHCKKRYSPDUPWO
EFRBGPVPMSWYVI
LRMDHLUYYEAVJK
NLOMINBBGNBUXZ
AASAWDMXHYMIPG
CBFIPVCIBVCKLJ
ZEEZNHOZQUXZTR
DUZVYGIUNBPGDF
TOWRCFKLOMLJAH
RTANJCZVZLTBOE
XSXSBUGKUADMIN <- flag在这里
MGJJFKJMTOSCKM
JJGUZTXRWIRSFC
QVDWDEPADRJRZQ
ODLFRBLFCFQFGT
YKUHUSQDVCZHHD

在里面找到有意义的字符串

flag
1
flag{xsxsbugkuadmin}

57. zip 伪加密

zip 伪加密,随波逐流修复下就好了

flag
1
flag{Adm1N-B2G-kU-SZIP}

58. 告诉你个秘密

636A56355279427363446C4A49454A7154534230526D6843
56445A31614342354E326C4B4946467A5769426961453067

16 进制转 Base64 得到 Y2pWNVJ5QnNjRGxKSUVKcVRTQjBSbWhDVkRaMWFDQjVOMmxLSUZGeldpQmlhRTBn

连续两次 base64 解码得到 r5yG lp9I BjM tFhBT6uh y7iJ QsZ bhM

最后键盘密码 (Keyboard Cipher) 解密(感谢评论区的大佬们,这个真没想到😂)

flag
1
flag{TONGYUAN}

59. 这不是 md5

随波逐流秒了,16 进制转字符

flag
1
flag{ae73587ba56baef5}

60. 贝斯家族

随波逐流秒了,base91 解码

flag
1
flag{554a5058c9021c76}