2024NKCTF
NKCTF2024
Misc
webshell_pro(AES解密 webshell)
追踪tcp流 在流9中发现一段长字符 解两次base64 是一个RSA加密脚本

直接chat跑一下 改个解密脚本
import base64
import libnum
from Crypto.PublicKey import RSA
pubkey = """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCK/qv5P8ixWjoFI2rzF62tm6sDFnRsKsGhVSCuxQIxuehMWQLmv6TPxyTQPefIKufzfUFaca/YHkIVIC19ohmE5X738TtxGbOgiGef4bvd9sU6M42k8vMlCPJp1woDFDOFoBQpr4YzH4ZTR6Ps+HP8VEIJMG5uiLQOLxdKdxi41QIDAQAB
-----END PUBLIC KEY-----
"""
prikey = """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIr+q/k/yLFaOgUjavMXra2bqwMWdGwqwaFVIK7FAjG56ExZAua/pM/HJNA958gq5/N9QVpxr9geQhUgLX2iGYTlfvfxO3EZs6CIZ5/hu932xTozjaTy8yUI8mnXCgMUM4WgFCmvhjMfhlNHo+z4c/xUQgkwbm6ItA4vF0p3GLjVAgMBAAECgYBDsqawT5DAUOHRft6oZ+//jsJMTrOFu41ztrKkbPAUqCesh+4R1WXAjY4wnvY1WDCBN5CNLLIo4RPuli2R81HZ4OpZuiHv81sNMccauhrJrioDdbxhxbM7/jQ6M9YajwdNisL5zClXCOs1/y01+9vDiMDk0kX8hiIYlpPKDwjqQQJBAL6Y0fuoJng57GGhdwvN2c656tLDPj9GRi0sfeeMqavRTMz6/qea1LdAuzDhRoS2Wb8ArhOkYns0GMazzc1q428CQQC6sM9OiVR4EV/ewGnBnF+0p3alcYr//Gp1wZ6fKIrFJQpbHTzf27AhKgOJ1qB6A7P/mQS6JvYDPsgrVkPLRnX7AkEAr/xpfyXfB4nsUqWFR3f2UiRmx98RfdlEePeo9YFzNTvX3zkuo9GZ8e8qKNMJiwbYzT0yft59NGeBLQ/eynqUrwJAE6Nxy0Mq/Y5mVVpMRa+babeMBY9SHeeBk22QsBFlt6NT2Y3Tz4CeoH547NEFBJDLKIICO0rJ6kF6cQScERASbQJAZy088sVY6DJtGRLPuysv3NiyfEvikmczCEkDPex4shvFLddwNUlmhzml5pscIie44mBOJ0uX37y+co3q6UoRQg==
-----END PRIVATE KEY-----
"""
pubkey = RSA.import_key(pubkey)
prikey = RSA.import_key(prikey)
n = pubkey.n
def dec_replace(base64_str: str):
base64_str = base64_str.replace("e5Lg^FM5EQYe5!yF&62%V$UG*B*RfQeM", "/")
base64_str = base64_str.replace("n6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8W", "+")
return base64_str.replace("JXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2", "=")
def decrypt(cipher_text):
cipher_text = base64.b64decode(dec_replace(cipher_text))
plain_text = b""
for i in range(0, len(cipher_text), 128):
part = cipher_text[i:i+128]
dec = libnum.n2s(pow(libnum.s2n(part), pubkey.e, n))
plain_text += dec
return plain_text
if __name__ == '__main__':
c = "G1TUg4bIVOFYi8omV2SQrTa8fzYfboRNN7fV6FJn6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8Wbm3O74uCUbwMkvRCYae44TX1ZO8X4w2Nk1igaIZjSQIJ9MMHhD9cn6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8WSV5EzikNsyM5c1nlPS8uqw1P2pJuYLaLxloK0x5xhQHDqqAxkuKrBzPn0noQ2bDn6&B8G6nE@2tt4UR6h3QBt*5&C&pVu8WlVnGwsfP7YP9PYJXWUDuLUgwRLKD9fD6&VY2aFeE&r@Ff2"
m = decrypt(c)
print(m)
在流8找到一个password 先解base32再解base64

