for key in F[1:]: flag = "" for c in enc: i = ALPHABET.index(c) ii = F.index(F[i] / key) flag += ALPHABET[ii] if flag.endswith("=="): print(base64.b64decode(flag)) break
其實就如 flag 所說的,這個也只是 substitution cipher,只是是在 finite field 中的版本而已。
for a inrange(256): for b inrange(256): key = key_prefix + bytes([a, b]) xpt = decrypt_ecb(ct, key) enc1 = xor(xpt, pt) iv = xor(decrypt_ecb(enc1, key), pt) flag = decrypt(enc_flag, iv, key) ifall(20 <= x < 127for x in flag): print(flag)
Symbols
這題只給了你一個圖片,裡面有很多數學符號,看起來像是 substitution cipher。
我的解法是用 Detexify 去查 symbol 的 LaTeX command,然後可以注意到 command 的第一個字元應該就是 flag 的字元,所以最後找出了 CCTF{Play_with_LaTeX}。
medium-easy
Rima
這題是把 flag 變為 0 1 陣列,然後做一些移位的加法去 encode,不過他的程式碼很難讀所以我也放棄去理解那個 code,直接上 z3 就能解了。
f = [Int(f"f_{i}") for i inrange(8 * 32 - 1)] f0 = list(f)
f.insert(0, 0) for i inrange(len(f) - 1): f[i] += f[i + 1]
a = next_prime(len(f)) b = next_prime(a)
g, h = [[_ for i inrange(x) for _ in f] for x in [a, b]]
c = next_prime(len(f) >> 2)
for _ in [g, h]: for __ inrange(c): _.insert(0, 0) for i inrange(len(_) - c): _[i] += _[i + c]
sol = Solver() for x in f0: sol.add(Or(x == 0, x == 1)) sol.add(And([x == y for x, y inzip(g, gg)])) sol.add(And([x == y for x, y inzip(h, hh)])) assert sol.check() == sat m = sol.model() f = [m.eval(x).as_long() for x in f0] f = int("".join(map(str, f))[::-1], 2) print(long_to_bytes(f))
Hyper Normal
這題會先把 flag encode 成一個 的矩陣 然後打亂,假設 flag 的字元是 ,那麼矩陣 會先變為:
from sage.allimport * from functools import reduce from operator import and_ import random
p = 8443
deftranspose(x): result = [[x[j][i] for j inrange(len(x))] for i inrange(len(x[0]))] return result
defvsum(u, v): assertlen(u) == len(v) l, w = len(u), [] for i inrange(l): w += [(u[i] + v[i]) % p] return w
defsprod(a, u): w = [] for i inrange(len(u)): w += [a * u[i] % p] return w
defencrypt(msg): l = len(msg) hyper = [ord(m) * (i + 1) for (m, i) inzip(list(msg), range(l))] V, W = [], [] for i inrange(l): v = [0] * i + [hyper[i]] + [0] * (l - i - 1) V.append(v) random.shuffle(V) # V = [V[1],V[0],V[3],V[2]] print(V) for _ inrange(l): R, v = [random.randint(0, 126) for _ inrange(l)], [0] * l for j inrange(l): v = vsum(v, sprod(R[j], V[j])) W.append(v) print(R, v) random.shuffle(transpose(W)) return W
l = len(W) Z = GF(p) W = Matrix(Z, W)
ccand = [] for k inrange(l): cand = [set() for _ inrange(l)] for i inrange(1, 126): v = vector(x / i for x in W[k]) for j inrange(len(v)): cand[j].add(v[j]) ccand.append(cand)
tccand = transpose(ccand) flag = "" for i inrange(l): # tccand[i] = tccand[i][1::2] # without this line: CCTF{0w_f1Nd_th3_4lL_3I9EnV4Lu35_iN_FiN173_Fi3lD5 ???} # with this line: CCTF{H0w_f1Nd_th3_4l _3I9EnV4Lu35_iN_FiN173_Fi3lD5!???} # so we have the full flag
ss = tccand[i][0] j = 1 whilelen(ss) > 1: ss = ss & tccand[i][j] j += 1 iflen(ss) > 0: c = list(ss)[0] // (i + 1) else: c = ord(" ") flag += chr(c) print(flag)
n = 98027132963374134222724984677805364225505454302688777506193468362969111927940238887522916586024601699661401871147674624868439577416387122924526713690754043 c = 42066148309824022259115963832631631482979698275547113127526245628391950322648581438233116362337008919903556068981108710136599590349195987128718867420453399
k = 246389259423689900229483388850933720271363907782961941639413620688310657308991195363798484778005049234253341752674411282663124201840620584781830032437353134292733496201534415265400175632719346764031781179041636 # xy-x+y-b=0 # (y-1)(x+1)=b-1 b = 992752253935874779143952218845275961347009322164731344882417010624071055636710540798045985678351986133613
# big factors are factored using FactorDB f1 = 1357459302115148222329561139218955500171643099 f2 = 17258104558019725087 f3 = 2035395403834744453
defdivs(f): # copied from sage output = [1] for p, e in f: prev = output[:] pn = 1 for i inrange(e): pn *= p output.extend(a * pn for a in prev) output.sort() return output
for d in divs(fs): x, y = d - 1, (b - 1) // d + 1 assert (y - 1) * (x + 1) == b - 1 flag = long_to_bytes(x) + long_to_bytes(y) ifb"CCTF"in flag: print(flag) break
d = ( 83818189125408135328873033373544374818199783983 * 34388019287720328558025059428347529 * 10803954241 ) assert d < mn assert (l + 1) % d == 0 e = (l + 1) // d assert e < mn assert e * d % phi(k1) == 1 assert e * d % phi(k2) == 1 assert e * d % phi(k3) == 1
withopen("output.txt") as f: lines = [line.strip() for line in f.readlines() if"="notin line] vecs = [[int(x) for x in line[1:-1].split(" ") if x] for line in lines] E = matrix(GF(p), vecs[:11]) LUL = matrix(GF(p), vecs[11:22]) L1S2L = matrix(GF(p), vecs[22:33]) R1S8 = matrix(GF(p), vecs[33:44])
U = L1S2L * LUL ^ -1 S2 = LUL * U R = (R1S8 * S2 ^ -4) ^ -1 A = U ^ -1 * E - R print(A)
io = remote("05.cr.yp.toc.tf", 14010) io.sendlineafter(b"Options", b"G") io.recvuntil(b"Parameters = ") n, f, v = ast.literal_eval(io.recvlineS().strip()) assert f % 2 == 0 g = pow(48763, n, n * n) x = pow(g, f // 2, n * n) y = n * n - x assertpow(x, f, n * n) == 1 assertpow(y, f, n * n) == 1 print(x) print(y) io.sendlineafter(b"Options", b"R") io.sendlineafter(b"comma: ", f"{x},{y}".encode()) print(io.recvallS())
e = 65537 a = 769 b = 90 # (p - a)**2 + (q - b)**2 == k**7 k = 533349483431826854866479442416204129077526048835547852310509534957185 c = 4478819143432789024587861603523572305269547479550443133641110109373270566470025946722977115602647046295004476694988416461505550664119915082335497331912881526446940124404687029541487759747406116312872601161581904176763818623358120927587871262018474674411074996384180525486478668863914557062661081721929081678057785839028975581815732964462013512812566725502749216649190469493027431158255717939171221374546082898410798277258418126725070247145397363980604758633071972900958843430904130 p, q = var("p q") assume(p, "integer") assume(q, "integer") sol = solve(p ** 2 + q ** 2 == k, p, q) sol = [(Integer(p), Integer(q)) for p, q in sol if p > 0and q > 0]
defmul(r, s): # https://en.m.wikipedia.org/wiki/Brahmagupta%E2%80%93Fibonacci_identity a, b = r c, d = s x1 = a * c - b * d y1 = a * d + b * c x2 = a * c + b * d y2 = a * d - b * c return (abs(x1), abs(y1)), (abs(x2), abs(y2))
defsqr(r): return mul(r, r)[0]
for r in sol: r2 = sqr(r) r4 = sqr(r2) for r3 in mul(r, r2): for r7 in mul(r4, r3): p, q = r7 assert p ^ 2 + q ^ 2 == k ^ 7 p += a q += b if isPrime(p) and isPrime(q): print(p, q) n = p * q phi = (p - 1) * (q - 1) d = inverse_mod(e, phi) m = power_mod(c, d, n) print(long_to_bytes(m)) exit()
whileTrue: a, b, p = gen_anomalous(160) q = nextPrime(p) mxq = max([p for p, e in factor(EllipticCurve(GF(q), [a, b]).order())]) if mxq < 2 ^ 40: print(a, b) print(p, q) break print(log(mxq, 2).n())
a, b = ( 823375435824563939268788611110428744408176719161, 128395071361889866257668408194030595816566744161, ) p, q = ( 890662999704698873790151198748536866796172800897, 890662999704698873790151198748536866796172800963, ) Ep = EllipticCurve(GF(p), [a, b]) Eq = EllipticCurve(GF(q), [a, b])
assert Ep.order() == p
defsmart_attack(P, G, p): # https://crypto.stackexchange.com/a/70508 E = G.curve() Eqp = EllipticCurve(Qp(p, 2), [ZZ(t) + randint(0, p) * p for t in E.a_invariants()])
P_Qps = Eqp.lift_x(ZZ(G.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == G.xy()[1]: break
Q_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == P.xy()[1]: break
from Crypto.Util.number import * from pwn import remote from sage.matrix.matrix2 import Matrix import ast
defresultant(f1, f2, var): # Copied from https://jsur.in/posts/2021-07-19-google-ctf-2021-crypto-writeups#h1 return Matrix.determinant(f1.sylvester_matrix(f2, var))
io = remote("03.cr.yp.toc.tf", 25010) io.sendlineafter(b"Options", b"S") io.recvuntil(b"p = ") p = int(io.recvlineS().strip()) io.recvuntil(b"r = ") r = int(io.recvlineS().strip()) io.sendlineafter(b"Options", b"P") io.recvuntil(b"pubkey = ") pubkey = ast.literal_eval(io.recvlineS().strip()) print(p, r) print([hex(x) for x in pubkey])
ks = [pow(r, c + 1, p) for c inrange(0, 5)] Z = Zmod(p) PP = PolynomialRing(Z, "s,x1,x2") s, *xs = PP.gens() f1 = ks[0] * s - (pubkey[0] + xs[0]) f2 = ks[1] * s - (pubkey[1] + xs[1]) f = PP.remove_var(s)(resultant(f1, f2, s)) print(f) load("~/workspace/coppersmith.sage") xs = small_roots(f, (2 ** 40, 2 ** 40), m=4, d=2)[0] s = Z(pubkey[0] + xs[0]) / ks[0] assert s == Z(pubkey[1] + xs[1]) / ks[1] print(s) U = [pow(r, c + 1, p) * s % p for c inrange(0, 5)] S = [int(bin(u)[2:][-32:], 2) for u in U] print([hex(x) for x in S])
defsign(msg, privkey, d): msg = msg.encode("utf-8") l = len(msg) // 4 M = [bytes_to_long(msg[4 * i : 4 * (i + 1)]) for i inrange(l)] q = int(next_prime(max(M))) sign = [M[i] * privkey[i] % q for i inrange(l)] return sign
p = 512156828103365901048559505103237592010730651992953680223000172094095757203886681225415426450387040031253612295400192069437985566674257501701743368686395531 u = 278331424202715529007235225803083105831898177238768088959896273495587346471193489462465390715976004916539200837444755811532762335472120306642106779851779895 v = 256568421561256620412008753530664775781745915184311927713911832406042827934333342438038088202080460367979454347035971495645785213472938457309904032230294302 w = 291075584404341211571864039572762475831612407000784732318354539887997569888845019102264165940156056600333692694871997403708476839090241830333179131216949773 ca, cb, cc = ( 287555353447321752440570170565916634951105240901098656962539161570704086923490274350537706807786632105173520254109700863175418950150190301275461066194290273, 61323835393791353232128681044112245885676777670178659696073015541712435637029773641799576432645407470518574659302988690944012552307615441231640649288656467, 24320377817820710179555605271052810810335284586923974087878926553551229844983654115310868607990402619355506091875700785511303195656702348392817435407241231, )
Z = Zmod(p)
r = discrete_log(Z(ca), Z(u)) print(r) s = discrete_log(Z(cb), Z(v)) print(s) wrs = Z(w) ** (r + s) m = Z(cc) / wrs print(long_to_bytes(m))
defsolve_xy(): P = PolynomialRing(QQ, "x,y") x, y = P.gens() I = P.ideal([y ^ 2 + x - s1, y ^ 4 - x * y ^ 2 + x ^ 2 - s2]) V = I.variety() for sol in V: yield (Integer(sol[x]), Integer(sol[y]))
defsolve_z(x, y): P = PolynomialRing(ZZ, "z") z = P.gen() f = x ^ 4 + y ^ 5 + x * y * z - k2 rs = f.roots() iflen(rs) > 0: return Integer(rs[0][0])
for x, y in solve_xy(): z = solve_z(x, y) if z isNone: continue print(x, y, z) assert2 * z ** 5 - x ** 3 + y * z == k1 assert x ** 4 + y ** 5 + x * y * z == k2 assert y ** 6 + 2 * z ** 5 + z * y == k3 p = nextPrime(x ** 2 + z ** 2 + y ** 2 << 76) print(p) q = nextPrime(z ** 2 + y ** 3 - y * x * z ^^ 67) print(q) n, e = p * q, 31337 d = inverse_mod(e, (p - 1) * (q - 1)) print(long_to_bytes(power_mod(c, d, n)))
from sage.allimport * from Crypto.Util.number import *
n = 43216667049953267964807040003094883441902922285265979216983383601881964164181 U = 18230294945466842193029464818176109628473414458693455272527849780121431872221 V = 13100009444194791894141652184719316024656527520759416974806280188465496030062 W = 5543957019331266247602346710470760261172306141315670694208966786894467019982
p = 190116434441822299465355144611018694747 q = n // p
Ep = EllipticCurve(GF(p), [0, U, 0, V, W]) Eq = EllipticCurve(GF(q), [0, U, 0, V, W]) Gp = Ep( 6907136022576092896571634972837671088049787669883537619895520267229978111036, 35183770197918519490131925119869132666355991678945374923783026655753112300226, ) Gq = Eq( 6907136022576092896571634972837671088049787669883537619895520267229978111036, 35183770197918519490131925119869132666355991678945374923783026655753112300226, ) sGp = Ep( 14307615146512108428634858855432876073550684773654843931813155864728883306026, 4017273397399838235912099970694615152686460424982458188724369340441833733921, ) sGq = Eq( 14307615146512108428634858855432876073550684773654843931813155864728883306026, 4017273397399838235912099970694615152686460424982458188724369340441833733921, )
deffind_embedding_degree(E): p = E.base().order() n = E.order() if p == n: # anomalous curve return0 for k inrange(1, 13): if (p ** k - 1) % n == 0: return k
defsmart_attack(P, G, p): # https://crypto.stackexchange.com/a/70508 E = G.curve() Eqp = EllipticCurve(Qp(p, 2), [ZZ(t) + randint(0, p) * p for t in E.a_invariants()])
P_Qps = Eqp.lift_x(ZZ(G.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == G.xy()[1]: break
Q_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == P.xy()[1]: break
print( gcd( resultant(resultant(P, tQ, c), resultant(sP, Q, c), d), resultant(resultant(P, Q, c), resultant(sP, tQ, c), d), ) ) # Then factordb get p = 903968861315877429495243431349919213155709
defresultant(f1, f2, var): # Copied from https://jsur.in/posts/2021-07-19-google-ctf-2021-crypto-writeups#h1 return Matrix.determinant(f1.sylvester_matrix(f2, var))
p = 903968861315877429495243431349919213155709 P.<c, d> = GF(p)[]
defpoint(u, v): return u ** 2 + v ** 2 - c ** 2 * (1 + d * u ** 2 * v ** 2) P = point( 398011447251267732058427934569710020713094, 548950454294712661054528329798266699762662, ) Q = point( 139255151342889674616838168412769112246165, 649791718379009629228240558980851356197207, ) sP = point( 730393937659426993430595540476247076383331, 461597565155009635099537158476419433012710, ) tQ = point( 500532897653416664117493978883484252869079, 620853965501593867437705135137758828401933, ) dd = resultant(P,Q,c).univariate_polynomial().roots()[0][0] cc = resultant(P,Q,d).univariate_polynomial().roots()[0][0] print(cc, dd)
現在 都有了,問題在於 sage 的 EllipticCurve 不支援 Edwards Curve。其實去 wiki 就能查到一句話 Every Edwards curve is birationally equivalent to an elliptic curve in Weierstrass form,所以代表有辦法把曲線轉換成 Weierstrass form 之後讓 sage 來處理。
我首先是在這邊有找到把 形式的 Edwards Curve 轉換回 Weierstrass form 的公式,問題在於這條曲線有多出一個參數 。之後繼續查下去可以發現這種有 參數形式的是另外兩個人發明的,去找到了他們原本的 paper 就在第五頁看到了 和 互相轉換的公式。