0x01 题目:Top_secret
- 附件📎:
task.py
#python3
from random import *
from gmpy2 import *
from secret import flag
from Crypto.Util.number import *
e = 0x10001
common_p = next_prime(getrandbits(64))
common_q = next_prime(getrandbits(64))
common_n = common_p*common_q
common = '(%d,%d)' % (common_n, e)
f = open('test', 'w')
f.write(common+'\n')
test = [getrandbits(64) for i in range(512)]
for i in test:
f.write(str(pow(i, e, common_n))+'\n')
top_p = next_prime(getrandbits(1024))
top_q = next_prime(getrandbits(1024))
top_n = top_p*top_q
top = '(%d,%d)' % (top_n, e)
m=bytes_to_long(flag)
f = open('top_secret', 'w')
f.write(top+'\n')
f.write(str(pow(m, e, top_n)))
test
(76436798540605363536477649122583467659,65537)
55186050945367642475707075553224810751
25230392976483617195172228672398327555
13402586421932983833466929662539579831
73577334713803339185143437689323273802
38551782957548094407447800753751168665
······(此处省略...)
66417142292403444690306408437925436539
16917869489156458807988614612206952611
62433740855979549009477295935969264632
54703498276817055998735161171803303420
top_secret
(6778341886512983471750645456950728290523536616361299371741958671360393083882319627518672438545115814129137657182781214208816455570796241668174521203607532582846713044946980024783900646043967377749611378975743784775184880644929838298609658208272073728954171415761808451070649956713090699363828704967425598555240857728605492559334918924364181243615109772911597831275788990921847678043264311757080086808770372237384343263695430482298224638114383481163242159396421912093570982271665015871698548239583006809079195143432434252317382780354946136908751753704879594650848364606279745816610061533345112504778579863240146035803,65537)
418919112322035719080296282188262556318147208740019952103384898777739260429989830467043589948286391778496053290993573572770668930715827948954387013380968757428438279330384918638646330090780491358345953558632784799412654785756895941844475592045474255714700612603241873989982425698945208444003573618381342067450033353217477323111093274062491515883275867746619738296397569905705052009378569807157998159800021519653031832333686380591812114745289471610313845110578057278593565079742329184877876988624852591803357662165422951088131736792374156848783221486055402017298046164798460834330495680663944310194208485613787684535
0x02 解题思路
分析源码,第一部分代码:
e = 0x10001
common_p = next_prime(getrandbits(64))
common_q = next_prime(getrandbits(64))
common_n = common_p*common_q
common = '(%d,%d)' % (common_n, e)
f = open('test', 'w')
f.write(common+'\n')
test = [getrandbits(64) for i in range(512)]
for i in test:
f.write(str(pow(i, e, common_n))+'\n')
通过两个64位的common_p
和common_q
生成公钥
(
N
,
e
)
(N,e)
(N,e)存放到test文件中。
这对公钥common_n,e
用来加密512串64位的随机数,也将其存放到test文件中。
e
和common_n
题目给出,且common_n
位数较低,直接通过大数分解即可得到common_p
和common_q
然后就可以逆向出512个随机数了。
再看看第二部分代码:
top_p = next_prime(getrandbits(1024))
top_q = next_prime(getrandbits(1024))
top_n = top_p*top_q
top = '(%d,%d)' % (top_n, e)
m=bytes_to_long(flag)
f = open('top_secret', 'w')
f.write(top+'\n')
f.write(str(pow(m, e, top_n)))
第二部分比较简单,
通过getrandbits(1024)
生成一个1024位的随机数,再通过next_prime()
取这个数的下一个素数
,
从而生成
p
p
p和
q
q
q,再用
p
p
p和
q
q
q计算得到的公钥对flag
进行加密,将公钥
(
n
,
e
)
(n,e)
(n,e)和密文
c
c
c存储到top_secret
文件中。
本题的漏洞点是getrandbits()
函数,
最终可以利用第一部分代码给出的大量随机数,预测出top_p
和top_q
。
我使用的是python3的randcrack进行预测
安装方法:
pip3 install randcrack
首先需要解出第一部分所给出的512个随机数,
大数分解common_n
得到common_p
和common_q
:
具体代码如下:
from Crypto.Util.number import *
from gmpy2 import *
e = 65537
common_n = 76436798540605363536477649122583467659
common_p = 5105923512372711233
common_q = 14970220050375442123
common_phi = (common_p-1)*(common_q-1)
common_d = gmpy2.invert(e, common_phi)
test = open('test', 'r').readlines()
with open('deTest', 'w') as deTest:
for i in test:
if i == "(76436798540605363536477649122583467659,65537)\n":
continue
# print(pow(int(i),common_d,common_n))
deTest.write(str(pow(int(i),common_d,common_n))+'\n')
得到原512个随机数:
6664610210323177003
17661097700361369885
303206696427486439
16389245002373137587
5670728676319485833
12056818099668268163
······(此处省略...)
1106083047604041265
17146900470736302356
由于RandCrack()
所需的随机数为32位,而题目中getrandbits(64)
是64位,
了解一下getrandbits对于高位随机数的生成方式,即可将64位的随机数分解为32位的随机数。
具体原理如下:
import random
random.seed(0)
a=random.getrandbits(32)
b=random.getrandbits(32)
print(a)
print(b)
print((b<<32)+a)
random.seed(0)
print(random.getrandbits(64))
输出:
3626764237
1654615998
7106521602475165645
7106521602475165645
即先得到32位随机数作为低位,然后再用下一个32位随机数作为高位,组成一个64位随机数。
得知原理后,编写python脚本进行预测最后的top_p
和top_q
。
这里面还有一个小坑,
randcrack
中最多提供624个32bits的随机数进行预测,否则会报错:ValueError: Already got enough bits
所以只需要取最后624/2=312个随机数就行,前面200个舍弃,具体在下面给的代码中会有体现
最终exp如下:
from Crypto.Util.number import *
from gmpy2 import *
e = 65537
common_n = 76436798540605363536477649122583467659
common_p = 5105923512372711233
common_q = 14970220050375442123
common_phi = (common_p-1)*(common_q-1)
common_d = gmpy2.invert(e, common_phi)
test = open('test', 'r').readlines()
with open('deTest', 'w') as deTest:
for i in test:
if i == "(76436798540605363536477649122583467659,65537)\n":
continue
# print(pow(int(i),common_d,common_n))
deTest.write(str(pow(int(i),common_d,common_n))+'\n')
from randcrack import RandCrack
rc = RandCrack()
bitsum = 0
with open('deTest') as f:
s = f.readlines()
for l in s:
a = int(l) % (2 ** 32)
b = int(l) // (2 ** 32)
# print(f"{bitsum} : {(b<<32)+a}")
bitsum += 1
if bitsum <= 200:
continue
rc.submit(a)
rc.submit(b)
p = next_prime(rc.predict_getrandbits(1024))
q = next_prime(rc.predict_getrandbits(1024))
# print(p)
# print(q)
n = p * q
print(f"n = {n}")
top_n = 6778341886512983471750645456950728290523536616361299371741958671360393083882319627518672438545115814129137657182781214208816455570796241668174521203607532582846713044946980024783900646043967377749611378975743784775184880644929838298609658208272073728954171415761808451070649956713090699363828704967425598555240857728605492559334918924364181243615109772911597831275788990921847678043264311757080086808770372237384343263695430482298224638114383481163242159396421912093570982271665015871698548239583006809079195143432434252317382780354946136908751753704879594650848364606279745816610061533345112504778579863240146035803
c = 418919112322035719080296282188262556318147208740019952103384898777739260429989830467043589948286391778496053290993573572770668930715827948954387013380968757428438279330384918638646330090780491358345953558632784799412654785756895941844475592045474255714700612603241873989982425698945208444003573618381342067450033353217477323111093274062491515883275867746619738296397569905705052009378569807157998159800021519653031832333686380591812114745289471610313845110578057278593565079742329184877876988624852591803357662165422951088131736792374156848783221486055402017298046164798460834330495680663944310194208485613787684535
phi = (p-1)*(q-1)
assert gmpy2.gcd(e, phi) == 1 and n == top_n
d = gmpy2.invert(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
运行后即可得到flag:
flag{4f43bb3f-12bc-4c85-a19a-833185d1deae}