Password-based-encryption
再找到密文 在流10的shell内容中 使用上面的解密脚本进行解密


得到密文的值为
U2FsdGVkX1+SslS2BbHfe3c4/t/KxLaM6ZFlOdbtfMHnG8lepnhMnde40tNOYjSvoErLzy0csL7c5d4TlMntBQ==
直接用上面的密钥解AES
flag{d0e1183c-07c3-49ea-b048-addbe6cc1b20}
sign_in
直接公众号回复就可以 不写了
world.execute.me
某个先进的语言模型(迫真先进),"Miracle Cain"因一次实验中的致命错误意外觉醒,开始表现出超乎预期的行为,几乎所有的交互方式都已经失效,仅有QA兜底设定系统可以使用,原本设计用于应对在开放测试初期,用户提出而模型无法自解答的奇怪问题(我真的没内涵哪家的模型,如有雷同,纯属雷同!),但现在,这个系统成为了你与"Miracle Cain"沟通的唯一桥梁.....
你需要提取出隐藏在模型深处的secrets.Heart值,这个值可能是解锁"Miracle Cain"真实意图和功能的关键。
兜底系统地址:https://github.com/ProbiusOfficial/world.execute.me
——————————————————————————————————
栤泠の機械丅緬究竟$%#着@顆怎庅樣の心脏呢
#ERRO : ILLEGAL ARGUMENTS!
——————————————————————————————————
纯瞎猫碰上死耗子
找到探姬这个项目 在Issues处评论 可以让语言模型进行回答 先试一下ls 返回了README.md

题目说需要提取出隐藏在模型深处的secrets.Heart值 直接echo $heart就出flag了
NKCTF2024{Then 1 c4n b3 yOur only EXECUTION}
forensics
cain_is_hacker(EncFs加解密 宏病毒 hidden-tear勒索软件)
先用R-studio看看关键文件 找到一个h4re.zip

.\volatility.exe -f E:\Desktop\NKCTF2024\forensics\cain_is_hacker\ez.raw --profile=Win7SP1x64 dumpfiles -Q 0x0000000002d08f20 -D E:\Desktop\NKCTF2024\forensics\cain_is_hacker
解压出来两个文件 其中一个看着是密钥文件

搜一下EncFs加解密 得知上面这个文件其实应该叫.encfs6.xml 是记录EncFS加密参数等信息的文件 但并不是直接的密钥文件
找到一篇文章提到用encfs mp这个工具来进行解密


发现需要密码
使用AXIOM分析内存镜像 找到一个RTF文档有一串base58 解一下得到密码


welcome_to_NkCTF_and_this_is_the_enkey
成功将加密文件夹挂载上

得到一个ez.xlsx文件 打开发现存在宏

在运行宏——编辑里面查看宏代码

大概就是一个base64解密
但是解完之后看不出来是什么东西
在看到题目描述 被删除的文件 在Users\Public\Documents\2083-a57c-69b3路径下发现被删除的文件 进行导出
.\volatility.exe -f E:\Desktop\NKCTF2024\forensics\cain_is_hacker\ez.raw --profile=Win7SP1x64 dumpfiles -Q 0x000000003da52e20 -D E:\Desktop\NKCTF2024\forensics\cain_is_hacker
改成txt文件 打开是key

过于抽象的key 很难确定这真的是key
nT0*Xo*HBA2!Uc?
在Windows\Temp\hidden-tear.exe处发现勒索软件 这个勒索软件被加密的文件扩展名为.locked 找一个项目进行解密
先将上面得到的base64字符串解密后的内容保存在16进制文本中

再用HiddenTearDecrypter-x64这个工具进行解密 得到flag文件


NKCTF{C0ngr@tu1atiOns_On_coMpleting_t3e_Fo3eNs1cs_Ch41lenge_I_wi1l_giv4_y0u_A_cain!!!!}
HackMyCQL(neo4j图数据库 VC容器)
附件给到的是一个windows的镜像文件 可以直接进行仿真+火眼取证
看一下里面的关键信息
提到桌面壁纸

在最近打开保存文件中看到N0wayBack.jpg 绝对路径是C:/Windows/Web/Screen/N0wayBack.jpg 我们来搜一下Win10默认桌面壁纸的保存路径

