Home buckeye CTF 2022 writeup
Post
Cancel

buckeye CTF 2022 writeup

初めに

Nu1L ctf でpocかけなくて撃沈。。。

[crypto] megaxord [312 solve]

chall

bytesのファイルのみ

solve

順に探索して終わり

1
2
3
4
5
6
7
8
9
10
11
12
13
def bxor(a,b):
    return bytes([_a^b for _a in a])

f = open("megaxord.txt","rb").read()
for i in range(256):
    if b"buckeye{" in bxor(f,i):
        for x in bxor(f,i).decode().split(" "):
            if "buckeye{" in x:
                print(x)
                exit()
                
# buckeye{m1gh7y_m0rph1n_w1k1p3d14_p4g3}

[crypto] Twin prime RSA [ 167 solve]

chall

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 Crypto.Util.number as cun

while True:
    p = cun.getPrime(1024)
    q = p + 2
    if cun.isPrime(q):
        break

n = p * q
e = 0x10001

phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)

FLAG = cun.bytes_to_long(b"buckeye{?????????????????????????????????????????????????????????????}")
c = pow(FLAG, e, n)
assert pow(c, d, n) == FLAG

print(f"n = {n}")
print(f"c = {c}")

"""
Output:
n = 20533399299284046407152274475522745923283591903629216665466681244661861027880216166964852978814704027358924774069979198482663918558879261797088553574047636844159464121768608175714873124295229878522675023466237857225661926774702979798551750309684476976554834230347142759081215035149669103794924363457550850440361924025082209825719098354441551136155027595133340008342692528728873735431246211817473149248612211855694673577982306745037500773163685214470693140137016315200758901157509673924502424670615994172505880392905070519517106559166983348001234935249845356370668287645995124995860261320985775368962065090997084944099
c = 786123694350217613420313407294137121273953981175658824882888687283151735932871244753555819887540529041840742886520261787648142436608167319514110333719357956484673762064620994173170215240263058130922197851796707601800496856305685009993213962693756446220993902080712028435244942470308340720456376316275003977039668016451819131782632341820581015325003092492069871323355309000284063294110529153447327709512977864276348652515295180247259350909773087471373364843420431252702944732151752621175150127680750965262717903714333291284769504539327086686569274889570781333862369765692348049615663405291481875379224057249719713021
"""

solve

二次方程式組み立てて$p,q$求めて終わり
最近グレブナーでサボってたから真面目にやりましたまる…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from  Crypto.Util.number import *
from gmpy2 import iroot
n = 20533399299284046407152274475522745923283591903629216665466681244661861027880216166964852978814704027358924774069979198482663918558879261797088553574047636844159464121768608175714873124295229878522675023466237857225661926774702979798551750309684476976554834230347142759081215035149669103794924363457550850440361924025082209825719098354441551136155027595133340008342692528728873735431246211817473149248612211855694673577982306745037500773163685214470693140137016315200758901157509673924502424670615994172505880392905070519517106559166983348001234935249845356370668287645995124995860261320985775368962065090997084944099
c = 786123694350217613420313407294137121273953981175658824882888687283151735932871244753555819887540529041840742886520261787648142436608167319514110333719357956484673762064620994173170215240263058130922197851796707601800496856305685009993213962693756446220993902080712028435244942470308340720456376316275003977039668016451819131782632341820581015325003092492069871323355309000284063294110529153447327709512977864276348652515295180247259350909773087471373364843420431252702944732151752621175150127680750965262717903714333291284769504539327086686569274889570781333862369765692348049615663405291481875379224057249719713021

p = (-2+iroot(4+4*n,2)[0])//2
q =n//p

assert n == p*q

phi = (p-1)*(q-1)
e = 0x10001
print(long_to_bytes(pow(c, pow(e,-1,phi), n)))
# buckeye{B3_TH3R3_OR_B3_SQU4R3__abcdefghijklmonpqrstuvwxyz__0123456789}

[crypto] fastfor [ 111 solve]

