ctf-writeups

4sa

附件:

from Crypto.Util.number import getPrime, isPrime, bytes_to_long

p1 = getPrime(512)
q1 = getPrime(512)

p2 = p1 + 1
q2 = q1 + 1
while not isPrime(p2):
    p2 += 1
while not isPrime(q2):
    q2 += 1
    
n = p1 * q1 * p2 * q2
e = 0x10001

flag = b"flag{********************}"
cipher = pow(bytes_to_long(flag), e, n)


print(n, cipher)
"""
16043540260711155677255538751089366052043709595227783852052656066597002774407800122418007523362396728183266465761392565042428599355725500826143539420140456329895849730908022450275066086440965293522897107194146134290754743462502643795143263235203956531033501481970113556323667497120218175024737224022249204376993498195979835136345239487464596152504444779379295276044877361199024987337843924242871234441022266042777175260303907206315473401337359800612700541755058981449179303221570098930479897624992090983015909248329973983829662506812701491729279714342396205919529925242196142897730112916106987201487845243489306705801 2066115663525746527006627359468128590893825280358396250104208720689490275374897638123457041234796490924868928927502724180110038497520643860784458150587807106541897109745721030614711936742403635641095902699269742164975578028469069698973292309477453808933330564169489658364371933906178410011496479652100451402540714190682735935110606874460167002975309353684815782160263967640258741617713920964735873147902859161068038608313491537620215756103436033073775233266496545167522336487886276048486169352197432827513155409694413942465378979586580718575345608052451570799077424423767881137206427719277073748039170388976335980108
"""

题目生成了两个素数 p1q1,分别求出它们的下一个素数 p2q2,给定 p1 * p2 * q1 * q2,求分解。

@Sceleri 给出了思路:

n开根后跟(p1*q2+p2*q1)/2很近,然后解方程得出p2*q1的近似值,然后coppersmith得到p2*q1的准确值,后面随便枚举一下p2-p1

实测下来,根号 n 与 (p1 * q2 + p2 * q1) / 2 确实很接近,所以可以枚举。枚举时,就可以求出 p1 * q2p2 * q1,再根据 p1p2 很接近,q1q2 很接近的性质,枚举它们之间的差值,最终可以找到正确的分解。

攻击代码:

from Crypto.Util.number import *
import math
from pwn import *
import tqdm

if args.TEST:
    p1 = getPrime(512)
    q1 = getPrime(512)

    p2 = p1 + 1
    q2 = q1 + 1
    while not isPrime(p2):
        p2 += 1
    while not isPrime(q2):
        q2 += 1

    n = p1 * q1 * p2 * q2
    print("avg - sqrt", (p1 * q2 + p2 * q1) // 2 - math.isqrt(n))
    print("p2 - p1", p2 - p1)
    print("q2 - q1", q2 - q1)
    e = 0x10001
    flag = b"flag{fake_flag_for_testing}"
    cipher = pow(bytes_to_long(flag), e, n)
else:
    e = 0x10001
    n = 16043540260711155677255538751089366052043709595227783852052656066597002774407800122418007523362396728183266465761392565042428599355725500826143539420140456329895849730908022450275066086440965293522897107194146134290754743462502643795143263235203956531033501481970113556323667497120218175024737224022249204376993498195979835136345239487464596152504444779379295276044877361199024987337843924242871234441022266042777175260303907206315473401337359800612700541755058981449179303221570098930479897624992090983015909248329973983829662506812701491729279714342396205919529925242196142897730112916106987201487845243489306705801
    cipher = 2066115663525746527006627359468128590893825280358396250104208720689490275374897638123457041234796490924868928927502724180110038497520643860784458150587807106541897109745721030614711936742403635641095902699269742164975578028469069698973292309477453808933330564169489658364371933906178410011496479652100451402540714190682735935110606874460167002975309353684815782160263967640258741617713920964735873147902859161068038608313491537620215756103436033073775233266496545167522336487886276048486169352197432827513155409694413942465378979586580718575345608052451570799077424423767881137206427719277073748039170388976335980108

# observation: math.isqrt(n) is very close to (p1 * q2 + p2 * q1) // 2
for diff in tqdm.trange(1000000):
    # guess (p1 * q2 + p2 * q1) // 2
    avg = math.isqrt(n) + diff
    sum = avg * 2

    # given:
    # p1 * q2 + p2 * q1 = sum
    # p1 * q2 * p2 * q1 = n
    # solve p1 * q2:
    # x^2 - sum * x + n = 0
    delta = sum**2 - 4 * n
    if delta < 0:
        continue

    # got p1 * q2, p2 * q1
    x1 = (sum + math.isqrt(delta)) // 2
    x2 = (sum - math.isqrt(delta)) // 2
    if x1 * x2 != n or x1 + x2 != sum:
        continue
    print("Found possible x1, x2")

    # p2 - p1, q2 - q1 are both small
    # p2 = p1 + i
    # q2 = q1 + j
    # enumerate i+j
    for sum in tqdm.trange(0, 100000, 2):
        for j in range(2, sum, 2):
            i = sum - j

            # p1 * (q1 + j) = x1
            # q1 * (p1 + i) = x2
            # p1 * j - q1 * i = x1 - x2
            # p1 * j = x1 - x2 + q1 * i
            # (x1 - x2 + q1 * i) * (q1 + j) = x1 * j
            # i * q1 ** 2 + (x1 - x2 + i * j) * q1 + (x1 - x2) * j - x1 * j = 0
            delta = (x1 - x2 + i * j) ** 2 - 4 * i * ((x1 - x2) * j - x1 * j)
            if delta < 0:
                continue
            q1_recover = (-(x1 - x2 + i * j) + math.isqrt(delta)) // (2 * i)
            if q1_recover <= 0:
                continue

            q2_recover = q1_recover + j
            p1_recover = x1 // q2_recover
            p2_recover = p1_recover + i
            if p1_recover * p2_recover * q1_recover * q2_recover == n:
                print("Success")
                print("p1", p1_recover)
                print("p2", p2_recover)
                print("q1", q1_recover)
                print("q2", q2_recover)
                d = pow(
                    e,
                    -1,
                    (p1_recover - 1)
                    * (p2_recover - 1)
                    * (q1_recover - 1)
                    * (q2_recover - 1),
                )
                flag = long_to_bytes(pow(cipher, d, n))
                print(flag)
                exit(0)

输出:

 22%|█████████████████████████                                                                                       | 224132/1000000 [00:01<00:04, 171879.47it/s]Found possible x1, x2
 22%|█████████████████████████                                                                                       | 224132/1000000 [00:20<00:04, 171879.47it/sSuccess████████████████████████████████████████████████▋                                                                     | 20864/50000 [12:40<35:34, 13.65it/s]
p1 11625804744635154350371950732800383322411952133939525583724573459942114636120987807547656193712145536570486366194633820156730838351920778775000255332893323
p2 11625804744635154350371950732800383322411952133939525583724573459942114636120987807547656193712145536570486366194633820156730838351920778775000255332914187
q1 10894996110141523785973254710653587130206370977430192463414696444537186690532925612365891389623700928168375231738096236398363290305482179680063492895631913
q2 10894996110141523785973254710653587130206370977430192463414696444537186690532925612365891389623700928168375231738096236398363290305482179680063492895652777
b'flag{c7b9f4e1-8b42-4df0-9a9d-1d2b3e4c9f76}'
 42%|█████████████████████████████████████████████████▋                                                                     | 20864/50000 [12:40<17:42, 27.43it/s]
 23%|██████████████████████████▍                                                                                        | 229437/1000000 [12:42<42:39, 301.09it/s]