這次我加入了 isanapap
和其他幾位台灣的 ctf crypto player 參加了今年的 Crypto
CTF,並且很幸運的拿下了第一名。這邊大概只會寫我認為比較值得寫的題目而已,且有寫的題目也不一定是我賽中有解的題目。
medium-easy
SOTS
這題沒有 source,用 nc 連上去之後會看到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| | Hey math experts, in this challenge we will deal with the numbers | | those are the sum of two perfect square, now try hard to find them! | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| | Generating the `n', please wait... | Options: | [G]et the n | [S]olve the challenge! | [Q]uit G | n = 4681699452900793124255588703544500598149979834311033888906299527764579361 | Options: | [G]et the n | [S]olve the challenge! | [Q]uit S | Send your pair x, y here:
簡單來說有個 ,要找到兩個正整數
符合 。而這個最簡單的解法是在 Google
一下之後會發現 sage 中有內建 two_squares
的函數,它正好就是解這個問題的。所以直接輸進去之後就能解開了。
n = 7069789930583271525053215046247773438899869283661158227309691853515987055334306019600324056376312479212090202373516405860759222837585952590589336295698718699890424169542280710721069784487366121478569760563045886361884895363592898476736269784284754788133722060718026577238640218755539268465317292713320841554802703379684173485217045274942603346947299152498798736808975912326592689302969859834957202716983626393365387411319175917999258829839695189774082810459527737342402920881184864625678296442001837072332161966439361793009893108796934406114288057583563496587655548536011677451960307597573257032154009427010069578913 c_1 = 488692928085934899944055554857568564903346089951134051486941368561567330884363274156339625953702601270565654444836193796061118053575538224794730472032345171432952984560662218697488844007827176184413713651118743456250147472678673801728916283759932987216388378211555067885210167894310696549664382751443669387953644382833924884208966436685137553434532738845959014828804809425096115758364136546390809453200055265653531950423111482644330073443545410319576097902472017235065047191133112557289289189187696092145850680765843608118584107829268136014912479701945735063525799796920293418182776436767911172221104640501952880057 c_2 = 109770827223661560471527567179288748906402603483328748683689436879660543465776899146036833470531024202351087008847594392666852763100570391337823820240726499421306887565697452868723849092658743267256316770223643723095601213088336064635680075206929620159782416078143076506249031972043819429093074684182845530529249907297736582589125917235222921623698038868900282049587768700860009877737045693722732170123306528145661683416808514556360429554775212088169626620488741903267154641722293484797745665402402381445609873333905772582972140944493849645600529147490903067975300304532955461710562911203871840101407995813072692212 e = 0x10001
a = 2 for p in primes(2 ^ 19): a = power_mod(a, p, n) p = gcd(a - 1, n) if1 < p < n: print(p) break q = n // p m_1 = power_mod(c_1, inverse_mod(e, (p - 1) * (q - 1)), n) m_2p = GF(p)(c_2).log(e) m_2q = GF(p)(c_2).log(e) m_2 = crt([ZZ(m_2p), ZZ(m_2q)], [p, q]) print(long_to_bytes(m_1) + long_to_bytes(m_2))
Diploma
這題也沒有 source,用 nc 連上去之後會看到
1 2 3 4 5 6 7 8 9 10 11 12
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| | Hi all, cryptographers know that the calculation of the order of a | | given element in a group is not easy at all. We are working in the | | group GL(d, p), the group of invertible matrices of order `d' on a | | finite field of order `p'. In each stage send the order matrix M. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| | Generating the parameters for p = 127, please wait... | M = [ 44 119 6] [123 30 99] [ 57 23 50] | Send the order of matrix M:
defmain(): border = "|" pr(border*72) pr(border, "Hello guys! This is a another challenge on fault attack too, again ", border) pr(border, "our storage could apply at most `l' bit fault on ElGamal modulus, p,", border) pr(border, "try to sign the given message and get the flag! Have fun and enjoy!!", border) pr(border*72) pr(border, "Generating the parameters, it's time consuming ...") nbit = 256 whileTrue: _p = getPrime(255) p = 2 * _p + 1 if isPrime(p): g = 2 ifpow(g, _p, p) != 1: break else: g += 1 x = getRandomRange(2, p // 2) y = pow(g, x, p)
B, l = [int(b) for b inbin(p)[2:]], 30 MSG = "4lL crypt0sy5t3ms suck5 fr0m faul7 atTaCk5 :P" m = bytes_to_long(MSG.encode('utf-8'))
whileTrue: pr("| Options: \n|\t[A]pply fault \n|\t[G]et the parameters \n|\t[S]ign the message \n|\t[V]erify the signature \n|\t[Q]uit") ans = sc().lower() if ans == 'a': _B = B pr(border, f"please send at most {l}-tuple array from indices of bits of ElGamal modulus, like: 5, 12, ...") ar = sc() try: ar = [int(_) for _ in ar.split(',')] iflen(ar) <= l: for i inrange(len(ar)): _B[ar[i]] = (_B[ar[i]] + 1) % 2 P = int(''.join([str(b) for b in _B]), 2) Y = pow(g, x, P) else: raise Exception('Invalid length!') except: pr(border, "Something went wrong!!") # omitted...
bits = [int(x) for x inbin(n)[2:]] print(bits) idxs = [i for i, b inenumerate(bits) if b] print(idxs) chk = 2 for grp in [idxs[i : i + chk] for i inrange(0, len(idxs), chk)]: print(grp) io.sendline(b"a") io.sendline(",".join(map(str, grp)).encode())
p = getPrime(n.bit_length()) bits = [int(x) for x inbin(p)[2:]] print(bits) idxs = [i for i, b inenumerate(bits) if b] print(idxs) chk = 2 for grp in [idxs[i : i + chk] for i inrange(0, len(idxs), chk)]: print(grp) io.sendline(b"a") io.sendline(",".join(map(str, grp)).encode()) m = bytes_to_long(b"4lL crypt0sy5t3ms suck5 fr0m faul7 atTaCk5 :P") e = 65537 d = pow(e,-1,p-1) io.sendline(b"v") io.sendline(str(pow(m,d,p)).encode())
from Crypto.Util.number import * from collections.abc import Iterable
x = 10715086071862673209484250490600018105614048117055336074437503883703510511249361224931983788156958581275946729175531468251871452856923140435984577574698574803934567774824230985421074605062371141877954182153046477020617917601884853827611232355455223966039590143622792803800879186033924150173912925208583 a = 31337 b = 66826418568487077181425396984743905464189470072466833884636947306507380342362386488703702812673327367379386970252278963682939080502468506452884260534949120967338532068983307061363686987539408216644249718950365322078643067666802845720939111758309026343239779555536517718292754561631504560989926785152983649035 n = 117224988229627436482659673624324558461989737163733991529810987781450160688540001366778824245275287757373389887319739241684244545745583212512813949172078079042775825145312900017512660931667853567060810331541927568102860039898116182248597291899498790518105909390331098630690977858767670061026931938152924839936
# ecm.factor(n) p = 2 q = 690712633549859897233 r = 651132262883189171676209466993073 assert n == p ^ 63 * q ^ 6 * r ^ 5
defsingle_lift(f, df, p, k, rs): # f(r) = 0 (mod p^k) # df(r) != 0 (mod p^k) # returns s such that f(s) = 0 (mod p^(k+1)) pk = p**k pk1 = pk * p for r in rs: assert f(r) % pk == 0, (r, k) # assert df(r) % (p ** k) != 0, (r,k) if df(r) % p != 0: a = inverse_mod(df(r), p) s = r - f(r) * a assert f(s) % pk1 == 0 yield s else: for t inrange(0, p): s = r + t * pk if f(s) % pk1 == 0: yield s
defhensel_lift(f, df, p, k, m, rs): # f(r) = 0 (mod p^k) # df(r) != 0 (mod p^k) # returns s such that f(s) = 0 (mod p^m) ifnotisinstance(rs, Iterable): rs = [rs] assert m >= k if m == k: return rs return hensel_lift(f, df, p, k + 1, m, single_lift(f, df, p, k, rs))
y2 = (x ^ 3 + a * x + b) % n f = lambda x: x ^ 2 - y2 df = lambda x: 2 * x for yp in hensel_lift(f, df, p, 1, 63, [ZZ(GF(p)(y2).sqrt())]): for yq in hensel_lift(f, df, q, 1, 6, [ZZ(GF(q)(y2).sqrt())]): for yr in hensel_lift(f, df, r, 1, 5, [ZZ(GF(r)(y2).sqrt())]): y = crt([ZZ(yp), ZZ(yq), ZZ(yr)], [p ^ 63, q ^ 6, r ^ 5]) EllipticCurve(Zmod(n), [a, b])(x, y) print(long_to_bytes(y)) # CCTF{8E4uTy_0f_L1f7iN9_cOm3_Up!!}
from Crypto.Util.number import * from pwn import * import numpy as np
defkeygen(nbit): p, q = [getPrime(nbit) for _ in"__"] n, phi = p * q, (p - 1) * (q - 1) // 2 whileTrue: g = getRandomRange(2, n**2) if GCD(g, n) == 1: w = (pow(g, phi, n**2) - 1) // n if GCD(w, n) == 1: u = inverse(w, n) break pkey, skey = (n, g), (phi, u) return pkey, skey
defadd(pkey, a, b): n, g = pkey returnpow(a * b, 1, n**2)
defmultiply(pkey, a, k): n, g = pkey returnpow(a, k, n**2)
defencrypt(pkey, m): n, g = pkey whileTrue: r = getRandomRange(2, n) if GCD(r, n) == 1: break return (pow(g, m, n**2) * pow(r, n, n**2)) % (n**2)
defdecrypt(pkey, skey, c): n, g = pkey phi, u = skey t = (pow(c, phi, n**2) - 1) // n return (t * u) % n
defevaluate_poly(pkey, poly, point): n, g = pkey _point = point result = poly[0] for term in poly[1:]: result = add((n, g), result, multiply((n, g), term, _point)) _point = (_point * point) % n return result
defsendparam(io, n, g, poly): io.sendline(b"S") io.sendline(f'{n},{g},{" ".join(map(str, poly))}'.encode()) res = [] for _ inrange(lenflag): io.recvuntil(b"=") res.append(int(io.recvline())) return [decrypt(pkey, skey, x) for x in res]
defencrypt_poly(poly): return [encrypt(pkey, x) for x in poly]
n, g = pkey res = sendparam(io, *pkey, encrypt_poly([n])) s = np.array(res).argsort() f = np.array([x % (2**10) for x in res])[s] print(bytes(f.tolist())) # CCTF{4n0t3R_h0MomORpH1C_3NcRyP7!0n_5CH3Me!}
Soda
這題有個基於 RSA 的特殊 signature:
1 2 3 4 5 6 7 8 9
defsoda(g, p, q, m): n, phi = p * q, (p - 1) * (q - 1) if isPrime(m) and m.bit_length() <= 128: e = m else: e = 2 * (pow(g, m**2, n) % 2**152) ^ 1 if GCD(e, phi) == 1: d = inverse(e, phi) returnpow(g, d, n)
m = bytes_to_long(b"Long Live Crypto :))") io.sendline(b"T") io.sendline(str(-m).encode()) io.recvuntil(b"soda(g, p, q, m) = ") sig = int(io.recvline()) io.sendline(b"V") io.sendline(str(sig).encode()) io.interactive() # CCTF{f4cToriZat!On__5Tt4cK_0n_4_5i9na7urE!}
from Crypto.Util.number import * from flag import flag
defsparse(p, k): nbit = p.bit_length() whileTrue: CF = [getRandomRange(-1, 1) for _ in'_' * k] XP = [getRandomRange(3, nbit - 3) for _ in'_' * k] A = sum([CF[_] * 2 ** XP[_] for _ inrange(0, k)]) q = p + A if isPrime(q) * A != 0: return q
p = getPrime(417) q = sparse(p, 5) e, n = 65537, p * q print(f'n = {n}') m = bytes_to_long(flag.encode('utf-8')) assert m < n c = pow(m, e, n) print(f'c = {c}')
from Crypto.Util.number import * from itertools import combinations, chain import gmpy2
n = 94144887513744538681657844856583985690903055376400570170371837200724227314957348031684706936655253125445176582486308241015430205703156336248578475428712275706238423997982248462635972817633320331030484841129628650918661036694615254018290264619628335177 c = 80250313885079761377138486357617323555591919111371649902793873860183455237161293320577683249054725852540874552433031133240624696119120378419135912301004715004977978507247634217071922495893934816945961054193052791946557226599493364850793396744903765857
tp = [gmpy2.mpz(1 << i) for i inrange(512)] it = chain(*[combinations(range(3, 417 - 3), i) for i inrange(4)]) for cf in it: A = -sum([tp[i] for i in cf]) # q = p + A # n=pq=p(p+A) # p^2+Ap-n=0 # D=A^2-4*1*-n D = A**2 + 4 * n if gmpy2.is_square(D): d = gmpy2.isqrt(D) p = (-A + d) // 2 q = n // p print(p) print(q) print(p * q == n) print(bin(p - q)) break e = 65537 d = pow(e, -1, (p - 1) * (q - 1)) print(long_to_bytes(pow(c, d, n)))
另外賽後有人說可以用類似 Google CTF 有題 YAFM 的做法 (或是 PlaidCTF
的 xorsa),從 LSB 開始 bit by bit 恢復,然後 coppersmith。
n = 141886649864474336567180245736091175577519141092893110664440298696325928109107819365023509727482657156444454196974621121317731892910779276975799862237645015028934626195549529415068518701179353407407170273107294065819774663163018151369555922179926003735413019069305586784817562889650637936781439564028325920769 p = 106618752612001652530923691512073519044983443846656721126867402977583225110529 q = 104492689192892408108975038373966852967734827395344990285038653889732962680833 r = 12735676857401163601385118447483795668229644118624917660231942016044435957817541173149617917604011645058841872384142319678290750015804888147769138207522817 assert p * q * r == n assertall(is_prime(p) for p in [p, q, r]) n, e, fi, th = ( 141886649864474336567180245736091175577519141092893110664440298696325928109107819365023509727482657156444454196974621121317731892910779276975799862237645015028934626195549529415068518701179353407407170273107294065819774663163018151369555922179926003735413019069305586784817562889650637936781439564028325920769, 65537, 125494383162828289973475117066203219587304356806057400173045477137700391356840397636206107925460433939119412469184723408274805651096828270461235114589209044543108910295997506041345432448035371092981112305692014036117962906342882215492784319467728201344342591126197621795974549431806828947671232171059809967991, 138257736445723754207239869344459794807808248188757696052272858978544083465381926995900887162870612185045399616892685750962667762789508194359878372465943702647287813020223160406789982302692329883577043521781397505345137392777694159916452699296748509096494301465498192136911589776144421856343483031920756519249, ) c1, c2, c3, c4 = ( 88920444409754899592335110119456825172544580816901497880270628553955508488170483498726344301421934007876515783471747430111559265733377611608113080609941423596790625452564403457107243481310552344096683637970851198148957553062631972064855184560312748315536290880767375156429548232884895308088306625307674645678, 45539956581550314230977168288877082058214432324397034618326297663129864608739856352261029083496409133620455599376139981575342903237304167908534019438874239934645347320209162850653298515960349717851268830205737252263548268549179642907155075129172651815656517165432021020317138111104384072600486843574535899860, 69849817078368866947686316374564245958824276178721440086311727765763093314086243149277327430285562537315291446874425715021031882041090977200029684675392021083309757246079110723453995717856469919242618068208424495615283285085190255592463862108516827540775850882615540406750734639040903336048095547788528187976, 20285007564778647051596518902857046010716548094264173639037549746086538656814534621919993169453446815272789643882592631536755194356753848872566348635207131520597253599540337542405837637606323276917410384296602682043902830628022440639028040137219164743287397377174047728489836106561656239657061612908104843401, )
from sage.allimport * from pwn import * from Crypto.Util.number import *
defgen(): whileTrue: p = ZZ(getPrime(128)) rs = [ZZ(x) for x in GF(p)(1).nth_root(3, all=True) if x != 1] iflen(rs) == 0: continue g = rs[0] t = inverse_mod(g - 1, p) # t*g=t+1 if (64 < t.nbits() < 128) and (64 < (t * g % p).nbits() < 128): return p, g, t
defkeygen(u, v): return [a ^ b for a, b inzip(u, v)][:20]
defencrypt(msg, key): msg = padding(msg) blocks = [msg[i * 32 : i * 32 + 32] for i inrange(len(msg) // 32)] ciphers = [] enc = encrypt_iginition([ord(item) for item in blocks[0]], key) ciphers.append(enc) for i inrange(len(blocks) - 1): enc = encrypt_iginition( [ord(item) for item in blocks[i + 1]], keygen([ord(item) for item in blocks[i]], ciphers[i]), ) ciphers.append(enc) return"".join("".join(str(format(i, "02x")) for i in item) for item in ciphers)
pt = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ct = bytes.fromhex( "127f1023480f4f61caab33b69825447a38f8d62592f4c1b0fb2f92cf3b10e4bb168e208a12f39b725addbe1e8e9f923477fa62508c0f9d7fbe0ca52026e470458f6fbf2b0ca4865e50018d735e055233ab0953882f0f3da553fcab3db6710621722ae640fa3b9e3b47c52597b9e91dc1bf6d139ea7c1cb58c83228d7b8ae3250" ) blks = [ct[i : i + 32] for i inrange(0, len(ct), 32)] for prev, cur inzip(blks, blks[1:]): key = keygen(pt, prev) pt = bytes(encrypt_iginition(cur, key)) print(pt.decode(), end="") # the flag is: CCTF{d0_yOu_tH47_the_ori9iN_of_Iraqi_C1ph3r_Iz_Iran?!!}****************************