chall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from PIL import Image
import numpy

def check_hash(fi):
    image = numpy.asarray(Image.open('static/IMG.png'))
    submission = numpy.asarray(Image.open(fi))
    if image.shape != submission.shape:
        return False
    same = numpy.bitwise_xor(image, submission)
    if (numpy.sum(same) == 0):
        return False
    im_alt = numpy.fft.fftn(image)
    in_alt = numpy.fft.fftn(submission)
    im_hash = numpy.std(im_alt)
    in_hash = numpy.std(in_alt)
    if im_hash - in_hash < 1 and im_hash - in_hash > -1:
        return True
    return False

solve

2つの画像の入力から近い標準偏差の値を求めろらしいです。
お試し感覚でIMG.pngの0,0チャンクの値を+1したものを突っ込んだらフラグ出た…

1
2
3
4
5
6
7
8
9
10
11
import check_hash
from PIL import Image

img2 = Image.open('static/IMG.png')

img2.putpixel((0,0),(159, 227, 255, 118))
img2.save("test.png")

print(check_hash.check_hash("test.png"))
# buckeye{D33p_w0Rk_N07_WhY_574ND4RD_d3V}

[crypto] powerball [ 78 solve]

chall

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import express from 'express'
import http from 'http'
import { Server } from 'socket.io'
import crypto from 'crypto'

function nextRandomNumber () {
  return (multiplier * seed) % modulus
}

function areArraysEqual (a, b) {
  return (
    a.length === b.length &&
    a.every((x, i) => {
      return x === b[i]
    })
  )
}

function seedToBalls (n) {
  const balls = []
  for (let i = 0; i < 10; i++) {
    balls.push(Number(n % 100n))
    n = n / 100n
  }
  return balls
}

const app = express()
app.use(express.static('static'))

const server = http.createServer(app)
const io = new Server(server)

const modulus = crypto.generatePrimeSync(128, { safe: true, bigint: true })
const multiplier = (2n ** 127n) - 1n
let seed = 2n
for (let i = 0; i < 1024; i++) {
  seed = nextRandomNumber()
}
let winningBalls = seedToBalls(seed)
let lastLotteryTime = Date.now()

setInterval(() => {
  seed = nextRandomNumber()
  winningBalls = seedToBalls(seed)
  lastLotteryTime = Date.now()
}, 60 * 1000)

io.on('connection', (socket) => {
  socket.ticket = { balls: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], submissionTime: 0 }
  
  socket.on('updateRequest', () => {
    let flag = ''

    if (
      areArraysEqual(socket.ticket.balls, winningBalls) &&
      socket.ticket.submissionTime < lastLotteryTime
    ) {
      flag = process.env.FLAG
    }

    socket.emit('update', {
      last_winning_seed: seed.toString(),
      flag: flag
    })
  })

  socket.on('submitBalls', (balls) => {
    if (!(Array.isArray(balls) && balls.length === 10)) return
    for (let i = 0; i < 10; i++) {
      if (typeof balls[i] !== 'number') return
    }

    socket.ticket = { balls: balls, submissionTime: Date.now() }
    
  })
})

server.listen(3000, () => {
  console.log('Ready')
})


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
58
59
60
61
62
63
64
65
const socket = io() // eslint-disable-line no-undef
let seenFlag = false

function seedToBalls (n) {
  const balls = []
  for (let i = 0; i < 10; i++) {
    balls.push(Number(n % 100n))
    n = n / 100n
  }
  return balls
}

function handleUpdate (update) {
  console.log(update)

  if (update.flag && !seenFlag) {
    alert(update.flag)
    seenFlag = true
  }

  const balls = seedToBalls(BigInt(update.last_winning_seed))
  for (let i = 0; i < 10; i++) {
    document.getElementById(`ball${i}`).innerText = balls[i]
  }
}

function initSocket () {
  socket.on('update', handleUpdate)
  socket.emit('updateRequest')

  setInterval(() => {
    console.log(Date.now() / 1000)
    socket.emit('updateRequest')
  }, 5000)
}

