iami233
iami233
文章175
标签37
分类4

文章分类

文章归档

NKCTF 2024 1z_F0r3ns1c5 Writeup

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'

image-20240324154714864

可以发现一个键名为 n0wayback 的环境变量

1
n0wayback	HPahXR4NvAnZXB16tNK6hAaNVNU++

直接 XXencode随波逐流 一键解码即可

1
2
HPahXR4NvAnZXB16tNK6hAaNVNU
// nkctf{39c429eb-2faf

flag02

通过查询进程(pslist)或获取屏幕截图(screenshot,但是比较抽象)可以看到当前正在运行 mspaintcmdcode

1
2
3
4
5
6
7
.\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

image-20240324154726984

既然存在 mspaint 进程,我们直接通过 memdump 导出后,使用 Gimp 进行还原即可

1
.\volatility.exe -f .\1.raw --profile=Win7SP1x64 memdump -p 2052 -D ./

宽高可以尝试使用常见的显示屏分辨率,诸如 1920*10241024*768 之类的,这里没有标准的值,大差不差即可。

随后就是不断地进行偏移量的调整即可

1
-49a0-bd24-

image-20240324154449926

flag03

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

image-20240324154739326

通过 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 即可得到源代码

image-20240324154749015

根据 README 中要求,我们通过 Docker 对其进行部署

image-20240324154953798

访问 8080 端口,得到一个 Secret Generator,要求我们输入 加密文字上传字体

image-20240324143415899

结合最初 描述文件 中提到 鼠鼠最喜欢等宽字体了,以及题目描述开头提到 本鼠鼠正在Coding,联想到进程中的 Vscode

直接百度搜索其配置文件默认路径即可得到 Vscode 中所使用的等宽字体

1
2
3
4
5
6
// 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 即可

image-20240315122909273

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

1
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

image-20240324145041345

我们发现通过指定字母表进行生成图片时,后端代码对我们进行了字符串拼接,多出来了 pass[空格] 这五个字符

1
2
3
4
5
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 时在切割的时候,前五个字符会直接被覆盖(文件命名时不允许存在同名文件)

另外一种方法,直接稍微改一下代码逻辑即可

1
2
3
4
5
6
7
# 可改为如下形式
# 注释掉 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)

image-20240324143801054

下面,我们直接通过 Python 的 PIL 库对每个字符进行裁剪,所以需要知道每个字的宽高,代码中也写明了宽高

另外,你也可以通过 图片宽度 / 字符数量 计算得到每个字符所占宽度

1
2
H = 60
W = 30

下面直接人工写代码或 PUA AI 帮你写代码即可,话术如下

我有一张 xxx.png 图片,宽高为 1086*60 ,请通过 Python 的 PIL 库将它裁剪为 30*60 的图片,切割出来的图片保存到 dict 文件夹中,另外切割出的图片命名规则根据如下命名表

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

最后,如果文件名为大写字母则 文件名-大写.png

这里我加了一个判断,如果是大写字母则 文件名-大写.png,原因在于 WindowsA.pnga.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 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.sumsha256md5 之类的均可,只要能代表唯一。防止有人不会写代码,接着用 AI

通过 Python 读取 dict 文件夹中的所有文件,获取该文件的 md5,输出形式为字典,格式为 文件名:md5值,将其存到 dict 字典中,pass 文件夹进行相同的操作,将其存到 pass 字典中

pass 字典中的 valuedict字典中的 value 进行比对,如果两个 value 相等,则输出dictkey

这里生成的代码就有些许误差了,但是大部分逻辑是没问题的,自己手动改改即可

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 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 容器密码

1
b143a6268e2a233

VeraCrypt 挂载后即可得到 flag3,最终 flag 如下

1
nkctf{39c429eb-2faf-49a0-bd24-c4f222879312}
本文作者:iami233
本文链接:https://5ime.cn/nkctf-2024.html
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可