NKCTF 2024 1z_F0r3ns1c5 Writeup 写在前面 整体出题思路是前两个为常规取证点,最后一个稍微套了一下,上上强度(赛方要求 ,不过后期上了 hint
)。原本打算该题弄成三个 flag
来着,后来考虑到一血有定制礼品不好计算只好作罢,最后就变成一个 flag
了
另外今年貌似参赛选手不太热衷取证,取证题型三道题均攻克率不高
1 2 3 HackMyCQL - 2解 1z_F0r3ns1c5 - 2解 cain_is_hacker - 3 解
1z_F0r3ns1c5 本鼠鼠正在Coding,突然一声OPEN THE DOOR!本鼠鼠直接鼠躯一颤就双手抱头蹲下了,果然本鼠鼠只适合生活在阴暗的下水道 被黑猫警长抓走的时候本鼠鼠还想辩解一下,但是他们拿出你的照片的时候,本鼠鼠认罪了 昨晚和其他鼠鼠聊天的时候其他鼠鼠问本鼠鼠:“你到底喜欢她什么啊?” “喜欢一个人不需要理由” 本鼠鼠很快敲完了键盘,刚要按下回车的时候突然愣住了。 真的不需要理由吗? 请找到鼠鼠的答案吧。
下载附件后,总共会得到三个东西:内存镜像、描述文件和压缩包 (加密容器、马赛克图片)
本鼠鼠的flag总共分为三段捏,flag为nkctf{uuid}形式,另外鼠鼠最喜欢等宽字体了,快快去找吧。
flag01 1 .\volatility.exe -f .\1 .raw --profile=Win7SP1x64 envars | Select-String 'n0wayback'
可以发现一个键名为 n0wayback
的环境变量
1 n0wayback HPahXR4NvAnZXB16tNK6hAaNVNU++
直接 XXencode 或 随波逐流
一键解码即可
1 2 HPahXR4NvAnZXB16tNK6hAaNVNU // nkctf{39c429eb-2faf
flag02 通过查询进程(pslist
)或获取屏幕截图(screenshot
,但是比较抽象)可以看到当前正在运行 mspaint
、cmd
和 code
1 2 3 4 5 6 7 .\volatility.exe -f .\1 .raw --profile=Win7SP1x64 pslist0 xfffffa8001a022a0 mspaint.exe 2052 1028 6 120 1 0 2024 -03 -04 05 :50 :22 UTC+0000 0 xfffffa8003c68a80 cmd .exe 4188 1028 3 111 1 0 2024 -03 -04 05 :50 :26 UTC+0000 0 xfffffa800418c060 Code.exe 888 1028 31 696 1 0 2024 -03 -04 05 :52 :52 UTC+0000
既然存在 mspaint
进程,我们直接通过 memdump
导出后,使用 Gimp
进行还原即可
1 .\volatility.exe -f .\1 .raw --profile=Win7SP1x64 memdump -p 2052 -D ./
宽高可以尝试使用常见的显示屏分辨率,诸如 1920*1024
、1024*768
之类的,这里没有标准的值,大差不差即可。
随后就是不断地进行偏移量的调整即可
flag03 1 .\volatility.exe -f .\1 .raw --profile=Win7SP1x64 consoles
通过 consoles
发现通过 git clone
下载了一份源码
1 git clone https://github.com/5ime/Secret_Generator.git
我们直接访问该地址,首先写明需要 Docker
启动,同时让我们找到源码代码在哪
1 2 3 Compilable with Dockerfile or Python 3.7.2 only. Hey, hold on a second... Where's my source code?
我们在 Commits 中看到了源代码,直接手动 git clone
后,通过 git reset
即可得到源代码
根据 README
中要求,我们通过 Docker
对其进行部署
访问 8080
端口,得到一个 Secret Generator
,要求我们输入 加密文字
和 上传字体
结合最初 描述文件
中提到 鼠鼠最喜欢等宽字体了
,以及题目描述开头提到 本鼠鼠正在Coding
,联想到进程中的 Vscode
直接百度搜索其配置文件默认路径即可得到 Vscode
中所使用的等宽字体
1 2 3 4 5 6 { "editor.fontFamily" : "'Fira Code', Consolas, 'Comrier New', monospace" , "window.zoomLevel" : 1 , "security.workspace.trust.untrustedFiles" : "open" }
我们直接下载 Fira Code 即可,注意下载后会存在多个粗细的字体,默认情况下直接使用 Regular
即可
剩下的解题方法基本和 CISCN 2023 国粹
一题类似,唯一的区别就是我们需要手动生成一张表(这也是前面需要找到字体的原因)
1 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
我们发现通过指定字母表进行生成图片时,后端代码对我们进行了字符串拼接,多出来了 pass[空格]
这五个字符
1 2 3 4 5 secret = 'pass ' + secretfor i in range (5 , len (secret)-1 ): mosaic_img(canvas, W*i, 0 , W*i+W, H//2 ) mosaic_img(canvas, W*i, H//2 , W*i+W, H)
这里提供两种方法,字母表前面加个 AAAAA
字符,以及在末尾多加一个 9
,原因在于前五位和最后一位不加密,且前五位为固定的 pass[空格]
,当我们添加 AAAAA
时在切割的时候,前五个字符会直接被覆盖(文件命名时不允许存在同名文件)
另外一种方法,直接稍微改一下代码逻辑即可
1 2 3 4 5 6 7 for i in range (len (secret)): mosaic_img(canvas, W*i, 0 , W*i+W, H//2 ) mosaic_img(canvas, W*i, H//2 , W*i+W, H)
下面,我们直接通过 Python 的 PIL
库对每个字符进行裁剪,所以需要知道每个字的宽高,代码中也写明了宽高
另外,你也可以通过 图片宽度 / 字符数量
计算得到每个字符所占宽度
下面直接人工写代码或 PUA AI
帮你写代码即可,话术如下
我有一张 xxx.png
图片,宽高为 1086*60
,请通过 Python 的 PIL
库将它裁剪为 30*60
的图片,切割出来的图片保存到 dict
文件夹中,另外切割出的图片命名规则根据如下命名表
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
最后,如果文件名为大写字母则 文件名-大写.png
这里我加了一个判断,如果是大写字母则 文件名-大写.png
,原因在于 Windows
下 A.png
和 a.png
不能共存,Linux
无此烦恼。
下面的代码来源于 ChatGPT 3.5
(当然,自己写也行),分别对我们自行生成 dict.png
和附件提供的 pass.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 from PIL import Imageimport os img = Image.open ("dict.png" ) width, height = img.size tile_width = 30 tile_height = 60 assert width % tile_width == 0 and height % tile_height == 0 if not os.path.exists("dict" ): os.makedirs("dict" ) char_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for i in range (width // tile_width): for j in range (height // tile_height): tile = img.crop((i * tile_width, j * tile_height, (i + 1 ) * tile_width, (j + 1 ) * tile_height)) index = i * (height // tile_height) + j filename = f"dict/{char_set[index]} " if char_set[index].isupper(): filename += "-大写" filename += ".png" tile.save(filename)print ("切割完成" )
切完图后,我们进行比对一下即可得到最终内容,这里可以用 np.sum
、sha256
或 md5
之类的均可,只要能代表唯一。防止有人不会写代码,接着用 AI
吧
通过 Python 读取 dict
文件夹中的所有文件,获取该文件的 md5
,输出形式为字典,格式为 文件名:md5值
,将其存到 dict
字典中,pass
文件夹进行相同的操作,将其存到 pass
字典中
将 pass
字典中的 value
与 dict
字典中的 value
进行比对,如果两个 value
相等,则输出dict
的 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 40 import osimport hashlibdef calculate_md5 (filename ): """计算文件的 MD5 值""" hasher = hashlib.md5() with open (filename, 'rb' ) as f: for chunk in iter (lambda : f.read(4096 ), b"" ): hasher.update(chunk) return hasher.hexdigest()def get_file_md5 (folder ): """获取文件夹中所有文件的 MD5 值""" file_md5_dict = {} for file_name in os.listdir(folder): file_path = os.path.join(folder, file_name) if os.path.isfile(file_path): file_md5_dict[file_name] = calculate_md5(file_path) return file_md5_dict dict_folder = "dict" dict_md5 = get_file_md5(dict_folder) pass_folder = "pass" pass_md5 = get_file_md5(pass_folder) common_keys = [key for key, value in pass_md5.items() if value in dict_md5.values()]print ("Pass 文件夹中与 Dict 文件夹中 MD5 值相同的文件名:" ) flag = '' for key in common_keys: matching_keys = [k for k, v in dict_md5.items() if v == pass_md5[key]] for matching_key in matching_keys: flag += matching_key.split('.png' )[0 ]print (flag + '3' )
运行后即可得到马赛克隐藏的 VeraCrypt
容器密码
VeraCrypt
挂载后即可得到 flag3
,最终 flag
如下
1 nkctf{39c429eb-2faf-49a0-bd24-c4f222879312}