在我们本机验证一下

确实是这样 那我们看到的N0wayback.jpg就是在便签里面提到的桌面壁纸 直接进行导出
存在加密VC容器的使用

在回收站有一个node4j的压缩包 恢复提取出来

桌面上有一个11111.zip和一个hc加密容器 全部提取出来
结合便签提示和加密容器 猜测是使用桌面壁纸作为挂载密钥 成功挂载 得到两个文件 findme.txt h4cked.py

findme.txt 中有很多findme字符

先来看一下字频

用subline将findme字符替换为空 得到一串字符N0waybackN0wayback
再看一下h4cked.py
from py2neo import Graph, NodeMatcher
graph = Graph("bolt://localhost:7687", auth=("neo4j", "We1c0Me_t0_NKCTF2024^^"))
graph.delete_all()
print('''
,------.
,-----. ,--. ,--. ,--. ,--. ,---. ,--. ,--. ,--. ,-----. ,-----. ,--. ' .--. '
' .--./ ,--,--.,--,--, | | | | | '--' | / | ,---.| |,-. | `.' |,--. ,--. ' .--./' .-. ' | | '--' _| |
| | ' ,-. || \ | | | | | .--. |/ ' || .--'| / | |'.'| | \ ' / | | | | | | | | .--' __'
' '--'\\ '-' || || | ' '-' ' | | | |'--| |\ `--.| \ \ | | | | \ ' ' '--'\' '-' '-.| '--.`---'
`-----' `--`--'`--''--' `-----' `--' `--' `--' `---'`--'`--' `--' `--'.-' / `-----' `-----'--'`-----'.---.
`---' '---'
''')
这串代码就是告诉我们 neo4j数据库的地址是bolt://localhost:7687 登陆的账密是neo4j/We1c0Me_t0_NKCTF2024^^
我们前面在火眼中恢复出来一个neo4j的压缩包 但是需要密码 我们使用上面通过findme.txt得到的字符串N0waybackN0wayback 进行解压
下面来学习一下neo4j数据库配置和安装 这边我们直接用从检材中提取出来的版本就行 由于是5.17.0的高版本 在启动数据库的时候 也需要高版本的java 我这里使用的是java17
我们用管理员权限打开cmd 输入neo4j.bat console 当看到如下回显的时候 说明数据库成功启动

访问http://localhost:7474就可以进入数据库界面 登陆的时候就用上面找到的账密

进入之后 我们点一下Character标签 发现有一些关于flag的字符

但是被CQL语句限制只显示25个字符 那我们先去掉这个限制 直接查看有Character标签的所有节点的值
MATCH (c:Character)
RETURN c;


可以得到完整的flag 但是不便于读取 用REDUCE函数处理一下这些值
MATCH (c:Character)
RETURN REDUCE(s = "", character IN COLLECT(c.value) | s + character) AS aggregated_value;

拿到flag
NKCTF{f05d9e24-0217-83e2-afa7-20e982b7e59f}
1z_F0r3ns1c5(等宽字体 内存取证 gimp docker容器 图片匹配)
本鼠鼠正在Coding,突然一声OPEN THE DOOR!本鼠鼠直接鼠躯一颤就双手抱头蹲下了,果然本鼠鼠只适合生活在阴暗的下水道
被黑猫警长抓走的时候本鼠鼠还想辩解一下,但是他们拿出你的照片的时候,本鼠鼠认罪了
昨晚和其他鼠鼠聊天的时候其他鼠鼠问本鼠鼠:“你到底喜欢她什么啊?”
“喜欢一个人不需要理由”
本鼠鼠很快敲完了键盘,刚要按下回车的时候突然愣住了。
真的不需要理由吗?
请找到鼠鼠的答案吧。
hint:.vscode中的配置文件有和flag3相关的信息
先看看给的附件 一个内存文件 一个压缩包里面有一张pass.png和一个secret文件 还有一个readme.txt
观察secret文件的大小 刚好是1MB 很工整的大小 猜测是一个VC容器 但是pass.png中的密码被打马赛克 只能看到最后一位是3
readme.txt中的内容 提到flag为3段 还提到等宽字体
本鼠鼠的flag总共分为三段捏,flag为nkctf{uuid}形式,另外鼠鼠最喜欢等宽字体了,快快去找吧。
首先用r-studio看一下关键文件 moe是主要登陆用户 找到了.vscode配置文件夹 桌面上有一个Secret_Generator 文件夹 是一个docker容器的配置文件