function sendBallsIfAvailable () {
  const balls = []
  for (let i = 0; i < 10; i++) {
    const a = parseInt(document.getElementById(`input-ball${i}`).value)
    if (isNaN(a) || a < 0 || a >= 100) return
    balls.push(a)
  }
  console.log(`Submitting balls ${balls}`)
  socket.emit('submitBalls', balls)
}

function initInput () {
  for (let i = 0; i < 10; i++) {
    document.getElementById(`input-ball${i}`).onkeypress = (event) => {
      const n = parseInt(event.key)
      if (isNaN(n)) return false
      setTimeout(sendBallsIfAvailable, 100)
    }
    document.getElementById(`input-ball${i}`).onpaste = (event) => {
      const n = event.clipboardData.getData('Text')
      if (isNaN(n)) return false
      setTimeout(sendBallsIfAvailable, 100)
    }
  }
}

initSocket()
initInput()

solve

個人的にはnode jsの仕様を理解するのに時間がかかった…(console.log使えると思ってなくてこれがeasyなわけないやろとか思ってたとか、思ってなかったとかorz)

クライアント側で動いているのはmain.jsなのでconsole.logからupdate.last_winning_seedを求めることができる。
よって二項間漸化式$ball_{n+1} \equiv a*ball_n \pmod p$から$p$の値が求まる。 この問題の目標として、画面に表示されている乱数よりも後の乱数を求めて入力する必要があるので、今が何項目か調べてやればOK

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
from sage.all import *
from Crypto.Util.number import *

def int_to_ball(a):
    ret = []
    for i in range(10):
        # print(a%100)
        ret.append(a%100)
        a = a//100
    return ret[::-1]

a = 2**127 -1
s1 = 38386045261155976433540741815806908550
s2 = 43312535513384515088100925378630654634
s3 = 201038737730550603713123190637463026163

nowball = 76544625579486203475251392218240548059

p = factor(gcd(s2*a-s3,s1*a-s2))[-1][0]

nowball = GF(p)(nowball)
win = GF(p)(2)
a = GF(p)(a)

for i in range(2**26):
    if int(win)==int(nowball):
        print(int_to_ball(int(win))[::-1])
        win =  win*a
        print(int_to_ball(int(win))[::-1])
        win =  win*a
        print(int_to_ball(int(win))[::-1])
        win =  win*a
        print(int_to_ball(int(win))[::-1])
        break
    win = win*a
# buckeye{y3ah_m4yb3_u51nG_A_l1N34r_c0nGru3Nt1al_G3n3r4t0r_f0r_P0w3rB4lL_wA5nt_tH3_b3st_1d3A}

[crypto] bounce [ 97 solve]

chall

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
import random

with open('sample.txt') as file:
    line = file.read()

with open('flag.txt') as file:
    flag = file.read()

samples = [line[i:i+28] for i in range(0, len(line) - 1 - 28, 28)]

samples.insert(random.randint(0, len(samples) - 1), flag)

i = 0
while len(samples) < 40:
    samples.append(samples[len(samples) - i - 2])
    i = random.randint(0, len(samples) - 1)

encrypted = []
for i in range(len(samples)):
    x = samples[i]
    if i < 10:
        nonce = str(i) * 28
    else:
        nonce = str(i) * 14
    encrypted.append(''.join(str(ord(a) ^ ord(b)) + ' ' for a,b in zip(x, nonce)))

with open('output.txt', 'w') as file:
    for i in range(0, 4):
        file.write('input: ' + samples[i] + '\noutput: ' + encrypted[i] + '\n')
    file.write('\n')
    for i in range(4, len(samples)):
        file.write('\ninput: ???\n' + 'output: ' + encrypted[i])

solve

そのまま突っ込んで終わり…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
f = open("output.txt").readlines()

part1_input = [f[i][7:-1] for i in range(0,8,2)]
part1_output = [[ k for k in  f[i+1][8:-2].split(" ") ]for i in range(0,8,2)]

