p+q高位攻击变体

题目一

from Crypto.Util.number import *
from secret import flag
 
def get_gift(p, q):
    noise = getPrime(40)
    p1, q1 = p + 2 * noise + 1, q - pow(noise, 2)
    gift = 2024 * (p1 + q1)
    return gift
 
p = getPrime(512)
q = getPrime(512)
n = p * q
e = 0x10001
m = bytes_to_long(flag)
 
c = pow(m, e, n)
gift = get_gift(p, q)
 
print(f'c = {c}')
print(f'n = {n}')
print(f'gift = {gift}')
'''
c = 101383046356447336426623798470530695448361708798731382238747567108067236241251384089401506320741815081024352908156466877907424203888923965647318146770258139921360377246187637085549628797640957048672797430217647039035455011311505942632107576730906489223641894279483592789523228409885925263914621255862261546919
n = 131097719698687108485813302886652389604731026998272796315024695395496199386497660846418712521921387496051077394308820230360184411431376692252923609505060476542577219656866593501271690536991944882324175509626138475159461332403161471880082192150081456601522403673111515117219716055561941951891570977025178643791
gift = 46635322848619790584491725916282901439691751328335921415278638528896063068132242718070261114525516272650970256270551306096774004921902972838212903368063625872
'''

解题思路

