第一次和Tea Deliverers打比赛,被学长带飞了,这次crypto方向的题目还是比较友好的,但是我写题和exp写的是真慢。td最后总排名第三。简单记录一下 crypto 方向题目的解法和exp。hitcon总体赛题质量还是非常顶的。
so easy rsa [210pts]
源码:
1 | from gmpy2 import next_prime, is_prime |
实现了一个简单的LCG伪随机数生成器,并且用该随机数生成器生成了p,q。利用LCG的特性,我们测试发现,p,q生成的过程,一般在1000轮以下,也就是next的执行次数,这个轮次完全可以爆破。利用LCG的方程:。得到p,q之间满足关系:。可以直接递推,也可以算通式。我们确定每个轮次直接p,q可能存在的关系,解下面的模方程:,如果,则分解成功。之后进行解密即可。
1 | # sage |
a little easy rsa [240pts]
源代码:
1 | from Crypto.Util.number import * |
其实一开始感觉有很多方法可以尝试,p,q相差看似很大,但是分解不出来,另外解密指数p大约是,很小,看似可以满足Wiener Attack的条件,但也是因为p,q相差过大,不能用,只可以在p,q比较接近的情况下才能Wiener Attack。只剩下e本身的特性了。注意到:,也就是解密的公式:。同样地就有:,而由费马定理,也就是模p乘法群的阶为p-1,。那么对于任意整数,我们就有:。所以。这样就分解了n,之后正常RSA解密即可。
算完发现可能非预期了,和coppersmith有关。
1 | from Crypto.Util.number import * |
so easy but not rsa [305pts]
源代码:
1 | from Crypto.Util.number import bytes_to_long as b2l |
注意到这句话generate a lots of random data,那么其实这题也就和NTRU格密码关系不大,但我一直往NTRU格密码方向想,直到zzh拿了一血,orz。这是典型的MT19937伪随机数生成器的问题,题目特意生成了连续的200*240比特的MT19937的随机数。那么完全可以恢复出之前的MT19937的状态。之后尝试加密flag时候的offset(找到 r = randomdpoly(18, 18) 时的MT19937矩阵的状态即可),直到解密出来的字符串满足flag的要求。
1 | l = [] |
still not rsa [321pts]
NTRU格密码的选择密文攻击,找到了两篇paper:
paper1:A Chosen-Ciphertext Attack against NTRU
paper2 : Imperfect Decryption and an Attack on the NTRU Encryption Schem
这里不再详细叙述NTRU格加密流程与理论叙述,想要了解可以参考wiki或者其他博客。
比赛的时候两篇论文都仔细读了,第一篇看的是paper1,发现利用paper1的攻击适合可选择的明文比较少的情况,一般只需要两次或者不超过几十次(穷举量),paper1的穷举量与f和g的结构相关,定义k=f AND G,AND指相应多项式次数的系数进行与运算,当多项式k的非0次数项较少时,不超过3(在NTRU的n比较大时,具体可以根据不同NTRU格参数的信息进行本地测试),我们可以简单选择两个密文对私钥进行攻击。记多项式k的非0系数的个数为collision,几种标准的NTRU参数的collision概率如下,collision越小,私钥攻击的穷举量越少。
该攻击的原理如下:
,其中c是整数,h是公钥,并且c的选取要保证。具体推导可以参考paper,如果我们得的解密的信息为m,那么私钥可以通过下面等式恢复:。从这个式子我们就可以明白为什么collision需要比较小,因为它直接决定了密钥恢复的穷举量。
本次赛题中给的是标准NTRU密码体系的case B参数,当然修改了部分参数,我在本地测试发现f和g的碰撞collision<=3的概率在0.1%,小于2的概率在0.01%,需要nc重连至少10000次,而且每次还需要进行不小的穷举量。肯定是不可行的。当然这篇paper的价值在于可以只通过一次选择密文恢复密钥。
另外一篇paper的算法就是本题的解法,它利用的是NTRU密码的一个特性:Decryption Fault,即解密错误。具体实现算法3即可,其中Decryption Fault的产生可以从加密时选择非0系数比较少的随机多项式r,m进行随机选择即可。
注意这只是恢复了一个等价密钥,另外需要完全恢复还需要乘以一个$x^k$或者$-x^k$,至于k,进行穷举即可。得到一组Decryption Fault之后就可以进行本地爆破。
脚本如下
1 | from Crypto.Util.number import bytes_to_long as b2l |
magic rsa [262pts]
源码:
1 | import os |
分析:
求m使得: m^e = byets_to_long (sha384(m)) mod N。其中N和e我们都可以自由选择,但是N限定高17*8位,并且N必须有384比特长。
实际上这就是一个离散对数的求解问题,对于这种高位数的离散对数,我们可以使用Pohlig-Hellman算法,条件是N必须是一个光滑阶的素数,所谓光滑阶要求,素数N的阶N-1进行素数分解后,它的素因子都比较小,这个上界可以参考一般离散对数求解的复杂度,因此最大素因子不超2^50次,对于sagemath来说都是很快就能解的。关于DLP一个详细的解析博客可以参考Lazzero师傅的博客:离散对数。
至于为什么找素数,这是因为素数存在原根,原根可以生成整个循环群,即dlp一定有解,否则对于一般的N,可能N的阶比N本身小很多,这会导致sha384(m)后大概率不在m所在阶群内,当然非素数也可以,只是需要额外穷举。
利用素数光滑阶群的特点,这题直接穷举即可,注意我们不要改变N的低位,我们只穷举固定17*8的高位后,剩下的最高位的2个字节,即2^16,这样不仅N的分解很快(相当于只要分解高位),还很容易找到满足条件的光滑阶素数,如果找不到满足条件的,重连即可。简单穷举脚本如下:
1 | magic_num = 0xcc5123495bada9ef3219e6fe522fd88277 |
magic dlog [262pts]
相比上题,要求N是素数,思路同时,脚本同上。
当然预期解是一篇论文,给定一个范围,寻找最近的光滑阶的素数P,是用LLL做的,如果这题限制多一点,就需要实现论文的方法了。
预期解论文:Finding Smooth Integers in Short Intervals Using CRT Decoding。