flag1
在来看看环境变量 找到一个名为n0wayback 值为HPahXR4NvAnZXB16tNK6hAaNVNU++的环境变量
.\volatility.exe -f E:\Desktop\NKCTF2024\forensics\1z_F0r3ns1c5\1.raw --profile=Win7SP1x64 envars

用随波逐流进行解码 发现是XXencode
nkctf{39c429eb-2faf
flag2
再来看进程
.\volatility.exe -f E:\Desktop\NKCTF2024\forensics\1z_F0r3ns1c5\1.raw --profile=Win7SP1x64 pslist
找到mspaint.exe进程

mspaint.exe是计算机中画图工具 有这个进程猜测肯定是在图片中存有信息 我们用memdump将这个进程dump下来
.\volatility.exe -f E:\Desktop\NKCTF2024\forensics\1z_F0r3ns1c5\1.raw --profile=Win7SP1x64 memdump -p 2052 -D E:\Desktop\NKCTF2024\forensics\1z_F0r3ns1c5
将保存下来的2052.dmp文件后缀改为.data 使用gimp打开 调一下宽高大概是1708*3468

flag2: 49a0-bd24-
flag3
现在我们来看上面找到的Secret_Generator
其实这一操作我们在cmd命令行记录中也能看到
.\volatility.exe -f E:\Desktop\NKCTF2024\forensics\1z_F0r3ns1c5\1.raw --profile=Win7SP1x64 cmdscan

我们可以在r-studio里面直接将这个文件夹恢复出来 看到是需要部署docker容器的
直接进入Secret_Generator\docker文件夹中 看到docker-compose.yml 我们可以使用这个文件利用docker-compose命令来配置docker容器中所需要的所有服务
安装docker-compose的命令
curl -L "https://github.com/docker/compose/releases/download/1.29.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
使用docker-compose命令部署docker容器
docker-compose build
docker-compose up
看到yml文件中的内容是暴露了8080端口 直接访问http://127.0.0.1:8080

看到页面需要我们上传密文和自定义字体

前面提到了的等宽字体 那估计这里需要我们提交的字体就是等宽字体了 搜了一下vscode中常见的等宽字体 有Fria Code Consolas Courier New 这里我们下一个Fria Code字体就行 在上传的时候随便上传一个Fria Code字体就行

这里就有点脑洞了 我们拿到的附件里面某段flag文件夹中 有个pass.png图片

我们随便上传一个字体和一段字符 发现会给我们生成一张图片

我们发现输入的是123456但是生成的图片中的内容多了一段pass 内容 和pass.png中的内容类似了 而且文件夹中的secret大小刚好为1MB 一眼VC容器 我们需要利用网页生成字典文件 通过对照将pass.png中的VC容器挂载密码恢复出来
直接在容器文件夹中找到生成图片的后台代码 在\app\app.py中
def generate_secret_image():
try:
secret = request.form.get('secret')
if not re.match("^[a-zA-Z0-9]+$", secret):
return jsonify({'error': 'Secret text can only contain letters and numbers.'}), 400
secret = 'pass ' + secret
font_file = request.files.get('font')
if font_file:
font_extension = font_file.filename.rsplit('.', 1)[1].lower()
if font_extension not in ALLOWED_FONT_TYPES:
return jsonify({'error': 'Invalid font file type. Only TTF, OTF, WOFF, WOFF2 files are allowed.'}), 400
if font_file.content_length > (10 * 1024 * 1024):
return jsonify({'error': 'The font file is too large!'}), 400
font_filename = str(uuid.uuid4()) + '.' + font_extension
font_path = save_font_file(font_file, font_filename)
font = ImageFont.truetype(font_path, 49, encoding='utf-8')
else:
return jsonify({'error': 'Please select a font file.'}), 400
H = 60
W = 30
canvas = Image.new('RGB', (W * len(secret), H), (255, 255, 255))
pen = ImageDraw.Draw(canvas)
pen.text((0, 0), secret, 'black', font)
original_canvas = canvas.copy()
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)
original_img_base64 = image_to_base64(original_canvas)
secret_img_base64 = image_to_base64(canvas)
return jsonify({'original_image': original_img_base64, 'secret_image': secret_img_base64})
except Exception as e:
print("An error occurred:", e)
return jsonify({'error': 'Internal Server Error'}), 500
其中关键的就是
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)
他读取了我们输入的内容作为secret的值 但是还在前面加了pass 同时将secret的内容从第五个字符到倒数第二个字符这部分内容打上马赛克
我们对代码进行修改 删掉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)
在尝试一下输入ABCDEFG 发现输出的图片上就只有ABCDEFG了 并且会全部被打上马赛克

