NCTF25Wp
NCTF 2025 WP by 0x5593e78d
0.Introduction
周末简单看了看NCTF的三个密码题,感觉这三个题其实不是很难
AES 的注入攻击根本看不懂,为啥Crypto开始写ShellCode了??后面有空再学吧。。。
1.Sign
看一眼题目:
1 |
|
util.py
1 |
|
看到FHE,貌似是一个全同态加密。对于单个字符 $m$:题目先生成了 $20000$ 个随机比特,也就是 $2500$ 随机字节。然后用同一个FHE对象进行加密。加密方式也很简单:随机生成 $16$ 个生成 $Ap+256B$,然后进行未知的变换,最后直接加上 $m$ 就OK了。
注意到下面的内容:
1 |
|
虽然我们不知道如何变换的,但 $X(Ap+256B)+Y(Cp+256D)$ 经过未知变换后,仍然具有 $Ap+256B$ 的形式。由于 $p$ 是 $77$ 比特,$A$ 是 $177$ 比特,$B$ 是 $17$ 比特。那么 $256B$ 就是 $25$ 比特。可以发现这个误差并不是很大,考虑 AGCD。
因此对于一组 $2500$ 个数据来说,可以取前 $120$ 个数据,构造(以四个为例,实际上最后取了 $120$ 个,4 个应该不够,这里是为了举例):
这样就有
$$
(A_0,A_1,A_2,A_3)\mathbf M= (A_02^{25},A_0B_1-A_1B_0,A_0B_2-A_2B_0,A_0B_3-A_3B_0)
$$
那么 $A_0$ 的值就有了,直接整除就可以得到 模数 $q$,然后就能够得到 $2500$ 个明文字节了。
得到加密内容后,我们发现 getrandbits(20000)
转成字节后,用于处理的是前 4 个字节,那么我们得到的真实字节是 $2496$ 个。这样恰好构成 $19968$ 比特。因此可以直接调工具(工具我自己写的,细看21年7月10日博文)解决。
解决后往后预测一组数据,简单算一下,就能够拿到一个 $m$ 字节了。题目给了 $30000$ 个数据,说明加密的字节数为 $12$。
exp:
1 |
|
2.Arcahv
套娃题X1
1 |
|
题目给了两个RSA系统:第一个RSA系统正常,第二个RSA系统加密时返回的内容是反着的。并且测试一下可以发现:解密时只会正确地返回第一个字符(相当于明文的低位)。
我们已知的内容是:R2的公钥、flag用R1加密的密文、R1的素数 $p$ 用 R2 加密的结果,以及 R1 的模数 $n$ 用 AES 加密的结果。。
那么肯定是先解处 R1 的模数 $n_1$:根据LCG的生成方式,如果每个LCG的最高那位十六进制位不为 $0$ (指的是写成 $256$ 位十六进制下),那么我们就可以运行 $80$ 次得到 LCG 的连续五个 state $a_0,a_1,a_2,a_3,a_4$。那么简单推下式子:设 $T_i=a_{i+1}-a_{i}$,那么有
$$
T_{1+i}\equiv aT_{i-1} \pmod p
$$
那么连续三个 $T_i$ 就可以得到 $p$:
$$
T_1^2-T_0T_2\equiv A^2T_0^2-T_0AT_1\equiv A^2T_0^2-A^2T_0^2\equiv 0\pmod p
$$
我们可以得到两组这样的式子,那么求一下最大公约数就可以得到 $p$ 了。如果验证结果不是素数,那么简单试除一下也可以。
1 |
|
求出 $p,A,B$ 后,由于加密密钥不超过 $2^{256}$,而模数是 $2^{1024}$ 级别的素数,那么直接逆回去,找到一个不超过 $2^{256}$ 的值就OK了。我们就得到了 $n_1$。
然后求 $p_1$ 的过程就很繁琐了:首先我们要将得到的内容转化成 bytes,倒过来再 bytes_to_long
,才能够得到实际的密文值。由于我们每次进行解密,只知道明文的第一字节,也就是实际明文的低位。这很明显是一个 ParityOracle问题,我们给 $c$ 乘上 ${256}e$,那么解密回来就是 $256m$。由于存在取模,因此最低一字节并非 $\mathtt{00H}$,而是要看到 $256m$ 整除 $n_2$ 的值。如果这个值是 $0$,那么会返回 $\mathtt{00H}$,如果这个值是 $k$,那么会返回 $-n_2k\mod 256$ 的值。 假如 $n_2\equiv 83\pmod {256}$,当服务器返回最最低字节为 $\mathtt{ADH}$ 时,也就是 $173\equiv -83\pmod {256}$,说明明文的值位于 $\left[\dfrac{n}{256},\dfrac{2n}{256}\right]$ 中。
当然,如果举个模 $5$ 的例子可能更方便些(猜猜这个表格在我博客中的哪里能找到)
然而,测了第一次,发现 $75$ 次机会除了第一次外,全都返回了 $00$。因为模数 $n_2$ 是 $2048$ 位,而 $p_1$ 是 $1024$ 位。那么直接从 $256^{125e}c$ 开始测!这里先给出到此为止的exp。该exp结束后,不再需要交互,后面转手工。
1 |
|
上面的运行结果:
然后直接开始爆破范围(注意!第一个2f
不能用)
1 |
|
上面的运行完了,调试可以发现 $R-L$ 还有 $464$ 比特差距。好在这个差距小于 $512$ 了,因此相当于 $p_1$ 高位已知。直接flatter求解。
1 |
|
最后十六进制果然是以 $\mathtt{2FH}$ 结尾,说明求解正确。然后直接解密就OK。
1 |
|
3.Qiyun
有人在这个题浪费了两坤时,我不说是谁。
1 |
|
题目给出了一个ECDSA和一个RSA,其中 ECDSA 的私钥为 $232$ 位长度的 $d$,RSA的私钥为 $d^4$,长度为 $928$ 位。题目只有两个选择:一是生成一个 RSA 系统并只有加密服务,二是验签。
既然要求 $d$,那就要考察 RSA 这边。但 RSA 这边 $e,n$ 都不给。不过RSA可以选择在加密时选择一个比特进行翻转,并给出这个比特翻转后的结果。
1 |
|
既然要求 $n$,那很显然,$e$ 肯定时奇数,并且题目没有说只能发送正数过去,那就发一个 $-1$ 过去,在第 $9999$ 位翻转,那得到的值就是 $-1$ 的奇数次方模 $n$ 的值,也就是 $n-1$。 那么 $n$ 我们就有了。。。
然后,我们可以发送 $2,0$ 过去。由于 $e$ 是奇数,因此 $e\oplus1=e-1$,我们就得到了 $2^{e-1}$ 次方,乘个 $2$ 取余数就得到了 $2^e$ 次方。
那么后面就是一比特一比特的爆破了。 我们可以计算 $2^{e\oplus2^k}$ 和 $2^e$ 对比。如果 $2^{e\oplus2^k}\times 2^k\equiv 2^e\pmod n$,那么说明 $e\oplus 2^k=e-2^k$,这一比特为 $1$。如果是 $2^{e\oplus2^k}\times 2^{-k}\equiv 2^e\pmod n$,那么这一比特就是 $0$ 了。这样我们就逐比特爆破得到了 $e$,交互时长大概五分钟。
如果我们退出,然后重新进去,服务器就会生成一组不同的数据,但私钥还是 $d^4$。这就说明这是个很明显的共私钥指数攻击。算一下界:
$$
\frac{1}{2}-\frac{1}{2(r+2)}>\frac{928}{2048}\rightarrow r>8.67
$$
因此取 $9$ 组数据就OK了,当然这里为了保险,取了 $10$ 组。构造矩阵LLL就能够拿到 $d^4$ 了,然后开根就是 $d$。
这样交互就是 $50$ 分钟 ,本地测一次也要 $20$ 分钟,调了我一下午。。。
有了 $d$ 之后,ECDSA 就可以验签了。然而这边也是随机的。也需要交互好几次(是为了防止我这种懒人最后一步手动操作吗)。。。
exp(注意ECDSA虽然是复制的题目中的,但有两处有微小改动,已经标明):
1 |
|
运行结果: