

NKCTF 2024 1z_F0r3ns1c5 Writeup
source link: https://5ime.cn/nkctf-2024.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.


NKCTF 2024 1z_F0r3ns1c5 Writeup
整体出题思路是前两个为常规取证点,最后一个稍微套了一下,上上强度(赛方要求,不过后期上了 hint
)。原本打算该题弄成三个 flag
来着,后来考虑到一血有定制礼品不好计算只好作罢,最后就变成一个 flag
了
另外今年貌似参赛选手不太热衷取证,取证题型三道题均攻克率不高
HackMyCQL - 2解
1z_F0r3ns1c5 - 2解
cain_is_hacker - 3 解
1z_F0r3ns1c5
本鼠鼠正在Coding,突然一声OPEN THE DOOR!本鼠鼠直接鼠躯一颤就双手抱头蹲下了,果然本鼠鼠只适合生活在阴暗的下水道
被黑猫警长抓走的时候本鼠鼠还想辩解一下,但是他们拿出你的照片的时候,本鼠鼠认罪了
昨晚和其他鼠鼠聊天的时候其他鼠鼠问本鼠鼠:“你到底喜欢她什么啊?”
“喜欢一个人不需要理由”
本鼠鼠很快敲完了键盘,刚要按下回车的时候突然愣住了。
真的不需要理由吗?
请找到鼠鼠的答案吧。
下载附件后,总共会得到三个东西:内存镜像、描述文件和压缩包 (加密容器、马赛克图片)
本鼠鼠的flag总共分为三段捏,flag为nkctf{uuid}形式,另外鼠鼠最喜欢等宽字体了,快快去找吧。
flag01
.\volatility.exe -f .\1.raw --profile=Win7SP1x64 envars | Select-String 'n0wayback'

可以发现一个键名为 n0wayback
的环境变量
n0wayback HPahXR4NvAnZXB16tNK6hAaNVNU++
直接 XXencode 或 随波逐流
一键解码即可
HPahXR4NvAnZXB16tNK6hAaNVNU
// nkctf{39c429eb-2faf
flag02
通过查询进程(pslist
)或获取屏幕截图(screenshot
,但是比较抽象)可以看到当前正在运行 mspaint
、cmd
和 code
.\volatility.exe -f .\1.raw --profile=Win7SP1x64 pslist
0xfffffa8001a022a0 mspaint.exe 2052 1028 6 120 1 0 2024-03-04 05:50:22 UTC+0000
0xfffffa8003c68a80 cmd.exe 4188 1028 3 111 1 0 2024-03-04 05:50:26 UTC+0000
0xfffffa800418c060 Code.exe 888 1028 31 696 1 0 2024-03-04 05:52:52 UTC+0000

既然存在 mspaint
进程,我们直接通过 memdump
导出后,使用 Gimp
进行还原即可
.\volatility.exe -f .\1.raw --profile=Win7SP1x64 memdump -p 2052 -D ./
宽高可以尝试使用常见的显示屏分辨率,诸如 1920*1024
、1024*768
之类的,这里没有标准的值,大差不差即可。
随后就是不断地进行偏移量的调整即可
-49a0-bd24-

flag03
.\volatility.exe -f .\1.raw --profile=Win7SP1x64 consoles

通过 consoles
发现通过 git clone
下载了一份源码
git clone https://github.com/5ime/Secret_Generator.git
我们直接访问该地址,首先写明需要 Docker
启动,同时让我们找到源码代码在哪
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
中所使用的等宽字体
// C:\Users\moe\AppData\Roaming\Code\User\Settings.json
{
"editor.fontFamily": "'Fira Code', Consolas, 'Comrier New', monospace",
"window.zoomLevel": 1,
"security.workspace.trust.untrustedFiles": "open"
}
我们直接下载 Fira Code 即可,注意下载后会存在多个粗细的字体,默认情况下直接使用 Regular
即可

剩下的解题方法基本和 CISCN 2023 国粹
一题类似,唯一的区别就是我们需要手动生成一张表(这也是前面需要找到字体的原因)
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

我们发现通过指定字母表进行生成图片时,后端代码对我们进行了字符串拼接,多出来了 pass[空格]
这五个字符
secret = 'pass ' + secret
for 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
时在切割的时候,前五个字符会直接被覆盖(文件命名时不允许存在同名文件)
另外一种方法,直接稍微改一下代码逻辑即可
# 可改为如下形式
# 注释掉 secret 变量的字符串拼接,以及 range 范围从 0 开始
# secret = 'pass ' + secret
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
库对每个字符进行裁剪,所以需要知道每个字的宽高,代码中也写明了宽高
另外,你也可以通过 图片宽度 / 字符数量
计算得到每个字符所占宽度
H = 60
W = 30
下面直接人工写代码或 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
进行切割
from PIL import Image
import os
# 打开图片
img = Image.open("dict.png")
# img = Image.open("pass.png")
# 获取图片尺寸
width, height = img.size
# 定义切割尺寸
tile_width = 30
tile_height = 60
# 确保切割后的图片尺寸可以被整除
assert width % tile_width == 0 and height % tile_height == 0
# 创建 dict 文件夹
if not os.path.exists("dict"):
os.makedirs("dict")
# if not os.path.exists("pass"):
# os.makedirs("pass")
# 定义命名规则
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]}"
# filename = f"pass/{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
这里生成的代码就有些许误差了,但是大部分逻辑是没问题的,自己手动改改即可
import os
import hashlib
def 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 文件夹中的所有文件的 MD5 值并存储到字典中
dict_folder = "dict"
dict_md5 = get_file_md5(dict_folder)
# 读取 pass 文件夹中的所有文件的 MD5 值并存储到字典中
pass_folder = "pass"
pass_md5 = get_file_md5(pass_folder)
# 找到 pass 文件夹中与 dict 文件夹中 MD5 值相同的文件名对应的 dict 中的 key
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
容器密码
b143a6268e2a233
VeraCrypt
挂载后即可得到 flag3
,最终 flag
如下
nkctf{39c429eb-2faf-49a0-bd24-c4f222879312}
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK