# LACTF Writeup 真的只會做密碼學的 ## VeryHot ```python from Crypto.Util.number import getPrime, isPrime, bytes_to_long from flag import FLAG FLAG = bytes_to_long(FLAG.encode()) p = getPrime(384) while(not isPrime(p + 6) or not isPrime(p + 12)): p = getPrime(384) q = p + 6 r = p + 12 n = p * q * r e = 2**16 + 1 ct = pow(FLAG, e, n) print(f'n: {n}') print(f'e: {e}') print(f'ct: {ct}') ``` 就RSA,他的n有3個質因數,可以很容易的分解 ```python from Crypto.Util.number import * p = 21942765653871439764422303472543530148312720769660663866142363370143863717044484440248869144329425486818687730842077 b = 481484964540720113472607671311958003544610499422246847987589354560178323791038264141087579324461879222750445224381527035495936943975826481974450340297411968236802328325641633227089888411307197205354087888800340238955852318668831387 q = p + 6 r = b // q e = 2**16 + 1 ct = 9953835612864168958493881125012168733523409382351354854632430461608351532481509658102591265243759698363517384998445400450605072899351246319609602750009384658165461577933077010367041079697256427873608015844538854795998933587082438951814536702595878846142644494615211280580559681850168231137824062612646010487818329823551577905707110039178482377985 n = 10565111742779621369865244442986012561396692673454910362609046015925986143478477636135123823568238799221073736640238782018226118947815621060733362956285282617024125831451239252829020159808921127494956720795643829784184023834660903398677823590748068165468077222708643934113813031996923649853965683973247210221430589980477793099978524923475037870799 phi = (p - 1) * (q - 1) * (r - 1) d = pow(e, -1, phi) flag = pow(ct, d, n) print(long_to_bytes(flag)) ``` ## hOlyT ```python from Crypto.Util.number import getPrime, bytes_to_long import random def legendre(a, p): return pow(a, (p - 1) // 2, p) def tonelli(n, p): q = p - 1 s = 0 while q % 2 == 0: q //= 2 s += 1 if s == 1: return pow(n, (p + 1) // 4, p) for z in range(2, p): if p - 1 == legendre(z, p): break c = pow(z, q, p) r = pow(n, (q + 1) // 2, p) t = pow(n, q, p) m = s t2 = 0 while (t - 1) % p != 0: t2 = (t * t) % p for i in range(1, m): if (t2 - 1) % p == 0: break t2 = (t2 * t2) % p b = pow(c, 1 << (m - i - 1), p) r = (r * b) % p c = (b * b) % p t = (t * c) % p m = i return r def xgcd(a, b): if a == 0 : return 0,1 x1,y1 = xgcd(b%a, a) x = y1 - (b//a) * x1 y = x1 return x,y def crt(a, b, m, n): m1, n1 = xgcd(m, n) return ((b *m * m1 + a *n*n1) % (m * n)) def advice(x, p, q): if legendre(x, p) != 1: exit() if legendre(x, q) != 1: exit() x1 = tonelli(x, p) * random.choice([1, -1]) x2 = tonelli(x, q) * random.choice([1, -1]) y = crt(x1, x2, p, q) return y def main(): p = getPrime(1024) q = getPrime(1024) N = p * q e = 65537 m = bytes_to_long(b"lactf{redacted?}") ct = pow(m, e, N) print(f"ct = {ct}") print(f"N = {N}") print(f"e = {e}") while 1: x = int(input("What do you want to ask? > ")) ad = advice(x, p, q) print(ad) if __name__ == "__main__": main() ``` 這題也是RSA,你可以給他一個數,他會回傳給你他模n下的平方根。因為模n下平方根和n的因式分解是等價問題,可以直接轉換。方法是取一對不是相反數的平方根,兩個相減再和n取gcd ```python from Crypto.Util.number import * from pwn import * r = remote("chall.lac.tf", 31171) def get_sqrt(x): r.sendlineafter(b"> ", str(x).encode()) return int(r.recvline().decode().strip()) ct = int(r.recvline().decode().split(' ')[-1].strip()) n = int(r.recvline().decode().split(' ')[-1].strip()) e = int(r.recvline().decode().split(' ')[-1].strip()) a = 2 b = get_sqrt(4) while b == 2: b = get_sqrt(4) q = GCD(a - b, n) p = n // q phi = (p - 1) * (q - 1) d = pow(e, -1, phi) flag = pow(ct, d, n) print(long_to_bytes(flag)) ``` ## Prove It ```python #!/usr/local/bin/python import random flag = "lactf{??????????}" p = 171687271187362402858253153317226779412519708415758861260173615154794651529095285554559087769129718750696204276854381696836947720354758929262422945910586370154930700427498878225153794722572909742395687687136063410003254320613429926120729809300639276228416026933793038009939497928563523775713932771366072739767 if __name__ == "__main__": s = random.getrandbits(128) alpha = random.getrandbits(40) g = redacted ss = [pow(g, s**i, p) for i in range(1,8)] alphas = [pow(g, alpha * s**i, p) for i in range(1,8)] print(f"Use these values to evaluate your polynomials on s") print(f"Powers of s: {ss}") print(f"Powers of alpha*s: {alphas}") tries = 0 while True: if tries >= 2: print("Fool me once shame on you, fool me twice shame on me") break print("Can you prove to me you know the polynomial f that im thinking of?") target = [] for i in range(8): target.append(random.randrange(p)) print(f"Coefficients of target polynomial: {target}") ts = sum([(pow(s,7 - i, p) * target[i]) % p for i in range(len(target))]) % p f = int(input("give me your evaluation of f(s) > ")) % p h = int(input("give me your evaluation of h(s) > ")) % p fa = int(input("give me your evaluation of f(alpha * s) > ")) % p if f <= 1 or h <= 1 or fa <=1 or f == p-1 or h == p-1 or fa == p-1: print("nope") exit() if pow(f, alpha, p) != fa or f != pow(h, ts, p): print(f"failed! The target was {ts}") tries += 1 continue print(f"you made it! here you got {flag}") break ``` 這題先有未知的$g, s, \alpha$和已知的$p$然後給你$g^{s^i}和g^{\alpha s^i}$,$i從1到7$。然後給你一個$target$陣列,叫你求$f, h, fa$使得$fa = f^\alpha且f=h^{ts}$,其中$ts=\sum_{i=0}^7s^{7 - i}target_i$ 這題其實沒那麼難,只是我把他搞得很複雜,後來才想到簡單的做法。 我的作法是求出$s和\alpha$,首先先用ss[0]和alphas[0]去把$\alpha$求出來,因為$\alpha<2^{40}$,所以bsgs很快就能跑出來。接下來因為他能多問一次,而且會給我$ts$,所以我可以構造出一個多項式,用coppersmith,然後再用ss的前兩項去檢查哪個根是正確的。 ```python from pwn import * # from tqdm import tqdm from math import ceil, sqrt from sage.all import * import random r = remote("chall.lac.tf", int(31179)) p = 171687271187362402858253153317226779412519708415758861260173615154794651529095285554559087769129718750696204276854381696836947720354758929262422945910586370154930700427498878225153794722572909742395687687136063410003254320613429926120729809300639276228416026933793038009939497928563523775713932771366072739767 r.recvuntil(b": ") ss = list(map(int, r.recvline().decode().strip('\n[]').split(', '))) r.recvuntil(b": ") alphas = list(map(int, r.recvline().decode().strip('\n[]').split(', '))) def bsgs(g, y, p): m = int(ceil(sqrt(1 << 40))) S = {pow(g, j, p): j for j in range(m)} gs = pow(g, p - 1 - m, p) for i in range(m): if y in S: return i * m + S[y] y = y * gs % p return None x = ss[0] y = alphas[0] alpha = bsgs(x, y, p) r.recvuntil(b": ") target = list(map(int, r.recvline().decode().strip('[]\n').split(', '))) r.sendlineafter(b"> ", b"2") r.sendlineafter(b"> ", b"2") r.sendlineafter(b"> ", b"2") r.recvuntil(b"was ") res = int(r.recvline().decode().strip()) # print(res) F = GF(p) P.<x> = PolynomialRing(F) f = 0 for i in range(len(target)): f += x ** (7 - i) * target[i] f -= res f = f.monic() root = f.roots() s = root[0][0] for a, b in root: if pow(ss[int(0)], int(a), p) == ss[int(1)] % p: s = a print("Hello") break ts = int(0) for i in range(len(target)): ts += s ** (7 - i) * target[i] print(ts % p == res % p) r.recvuntil(b": ") target = list(map(int, r.recvline().decode().strip('[]\n').split(', '))) ts = int(0) for i in range(len(target)): ts += s ** (7 - i) * target[i] h = 2 f = pow(h, ts, p) fa = pow(f, alpha, p) r.sendlineafter(b"> ", str(f).encode()) r.sendlineafter(b"> ", str(h).encode()) r.sendlineafter(b"> ", str(fa).encode()) res = r.recvline().decode() print(res) ``` 後來我有想到另一個,在我求出$s$之後,我可以試很多次直到$s$和$p - 1$互質之後,就能用類似RSA的方式把$g$求出來,然後他g很漂亮的是2。我們可以發現他的結果其實可以用ss和alphas指數的線性組合求出來,也就是說每個做$target_i$次方之後再相乘,而$h$就設$g$就好。我沒去寫他,不過看起來很完美。 ## selamat pagi 我真的不太會古典密碼,這題就是一個參雜印尼文的substition cipher ,作法就是先頻率,讓英文顯現出來,稍微校正一下後去查一下那些印尼文,然後在調整一下去頻率分析,答案就出來了。 ## budget-bag(賽後) 這提示橢圓曲線的背包問題,光是橢圓曲線的離散對數問題就很複雜了,還要做成背包? ```python #部分橢圓曲線的函數我直接省略掉,太佔空間了 import random from hidden import DollarStore p = 95773813056613526295315889615423014145567365190638271416026411091272805051097 flag = "lactf{REDACTED}" flag = flag.lstrip("lactf{").rstrip("}") flagarray = [((random.randrange(2**10) >> 8) << 8) + ord(flag[i]) for i in range(len(flag))] ec = DollarStore() points = [] while len(points) < len(flagarray): x = random.randrange(p) point = ec.getPoint(x) if point != None: points.append(point) s = scalar_mult(points[0], flagarray[0], ec) for i in range(1,len(flagarray)): s += (scalar_mult(points[i], flagarray[i], ec)) print(f"s= {s}") print(f"points = {points}") # s= (4690331917760414380672348505790486524786128272326163170078478915876334878778,77902523131087061897126273610460347147805642819184490444996378236375931739511) # points = [(48868244275342945713292068450286493306842109652612873048852850861527337784625,8598765896895208028227058726713353098258128734049351946507804225327296634514), (72658254142472216221352003377742816858998248904595208415554148006123670275598,71428807550814004521976397789820845661680868197262344890701351814974388342261), (67870089007566708724312416903523370462932434169535354040601985891516954713089,8943567664814248988234978385637474582139875658136599562153182844933110782161), (33253625948635661442757699647500419774784100109285386672321733886811106048412,12192737861983844212244996021850136772489630870822316368037176061423925074233), (66860062037424526223822695383825242848539148827022939438364758097578810377221,58417846450117166884245720025471073320694519590707170823524720597081409174976), (28494672393178906436249791305135018577103930778774058381902322150406763293156,62195276890799494520787073550388771924399268413246266294642567344115775718432), (6537035443546098222408014705048663302006517570643500213251743726265246911764,63349815264954414001975570394948853390727370840181019491330859064400691177034), (9320970269062399750695489734826172234568831562993926393303183822807296243087,34696165501221265426922128243581570921860165265147807824051427532585333222152), (18573558660261827041235280489756954152038727051914639466830055088004904233178,19919769712016625050600762893366674039791157427716018448797516577097120142300), (16615614708193334022500574592689709918593068922396962286039360740901902767297,60958319794367285409532630915520850753365486357306708351189641978639088395040), (83083247653266404965046108107297707899250352411953326131401506377288013650507,88351052151438822336155560751456315613347554340471433918143267841048354530206), (13931833861234698857264824825124064884725829615801172933808997361805649306770,1863009430945401243652911885817518397081699859060155426336485589307965515267), (82335726721747347474239651051504187390571418091882782437152555488102933140061,75024532808592482514720186660630997309420973593419858494992760319543418493327), (15679700475641029146383479082391061723840711201191220489551315259170835916116,4902015468380176276259510377701509023242706106231852614815501538737950056248), (69563107209744135957874607288632923528358557330713606279779688661831937343313,47637699504526623569498379978585572196126141235987754666684744952203096777656), (12240300385069346445384926360689199410381482418493148959106963671703717348546,23386458637256108310036365005044561845597767155973474723611711176033278672500), (2686945382892345707901179849163807293774497407910463972517367748359032899306,63993175447658077329987948939524654748779134053698498595737451593308819319039)] ``` 橢圓曲線的$a, b$不難求,只要帶兩個點進去解方程就好了,會發現$a=b=0$,代表這是一個$singular\ curve$,我們構造一個函數$\phi(x, y) = x/y\ (mod\ p)$,那這個橢圓曲線的加法群會同態於經過映射後的加法群,$\phi(P+Q)=\phi(P)+\phi(Q)$,也就是橢圓曲線的離散對數直接簡化為了乘法的相乘。也就是原本的背包問題$x_1P_1+x_2P_2+...+x_nP_n=S$可以簡化為$x_1\phi(P_1)+x_2\phi(P_2)+...+x_n\phi(P_n)=\phi(S)$,也就是普通的背包問題。好啦,也不普通拉,因為他是模$p$下的背包問題,不過也只是在$lattice$中多加一列$p$進去,讓他可以進去線性組合。 ```python from sage.all import * from Crypto.Util.number import * from solve_mod import * points = [(48868244275342945713292068450286493306842109652612873048852850861527337784625,8598765896895208028227058726713353098258128734049351946507804225327296634514), (72658254142472216221352003377742816858998248904595208415554148006123670275598,71428807550814004521976397789820845661680868197262344890701351814974388342261), (67870089007566708724312416903523370462932434169535354040601985891516954713089,8943567664814248988234978385637474582139875658136599562153182844933110782161), (33253625948635661442757699647500419774784100109285386672321733886811106048412,12192737861983844212244996021850136772489630870822316368037176061423925074233), (66860062037424526223822695383825242848539148827022939438364758097578810377221,58417846450117166884245720025471073320694519590707170823524720597081409174976), (28494672393178906436249791305135018577103930778774058381902322150406763293156,62195276890799494520787073550388771924399268413246266294642567344115775718432), (6537035443546098222408014705048663302006517570643500213251743726265246911764,63349815264954414001975570394948853390727370840181019491330859064400691177034), (9320970269062399750695489734826172234568831562993926393303183822807296243087,34696165501221265426922128243581570921860165265147807824051427532585333222152), (18573558660261827041235280489756954152038727051914639466830055088004904233178,19919769712016625050600762893366674039791157427716018448797516577097120142300), (16615614708193334022500574592689709918593068922396962286039360740901902767297,60958319794367285409532630915520850753365486357306708351189641978639088395040), (83083247653266404965046108107297707899250352411953326131401506377288013650507,88351052151438822336155560751456315613347554340471433918143267841048354530206), (13931833861234698857264824825124064884725829615801172933808997361805649306770,1863009430945401243652911885817518397081699859060155426336485589307965515267), (82335726721747347474239651051504187390571418091882782437152555488102933140061,75024532808592482514720186660630997309420973593419858494992760319543418493327), (15679700475641029146383479082391061723840711201191220489551315259170835916116,4902015468380176276259510377701509023242706106231852614815501538737950056248), (69563107209744135957874607288632923528358557330713606279779688661831937343313,47637699504526623569498379978585572196126141235987754666684744952203096777656), (12240300385069346445384926360689199410381482418493148959106963671703717348546,23386458637256108310036365005044561845597767155973474723611711176033278672500), (2686945382892345707901179849163807293774497407910463972517367748359032899306,63993175447658077329987948939524654748779134053698498595737451593308819319039)] p = 95773813056613526295315889615423014145567365190638271416026411091272805051097 F = GF(p) x = [[points[0][0], 1], [points[1][0], 1]] x = Matrix(F, x) y = [points[0][1] ^ 2 - points[0][0] ^ 3, points[1][1] ^ 2 - points[1][0] ^ 3] y = vector(F, y) a, b = x.solve_right(y) #a = b = 0 s= (4690331917760414380672348505790486524786128272326163170078478915876334878778,77902523131087061897126273610460347147805642819184490444996378236375931739511) def phi(P: tuple): x, y = P return x * pow(y, -1, p) % p phi_points = [phi(i) for i in points] res = phi(s) m = [[0 for i in range(len(phi_points) + 2)] for j in range(len(phi_points) + 2)] for i in range(len(phi_points)): m[i][i] = 1 m[i][-1] = int(phi_points[i]) m[-1][-1] = -int(res) m[-2][-1] = p m[-2][-2] = 1 m = matrix(ZZ, m) m = m.LLL() for i in m: if i[-1] == 0: k = "" for j in i: k += chr(j & ((1 << 8) - 1)) print(k[:-2]) ```