LilCTF 2025 Writeup(复现)
当时跟 sekai CTF 时间有点近,所以没打,这几天没什么事做一下题看看(绝对不是被 sekai CTF 鞭打了回来找找自信٩(ŏ﹏ŏ、)۶
Crypto
ez_math
题目:
1 | |
分析:
我们已知 A 向量,为了后续方便运算,我们写成列向量的形式:
$$
A =
\begin{bmatrix}
v_1 \newline
v_2
\end{bmatrix}
$$
我们根据 B 的生成方式,可以将其进行分解成两个矩阵相乘的形式:
$$
B =
\begin{bmatrix}
v_1\lambda_1 \newline
v_2\lambda_2
\end{bmatrix}
=
\begin{bmatrix}
\lambda_1 & 0 \newline
0 & \lambda_2
\end{bmatrix}
\begin{bmatrix}
v_1 \newline
v_2
\end{bmatrix}
=
\begin{bmatrix}
\lambda_1 & 0 \newline
0 & \lambda_2
\end{bmatrix}
A
$$
然后再观察生成的 C,可以发现貌似找到了思路:
$$
C = A^{-1}B=A^{-1}
\begin{bmatrix}
\lambda_1 & 0 \newline
0 & \lambda_2
\end{bmatrix}
A
$$
我们设
$$
D=
\begin{bmatrix}
\lambda_1 & 0 \newline
0 & \lambda_2
\end{bmatrix}
$$
上述的分析说明,C 和 D 矩阵相似,由相似矩阵的特性可知其特征值一样,而 D 的特征值很容易得到为 $\lambda_1,\lambda_2$,那么说明 C 的特征值也是 $\lambda_1,\lambda_2$。
那么直接求C的特征值便可以得到 $\lambda_1,\lambda_2$,再转换为 ASCII 字符便能获取到 flag。
解答:
1 | |
mid_math
题目:
1 | |
分析:
本题和上边题目还是比较相似的,只是多了一步求解离散对数问题。
根据分析,可以发现矩阵 C 相似于一个对角矩阵:
$$
\begin{bmatrix}
a & 0 & 0 & 0 & 0 \newline
0 & b & 0 & 0 & 0 \newline
0 & 0 & c & 0 & 0 \newline
0 & 0 & 0 & d & 0 \newline
0 & 0 & 0 & 0 & 0
\end{bmatrix}
$$
其特征值为 $a,b,c,d,0$ 那么由$D = C^{key}$,则 D 对应的特征值为 $a^{key},b^{key},c^{key},d^{key},0 \mod p$
在求出特征值后,我们便可以通过求解离散对数问题获取 key。
之后便可以获取flag
解答:
1 | |
Linear
题目:
1 | |
分析:
已知有
$$
A\vec{x} = \vec{b}
$$
设 $y=[x_1,x_2,,,x_n,1]$ 可以转换为
$$
\begin{bmatrix}
A \newline
-\vec{b}
\end{bmatrix}
*y=0
$$
说明 y 存在于该增广矩阵的右核空间中,由于 $\vec{x}$ 中都是整数,而且都是比较小的数,因此可以使用 LLL 在候选的 y 中规约出最短向量
解答:
1 | |
Space Travel
题目:
1 | |
考点:
1 | |
分析:
这题是鸡块师傅出的题,虽然简短,但十分有意思,由于我最近事情有点多,为了快点把前边的缺的题目看完,因此这里也是懒得写脚本了,直接将想法告诉 ai 就能直接出(太罪恶了,以后一定会好好写脚本的呜呜呜。。
那么就进入正题吧,这题看得出来是在考察线性代数,整个加密逻辑可以理解为在 $GF(2)$下:
$$
A_{600\times 800}key_{800\times 1} = b_{800\times 1}
$$
可以看出,如果把上式当作线性方程组求解的话,key 中未知的位数有 800 bits,而我们总共有600个方程组,因此该方程组是欠定的,我们直接求解的话,会存在 200 bits 的解空间,通常在没有优化方法的情况下高于 30 bits 我们就不考虑爆破了,因此解题的关键不在于此。
再观察给出的附件,我们会发现有个附件我们似乎还没用上,那就是 params.py,它给出了用来生成 key 的列表 vecs,我们查看一下列表长度会发现,列表长度并不是我们下意识以为的 65536($2^{16}$),而是 4096($2^{12}$),貌似发现了关键所在。
询问 ai 后,ai 猜测存在仿射子空间,仔细一想,确实如此啊(因为 vecs 中不存在子向量,说明它不是线性子空间),key 并不是在 16 维向量空间中生成的,而是在 12 维的子空间生成的,也就是说,其实我们可以将 key 映射到其仿射子空间中,这样的话 key 的未知位数就能从 50×16=800 bits 变为 50×12=600bits 刚好能够求解,说明我们的方向是正确的。
想要将其映射到仿射子空间,我们要知道变换过程中的线性子空间的一组基向量和偏移量:
- $v(偏移量):$ 由于偏移量就是线性子空间向仿射子空间的偏移,且线性子空间中存在零向量,因此我们只要随意取仿射子空间中的一个向量作为偏移量即可,通常为了方便,我们会取第一个向量作为偏移量,即
vecs[0]。 - $L(基向量):$ 我们可以通过得到一组线性子空间的向量(如本题的 4096 个向量),然后通过GF(2)下高斯消元即可得到一组基向量(12 个)。
在得到这些值之后,我们便可以将 key 映射到 仿射子空间上:
$$
key_{1 \times 16} = x_{1 \times 12} L_{12\times 16} + v_{1 \times 16}
$$
注意,我这里只是单独列举了其中一个组,共有 50 组,每个 key 都要进行这样的变换。
代码:
ohh,由于我比较懒,ai 写的代码也比较乱,太难看了(不想拿出来丢人了,如果有需要请直接看鸡块师傅的官方exp(2025-8-wp-crypto | 糖醋小鸡块的blog),非常的简洁明了
[WARM UP] 对称!Just Decrypt
题目:
1 | |
分析:
确实是签到题,也是非常的 easy,我们首先能够明显看出来 key 的值是固定的(因为用来生成的随机数种子固定
然后我们发现 iv 的值也非常的可疑,因为他是 FLAG 的 10~25 字节的值,而我们指导 FLAG 的开头是 LILCTF{,也就是说我们已经知道了前 7 个字节,而我们结合 CBC 加密模式,能够得到 FLAG 前 16 个字节和 iv 的异或值,因此首先我们能够直接得到 iv 的前 7 个字节的值,也就是 FLAG 中10~16 字节的值,虽然 8,9 位置的 FLAG 字符还未知,但仅有两个字符,我们可以直接进行爆破,便能得到完整的 iv ,之后就能直接对 AES 进行 CBC 模式的解密了。
脚本:
1 | |
baaaaaag
题目:
1 | |
分析:
看完题目,可以发现是非常原始的背包加密系统,直接造格打 LLL 会发现出不了结果,但是使用 BKZ,然后 block_size 设置的稍微大一点就能出。
脚本:
1 | |
小结
至此,复现已完,虽然整体难度不大,但是还是有许多收获的,比如相似矩阵特征值相等、仿射子空间等题目之前并没有怎么接触过,补一补线性代数知识也是好事,不至于成为彻头彻底的脚本小子(虽然打板子也重要
也深刻认识到了如今 ai 对密码造成了非常大的影响,许多题目都能被 ai 一把梭,看到许多其他方向选手也能使用 ai 来 ak 密码题目,认真分析写脚本的纯粹密码手也越来越少(虽然我有时也偷懒,但我并不认可这种行为。。。
往后,密码手将何去何从啊