H&NCTF 2025

H&NCTF 2025
AristoreMisc
星辉骑士
把后缀.docx
改为.zip
后解压缩
在 word/media
下找到 flag.zip
伪加密修复后解压,垃圾邮件加密 spammimic - decoded 解密 999.txt
得到 flag{0231265452-you-kn*w-spanmimic}
1 | H&NCTF{0231265452-you-kn*w-spanmimic} |
谁动了黑线?
附件 sheidongleheixian.csv
的部分数据如下:
1 | from_address,to_address,amount_sol,timestamp,tx_hash |
不难发现这是区块链交易数据,其中 tx_hash
还经过了 base58 编码,下面先对 tx_hash
列进行 base58 解码并将结果添加到一个新列 decoded_tx_hash
中,然后保存到文件 sheidongleheixian_decoded.csv
:
1 | import csv |
得到的部分数据如下:
1 | from_address,to_address,amount_sol,timestamp,tx_hash,decoded_tx_hash |
下面根据转出地址 (from_address) 和转入地址 (to_address) 来重建资金的流动路径:
1 | import pandas as pd |
将运行结果保存到 output.txt
中,得到部分数据如下:
1 | 成功加载 7030 条交易记录。 |
观察到第 2 条链有一个 decoded_tx_hash
不对劲,tx000002litt7H6R
里面包含小写字母,其他的 decoded_tx_hash
都是大写字母(当然 tx 是除外的)
并且里面的 litt
看起来像是 little
的前半部分,在 sheidongleheixian_decoded.csv
中搜索 le
发现了 tx000014le_dWAHC
那么接下来只要找出带小写字母的然后把它们拼起来就可以了
1 | import pandas as pd |
得到的输出如下:
1 | 286 tx001890mr!!ZIU4 |
1 | H&NCTF{little_dog_is_Aomr!!} |
Forensics
ez_game
对附件的磁盘镜像 challenge.vhd
进行分析,找到关键信息 readme.txt
,key.jpg
,hhhh
,hhhh.zip
其中 readme.txt
的内容如下:
1 | 这次是个简单的取证小游戏 |
首先第 1 条的 “藏了一个电脑” 指的是容器 hhhh
里的 CentOS 7 镜像(这个要靠第 2 条才能解出来),吐槽一下这里很简单的弱密码
指的是系统的密码,不过都拿到镜像文件了这个提示就没啥用
第 2 条想表达的是,key.jpg
就是 VeraCrypt 容器 hhhh
的密码,解开并挂载之后就能对里面的系统镜像进行分析了
第 3 条就是字面意思
在系统的 /root/test
目录下发现了文件 hhhh.txt
,内容如下:
1 | ·1234567890-= |
这里存在零宽字符隐写
得到提示 shift
,按住 shift 键把键盘上面那一排从左到右按一遍就行,得到:
1 | ~!@#$%^&*()_+ |
这个就是加密压缩包 hhhh.zip
的解压密码,这个压缩包被删除了,就是 $RQCSJAK.zip
解压得到 flag.drawio
,用官网提供的在线工具 draw.io 打开查看
1 | H&NCTF{YOU_R_SSSO_COOL} |
OSINT
Chasing Freedom 1
查询图片 exif 得知是 5 月 3 日
图片上的渔船有名字闽平渔65599
船位查询查询发现该渔船位置
1 | H&NCTF{0503-丁鼻垄} |
Chasing Freedom 2
查询图片 exif 得知是 5 月 4 日
识图搜索图中的黑白灯塔
1 | H&NCTF{0504-东庠岛灯塔} |
Chasing Freedom 3
查询图片 exif 得知是 5 月 34 日
图片下方的水桶写着岚庠渡
搜索发现这篇文章等一刻轮渡,过一天属于岛屿的生活 - 东庠岛生活画册_码头_海面_渔船
得到两个信息:位于流水码头,船的名字是岚庠渡 x 号
由于不知道具体是几号,从 1 号开始试,最终试出来是 3 号
1 | H&NCTF{0504-流水码头-岚庠渡3号} |
Crypto
哈基 coke
1 | import numpy as np |
猫变换
1 | H&NCTF{haji_coke_you_win} |
Reverse
签到 re
Gemini 一把梭
代码分析:
main
函数:- 程序使用硬编码的密钥
"MySecretKey123!"
。 - 调用
sub_11B9
函数,基于该密钥生成一个 32 位的加密密钥v5
。 - 读取用户输入
s
。 - 调用
sub_1452
函数,使用v5
对用户输入s
进行加密。 - 将加密后的结果与
byte_4080
的内容逐字节比较。如果完全相同,则输出 “right”。
- 程序使用硬编码的密钥
sub_11B9
函数:- 计算
"MySecretKey123!"
的 SHA256 哈希值。 - 取哈希值的前 4 个字节,记为
d0, d1, d2, d3
。 - 通过位运算生成最终的 4 字节加密密钥
k = [k0, k1, k2, k3]
:k0 = d0 | 1
k1 = d1 & 0xFE
k2 = d2 & 0xFE
k3 = d3 | 1
- 这个过程确保了
k0
和k3
是奇数,而k1
和k2
是偶数。
- 计算
sub_13AC
和sub_1452
函数:sub_1452
是主加密函数。它将输入数据填充到 4 字节的倍数,然后以 4 字节为一块进行处理。- 每一块 4 字节的明文被分成两个 2 字节的块。
sub_13AC
是核心的加密算法。它对每个 2 字节的块进行操作。其运算本质上是一个模 256 的 2x2 矩阵乘法:其中
(p0, p1)
是 2 字节的明文,(c0, c1)
是加密后的 2 字节密文,k
是从sub_11B9
得到的 4 字节密钥。
解题思路:
要得到 flag,我们需要执行以下步骤:
- 重新生成加密密钥: 完全按照
sub_11B9
的逻辑,从"MySecretKey123!"
生成 4 字节的密钥矩阵K
。 - 求逆矩阵: 为了解密,我们需要找到加密矩阵
K
在模 256 下的逆矩阵K_inv
。解密操作就是P = K_inv * C mod 256
。- 求矩阵的行列式:
det(K) = (k0*k3 - k1*k2) mod 256
。 - 求行列式的模逆元:
det(K)^-1 mod 256
。 - 计算逆矩阵:
K_inv = det(K)^-1 * [[k3, -k1], [-k2, k0]] mod 256
。
- 求矩阵的行列式:
- 解密数据:
- 从
byte_4080
中提取密文数据(从第 5 个字节开始)。 - 将密文数据以 4 字节为单位进行分割。
- 将每个 4 字节的块再分成两个 2 字节的块。
- 使用求得的逆矩阵
K_inv
对每个 2 字节的密文块进行解密。 - 将解密后的数据拼接起来。
- 从
- 提取 Flag:
byte_4080
的前 4 个字节(00 00 00 25)
指明了原始明文的长度是 37 字节。从解密后的数据中截取前 37 个字节,即为最终的 flag。
1 | import hashlib |
运行结果
1 | 🔑 生成的加密密钥 (k0,k1,k2,k3): [81, 22, 52, 251] |
1 | H&NCTF{840584fb08a26f01c471054628e451} |