2025.3.22NCTF
2025.3.22NCTF
是 2025.3.22NCTF 哒!
爆0了…所以给出的我自己的脚本有些是不会与靶机交互的,但最后会给出官方的wp才不是我懒得写
绮云
花灯百盏遥遥一线牵
1 |
|
题目允许我们:1.翻转e的任意一个比特位然后进行加密,返回加密结果;2.检查椭圆曲线的签名,正确就给flag。而椭圆曲线的d的4次方就是RSA的d。
我们需要通过明文和密文来推出e和n,进而推出d,再进行轻度的签名爆破即可。
首先要获取RSA的n。我是在lazzaro的博客里找到了这个
选择明/密文攻击
让服务器加密明文P
,得到结果C1
,再加密明文P**2
,得到结果C2
,那么就能得到C1**2-C2=kn
,再对另一个明文P2
重复操作即可得到k'n
,对这两个结果求GCD即可得到n,可能会有一些小素数因子存在,去掉即可。
由题可知GCD(e,(p-1)*(q-1))=1
,所以e一定是奇数,那么e的最低比特位一定是1。
我们就可以翻转最低比特位,再对2进行加密,返回的结果一定是2**(e-1)%n
,结果再乘2模n即是2**e%n
,我们就得到了未翻转任何比特位的加密结果。
之后,我们再继续翻转第k位比特位,把加密结果乘2**(1<<k)
再模n,这个操作相当于把e的第k位加1。
如果这个比特位翻转前是1,那么翻转后就是0,加1后又变回了1,也就是说会得到未翻转比特位的e的加密结果。
所以,当我们把加密结果乘以2**(1<<k)
后再模n,如果密文与前面得到的未翻转任何比特位的加密结果相同,说明这个比特位是1,反之是0
这里给出我比赛时用的得到n和e的脚本。
1 |
|
得到n,e后就不会了…
看了下wp是用格的,而且需要10组n,e,也就是说交互时间是非常长的。
wp的解释:
这是我复现的脚本:
1 |
|
1 |
|
成功得到d。
之后用题目中的sign函数对随机一个byte签名即可,然后尝试verify,直到靶机随机到你签名的那个byte。
官方wp:
1 |
|
Sign
这是恢复互联网的密钥,密码是实时生成的三万个随机数。
srv.py:
1 |
|
util.py:
1 |
|
题目的意思很简单,恢复字符串string,然后解密AES即可。
通过观察FHE的encrypt函数,我们不难得出msg=result%p%256
result%p
能得到getrandint(17) << 8+m
,而getrandint(17) << 8
的低8位正好是0,%256
即可得到msg。
求出mag后我们就可以恢复s[4:]
的值了,并且因为这20000bits是通过random模块生成的,random模块采用的是MT19937作为伪随机数生成器的,所以我们可以逆向MT19937来获得s[:4]
得到了s[:4]
后我们就可以恢复string了,具体细节之后再说
所以问题就变成了如何得到p。
嗯…比赛时卡这了,所以来看wp吧~
这是一个求近似最大公约数问题(AGCD/Approximate Greatest Common Divisor)。
所以,上格。
1 |
|
我们通过构造上面的格就可以还原出q0,进而求出p。
值得一提的是求出来的q0需要abs一下,因为LLL求出来的是最短向量,是有可能为负的,相比之下BKZ求出负值的概率就小一点了,但依旧存在。以及格中的短向量2**(ρ+1)
的值在很大程度上并不影响结果,取1都可以。
这里给出我复现时的求p的脚本:
1 |
|
求出p后我们就可以得到s[4:]
接下来需要求出s[:4]
这里涉及到random生成固定位数的随机数的方式
下面的拼接都是以16进制字符串的形式进行的拼接,不是加法
对于32位以下的随机数,比如getrandbits(16)
,random会先生成32位的随机数然后截断,只取前16位然后return
对于32位以上的随机数,比如getrandbits(64)
,random会生成2个32位是随机数,再进行拼接,后生成的随机数拼接在先生成的随机数的前面,即后生成的32位随机数+先生成的32位随机数
,对于不是32的倍数的bits的随机数,比如getrandbits(80)
,random会先按前面的方法生成64位随机数,再按32位一下的随机数的生成方法来生成16位的随机数,后生成的永远排在先生成的前面,即截断成16位的第3次生成的随机数+32位的第2次生成的随机数+32位的第1次生成的随机数
所以,得到s[:4]
的方法很明确了,通过s[4:]
来逆向MT19937,然后再生成一个32位的随机数就是s[:4]
了。
我复现的时候就是没弄清楚然后调了好久,一直以为s[:4]
是最先生成的32位随机数,还找了相关的脚本,这里顺便也贴一下:
1 |
|
这里提一下,random每生成624个随机数后就会调用一次twist来更新内部的state,也就是说我们向前面逆向每624个随机数就要调用一次backtrace_untwist。不明白的可以自己尝试一下,这里不多说了。
好的,我们回到题目中去。
刚刚我们做到了逆向MT19937这一步,接下来我们需要的就是用s[4:]
来逆向获取MT19937的内部状态,然后再根据这个来得到下一个32位的随机数,就是s[:4]
,逆向MT19937的方法我目前知道2个,一个是用randcrack这个库,你只需要提交624个32位的随机数,然后调用predic_getrandbits(32)
即可,挺方便的。另一个就是用和上面差不多的脚本,只不过就是不需要调用backtrace_untwist来转换状态了。把exp_mt19937返回的state直接传入r.setstate里即可。
这里给出我复现时的脚本。
1 |
|
官方wp(官方wp里没有对求出来的q取abs):
1 |
|
Arcahv
Delightful.
1 |
|
很麻烦,感觉很麻烦。感觉可以分成两道题了。
wp里也说了这是大杂烩,你能看到RSA LSB Orcale,LCG,Coppersmith。
这题比赛时也是只有大致的思路,具体攻击不会。
题目大致可以分成2部分,第一部分的RSA,通过RSA LSB Orcale来获得r1.p。
第二部分就是LCG,LCG生成的数的16进制的长度基本可以认为长度是256,有可能会出现255长度的情况,但只要多试一下就可以了。得到数后就是套公式了。
最后解个RSA就可以得到flag了
那么这题主要就是第一部分的RSA LSB Orcale了。
这个我复现的时候也是想了很久,有些地方wp里没有指出来。先把wp放出来。
一点点来看吧。
首先介绍一下乘法同态,说白了就是你将2个密文相乘,再解密,明文也会是2个明文相乘。类似的还有加法同态,只是把乘法换成了加法,你将2个密文相加,再解密,明文也会是2个明文相加。同时具有加法同态和乘法同态的,我们称之为全同态,也就是上面的FHE。
很明显,RSA具有乘法同态,这个稍微推一下就能出来。
那么我们就可以通过改变密文来使解出来的明文乘以一个确定的数。
如果我们将密文乘C*e,那么解出来的明文将会是原来的C倍。
因为这题我们可以得到明文mod 256的结果(256位以上的都被crystal_trick改变了,没办法得到),所以我们取C为256。
因为m是小于n的,所以我们可以得到256m%256=0,这个在C较小的时候是恒成立的。但当C较大时,也就是说存在一个数a,C=256**a
,使得m*C>n
,那么这时候就存在等式m'=C*m-k*n
(m’是更改密文后解出来的明文的值),其中k是正数。我们只能得到模256下的值,所以我们对两边模256,得到m'=-k*n (mod 256)
,易得k=-m'*n**(-1) (mod 256)
同时我们要明确一点,这里C=256**a
的a是使得C*m>n
的最小的a,也就是说k不会很大,我们可以认为k<256
,也就是说我们可以认为在模256下求出来的k就是k。
所以,我们可以回到最开始的等式C*m=m'+k*n
,m'<n
,那么C*m
就在k*n
和(k+1)*n
之间。即k*n/256**a<m<(k+1)*n/256**a
,我们就得到了m的一个范围。
我们令a=a+1
,即密文再乘一个256**e
,即在m'=C*m-k*n
两边再乘以一个256,我们可以得到256*m'=256*C*m-256*k*n
,其中256*m'
是有可能大于n的,而解密出来m''
是小于n的,有等式m''=256*m'-k2*n
,m''+k2*n=256*m'
,代回去得到m''+k2*n=256*C*m-256*k1*n
,m''=256**(a+1)*m-256*k1*n-k2*n
,m''=256**(a+1)*m-(256*k1+k2)*n
,重复上述步骤后能得到256*k1+k2 mod 256
的值,即k2,k1前面已经得到,所以我们可以再次缩小m的范围,(256*k1+k2)*n/256**a<m<((256*k1+k2)+1)*n/256**a
。这样每次都能把m的范围缩小1/256
,最后我们可以得到一个足够小的范围,其中左边界和右边界的高600位是相同的,也就是说我们获得了r1.p
的高600位信息,在后面LCG里得到了n后用coppersmith可以很容易求出r1.p
。
下面我重新整理一下推理的过程
1 |
|
这里给出我复现的脚本:
1 |
|
1 |
|
1 |
|
上面我只是假设已经得到n了。
所以接下来我们来逆向LCG来得到n
自己稍微改改源码把LCG的5个数的hex给print出来,你会发现在大部分情况下的长度都是256,只有少数情况下会遇到长度是255的,遇到这种情况直接重新获取一下就好了。之后就是套公式恢复参数了。
这里直接贴wp了
得到了n后就可以用上面的coppersmith解出p,然后解RSA1就可以了。
官方wp:
1 |
|
至此,三到题已经全部复现。剩下还有三题应该不会去复现了,我也没有去做过。
总得来说这次的NCTF的强度还是有点高的,内容也是非常的多。
那么就这样吧。