import os from Crypto.Util.number import bytes_to_long, getRandomNBitInteger, isPrime
defnextPrime(n): whilenot isPrime(n := n + 1): continue return n
defgen(): whileTrue: q = getRandomNBitInteger(256) r = getRandomNBitInteger(256) p = q * nextPrime(r) + nextPrime(q) * r if isPrime(p) and isPrime(q): return p, q, r
flag = os.environ.get("FLAG", "fakeflag").encode() m = bytes_to_long(flag)
p, q, r = gen() n = p * q
phi = (p - 1) * (q - 1) e = 0x10001 d = pow(e, -1, phi) c = pow(m, e, n)
n = 200697881793620389197751143658858424075492240536004468937396825699483210280999214674828938407830171522000573896259413231953182108686782019862906633259090814783111593304404356927145683840948437835426703183742322171552269964159917779 e = 65537 c = 77163248552037496974551155836778067107086838375316358094323022740486805320709021643760063197513767812819672431278113945221579920669369599456818771428377647053211504958874209832487794913919451387978942636428157218259130156026601708 r = 30736331670163278077316573297195977299089049174626053101058657011068283335270
x = polygen(Zmod(n)) f = qp + x * rp rs = f.monic().small_roots(X=2**256 // rp, beta=0.33, epsilon=0.02) if rs: g = gcd(ZZ(f(rs[0])), n) if g != 1and g != n: print(g) q = g # q = 57138703210086603216917938147752779170509477993762976004506899310197198907231 p = n // q assert p * q == n phi = (p - 1) * (q - 1) d = inverse_mod(e, phi) m = pow(c, d, n) flag = int(m).to_bytes(100, "big").strip(b"\x00") print(flag) break # Alpaca{q_and_r_have_nothing_to_do_with_QR_code}
不過後來發現說我這個解法其實很 overkill,因為其實可以注意到:
所以有
而且這個接近是非常的接近,它們的差值和
是同個量級的,所以直接爆即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
n = 200697881793620389197751143658858424075492240536004468937396825699483210280999214674828938407830171522000573896259413231953182108686782019862906633259090814783111593304404356927145683840948437835426703183742322171552269964159917779 e = 65537 c = 77163248552037496974551155836778067107086838375316358094323022740486805320709021643760063197513767812819672431278113945221579920669369599456818771428377647053211504958874209832487794913919451387978942636428157218259130156026601708 r = 30736331670163278077316573297195977299089049174626053101058657011068283335270
q = (n // r // 2).isqrt() while n % q: q -= 1 p = n // q assert p * q == n phi = (p - 1) * (q - 1) d = inverse_mod(e, phi) m = pow(c, d, n) flag = int(m).to_bytes(100, "big").strip(b"\x00") print(flag)
import os import random from math import prod from Crypto.Util.number import isPrime, bytes_to_long
r = random.Random(0) defdeterministicGetPrime(): whileTrue: if isPrime(p := r.getrandbits(64)): return p
# This is not likely to fail assert deterministicGetPrime() == 2710959347947821323, "Your Python's random module is not compatible with this challenge."
defgetPrime(bit): factors = [deterministicGetPrime() for _ inrange(bit // 64)] whileTrue: p = 2 * prod(factors) + 1 if isPrime(p): return p factors.remove(random.choice(factors)) factors.append(deterministicGetPrime())
flag = os.environ.get("FLAG", "fakeflag").encode() m = bytes_to_long(flag)
p, q = getPrime(1024), getPrime(1024) n = p * q e = 0x10001 c = pow(m, e, n)
print(f"{n=}") print(f"{e=}") print(f"{c=}")
也是 RSA,不過這題 是用很多
64 bits 的 deterministic prime
加上額外的 random 來生成的。
import os import random from math import prod from Crypto.Util.number import isPrime, bytes_to_long import gmpy2
r = random.Random(0)
defdeterministicGetPrime(): whileTrue: if isPrime(p := r.getrandbits(64)): return p
n = 2350478429681099659482802009772446082018100644248516135321613920512468478639125995627622723613436514363575959981129347545346377683616601997652559989194209421585293503204692287227768734043407645110784759572198774750930099526115866644410725881688186477790001107094553659510391748347376557636648685171853839010603373478663706118665850493342775539671166315233110564897483927720435690486237018231160348429442602322737086330061842505643074752650924036094256703773247700173034557490511259257339056944624783261440335003074769966389878838392473674878449536592166047002406250295311924149998650337286245273761909 e = 65537 c = 945455686374900611982512983855180418093086799652768743864445887891673833536194784436479986018226808021869459762652060495495939514186099959619150594580806928854502608487090614914226527710432592362185466014910082946747720345943963459584430804168801787831721882743415735573097846726969566369857274720210999142004037914646773788750511310948953348263288281876918925575402242949315439533982980005949680451780931608479641161670505447003036276496409290185385863265908516453044673078999800497412772426465138742141279302235558029258772175141248590241406152365769987248447302410223052788101550323890531305166459
x = 48763**2 whileTrue: x = gmpy2.powmod(x, deterministicGetPrime(), n) p = gmpy2.gcd(x - 1, n) if p != 1and p != n: print(p) break q = n // p assert p * q == n phi = (p - 1) * (q - 1) d = gmpy2.invert(e, phi) m = gmpy2.powmod(c, d, n) flag = int(m).to_bytes(100, "big").strip(b"\x00") print(flag) # Alpaca{n0t_s0_sm00th_y3t_n0t_s0_s4f3}
import os import random from Crypto.Util.number import bytes_to_long
flag = os.environ.get("FLAG", "fakeflag").encode() bit_length = len(flag) * 8
# BLS12-381 curve p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab K = GF(p) E = EllipticCurve(K, (0, 4))
G1, G2 = E.gens() o1, o2 = G1.order(), G2.order()
xs = [random.randrange(0, o1) for _ inrange(bit_length + 1)] m = bytes_to_long(flag)
cs = [] for c, (x1, x2) inzip(bin(m)[2:].zfill(bit_length), zip(xs[:-1], xs[1:])): if c == "1": x1, x2 = x2, x1 cs.append(x1 * G1 + x2 * G2)
import json import os import random import signal import string from Crypto.Util.number import getPrime, getRandomInteger
classRollingHash: def__init__(self, p=None, base=None) -> None: self.p = getPrime(64) if p isNoneelse p self.base = (getRandomInteger(64) if base isNoneelse base) % self.p defhash(self, s: str): res = 0 for i, c inenumerate(s): res += ord(c) * (self.base ** i) res %= self.p return res
defcheck_str(s: str, max_len: int): assertlen(s) <= max_len, "too long!" for i, c inenumerate(s): assert c in string.ascii_lowercase, f"found invalid char {c} at {i}"
signal.alarm(3 * 60)
flag = os.environ.get("FLAG", "fakeflag") MAX_LEN = 54
rhs = [RollingHash() for _ inrange(3)] print("params:",json.dumps([{ "base": rh.base, "p": rh.p } for rh in rhs]))
for _ inrange(3): target_hash = [random.randrange(0, rh.p) for rh in rhs] print('target:', target_hash) s = input("> ") check_str(s, MAX_LEN)
actual_hash = [rh.hash(s) for rh in rhs] if target_hash != actual_hash: print("Oops! You missed the target hash. Better luck next time!") exit(1)
print("Congratulations! Here is your flag:", flag)
簡單來說這題是要同時對三個 rolling hash function 找的三個 target
找個共同的 preimage。而 rolling hash 本身對於一個訊息 是定義為:
from sage.allimport * from Crypto.Util.number import getPrime, getRandomInteger from lll_cvp import solve_inequality, kannan_cvp, BKZ from functools import partial from pwn import process, remote, context import json, ast
classRollingHash: def__init__(self, p=None, base=None) -> None: self.p = getPrime(64) if p isNoneelse p self.base = (getRandomInteger(64) if base isNoneelse base) % self.p
defhash(self, s: str): res = 0 for i, c inenumerate(s): res += ord(c) * (self.base**i) res %= self.p return res
defsolve(params, target_hash, n=54): m = len(target_hash) assert m == len(params), "???" L = matrix(ZZ, n + m, n + m) for i inrange(m): L[i, i] = params[i]["p"] for i inrange(m, n + m): for j inrange(m): L[i, j] = pow(params[j]["base"], i - m, params[j]["p"]) L[i, i] = 1 lb = target_hash + [97] * n ub = target_hash + [122] * n res = solve_inequality(L, lb, ub, cvp=partial(kannan_cvp, reduction=BKZ)) s = bytes(res[m:]).decode() rhs = [RollingHash(params[i]["p"], params[i]["base"]) for i inrange(m)] for i inrange(m): assert rhs[i].hash(s) == target_hash[i] return s