这次crypto题目总体难度不大,重点是对一些基础知识的理解运用。
EzRSA
题目:
1 | from Crypto.Util.number import * |
有以下信息:
- m.bit_length()<200 , 说明明文较小
- kbits = 103 , m = (m >> kbits) << kbits , 隐藏了明文低位
- hint1 = (2021-2023*m) % Mod
- hint2 = pow(2, 2023, Mod)
种种都指向coppersmith , 首先看hint2,
利用同余关系,
得到Mod的k倍,因此可以利用k*Mod建立环,解出hint1中的小根m,解得m高位后已知高位攻击即可。
exp.ipynb:
1 | from Crypto.Util.number import * |
得到flag:
NSSCTF{Rea1_Si9n3n}(赛中的时候,这题解数比funnyencrypt还多,当时就感觉有点诡异。赛后才发现因为明密文都很小,所以直接开三次根就可以了。。)
FunnyEncrypt
题目:
1 | ✧✡✭ |
见过的图形加密中并没有类似这个的,不过翻看一下马上就能发现文件尾部的这一串:
1 | ✮✧✧✩✣✤{✩✢✵✷✣✬_✡✧_✧✬_✡✮✣✯✢✯✧✣✡✮✰_★✴✵_✱✬✮'✣_✵✬✳_✫✬✡✮_✳✧} |
前缀肯定是nssctf,是对的上的,猜测是简单的替换密码,写个脚本后交给quipqiup即可
exp.py:
1 | c = """ |
得到flag:
NSSCTF{crypto_is_so_interesting_why_don't_you_join_us}(前缀居然要大写,这就有点坑了。。)
Math
题目:
1 | from secret import flag |
题目将flag分为两部分分别进行加密,分开来说:
- 第一部分
给了p关于q的逆元及q关于p的逆元,hitconctf 2019 quals出过这个题目,具体推导过程参考这一篇](https://aidaip.github.io/ctf/2019/10/16/HITCON-CTF-2019-Quals-Writeup.html))
- 第二部分
已知:
这种题目显然是构造出p或q的倍数,从而与n求gcd得到分解的。对于这个等式,很容易就能想到利用同余性质先化为一下两个等式:
乍一看应该是第二个等式更加好用,因为可以利用费马小定理消去指数,变形为:
但是这里就卡壳了,因为即使利用同余性质把模等式转化为等式,得到的依然含有p,q两个因子,没有办法与n求gcd。
所以考虑利用另一个等式,由于指数q没有办法消掉了,所以只能利用二项式定理展开。又由于mod p的关系,模等式正好只剩下了最后一项,即:
怎么利用这个等式呢?这个时候需要敏锐一点察觉到费马小定理(也许刚刚拆分出来的另一个式子的变形就是给我们的提示),由费马小定理我们知道:
把 $114514^q$ 看作a,就得到:
所以有:
也就是说,将$(hint - 114514^n)$与n求gcd,即可得到p,进而求解RSA
exp.py:
1 | from Crypto.Util.number import * |
得到flag:
NSSCTF{e713afa4-fcd8-419f-a1a6-959449b4df5a}LatticeLCG
题目:
1 | from Crypto.Util.number import * |
卡了很久,搜了很多格相关问题也没有搜到类似的,打算放弃这个题去睡觉的时候,突然恍然大悟。这里好好阐述一下我的思路,希望能帮助到一些和我一样刚刚接触格的ctfer。
首先,题目很明显的分成了三个部分:
- 共模攻击
- LCG求参数
- Lattice
首先要明确求解顺序。flag以LCG中参数b的形式存在,因此LCG应该是题目的最后一步。并且要想求解出这个LCG的b,是需要知道a与n的值的,在第一部共模攻击中显然是已知n求解a。所以就明确了如下的解题顺序:
- Lattice求解模数n
- 共模攻击求解a
- LCG恢复参数b,得到flag
后面两个步骤非常容易,主要问题就在第一步:为什么要利用格求解n? 怎么用格求解n?下面是我对这个问题的分析:
先来看看给了些什么条件:一个64bit的小量m,依次产生20个128bit的素数对其进行类似RSA的加密,并且给了我们加密指数的列表以及密文的列表。题目满足两个经典条件:存在小量 ,提供多个方程组参数,这样的问题在很多crypto题目中都是用格方法求解的,所以要想到利用格方法(题目的名字虽然说得很明白,但是如果没有,看到这种形式也应该联想到这个方法)
注意到m不变,模数n也不变,同时加密指数互素,这其实很像共模攻击的情景,只是n未知。回想一下在已知模数n的情况下共模攻击的实施方法,不难产生下面这个解题思路:
取20个方程的前三个如下:
因为e1,e2互素,所以存在a,b,使得:
所以可以得到:
这有什么用呢?我们同样也对2、3两式,1、3两式进行这样的操作,结合上面这个式子能得到三组模等式:
1、2式作差,2、3式作差,就得到:
而现在等式左侧已经没有未知量了(a,b,c,d,f,g均能够通过扩展欧几里得求出),那么就可以求解他们的gcd得到n。
可以说,想到这个思路的时候我为之一振,可惜实际操作的时候这个方法并不能实施,原因也很简单,我们进行的并非模幂运算,而是普通幂运算,并且a,b这些指数数量级很大(注意这一点),所以是完全没有办法照这个思路解下去的。这时候我也没有想到怎么利用格,所以进度也停滞了,一卡卡到了晚上。
晚上我反复思考的时候,又想到了我刚刚说的那一点,也就是实施不了共模攻击的原因,在于指数的数量级很大,没有办法幂运算。我也突然联想到了Lattice中LLL算法的重要应用——求解最短向量。那么一切也就说得通了,之所以给20个素数作为加密指数,就是可以应用于格密码中,克服刚才共模攻击中两两组合时计算出的a,b过大的问题。所以构造格的思路就来了:
因为20个指数e均互素,所以一定存在a1,a2,a3…a20,使得
所以可以列出等式:
很明显,这个格符合我们的要求,我们只需要从规约出来的短向量中挑出两组,按理来说,我们只需要类似的进行刚才的共模攻击即可。
可是实际操作又遇到了问题,这样规约出来的向量组是这样的:
1 | [ 45 -58 5 -16 12 -7 -27 19 6 14 29 -23 -36 44 -15 1 8 14 -7 11 -9] |
第一列并不是我们想要的1,说明第一列是1的向量对比起来长度并不小。再想一下规约的目的,其实很容易就能想通第一列是多少并不重要,重要的是短向量的第一列相同(这一点非常容易想通,没理解的话仔细想想)。而要让他们相同,最有效的办法就是让他们均为0,想到这一点后,就可以在格的第一列乘上一个大数K,从而有效的调整一下格,如下:
这样一来,最短向量的第一列就不太可能不是0了(因为会对应的扩大K倍,显著地使规约向量变长),我测试出取100左右即可,然后就可以求解最大公约数(此时还需注意两点小问题:一是规约出的短向量有负数,普通幂运算中会变成分数形式,通分至等式右侧即可;二是求得的公约数仍有可能是k倍的n,需要去除一些小因子),最终得到n。
recovern.ipynb:
1 | e= [] |
后两个问题也就迎刃而解,最终得到flag:
NSSCTF{407f8832-6ffd-43bf-91a0-6900758cdff7}总结
总的说来,对格的应用还不够灵活,还需要加深学习。
如果各位有不懂的地方或者发现了文中的问题,欢迎联系我,一起学习进步。