法1

  • 首先我们注意到noise=getPrime(40),可知noise40位的
  • 已知gift=2024*(p1+q1)=2024*(p+q+1+2*noise-noise**2)=2024*[(p+q)-(noise-1)**2+2]
  • 其中noise-1在平方后大概是80
  • pq都是512位的,所以可以构造右偏>>抵消掉(noise-1)**2,仅泄露p+q高位
  • 因此构造右偏(需要注意的是宁偏多也不要偏少,如果刚好取80则可能无法完全抵消掉noise)(取大也不影响求解结果)
  • gift=(gift//2024)-2
  • x1=(gift>>90)<<90
  • 然后copper就可以还原p,q
  • 脚本参考已知p+q高位攻击 - sevensnight - 博客园的方法1

法2

解答一

法1

脚本1

import gmpy2

gift = 46635322848619790584491725916282901439691751328335921415278638528896063068132242718070261114525516272650970256270551306096774004921902972838212903368063625872
c=101383046356447336426623798470530695448361708798731382238747567108067236241251384089401506320741815081024352908156466877907424203888923965647318146770258139921360377246187637085549628797640957048672797430217647039035455011311505942632107576730906489223641894279483592789523228409885925263914621255862261546919
n=131097719698687108485813302886652389604731026998272796315024695395496199386497660846418712521921387496051077394308820230360184411431376692252923609505060476542577219656866593501271690536991944882324175509626138475159461332403161471880082192150081456601522403673111515117219716055561941951891570977025178643791
gift=(gift//2024)-2
x1=(gift>>90)<<90 #往大的取
e = 0x10001
PR.<x> = PolynomialRing(RealField(1000))
f = x*(x1-x) - n
phigh = int(f.roots()[0][0])
print(phigh,phigh.bit_length())
#10245385971395060175404025994260899026018327732803454139079733202684248559362997918630991266112903887717159293307112056037936651001716489445626599091213586
PR.<x> = PolynomialRing(Zmod(n))
f = phigh + x
res = f.small_roots(X=2**90, beta=0.4,epsilon=0.01)[0] #X的上限一般大于根的位数
p=int(res+phigh)
q=n//p
assert p*q==n
d=gmpy2.invert(e,(p-1)*(q-1))
m=int(pow(c,d,n))
print(bytes.fromhex(hex(m)[2:]))
#BuildCTF{M@y_b3_S0m3th1ng_go_wr0ng}

脚本2

c = 101383046356447336426623798470530695448361708798731382238747567108067236241251384089401506320741815081024352908156466877907424203888923965647318146770258139921360377246187637085549628797640957048672797430217647039035455011311505942632107576730906489223641894279483592789523228409885925263914621255862261546919
n = 131097719698687108485813302886652389604731026998272796315024695395496199386497660846418712521921387496051077394308820230360184411431376692252923609505060476542577219656866593501271690536991944882324175509626138475159461332403161471880082192150081456601522403673111515117219716055561941951891570977025178643791
gift = 46635322848619790584491725916282901439691751328335921415278638528896063068132242718070261114525516272650970256270551306096774004921902972838212903368063625872
gift = (gift//2024) >> 233 #随便取的(>=86即可)
e = 65537
BITS = 233
 
PR.<x> = PolynomialRing(RealField(1000))
f = x * ((gift << BITS) - x) - n
p2high = int(f.roots()[0][0])
 
PR.<x> = PolynomialRing(Zmod(n))
f = p2high + x
res = f.small_roots(X=2^(BITS+10), beta=0.4, epsilon=0.01)[0]
p2 = int(p2high + res)
print(p2)
#10245385971395060175404025994260899026018327732803454139079733202684248559362997918630991266112903887717159293307112056037936650791910220424100097140852483
q2 = n // p2
print(q2)
#12795781443930923314957498667934210380943999998587811382302993343213015802362418444051944067150691108651897157419444913377465525553505809994424022558382277
d2 = inverse_mod(e, (p2-1)*(q2-1))
leak2 = pow(c, d2, n)
print(bytes.fromhex(hex(leak2)[2:]))
#BuildCTF{M@y_b3_S0m3th1ng_go_wr0ng}

法2

N = 131097719698687108485813302886652389604731026998272796315024695395496199386497660846418712521921387496051077394308820230360184411431376692252923609505060476542577219656866593501271690536991944882324175509626138475159461332403161471880082192150081456601522403673111515117219716055561941951891570977025178643791
gift = 46635322848619790584491725916282901439691751328335921415278638528896063068132242718070261114525516272650970256270551306096774004921902972838212903368063625872
gift=gift//2024

gift=(gift>>90)<<90

PR.<x> = PolynomialRing(Zmod(N))
ok = False
def pq_add(tp,tq,tgift,idx):
    global ok 
    if ok:
        return 
    if tp*tq>N:
        #print('>')
        return 
    
    if (tp+(2<<idx))*(tq+(2<<idx))<N:
        #print('<', hex((tp+(1<<(idx+2))))[:20], hex(tq+(2<<idx))[:20], hex(N)[:20])
        return 
        
    if idx<=90:
        try:
            f = tp + x 
            rr = f.monic().small_roots(X=2^90, beta=0.4)
            if rr != []:
                print(rr)
                print(tp)
                print('p =',f(rr[0]))
                ok = True
                return
        except:
            pass
        
        return
    
    idx -=1
    b = tgift >>idx 
    one = 1<<idx
    
    #print(hex(tp)[:20],hex(tq)[:20],hex(tgift)[:20],idx,b)
    
    if b==0 or b==1:
        pq_add(tp,tq,tgift,idx)    
    if b==1 or b==2:
        pq_add(tp+one,tq,tgift-one,idx)
        pq_add(tp,tq+one,tgift-one,idx)
    if b==2 or b==3:
        pq_add(tp+one,tq+one,tgift-(one<<1),idx)
    
tp = 1<<511
tq = 1<<511
tgift = gift -tp -tq 
pq_add(tp,tq,tgift,511)
'''
[700928575142400370721495237]
12795781443930923314957498667934210380943999998587811382302993343213015802362418444051944067150691108651897157419444913377465524852577234852023651836887040
p = 12795781443930923314957498667934210380943999998587811382302993343213015802362418444051944067150691108651897157419444913377465525553505809994424022558382277
'''
from Crypto.Util.number import *

e = 65537
c = 101383046356447336426623798470530695448361708798731382238747567108067236241251384089401506320741815081024352908156466877907424203888923965647318146770258139921360377246187637085549628797640957048672797430217647039035455011311505942632107576730906489223641894279483592789523228409885925263914621255862261546919
n = 131097719698687108485813302886652389604731026998272796315024695395496199386497660846418712521921387496051077394308820230360184411431376692252923609505060476542577219656866593501271690536991944882324175509626138475159461332403161471880082192150081456601522403673111515117219716055561941951891570977025178643791
p = 12795781443930923314957498667934210380943999998587811382302993343213015802362418444051944067150691108651897157419444913377465525553505809994424022558382277
q = n // p
#10245385971395060175404025994260899026018327732803454139079733202684248559362997918630991266112903887717159293307112056037936650791910220424100097140852483
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
#BuildCTF{M@y_b3_S0m3th1ng_go_wr0ng}

题目二

from Crypto.Util.number import bytes_to_long, getPrime, inverse
from secret import flag


def genKeys(nbits):
    e = 0x10001
    p = getPrime(nbits // 2)
    q = getPrime(nbits // 2)
    n = p * q
    phi = n - (p + q) + 1
    d = inverse(e, phi)
    pubkey = (n, e)
    prikey = (d, p, q)
    
    return pubkey, prikey


def encrypt(msg, pubkey):
    m = bytes_to_long(msg)
    n, e = pubkey
    c = pow(m, e, n)
    return c


def get_gift(prikey):
    a = bytes_to_long(b'miniL')
    b = bytes_to_long(b'mini7')
    p, q = prikey[1:]
    phi = (p - 1)*(q - 1)
    giftp = p + a
    giftq = q + b
    gift = pow((giftp + giftq + a*b), 2, phi)
    return gift >> 740


if __name__ == "__main__":
    nbits = 1024
    pubkey, prikey = genKeys(nbits)
    c = encrypt(flag, pubkey)
    gift = get_gift(prikey)
    with open('output.txt', 'a') as f:
        f.write('pubkey = ' + str(pubkey) + '\n')
        f.write('c = ' + str(c) + '\n')
        f.write('gift = ' + str(gift) + '\n')

'''
pubkey = (65537,103894244981844985537754880154957043605938484102562158690722531081787219519424572416881754672377601851964416424759136080204870893054485062449999897173374210892603308440838199225926262799093152616430249061743215665167990978654674200171059005559869946978592535720766431524243942662028069102576083861914106412399)
c = 50810871938251627005285090837280618434273429940089654925377752488011128518767341675465435906094867261596016363149398900195250354993172711611856393548098646094748785774924511077105061611095328649875874203921275281780733446616807977350320544877201182003521199057295967111877565671671198186635360508565083698058
gift = 2391232579794490071131297275577300947901582900418236846514147804369797358429972790212
'''

解题思路

  • 已知giftp = p + a giftq = q + b gift = pow((giftp + giftq + a*b), 2, phi)
  • gift = (p + q + a + b + a * b)^2 % phi
  • C = a + b + a * b
  • gift = (p + q + C)^2 - k*phi
from Crypto.Util.number import *
a = bytes_to_long(b'miniL') # 469920278860
b = bytes_to_long(b'mini7') # 469920278839
C = a + b + a * b # 220825068474931677601239
print(len(bin(a))) # 41
print(len(bin(b))) # 41
print(len(bin(C))) # 80
  • 这里的C80位,因此在题右偏中已抵消了,再左偏回去就是(p+q)^2的高位(还是差很多)
  • 通过将<font style="background-color:rgb(236, 236, 236);">gift << 740</font>视为某个平方数的高位,计算其平方根近似值<font style="background-color:rgb(236, 236, 236);">a</font>(对应<font style="background-color:rgb(236, 236, 236);">p - q</font>的估值),基于公式(p-q)^2 = <font style="background-color:rgb(236, 236, 236);">(p+q)^2 - 4pq</font>(其中<font style="background-color:rgb(236, 236, 236);">pq = n</font>),但是需要注意这里的gift实际上减去了k倍的phi,因此无需再-4n
  • 计算<font style="background-color:rgb(236, 236, 236);">(gift << 740) + 4n</font>的平方根得到<font style="background-color:rgb(236, 236, 236);">b</font>(对应<font style="background-color:rgb(236, 236, 236);">p + q</font>的估值),基于公式<font style="background-color:rgb(236, 236, 236);">(p+q)^2 = (p-q)^2 + 4pq</font>(其中<font style="background-color:rgb(236, 236, 236);">pq = n</font>)
  • 接着a+b//2得到p的近似值
  • 然后就是p的高位攻击求解即可

解答二

import gmpy2

n = 103894244981844985537754880154957043605938484102562158690722531081787219519424572416881754672377601851964416424759136080204870893054485062449999897173374210892603308440838199225926262799093152616430249061743215665167990978654674200171059005559869946978592535720766431524243942662028069102576083861914106412399
c = 50810871938251627005285090837280618434273429940089654925377752488011128518767341675465435906094867261596016363149398900195250354993172711611856393548098646094748785774924511077105061611095328649875874203921275281780733446616807977350320544877201182003521199057295967111877565671671198186635360508565083698058
gift = 2391232579794490071131297275577300947901582900418236846514147804369797358429972790212

a = int(gmpy2.iroot(gift << 740, 2)[0])  # p-q
b = int(gmpy2.iroot((gift << 740) + 4*n, 2)[0])  # p+q
high = (a + b)//2

for i in range(2**10):
    tmp = ((high >> 235 << 10) + i) << 225
    R.<x> = PolynomialRing(Zmod(n))
    f = tmp + x
    res = f.small_roots(X=2**225, beta=0.4)
    if res != []:
        print(res)
        break

p = int(tmp + res[0])
q = n // p
assert p*q == n
d = pow(65537, -1, (p-1)*(q-1))
m = pow(c,d,n)
c = bytes.fromhex(hex(m)[2:])
print(c)
#miniL{D0_Y@U_Li)e_T&@_RRRSA??}
posted @ 2025-05-10 18:26  sevensnight  阅读(60)  评论(0)    收藏  举报