part2_output = [[ int(k) for k in  f[i+1][8:-2].split(" ") ]for i in range(10,len(f),2)]
output = part1_output+part2_output

for i in range(len(output)):
    x = output[i]
    if i < 10:
        nonce = str(i) * 28
    else:
        nonce = str(i) * 14
    if "eye" in ''.join(chr(int(a) ^ ord(b)) for a,b in zip(x, nonce)):
        print(''.join(chr(int(a) ^ ord(b)) for a,b in zip(x, nonce)))

# buckeye{some_say_somefish:)}

[crypto] SSSHIT [ 41 solve]

cahll

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import Crypto.Util.number as cun
import random
import ast


def evaluate_polynomial(polynomial: list, x: int, p: int):
    return (
        sum(
            (coefficient * pow(x, i, p)) % p for i, coefficient in enumerate(polynomial)
        )
        % p
    )


N_SHARES = 3


def main():
    print(
        f"I wrote down a list of people who are allowed to get the flag and split it into {N_SHARES} using Shamir's Secret Sharing."
    )
    MESSAGE = cun.bytes_to_long(b"qxxxb, BuckeyeCTF admins, and NOT YOU")

    p = cun.getPrime(512)

    polynomial = [MESSAGE] + [random.randrange(1, p) for _ in range(N_SHARES - 1)]
    points = [(i, evaluate_polynomial(polynomial, i, p)) for i in range(1, N_SHARES + 1)]

    print("Your share is:")
    print(points[0])
    print("The other shares are:")
    for i in range(1, len(points)):
        print(points[i])

    print()
    print("Now submit your share for reconstruction:")
    your_input = ast.literal_eval(input(">>> "))
    if (
        type(your_input) is not tuple
        or len(your_input) != 2
        or type(your_input[0]) is not int
        or type(your_input[1]) is not int
        or your_input[0] != 1
        or not (0 <= your_input[1] < p)
    ):
        print("Bad input")
        return

    points[0] = your_input

    xs = [point[0] for point in points]
    ys = [point[1] for point in points]

    y_intercept = 0
    for j in range(N_SHARES):
        product = 1
        for i in range(N_SHARES):
            if i != j:
                product = (product * xs[i] * pow(xs[i] - xs[j], -1, p)) % p
        y_intercept = (y_intercept + ys[j] * product) % p

    reconstructed_message = cun.long_to_bytes(y_intercept)
    if reconstructed_message == b"qxxxb, BuckeyeCTF admins, and ME":
        print("Here's your flag:")
        print("buckeye{?????????????????????????????????????????}")
    else:
        print(f"Sorry, only these people can see the flag: {reconstructed_message}")


main()

solve

2つの乱数を$r_1,r_2$とし、メッセージを$m$とすると最初の多項式の部分で$points_i = i^{2} *r_2 + i*r_1 + m \pmod p, (1 \leq i\leq 3)$を求めているが、それぞれのpointsの値はわかるのでそれをうまく用いることで$p$を復元でき、そこから$r_1,r_2$を求められる。
暗号を求めている部分を関数として、入力に、$points_i,i$を与え$f(points_1,points_2,points_3,1,2,3)$とできる。
ここで$points_1$はこちらで指定できる任意の値で出力として”qxxxb, BuckeyeCTF admins, and ME”を出すように変化させてやればOK

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
from pwn import *
from Crypto.Util.number import *
from sage.all import *

io = remote("pwn.chall.pwnoh.io" ,13382)
# io = process(["python3","chall.py"])

#295 bit
MESSAGE = bytes_to_long(b"qxxxb, BuckeyeCTF admins, and NOT YOU")
#255 bit
reconstructed_message = bytes_to_long(b"qxxxb, BuckeyeCTF admins, and ME")

def recover(points):
    y = points[1]
    p = factor(y[2]-3*y[1]+3*y[0]-MESSAGE)[-1][0]
    assert int(p).bit_length() == 512
    return p

