[RoarCTF2019]RSA
一看到题目,我就有些蒙了,A是代表了什么,
先来分解n
接下来可以暴力破解e了,因为e没有给出来,应该不会太大,猜测是四位数字
import gmpy2
import libnum
from Crypto.Util.number import long_to_bytes
A = 2683349182678714524247469512793476009861014781004924905484127480308161377768192868061561886577048646432382128960881487463427414176114486885830693959404989743229103516924432512724195654425703453612710310587164417035878308390676612592848750287387318129424195208623440294647817367740878211949147526287091298307480502897462279102572556822231669438279317474828479089719046386411971105448723910594710418093977044179949800373224354729179833393219827789389078869290217569511230868967647963089430594258815146362187250855166897553056073744582946148472068334167445499314471518357535261186318756327890016183228412253724
n = 117930806043507374325982291823027285148807239117987369609583515353889814856088099671454394340816761242974462268435911765045576377767711593100416932019831889059333166946263184861287975722954992219766493089630810876984781113645362450398009234556085330943125568377741065242183073882558834603430862598066786475299918395341014877416901185392905676043795425126968745185649565106322336954427505104906770493155723995382318346714944184577894150229037758434597242564815299174950147754426950251419204917376517360505024549691723683358170823416757973059354784142601436519500811159036795034676360028928301979780528294114933347127
c = 41971850275428383625653350824107291609587853887037624239544762751558838294718672159979929266922528917912189124713273673948051464226519605803745171340724343705832198554680196798623263806617998072496026019940476324971696928551159371970207365741517064295956376809297272541800647747885170905737868568000101029143923792003486793278197051326716680212726111099439262589341050943913401067673851885114314709706016622157285023272496793595281054074260451116213815934843317894898883215362289599366101018081513215120728297131352439066930452281829446586562062242527329672575620261776042653626411730955819001674118193293313612128
print(len(str(A)))
q = 842868045681390934539739959201847552284980179958879667933078453950968566151662147267006293571765463137270594151138695778986165111380428806545593588078365331313084230014618714412959584843421586674162688321942889369912392031882620994944241987153078156389470370195514285850736541078623854327959382156753458569
p = 139916095583110895133596833227506693679306709873174024876891023355860781981175916446323044732913066880786918629089023499311703408489151181886568535621008644997971982182426706592551291084007983387911006261442519635405457077292515085160744169867410973960652081452455371451222265819051559818441257438021073941183
phi = (p - 1) * (q - 1)
# ed=k*phi+1
for e in range(100000):
if gmpy2.gcd(e, phi) == 1:
d = gmpy2.invert(e, phi)
m = gmpy2.powmod(c, d, n)
m = libnum.n2s(int(m)) #libnum.n2s 函数用于将整数转换为字节串
if 'CTF' in str(m):
print(m)
break
根据以上,似乎没有发挥A的作用,没有直接参与RSA的解密过程
看了其他大神的wp,似乎常规解法是根据A来推测x,y的范围,解出x,y,根据q与iroot(n/(x*y),2)的值相接近.从而可以得出p,q的值.
import gmpy2
count = 0
min_gap = 99999
max_gap = 0
p = 21679967315669523832823488086602270908640556452221643267343405142715107120582778258062778680925402382895255206278432676733248308929062714997512704088603667
for i in range(1000):
pn = gmpy2.next_prime(p)
count = count + pn - p
max_gap = max(max_gap, pn - p)
min_gap = min(min_gap, pn - p)
p = pn
print(count / 1000)
print(min_gap)
print(max_gap)
这是
155位(10进制)左右素数之间gap的大小,然后确定爆破的范围,
分析x,y的关系
A转化为2进制后是2017位,而说明第一项只能为1,相比于第二项,第三项可以忽略,所以对A开316次根,得到y的估计值为83,考虑忽略项,实际y的值小于等于83
明显可以知道,x=2,y=83
import math
A = 2683349182678714524247469512793476009861014781004924905484127480308161377768192868061561886577048646432382128960881487463427414176114486885830693959404989743229103516924432512724195654425703453612710310587164417035878308390676612592848750287387318129424195208623440294647817367740878211949147526287091298307480502897462279102572556822231669438279317474828479089719046386411971105448723910594710418093977044179949800373224354729179833393219827789389078869290217569511230868967647963089430594258815146362187250855166897553056073744582946148472068334167445499314471518357535261186318756327890016183228412253724
for y in range(2, 85):
for x in range(2, 100):
try:
a=(((y%x)**5)%(x%y))**2019+y**316+(y+1)//x
if a-A <= 1000 and a- A>= -1000:
print(x, y, a-A)
except:
pass
暴力破解了x,y,
后面就可以根据x,y推出p,q了
import gmpy2
n = 117930806043507374325982291823027285148807239117987369609583515353889814856088099671454394340816761242974462268435911765045576377767711593100416932019831889059333166946263184861287975722954992219766493089630810876984781113645362450398009234556085330943125568377741065242183073882558834603430862598066786475299918395341014877416901185392905676043795425126968745185649565106322336954427505104906770493155723995382318346714944184577894150229037758434597242564815299174950147754426950251419204917376517360505024549691723683358170823416757973059354784142601436519500811159036795034676360028928301979780528294114933347127
def trybomb(t):
print('when t is ' + str(t) + ' :')
pv = gmpy2.isqrt(n // t) - 2000
count = 1
while count <= 1000:
p = gmpy2.next_prime(pv)
count += 1
if n % p == 0:
print('p = ', p)
q = n // p
break
else:
pv = p
trybomb(166)
print('finish')
最后还是暴力破解出来e
[RoarCTF2019]babyRSA
题目看起来很复杂,但是你去将n分解一下,发现了刚好可以分解成三个素数,就变得很简单了
直接得到了flag
import libnum
c=75700883021669577739329316795450706204502635802310731477156998834710820770245219468703245302009998932067080383977560299708060476222089630209972629755965140317526034680452483360917378812244365884527186056341888615564335560765053550155758362271622330017433403027261127561225585912484777829588501213961110690451987625502701331485141639684356427316905122995759825241133872734362716041819819948645662803292418802204430874521342108413623635150475963121220095236776428
e=4097 #将0x1001转化为十进制数
r=1276519424397216455160791032620569392845781005616561979809403385593761615670426423039762716291920053306063214548359656555809123127361539475238435285654851
p=5057572094237208127867754008134739503717927865750318894982404287656747895573075881186030840558129423864679886646066477437020450654848839861455661385205433
q=13242175493583584108411324143773780862426183382017753129633978933213674770487765387985282956574197274056162861584407275172775868763712231230219112670015751
n=p*q*r
phi =(p-1)*(q-1)*(r-1)
d=libnum.invmod(e,phi)
m=pow(c,d,n)
print(libnum.n2s(m))
维纳攻击:
e的指数很大(理论上d<N**0.25作为攻击的实现)
实现Wiener攻击,这是一种针对具有较小解密指数d
的RSA私钥的恢复
维纳攻击你要先知道以下几个知识点
连分数:
连分数的定义
连分数是一种特殊的分数表示法,其形式如下:
其中,a0是某个整数,而所有其他的数an(n>0)都是正整数。连分数可以是有限的(即终止于某个an),也可以是无限的(即不终止)。在数学中,任何一个有理数都可以表示为有限连分数,而任何一个无理数都可以表示为无限连分数。
连分数在RSA中的应用
- 理论基础:连分数在RSA中的应用通常与Wiener攻击有关。Wiener攻击是一种利用RSA私钥的某些性质(如解密指数d较小)来恢复私钥的攻击方法。在这种攻击中,连分数被用来求解与私钥相关的同余方程。
- Wiener攻击:Wiener攻击基于一个数学定理,该定理允许我们根据给定的实数a和某个条件来求解整数p和q。在RSA的上下文中,这个实数a可能与私钥的某些属性相关,而p和q则是模数N的质因子。通过求解与a相关的连分数,攻击者可以逐步逼近p和q的值,并最终恢复私钥。
- 实际运用:在实际运用中,Wiener攻击需要满足一定的条件才能成功。例如,解密指数d必须小于某个阈值(这个阈值通常与模数N的大小有关)。此外,攻击者还需要能够获取到足够多的关于私钥的信息(如公钥e和模数N)。如果这些条件都得到满足,那么攻击者就可以使用连分数技术来恢复私钥。
韦达定理
韦达定理在二次方程 ax^2 + bx + c = 0
根的和等于系数 b
的相反数除以系数 a
,即 x_1 + x_2 = -b/a
。
根的积等于常数项 c
除以系数 a
,即 x_1 * x_2 = c/a
来自己出一道题目
import libnum
import random
#生成随机的素数
p=libnum.generate_prime(512)
q=libnum.generate_prime(512)
m="flag{h3ll0-w0rld}"
#字符串转数字
m=libnum.s2n(m)
n=p*q
phi_n=(p-1)*(q-1)
#计算d
while True:
nbits=1024
d=random.getrandbits(nbits //4)
if (libnum.gcd(d,phi_n) ==1 and 36*pow(d,4) <n):
break
#计算出e
e=libnum.invmod(d,phi_n)
c=pow(m,e,n)
print("n=",n)
print("e=",e)
print("c=",c)
完整代码:
import gmpy2
import libnum
def continuedFra(x, y):
# #此函数用于计算x/y的连分数表示。它使用辗转相除法(欧几里得算法)来逐步减少分数,直到分母y变为0。
cf = []
while y:
cf.append(x // y)
x, y = y, x % y
return cf
def gradualFra(cf):
# #这个函数接收一个连分数列表cf,并返回列表最后一个元素的渐近分数。它通过逆序遍历连分数列表,并使用特定的公式来计算分子和分母
numerator = 0
denominator = 1
for x in cf[::-1]:
# 这里的渐进分数分子分母要分开
numerator, denominator = denominator, x * denominator + numerator
return numerator, denominator
def solve_pq(a, b, c):
"""使用韦达定理解出pq,x^2−(p+q)∗x+pq=0
:param a:x^2的系数
:param b:x的系数
:param c:pq
:return:p,q
"""
par = gmpy2.isqrt(b * b - 4 * a * c)
return (-b + par) // (2 * a), (-b - par) // (2 * a)
def getGradualFra(cf):
"""计算列表所有的渐近分数
:param cf: 连分数列表
:return: 该列表所有的渐近分数
"""
gf = []
for i in range(1, len(cf) + 1):
gf.append(gradualFra(cf[:i]))
return gf
def wienerAttack(e, n):
#它首先计算公钥e相对于模数n的连分数表示,然后使用getGradualFra函数生成所有可能的渐近分数。对于每个渐近分数(d, k),它检查是否满足(e * d - 1) % k == 0。如果满足,它尝试使用(e * d - 1) // k作为phi(n)的近似值,并使用solve_pq函数尝试分解n以找到p和q。如果找到了有效的p和q,那么它会计算私钥d并返回
cf = continuedFra(e, n)
gf = getGradualFra(cf)
for d, k in gf:
if k == 0: continue
if (e * d - 1) % k != 0:
continue
phi = (e * d - 1) // k
p, q = solve_pq(1, n - phi + 1, n)
if p * q == n:
return d
n= 92524912824457455478469479834145192399327339422419976426014024967106017250525784362251809870851330233714073309745926853809722723590007799727853843200857232710763391103522003213039635901415434225633578811817814757637106456030410677721613532510535968977938933636723316561537077411888416685226759599589065857021
e= 54061685907559431614093395962586317025387379705558008571034720802508811703397874391457074838986258081858042924777974376782261530254408986545475988227306300250798769475295193119450403415247506835654081051816773486037227848499432844796357648121686484398862179371192675792796946629089692057399103015987899220553
c= 75914541873509926793052668657170821143574989686045126269695603145212114658529526002484357527707015025922193311737442791537506782543615634954764108902795246498210682486601649383685688201749284019581721635618585026491003845052815757860692633274121793700743169931534720441816541558209434925881957085594022520969
d=wienerAttack(e, n)
m=pow(c, d, n)
print(libnum.n2s(m).decode())