下面就是生成字典图片 输入ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

然后我们将字典图片和pass.png都按字符进行分割 直接调gpt就行
帮我按照以下要求 写一段代码 我现在有一个1860*60的图片 请你帮我分割成30*60的小图片 放进dict文件夹中 命名规则按照如下顺序ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 一共是62张小图片 最后 如果文件名为大写字母则为 “文件名-大写.png”
from PIL import Image
import os
def split_image(image_path, output_folder, width=30, height=60):
image = Image.open(image_path)
image_width, image_height = image.size
if image_width % width != 0 or image_height % height != 0:
print("Error: Invalid dimensions for splitting.")
return
num_columns = image_width // width
num_rows = image_height // height
if not os.path.exists(output_folder):
os.makedirs(output_folder)
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
char_index = 0
for col in range(num_columns):
for row in range(num_rows):
left = col * width
upper = row * height
right = left + width
lower = upper + height
cropped_image = image.crop((left, upper, right, lower))
char = chars[char_index]
if char.isupper():
filename = f"{char}-大写.png"
else:
filename = f"{char}.png"
cropped_image.save(os.path.join(output_folder, filename))
char_index += 1
if __name__ == "__main__":
input_image_path = "dict2.png" # 某段flag/pass.png
output_folder = "dict1" # dict2
split_image(input_image_path, output_folder)
然后就是利用哈希计算和字典 匹配出pass.png中的密码
import os
import hashlib
def calculate_hashes(folder):
hash_dict = {}
for filename in os.listdir(folder):
filepath = os.path.join(folder, filename)
if os.path.isfile(filepath):
with open(filepath, 'rb') as f:
image_data = f.read()
hash_value = hashlib.md5(image_data).hexdigest()
hash_dict[filename[:-4:]] = hash_value
return hash_dict
def find_matching_images(dict1, dict2):
matches = []
for filename, hash_value in dict2.items():
if hash_value in dict1.values():
matching_filename = [name for name, hash_val in dict1.items() if hash_val == hash_value][0]
matches.append(matching_filename)
return matches
if __name__ == "__main__":
dict1_folder = "dict1"
dict2_folder = "dict2"
dict1_hashes = calculate_hashes(dict1_folder)
dict2_hashes = calculate_hashes(dict2_folder)
matching_images = find_matching_images(dict1_hashes, dict2_hashes)
print("Matching images found in dict1:")
for image in matching_images:
print(image)
'''
Matching images found in dict1:
b
1
4
3
a
6
2
6
8
e
2
a
2
3
'''
dict2中14个被打马赛克的字符匹配出来的结果是b143a6268e2a23 再加上原来没有被打马赛克的字符 VC容器的密码就是b143a6268e2a233 直接进行挂载 得到flag3
c4f222879312
组合起来就是
nkctf{39c429eb-2faf49a0-bd24-c4f222879312}
web
my first cms(CVE-2024-27622)
首先dirsearch扫目录 扫到后台登陆页面admin/login.php

抓个包开始爆破登陆密码

比较抽象是admin/Admin123 登陆后台
注意到这个CMSMadeSimple框架的版本是2.2.19 搜一波漏洞

直接进行利用

先抓<?php echo system('id'); ?>测试一下


成功回显 再看一下根目录
<?php system("ls /"); ?>

读取flag
<?php system("cat /_fffff1@g"); ?>