def catch():
    points = [[],[]]

    io.recvline()
    io.recvline()
    p = eval(io.recvline().decode())
    points[0].append(p[0])
    points[1].append(p[1])
    io.recvline()
    p = eval(io.recvline().decode())
    points[0].append(p[0])
    points[1].append(p[1])
    p = eval(io.recvline().decode())
    points[0].append(p[0])
    points[1].append(p[1])
    return points

def calc(points,p):
    N_SHARES = 3
    
    xs =  points[0]
    y = [0]+points[1][1:]
    
    pro = 0
    pro += (xs[0]*pow(xs[0]-xs[1],-1,p)*xs[2]*pow(xs[2]-xs[1],-1,p)*y[1])%p
    pro += (xs[0]*pow(xs[0]-xs[2],-1,p)*xs[1]*pow(xs[1]-xs[2],-1,p)*y[2])%p
    y0 = ((reconstructed_message - pro)*pow(xs[1]*pow(xs[1]-xs[0],-1,p)*xs[2]*pow(xs[2]-xs[0],-1,p),-1,p))%p
    return y0

points = catch()
p = recover(points)
io.recvuntil(b"> ")
y0 = calc(points,int(p))

io.sendline(f"(1,{y0})".encode())
io.interactive()

# buckeye{tH1s_SSS_sch3Me_c0uLd_u5e_s0M3_S1gna7Ur3s}

[crypto] Quad prime RSA [ 23 solve]

chall

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
import Crypto.Util.number as cun

p = cun.getPrime(500)

while True:
    q = cun.getPrime(1024)
    r = q + 2
    if cun.isPrime(r):
        break

s = cun.getPrime(500)

n_1 = p * q
n_2 = r * s

e = 0x10001
d_1 = pow(e, -1, (p - 1) * (q - 1))
d_2 = pow(e, -1, (r - 1) * (s - 1))

FLAG = cun.bytes_to_long(b"buckeye{??????????????????????????????????????????????????????????????????????}")
c_1 = pow(FLAG, e, n_1)
c_2 = pow(FLAG, e, n_2)

assert pow(c_1, d_1, n_1) == FLAG
assert pow(c_2, d_2, n_2) == FLAG

print(f"n_1 = {n_1}")
print(f"n_2 = {n_2}")
print(f"c_1 = {c_1}")
print(f"c_2 = {c_2}")

"""
Output:
n_1 = 266809852588733960459210318535250490646048889879697803536547660295087424359820779393976863451605416209176605481092531427192244973818234584061601217275078124718647321303964372896579957241113145579972808278278954608305998030194591242728217565848616966569801983277471847623203839020048073235167290935033271661610383018423844098359553953309688771947405287750041234094613661142637202385185625562764531598181575409886288022595766239130646497218870729009410265665829
n_2 = 162770846172885672505993228924251587431051775841565579480252122266243384175644690129464185536426728823192871786769211412433986353757591946187394062238803937937524976383127543836820456373694506989663214797187169128841031021336535634504223477214378608536361140638630991101913240067113567904312920613401666068950970122803021942481265722772361891864873983041773234556100403992691699285653231918785862716655788924038111988473048448673976046224094362806858968008487
c_1 = 90243321527163164575722946503445690135626837887766380005026598963525611082629588259043528354383070032618085575636289795060005774441837004810039660583249401985643699988528916121171012387628009911281488352017086413266142218347595202655520785983898726521147649511514605526530453492704620682385035589372309167596680748613367540630010472990992841612002290955856795391675078590923226942740904916328445733366136324856838559878439853270981280663438572276140821766675
c_2 = 111865944388540159344684580970835443272640009631057414995719169861041593608923140554694111747472197286678983843168454212069104647887527000991524146682409315180715780457557700493081056739716146976966937495267984697028049475057119331806957301969226229338060723647914756122358633650004303172354762801649731430086958723739208772319851985827240696923727433786288252812973287292760047908273858438900952295134716468135711755633215412069818249559715918812691433192840
"""

