from Crypto.Util.number import * from secret import flag
p = getPrime(1024) q = getPrime(1024) n = p * q e = 65537 phi = (p - 1) * (q - 1)
u = getPrime(16) t = 2 * u x = phi - 1 y = t + 1 k = 485723311775451084490131424696603828503121391558424003875128327297219030209620409301965720801386755451211861235029553063690749071961769290228672699730274712790110328643361418488523850331864608239660637323505924467595552293954200495174815985511827027913668477355984099228100469167128884236364008368230807336455721259701674165150959031166621381089213574626382643770012299575625039962530813909883594225301664728207560469046767485067146540498028505317113631970909809355823386324477936590351860786770580377775431764048693195017557432320430650328751116174124989038139756718362090105378540643587230129563930454260456320785629555493541609065309679709263733546183441765688806201058755252368942465271917663774868678682736973621371451440269201543952580232165981094719134791956854961433894740133317928275468758142862373593473875148862015695758191730229010960894713851228770656646728682145295722403096813082295018446712479920173040974429645523244575300611492359684052455691388127306813958610152185716611576776736342210195290674162667807163446158064125000445084485749597675094544031166691527647433823855652513968545236726519051559119550903995500324781631036492013723999955841701455597918532359171203698303815049834141108746893552928431581707889710001424400
''' n = 14070754234209585800232634546325624819982185952673905053702891604674100339022883248944477908133810472748877029408864634701590339742452010000798957135872412483891523031580735317558166390805963001389999673532396972009696089072742463405543527845901369617515343242940788986578427709036923957774197805224415531570285914497828532354144069019482248200179658346673726866641476722431602154777272137461817946690611413973565446874772983684785869431957078489177937408583077761820157276339873500082526060431619271198751378603409721518832711634990892781578484012381667814631979944383411800101335129369193315802989383955827098934489 e = 65537 c = 12312807681090775663449755503116041117407837995529562718510452391461356192258329776159493018768087453289696353524051692157990247921285844615014418841030154700106173452384129940303909074742769886414052488853604191654590458187680183616318236293852380899979151260836670423218871805674446000309373481725774969422672736229527525591328471860345983778028010745586148340546463680818388894336222353977838015397994043740268968888435671821802946193800752173055888706754526261663215087248329005557071106096518012133237897251421810710854712833248875972001538173403966229724632452895508035768462851571544231619079557987628227178358 '''
n = 14070754234209585800232634546325624819982185952673905053702891604674100339022883248944477908133810472748877029408864634701590339742452010000798957135872412483891523031580735317558166390805963001389999673532396972009696089072742463405543527845901369617515343242940788986578427709036923957774197805224415531570285914497828532354144069019482248200179658346673726866641476722431602154777272137461817946690611413973565446874772983684785869431957078489177937408583077761820157276339873500082526060431619271198751378603409721518832711634990892781578484012381667814631979944383411800101335129369193315802989383955827098934489 e = 65537 c = 12312807681090775663449755503116041117407837995529562718510452391461356192258329776159493018768087453289696353524051692157990247921285844615014418841030154700106173452384129940303909074742769886414052488853604191654590458187680183616318236293852380899979151260836670423218871805674446000309373481725774969422672736229527525591328471860345983778028010745586148340546463680818388894336222353977838015397994043740268968888435671821802946193800752173055888706754526261663215087248329005557071106096518012133237897251421810710854712833248875972001538173403966229724632452895508035768462851571544231619079557987628227178358 k = 485723311775451084490131424696603828503121391558424003875128327297219030209620409301965720801386755451211861235029553063690749071961769290228672699730274712790110328643361418488523850331864608239660637323505924467595552293954200495174815985511827027913668477355984099228100469167128884236364008368230807336455721259701674165150959031166621381089213574626382643770012299575625039962530813909883594225301664728207560469046767485067146540498028505317113631970909809355823386324477936590351860786770580377775431764048693195017557432320430650328751116174124989038139756718362090105378540643587230129563930454260456320785629555493541609065309679709263733546183441765688806201058755252368942465271917663774868678682736973621371451440269201543952580232165981094719134791956854961433894740133317928275468758142862373593473875148862015695758191730229010960894713851228770656646728682145295722403096813082295018446712479920173040974429645523244575300611492359684052455691388127306813958610152185716611576776736342210195290674162667807163446158064125000445084485749597675094544031166691527647433823855652513968545236726519051559119550903995500324781631036492013723999955841701455597918532359171203698303815049834141108746893552928431581707889710001424400
for u inrange(2**15, 2**16): ifnot isPrime(u): continue y = 2 * u + 1 a = (y - 1) ** 2 b = 2 * (1 + y**2 - 2*y) c_coef = y**2 + 1 - 2*y - 4*k # 使用求根公式 discriminant = b**2 - 4*a*c_coef if discriminant < 0: continue # 使用gmpy2计算大整数平方根 sqrt_disc = gmpy2.isqrt(discriminant) if sqrt_disc * sqrt_disc != discriminant: continue x1 = (-b + sqrt_disc) // (2 * a) x2 = (-b - sqrt_disc) // (2 * a) for x in [x1, x2]: if x <= 0: continue lhs = (x**2 + 1)*(y**2 + 1) - 2*(x - y)*(x*y - 1) rhs = 4*(k + x*y) if lhs != rhs: continue phi = x + 1 ifabs(n - phi) > n // 1000: # phi应该略小于n continue print(f"找到可能的解: u = {u}, y = {y}, x = {x}, phi = {phi}") p_plus_q = n - phi + 1 disc = p_plus_q**2 - 4*n if disc < 0: continue sqrt_disc_pq = gmpy2.isqrt(disc) if sqrt_disc_pq * sqrt_disc_pq != disc: continue p = (p_plus_q + sqrt_disc_pq) // 2 q = (p_plus_q - sqrt_disc_pq) // 2 if p * q == n: print(f"成功分解n!") print(f"p = {p}") print(f"q = {q}") # 解密 d = inverse(e, phi) m = pow(c, d, n) flag = long_to_bytes(m) print(f"Flag: {flag.decode()}") exit()
from Crypto.Util.number import bytes_to_long, isPrime from hashlib import sha256 from secret import flag, init_seed
classSecureSchnorrProver: def__init__(self, security_bits: int = 512): """ Initialize Schnorr protocol with cryptographically secure randomness """ self._init_deterministic_rng(init_seed) self.p = self._get_prime(security_bits) self.g = self._get_random_element() # Secret from flag (witness for discrete log problem) self.secret_a = bytes_to_long(flag.encode()) % (self.p - 1) # Public key (statement of the discrete log problem) self.A = pow(self.g, self.secret_a, self.p) def_init_deterministic_rng(self, seed): """ Initialize CSPRNG with deterministic seed """ self.rng_state = sha256(str(seed).encode()).digest() self.counter = 0 def_get_secure_random_bits(self, bits: int) -> int: """ Get cryptographically secure random bits using seeded CSPRNG """ data = self.rng_state + self.counter.to_bytes(8, 'big') self.counter += 1 result = 0 whilelen(bin(result)[2:]) < bits: hash_output = sha256(data).digest() result = (result << 256) | int.from_bytes(hash_output, 'big') data = hash_output + self.counter.to_bytes(8, 'big') self.counter += 1 return result % (1 << bits) def_get_prime(self, bits: int) -> int: """ Generate a random prime of specified bit length """ whileTrue: num = self._get_secure_random_bits(bits) num |= (1 << (bits - 1)) | 1 if isPrime(num): return num def_get_random_element(self) -> int: """ Get a random element in range [2, p-2] """ return self._get_secure_random_bits(self.p.bit_length() - 1) % (self.p - 2) + 2 defrun(self): """ Execute the Schnorr zero-knowledge proof protocol """ # Opening statement print("=" * 10) print("I know the flag for this challenge!") print("But I will ONLY prove that I know it.") print("You CANNOT extract ANY information about the flag from my proof!") print("=" * 10) print() # Show public parameters print("[PUBLIC PARAMETERS]") print(f"p = {self.p}") print(f"g = {self.g}") print(f"A = {self.A}") print() print("Note: The flag is the witness 'a' such that A = g^a mod p") print(" where a = bytes_to_long(flag) mod (p-1)") print() round_num = 0 whileTrue: round_num += 1 print(f"--- Round {round_num} ---") print() # Commitment phase print("[COMMITMENT]") b = self._get_secure_random_bits(self.p.bit_length() - 1) % (self.p - 2) + 1 B = pow(self.g, b, self.p) print(f"B = {B}") print() # Challenge phase print("[CHALLENGE]") print("(If you are an HONEST verifier, you should provide a RANDOM challenge!)") print(f"Please provide challenge x (integer: 1 ≤ x ≤ {self.p - 2}):") whileTrue: try: x = int(input("x = ").strip()) if1 <= x < self.p - 1: break print(f"Invalid! x must be between 1 and {self.p - 2}") except (ValueError, EOFError): print("Invalid input!") exit(0) print() # Response phase print("[RESPONSE]") z = (x * self.secret_a + b) % (self.p - 1) print(f"z = {z}") print() # Verification print("[VERIFICATION]") print("Check if: g^z ≡ A^x · B (mod p)") left = pow(self.g, z, self.p) right = (pow(self.A, x, self.p) * B) % self.p if left == right: print("✓ Equation holds! This round proves I know the secret with high probability!") else: print("✗ Verification failed!") print() # Ask to continue print("Continue to next round? (y/n):") choice = input().strip().lower() if choice != 'y': break print() print() print("=" * 10) print(f"Protocol completed after {round_num} round(s).") print("You learned ZERO knowledge about the flag!") print("=" * 10)
if __name__ == "__main__": prover = SecureSchnorrProver(security_bits=512) prover.run()
题目实现了一个 Schnorr 协议的证明者,声称知道 flag,但只提供零知识证明而不泄露 flag 本身。
Schnorr 协议流程如下:
公开参数:素数 $p$、生成元 $g$、公钥 $A = g^a \mod p$(其中 a 是从 flag 派生的秘密值)
state = int(p) defnext_rand(): global state state = (state * 1664525 + 1013904223) % 2**32 return state
dim = 12 M_bob = matrix(ZZ, dim, dim) for r inrange(dim): for c inrange(dim): M_bob[r,c] = (next_rand() % 10 + 10) if r == c else (next_rand() % 5) Y_bob = vector(ZZ, bob_raw) * M_bob
M_alice = matrix(ZZ, dim, dim) for r inrange(dim): for c inrange(dim): M_alice[r,c] = (next_rand() % 10 + 10) if r == c else (next_rand() % 5) Y_alice = vector(ZZ, alice_raw) * M_alice
output = { "params": {"a": a, "b": b}, "points": { "Pa": point_to_list(P2), "Qa": point_to_list(Q2), "Pb": point_to_list(P3), "Qb": point_to_list(Q3) }, "bob_obfuscated": [int(x) for x in Y_bob], "alice_obfuscated": [int(x) for x in Y_alice], "iv": iv.hex(), "ciphertext": ciphertext.hex() }
withopen("output.txt", "w") as f: f.write(str(output))
if EB.j_invariant() == EB_test.j_invariant(): print("✓ 密钥验证成功!")
# 现在计算共享密钥 # 恢复Alice的公钥 M_alice = matrix(ZZ, dim, dim) for r inrange(dim): for c inrange(dim): M_alice[r,c] = (next_rand() % 10 + 10) if r == c else (next_rand() % 5)
Y_alice = vector(ZZ, data["alice_obfuscated"]) alice_raw = list(Y_alice * M_alice.change_ring(QQ).inverse()) alice_raw = [Integer(round(x)) for x in alice_raw]
from Crypto.Cipher import AES from Crypto.Util.Padding import pad from secret import flag import random import os
classNumberGuesser: def__init__(self,MAX_CHANCES: int = 10, n: int = 624): self.MAX_CHANCES = MAX_CHANCES self.n = n self.seed = os.urandom(8) random.seed(self.seed)
self.hints = [random.getrandbits(32) for _ inrange(self.n)] self.enc = self.encrypt_flag(flag,self.seed)
defencrypt_flag(self,flag: str, iv:bytes) -> bytes: key = random.getrandbits(128).to_bytes(16,'big') ct = AES.new(key,AES.MODE_CBC,iv*2).encrypt(pad(flag.encode(), AES.block_size)) return ct defbanner(self): print("=== Number Guess Challenge ===") print(f"- Internally generates {self.n} 32-bit random numbers") print(f"- You may query at most {self.MAX_CHANCES} indices (0 ~ {self.n - 1})") print("- The seed is 8 bytes of true randomness, influencing AES-CBC key generation\n") print("Encrypted flag:") print(self.enc.hex(), "\n")
defquery_loop(self): for CHANCES inrange(1, self.MAX_CHANCES + 1): print(f"[{CHANCES}/{self.MAX_CHANCES}]") try: index = int(input(f"Enter index (0~{self.n - 1}): ").strip()) if0 <= index < self.n: print(f"hint[{index}] = {self.hints[index]}\n") else: print("Index out of range.\n") except ValueError: print("Invalid input. Please enter an integer.\n") print("Query phase ended. Use the hints to recover the PRNG state and decrypt the flag.")
voidinit_by_array(uint32_t key[], int keylen) { // 1. 用常数初始化 init_genrand(19650218); // MT[0..623] = 初始常数状态 C // 2. 混入 key 数组 for (i = 1; i < 624; i++) { j = (i - 1) % keylen; MT[i] = (MT[i] ^ ((MT[i-1] ^ (MT[i-1] >> 30)) * 1664525)) + key[j] + j; } // 3. 再次混合 for (i = 1; i < 624; i++) { MT[i] = (MT[i] ^ ((MT[i-1] ^ (MT[i-1] >> 30)) * 1566083941)) - i; } MT[0] = 0x80000000; // 确保非零 }
状态演化:
1
常数状态 C → 中间状态 J (混入 key) → 初始状态 I (最终混合)
3. Stackered 方法的数学原理
MT19937 的状态转移(Twist)
1 2 3 4 5 6 7
deftwist(): for i inrange(624): x = (MT[i] & 0x80000000) + (MT[(i+1) % 624] & 0x7FFFFFFF) xA = x >> 1 if x & 1: xA ^= 0x9908B0DF MT[i] = MT[(i + 397) % 624] ^ xA
from functions import untemper, invertStep, recover_Kj_from_Ii from Crypto.Util.Padding import unpad from Crypto.Cipher import AES from pwn import * import random import sys sys.path.append('python-random-playground')
context.log_level = 'info' HOST = '114.66.24.228' PORT = 30518
deftry_decrypt(enc_hex, key, seed): try: ct = bytes.fromhex(enc_hex) cipher = AES.new(key, AES.MODE_CBC, seed * 2) pt = unpad(cipher.decrypt(ct), AES.block_size) text = pt.decode('utf-8', errors='strict') if text.isprintable() and ('VNCTF{'in text or'flag{'in text): return text returnNone except: returnNone
from hashlib import shake_128 import sys import os
classOV: def__init__(self, o, v, q, key=None): self.params = (o, v, q) self.F = GF(q) self.pub, self.priv = self.keygen() if key == Noneelse key
defHash(self, msg): h = shake_128(msg).hexdigest(128) return vector(self.F, [int(h[i:i+4], 16) for i inrange(0,len(h),4)]) defkeygen(self): o, v, q = self.params n = o + v Fs = [] Ps = []
whileTrue: T = random_matrix(self.F, n, n) if T.is_invertible(): break
defsign(self, msg): o, v, q = self.params Fs, T = self.priv n = o + v M = self.Hash(msg.encode()) PR = PolynomialRing(self.F, 'x', o) X_oil = PR.gens()
whileTrue: V = random_vector(self.F, v).list() Y = vector(PR, V + list(X_oil)) L = [] B = [] for i inrange(o): tmp = Y*Fs[i]*Y L.append([tmp.coefficient(X_oil[j]) for j inrange(o)]) B.append(tmp([0for _ inrange(o)]))
L = matrix(self.F, L) B = vector(B) target = M - B
if L.is_invertible(): X_oil = L.solve_right(target) Y = vector(self.F, V + list(X_oil)) return T.inverse() * Y
defverify(self, msg, token): msg = self.Hash(msg.encode()) t = vector(token) for i inrange(self.params[0]): if t*self.pub[i]*t != msg[i]: returnFalse returnTrue
defmain(): flag = os.getenv('A1CTF_FLAG') o, v = 64, 64 n = o+v q = 0x10001
pub = [] withopen('pub.txt', 'r') as file: for i inrange(o): file.readline() pub.append(matrix(GF(q), eval(''.join([file.readline().strip() for _ inrange(n)]))))
Fs = [] T = None withopen('secret.txt', 'r') as file: for i inrange(o): file.readline() Fs.append(matrix(GF(q), eval(''.join([file.readline().strip() for _ inrange(n)]))))
file.readline() T = matrix(GF(q), eval(''.join([file.readline().strip() for _ inrange(n)])))
if choice == 2: sig = input('your signature:').strip()[1:-1] ifisinstance(sig, bytes): sig = sig.decode() sig = vector(GF(q), [int(_.strip()) for _ in sig.split(',')]) if ov.verify('admin', sig): print(flag) except: print('error!') sys.exit()
if __name__ == "__main__": main()
考点: 平衡 Oil and Vinegar 签名方案、Kipnis-Shamir 攻击、OneVector 攻击
for attempt inrange(10000): c = random_vector(F, n) # 构建线性系统: c^T * P_i * δ = (h_i - c^T*P_i*c) / 2 A = [] b = [] for i inrange(o): row = c * pub[i] * O_basis.transpose() A.append(row) rhs = (target_hash[i] - c * pub[i] * c) / 2 b.append(rhs) A = matrix(F, A) b = vector(F, b) if A.rank() == o: x = A.solve_right(b) delta = O_basis.transpose() * x forged_sig = c + delta # 验证并提交 if verify(forged_sig): submit_to_server(forged_sig) break
# sage from hashlib import shake_128 import sys import time
# 参数 o, v = 64, 64 n = o + v q = 0x10001 F = GF(q)
print("[*] Loading public key...") pub = [] withopen('pub.txt', 'r') as file: for i inrange(o): file.readline() pub.append(matrix(F, eval(''.join([file.readline().strip() for _ inrange(n)]))))
print("[*] Systematic MinRank Attack") print("[*] Trying combinations with small coefficients...")
min_rank = n best_kernel = None
# 阶段 1: 4个矩阵,系数 0-15 for c0 inrange(16): for c1 inrange(16): for c2 inrange(16): for c3 inrange(16): if c0 == 0and c1 == 0and c2 == 0and c3 == 0: continue # 线性组合 M = c0 * pub[0] + c1 * pub[1] + c2 * pub[2] + c3 * pub[3] rank = M.rank() if rank < min_rank: min_rank = rank best_kernel = M.right_kernel() print(f"[+] New min rank: {rank}, coeffs: [{c0},{c1},{c2},{c3}]") # 检查核向量 if best_kernel.dimension() > 0: for vec in best_kernel.basis(): vals = [vec * pub[i] * vec for i inrange(o)] num_zeros = sum(1for v in vals if v == 0) if num_zeros == o: print(f"[+] FOUND OIL VECTOR!") withopen('oil_vector_found.txt', 'w') as f: f.write(str(list(vec))) print(f"[+] Saved to oil_vector_found.txt") sys.exit(0)
# 参数 o, v = 64, 64 n = o + v q = 0x10001 F = GF(q)
print("[*] Loading public key...") pub = [] withopen('pub.txt', 'r') as file: for i inrange(o): file.readline() pub.append(matrix(F, eval(''.join([file.readline().strip() for _ inrange(n)]))))
# OneVector 攻击实现 defattack(G, v): m = len(G) J = matrix([v*g for g in G]) B = matrix(J.right_kernel().basis()) B2 = [] for g in G: ghat = B*g*B.transpose() for b in ghat.kernel().basis(): iflen(B2) == 0or b notin span(B2): B2.append(b) iflen(B2) == m: break B3 = matrix(B2) C = B3*B return C