Crypto
Easy_RSA_2
题目给出的python脚本内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import *import gmpy2flag = '##################################' p1 = getPrime(512 ) p2 = gmpy2.next_prime(p1) q1 = getPrime(512 ) q2 = gmpy2.next_prime(q1) n = p1*p2*q1*q2 e = 65537 phi = (p1-1 )*(p2-1 )*(q1-1 )*(q2-1 ) d = gmpy2.invert(e,phi) c = pow(bytes_to_long(flag),e,n) print pow(p1+q2,65537 ,n)print pow(p2+q1,65537 ,n)print n print c
getPrime(512)表示随机返回一个512bit的prime number(质数)
gmpy2.next_prime(p1)表示返回下一个大于p1的质数
1 2 p1 = getPrime(512 ) p2 = gmpy2.next_prime(p1)
由此可见,p1和p2比较接近
同理q1和q2也比较接近
又因为
可以理解为
1 2 n = (p1*q2)*(q1*p2) = (p1*q1)*(q2*p2)
那么n就可以使用fermat attack去分解
将RsaCtfTool里的fermat.py添加参数,就可以求出分解后的因子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from gmpy2 import isqrtdef fermat (n) : a = isqrt(n) b2 = a*a - n b = isqrt(n) count = 0 while b*b != b2: a = a + 1 b2 = a*a - n b = isqrt(b2) count += 1 p = a+b q = a-b assert n == p * q return p, q n = 8573641536164485111081609341110540574423426701587222458588002464807917555910942077276167528046769327390058096169685188870928286845342631974847171845103806710768418462668311621275704636581042137915505959767806384415314024549489538717607173007829067492516776714817262226691787436227002924225311861164296655909746846329870548266285498682510415418053656271623482202491805513797215793596385014264449282551352796096361524482384994633912515104414237252657058698433260597636367614328512751722615849959987780969423318207123668118325176544879335267439096589035064596631756303300860315257404427016819145298919974287174103934503 print fermat(n)
运行结果如下
1 (mpz(92593960581478990492272892082797582837598460722110672289472622026362327649808629839901870926342611079306340852878894165246912369940368199994436453135377198629507140256023102013131493810077402487620597574150419252484604398426042348936643985371724093575122287222535733709825807203222224632574777263054682547849L), mpz(92593960581478990492272892082797582837598460722110672289472622026362327649808629839901870926342611079306340852878894165246912369940368199994436453135375408481531310003733935540422655297910944762679239039783193372674051452613244254750117673971732027355424665752849172881712190463569600293779394868475158854447L))
但是这里存在4个质数(multi-primes)
可以修改fermat的代码,尝试得到两组满足条件的质数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 import gmpy2def fermat_factorization (n) : factor_list = [] gmpy2.get_context().precision = 2048 a = int(gmpy2.sqrt(n)) a2 = a * a b2 = gmpy2.sub(a2, n) while True : a += 1 b2 = a * a - n if gmpy2.is_square(b2): b2 = gmpy2.mpz(b2) gmpy2.get_context().precision = 2048 b = int(gmpy2.sqrt(b2)) factor_list.append([a + b, a - b]) if len(factor_list) == 2 : break return factor_list n = 8573641536164485111081609341110540574423426701587222458588002464807917555910942077276167528046769327390058096169685188870928286845342631974847171845103806710768418462668311621275704636581042137915505959767806384415314024549489538717607173007829067492516776714817262226691787436227002924225311861164296655909746846329870548266285498682510415418053656271623482202491805513797215793596385014264449282551352796096361524482384994633912515104414237252657058698433260597636367614328512751722615849959987780969423318207123668118325176544879335267439096589035064596631756303300860315257404427016819145298919974287174103934503 print fermat_factorization(n)
运行得到结果,可以看到有2组数据,4个质数
1 2 [[92593960581478990492272892082797582837598460722110672289472622026362327649808629839901870926342611079306340852878894165246912369940368199994436453135377198629507140256023102013131493810077402487620597574150419252484604398426042348936643985371724093575122287222535733709825807203222224632574777263054682547849L, 92593960581478990492272892082797582837598460722110672289472622026362327649808629839901870926342611079306340852878894165246912369940368199994436453135375408481531310003733935540422655297910944762679239039783193372674051452613244254750117673971732027355424665752849172881712190463569600293779394868475158854447L], [92593960581478990492272892082797582837598460722110672289472622026362327649808629839901870926342611079306340852878894165246912369940368199994436453135380738930065081697951287140042893321203653899791236620373028885001354525656928787903118347895059813547629304512431677110164891730095638508629167931628303931903L, 92593960581478990492272892082797582837598460722110672289472622026362327649808629839901870926342611079306340852878894165246912369940368199994436453135371868180973368561805750413511255786784693350508599993560583740157301325382357815783643311448396307382917648462953229481373105936696186417725004199901537674201L]]
标记为:[X1,Y1],[X2,Y2]
又根据
1 2 3 n = p1*p2*q1*q2 = p1*q1 * p2*q2 = X1 * Y1 = p1*q2 * p2*q1 = X2 * Y2
那么通过
就可以使用最大公约数(GCD)求得p1,然后q1也可以求得
1 2 3 4 p1 = GCD(X1,X2) q1 = X1/p1 p2 = GCD(Y1,Y2) q2 = Y1/p2
代码参考https://github.com/pcw109550/write-up/tree/master/2019/ISITDTU/Easy_RSA_2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 from Crypto.Util.number import GCD, inverse, long_to_bytes as l2bimport gmpy2def fermat_factorization (n) : factor_list = [] gmpy2.get_context().precision = 2048 a = int(gmpy2.sqrt(n)) a2 = a * a b2 = gmpy2.sub(a2, n) while True : a += 1 b2 = a * a - n if gmpy2.is_square(b2): b2 = gmpy2.mpz(b2) gmpy2.get_context().precision = 2048 b = int(gmpy2.sqrt(b2)) factor_list.append([a + b, a - b]) if len(factor_list) == 2 : break return factor_list def main () : c = 8436043641135865531308468859210199431445831063674810351906331674115825605849862045115409554309732867926457428348729196827592921108183774070414343257409618631078896543782150761081732376735501920417229787663210936174854000594130785353102718054331606096192133481536724402629697019651921188121029927710787682993814748802295545306899075962041017278877203965796981792702098381465051289581518257202127401748725944229037078896857591660248467597356051123218945757343652461844056927461929195427880969904210166880623689090977714615839624798930450630919330253477634839161931755642681718034910946900928731231093352169252474939674 e = 65537 n =8573641536164485111081609341110540574423426701587222458588002464807917555910942077276167528046769327390058096169685188870928286845342631974847171845103806710768418462668311621275704636581042137915505959767806384415314024549489538717607173007829067492516776714817262226691787436227002924225311861164296655909746846329870548266285498682510415418053656271623482202491805513797215793596385014264449282551352796096361524482384994633912515104414237252657058698433260597636367614328512751722615849959987780969423318207123668118325176544879335267439096589035064596631756303300860315257404427016819145298919974287174103934503 factor_list = fermat_factorization(n) print factor_list [X1, Y1] = factor_list[0 ] [X2, Y2] = factor_list[1 ] assert X1 * Y1 == n assert X2 * Y2 == n p1 = GCD(X1,X2) q1 = X1/p1 p2 = GCD(Y1,Y2) q2 = Y1/p2 phi = (p1 - 1 ) * (q1 - 1 ) * (p2 - 1 ) * (q2 - 1 ) d = inverse(e, phi) flag = l2b(pow(c, d, n)) print(flag) if __name__ == "__main__" : main()
运行后得到flag
decrypt to me
题目源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import binasciidef generate_prg_bit (n) : state = n while True : last_bit = state & 1 yield last_bit middle_bit = state >> len(bin(n)[2 :])//2 & 1 state = (state >> 1 ) | ((last_bit ^ middle_bit) << (len(bin(n)[2 :])-1 )) flag = '###########' enc = "OKQI+f9R+tHEJJGcfko7Ahy2AuL9c8hgtYT2k9Ig0QyXUvsj1B9VIGUZVPAP2EVD8VmJBZbF9e17" flag_bin_text = bin(int(binascii.hexlify(flag), 16 ))[2 :] prg = generate_prg_bit(len(flag_bin_text)) ctext = [] flag_bits = [int(i) for i in flag_bin_text] for i in range(len(flag_bits)): ctext.append(flag_bits[i] ^ next(prg)) ciphertext = '0b' + '' .join(map(str, ctext)) n = int(ciphertext, 2 ) print binascii.unhexlify('%x' % n).encode('base64' )
经过测试发现,len(flag_bin_text)和len(ciphertext)-2的值一样
于是可以由密文推导出伪随机数生成器 generate_prg_bit(n)的种子n
在测试过程中,可以发现需要在二进制数据前添加0才能保证前后长度一致
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import binasciidef generate_prg_bit (n) : state = n while True : last_bit = state & 1 yield last_bit middle_bit = state >> len(bin(n)[2 :])//2 & 1 state = (state >> 1 ) | ((last_bit ^ middle_bit) << (len(bin(n)[2 :])-1 )) flag = 'ISITDTU{B' print len(flag)enc = "OK9yn2BczcGa" flag_bin_text = bin(int(binascii.hexlify(flag), 16 ))[2 :] print len(flag_bin_text)prg = generate_prg_bit(len(flag_bin_text)) ctext = [] flag_bits = [int(i) for i in flag_bin_text] for i in range(len(flag_bits)): ctext.append(flag_bits[i] ^ next(prg)) ciphertext = '0b' + '' .join(map(str, ctext)) n = int(ciphertext, 2 ) print ciphertextprint len(ciphertext)-2 print binascii.unhexlify('%x' % n).encode('base64' )print bin(int(binascii.hexlify(enc.decode('base64' )), 16 ))ciphertext2 = bin(int(binascii.hexlify(enc.decode('base64' )), 16 ))[2 :] print len(ciphertext2)n2 = int(ciphertext2, 2 ) print binascii.unhexlify('%x' % n2).encode('base64' )
运行效果如下图,可以看得比较直观
由此可以得到准确的长度值
最终的脚本,参考https://github.com/pcw109550/write-up/blob/master/2019/ISITDTU/decrypt_to_me/solve.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import binasciifrom Crypto.Util.number import long_to_bytes as l2b, bytes_to_long as b2ldef generate_prg_bit (n) : state = n while True : last_bit = state & 1 yield last_bit middle_bit = state >> len(bin(n)[2 :])//2 & 1 state = (state >> 1 ) | ((last_bit ^ middle_bit) << (len(bin(n)[2 :])-1 )) flag = '###########' enc = "OKQI+f9R+tHEJJGcfko7Ahy2AuL9c8hgtYT2k9Ig0QyXUvsj1B9VIGUZVPAP2EVD8VmJBZbF9e17" flag_bin_text = bin(int(binascii.hexlify(flag), 16 ))[2 :] prg = generate_prg_bit(len(flag_bin_text)) ctext = [] flag_bits = [int(i) for i in flag_bin_text] for i in range(len(flag_bits)): ctext.append(flag_bits[i] ^ next(prg)) ciphertext = '0b' + '' .join(map(str, ctext)) n = int(ciphertext, 2 ) print bin(int(binascii.hexlify(enc.decode('base64' )), 16 ))ciphertext2 = bin(int(binascii.hexlify(enc.decode('base64' )), 16 ))[2 :] print len(ciphertext2)n2 = int(ciphertext2, 2 ) print binascii.unhexlify('%x' % n2).encode('base64' )ciphertext3 = '0' +ciphertext2 print ciphertext3prg = generate_prg_bit(len(ciphertext3)) pt = "" for e in ciphertext3: pt += str(next(prg) ^ int(e)) flag = l2b(int(pt,2 )) print flag
运行得到flag