solve

少し式変形してやると$n_1 = p*q, n_2 = (q+2)*r $となり少し展開して$n_1 = p*q, n_2 = q*r + 2*r $これは$n_i = p_i*q + r_i$の形で表せれられるので、Approximate GCD Problemと見て解くことができます…scriptはこれを使いました。便利…!!
実際は連分数を想定していたそう…???なるほど…

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
n_1 = 266809852588733960459210318535250490646048889879697803536547660295087424359820779393976863451605416209176605481092531427192244973818234584061601217275078124718647321303964372896579957241113145579972808278278954608305998030194591242728217565848616966569801983277471847623203839020048073235167290935033271661610383018423844098359553953309688771947405287750041234094613661142637202385185625562764531598181575409886288022595766239130646497218870729009410265665829
n_2 = 162770846172885672505993228924251587431051775841565579480252122266243384175644690129464185536426728823192871786769211412433986353757591946187394062238803937937524976383127543836820456373694506989663214797187169128841031021336535634504223477214378608536361140638630991101913240067113567904312920613401666068950970122803021942481265722772361891864873983041773234556100403992691699285653231918785862716655788924038111988473048448673976046224094362806858968008487
c_1 = 90243321527163164575722946503445690135626837887766380005026598963525611082629588259043528354383070032618085575636289795060005774441837004810039660583249401985643699988528916121171012387628009911281488352017086413266142218347595202655520785983898726521147649511514605526530453492704620682385035589372309167596680748613367540630010472990992841612002290955856795391675078590923226942740904916328445733366136324856838559878439853270981280663438572276140821766675
c_2 = 111865944388540159344684580970835443272640009631057414995719169861041593608923140554694111747472197286678983843168454212069104647887527000991524146682409315180715780457557700493081056739716146976966937495267984697028049475057119331806957301969226229338060723647914756122358633650004303172354762801649731430086958723739208772319851985827240696923727433786288252812973287292760047908273858438900952295134716468135711755633215412069818249559715918812691433192840
from Crypto.Util.number import *
# n1 = q*p + 0*s
# n2 = (q+2)+r = q*s + 2*s

# I use https://github.com/jvdsn/crypto-attacks/blob/master/attacks/acd/ol.py
def attack(x, rho):
    """
    Solves the ACD problem using the orthogonal based approach.
    More information: Galbraith D. S. et al., "Algorithms for the Approximate Common Divisor Problem" (Section 4)
    :param x: the x samples, with xi = p * qi + ri
    :param rho: the bit length of the r values
    :return: the secret integer p and a list containing the r values, or None if p could not be found
    """
    def symmetric_mod(x, m):
        """
        Computes the symmetric modular reduction.
        :param x: the number to reduce
        :param m: the modulus
        :return: x reduced in the interval [-m/2, m/2]
        """
        return int((x + m + m // 2) % m) - int(m // 2)
    
    assert len(x) >= 2, "At least two x values are required."

    R = 2 ** rho

    B = matrix(ZZ, len(x), len(x) + 1)
    for i, xi in enumerate(x):
        B[i, 0] = xi
        B[i, i + 1] = R

    B = B.LLL()

    K = B.submatrix(row=0, col=1, nrows=len(x) - 1, ncols=len(x)).right_kernel()
    q = K.an_element()
    r0 = symmetric_mod(x[0], q[0])
    p = abs((x[0] - r0) // q[0])
    r = [symmetric_mod(xi, p) for xi in x]
    if all(-R < ri < R for ri in r):
        return int(p), r
    
q,s = attack([n_1,n_2], 513)
p = n_1//q

phi = (p-1)*(q-1)
e = 0x10001
print(long_to_bytes(int(pow(c_1, pow(e,-1,phi), n_1))))

# buckeye{I_h0p3_y0u_us3D_c0nt1nu3d_fr4ct10Ns...th4nk5_d0R5A_f0r_th3_1nsp1r4t10n}


This post is licensed under CC BY 4.0 by the author.