from math import gcd from Crypto.Util.number import long_to_bytes
e = 131074 n = 587926815910957928506680558951380405698765957736660571041732511939308424899531125274073420353104933723578377320050609109973567093301465914201779673281463229043539776071848986139657349676692718889679333084650490543298408820393827884588301690661795023628407437321580294262453190086595632660415087049509707898690300735866307908684649384093580089579066927072306239235691848372795522705863097316041992762430583002647242874432616919707048872023450089003861892443175057 c1 = 92883677608593259107779614675340187389627152895287502713709168556367680044547229499881430201334665342299031232736527233576918819872441595012586353493994687554993850861284698771856524058389658082754805340430113793873484033099148690745409478343585721548477862484321261504696340989152768048722100452380071775092776100545951118812510485258151625980480449364841902275382168289834835592610827304151460005023283820809211181376463308232832041617730995269229706500778999 c2 = 46236476834113109832988500718245623668321130659753618396968458085371710919173095425312826538494027621684566936459628333712619089451210986870323342712049966508077935506288610960911880157875515961210931283604254773154117519276154872411593688579702575956948337592659599321668773003355325067112181265438366718228446448254354388848428310614023369655106639341893255469632846938342940907002778575355566044700049191772800859575284398246115317686284789740336401764665472 cm = 357982930129036534232652210898740711702843117900101310390536835935714799577440705618646343456679847613022604725158389766496649223820165598357113877892553200702943562674928769780834623569501835458020870291541041964954580145140283927441757571859062193670500697241155641475887438532923910772758985332976303801843564388289302751743334888885607686066607804176327367188812325636165858751339661015759861175537925741744142766298156196248822715533235458083173713289585866
# c1 = p^e - q^e mod pq # c2 = p^e + q^e mod pq # c1 + c2 = 2p^e mod pq
for mp in GF(p)(m2).sqrt(all=True): for mq in GF(q)(m2).sqrt(all=True): for mr in GF(r)(m2).sqrt(all=True): m = crt([ZZ(mp), ZZ(mq), ZZ(mr)], [p, q, r]) print(long_to_bytes(int(m))) # SECCON{being_able_to_s0lve_this_1s_great!}
from Crypto.Util.number import bytes_to_long, getPrime from random import randint from math import gcd from secret import FLAG from os import urandom
assertlen(FLAG) < 100
defgenerate_key(rng, seed): e = rng(seed) whileTrue: for _ inrange(randint(10,100)): e = rng(e) p = getPrime(1024) q = getPrime(1024) phi = (p-1)*(q-1) if gcd(e, phi) == 1: break
n = p*q return (n, e)
defgenerate_params(): p = getPrime(1024) a = randint(0, p-1)
return (p,a)
defmain(): p,a = generate_params() print("[+] The parameters of RNG:") print(f"{a=}") print(f"{p=}") b = int(input("[+] Inject [b]ackdoor!!: ")) rng = lambda x: (x**2 + a*x + b) % p
keys = [] seeds = [] for i inrange(5): seed = int(input("[+] Please input seed: ")) seed %= p if seed in seeds: print("[!] Same seeds are not allowed!!") exit() seeds.append(seed) n, e = generate_key(rng, seed) if e <= 10: print("[!] `e` is so small!!") exit()
keys.append((n,e))
flag = bytes_to_long(FLAG + urandom(16)) for n,e in keys: c = pow(flag, e, n) print("[+] Public Key:") print(f"{n=}") print(f"{e=}") print("[+] Cipher Text:", c)
from pwn import process, remote from Crypto.Util.number import long_to_bytes
# io = process(["python", "chall.py"]) io = remote("BBB.seccon.games", 8080) io.recvuntil(b"a=") a = int(io.recvline().strip()) io.recvuntil(b"p=") p = int(io.recvline().strip())
F = GF(p)
E = 11 P.<x, b> = F[] f = x ^ 2 + a * x + b bs = ( (f(f(f(f(f, b), b), b), b)(E, b) - E) .univariate_polynomial() .roots(multiplicities=False) )
for b in bs: print("=" * 40) P.<x> = F[] f = x ^ 2 + a * x + b isgood = False v = E seeds = set() for i inrange(10): print(i, v) seeds.add(int(v)) v = f(v) if v != E: # we don't want all same value isgood = True if isgood: break print(f"{b = }") print(f"{seeds = }") iflen(seeds) != 5ornotall([s % 2 == 0for s inset(seeds) - {E}]): print("bad seeds :(") exit() io.recvuntil(b"!!: ") io.sendline(str(b).encode()) for seed in seeds: io.recvuntil(b"seed: ") io.sendline(str(seed).encode()) ns = [] cs = [] for _ inrange(5): io.recvuntil(b"Public Key:") io.recvuntil(b"n=") n = int(io.recvline().strip()) io.recvuntil(b"e=") e = int(io.recvline().strip()) io.recvuntil(b"Cipher Text: ") c = int(io.recvline().strip()) print(f"#{_}") print(f"{n = }") print(f"{e = }") print(f"{c = }") if e != E: print("bad e :(", _) exit() ns.append(n) cs.append(c) print(f"{ns = }") print(f"{cs = }") c = crt(cs, ns) print(f"{c = }") m = c.nth_root(E) print(long_to_bytes(m)) # for i in $(seq 1 16); do python -u test.sage.py > out/out$i.txt &; done # SECCON{Can_you_find_d_in_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbdbbbbbbbbbbbbbbbbbbbbbbbbbbbbb?}
k = n.bit_length() - 2 - 8 L = 0xFF << k R = L + (1 << k) - 1 B = 1 << flag_length
defbatch_oracle(cc): io.sendline("\n".join(map(str, cc)).encode()) res = [] for _ inrange(len(cc)): io.recvuntil(b"c = ") res.append(io.recvline().strip() == b"True") return res
good = [] whilelen(good) < flag_length // 10 + 15: aa = [random.randrange(1, n) for _ inrange(2048)] cc = [pow(a, e, n) * c % n for a in aa] res = batch_oracle(cc) good += [a for a, r inzip(aa, res) if r] print("cur", len(good)) print(len(good))
from sage.allimport * from random import randint from Crypto.Util.number import getPrime, bytes_to_long, long_to_bytes from itertools import product from functools import reduce
withopen("output.txt") as f: cipher_text = eval(f.readline()) p = eval(f.readline()) shares = eval(f.readline())
eqs = [] for ((x, y), w), z inzip(shares, zs): ww = ( a1 * x + a2 * x**2 + a3 * x**3 + b1 * y + b2 * y**2 + b3 * y**3 + c * z + s ) eqs.append(ww - w)
M, v = Sequence(eqs).coefficient_matrix() v = vector(v)[:-1] M = M.dense_matrix().T.change_ring(ZZ) target = -M[-1] M = M[:-1] nr, nc = M.dimensions() print(nr, nc) IP = matrix.identity(nc) * p I = matrix.identity(nr) Z = matrix.zero(nc, nr) B = block_matrix(ZZ, [[M, I], [IP, Z]]) print(B.change_ring(Zmod(10)))
load("solver.sage") lb = list(target) + [0] * nr ub = list(target) + [ZZ(mono((2**128,) * nvars)) for mono in v] result, applied_weights, fin = solve(B, lb, ub) res = [x // y for x, y inzip(result, applied_weights)] print(v) print(vector(res)) cz1, cz2, cz3, cz4, a1, a2, a3, b1, b2, b3, s = res[nc:]
# a1,a2,a3,b1,b2,b3 are correct here # but cz1,cz2,cz3,cz4,s are not... # it seems cz1 is actually c*z1+k # and cz2 is actually c*z2+k # and s is actuall s-k
czs = [cz1, cz2, cz3, cz4] diffs = [x - y for x, y in product(czs, repeat=2)] c = reduce(gcd, diffs) z1, z2, z3, z4 = [x // c + 1for x in czs] k = cz1 - c * z1 assert k == cz2 - c * z2 s += k
from Crypto.Cipher import AES from Crypto.Random import get_random_bytes from Crypto.Util.Padding import pad, unpad from flag import flag, secret_spell
from pwn import process, remote import ast from Crypto.Cipher import AES from Crypto.Random import get_random_bytes from Crypto.Util.Padding import pad, unpad from Crypto.Cipher._mode_gcm import _GHASH, _ghash_portable as ghash_c from Crypto.Util.number import *
defdecrypt(ct, ret=True): io.sendlineafter(b"ciphertext: ", ct.hex().encode()) if ret: return ast.literal_eval(io.recvline().strip().decode())
defbatch_decrypt(cts): io.sendline("\n".join(map(bytes.hex, cts)).encode()) res = [] for _ inrange(len(cts)): io.recvuntil(b"ciphertext: ") r = ast.literal_eval(io.recvline().strip().decode()) res.append(r) return res
defxor(a, b): returnbytes([x ^ y for x, y inzip(a, b)])
defecb_enc(pt): print("Encrypting", pt) assertlen(pt) == 16 result = b"" for i inrange(16): pad = i + 1 ct = bytearray([0] * 16) for j inrange(256): ct[:i] = [pad ^ x for k, x inenumerate(result)] ct[i] = j
if decrypt(pt + ct[::-1]) != b"ofb error": result += bytes([j ^ pad]) break else: raise Exception(f"Padding oracle failed") return result[::-1]
defecb_enc(pt): # optimized for remote using batch decryption oracle print("Encrypting", pt) assertlen(pt) == 16 result = b"" for i inrange(16): pad = i + 1 cts = [] ct = bytearray([0] * 16) for j inrange(256): ct[:i] = [pad ^ x for k, x inenumerate(result)] ct[i] = j cts.append(bytes(ct)) res = batch_decrypt([pt + ct[::-1] for ct in cts]) for j, r inenumerate(res): if r != b"ofb error": result += bytes([j ^ pad]) break return result[::-1]
defofb_decrypt(iv, ct): pt = b"" for i inrange(0, len(ct), 16): iv = ecb_enc(iv) pt += xor(iv, ct[i : i + 16]) return pt
import os import signal import random import secrets
FLAG = os.getenv("FLAG", "fake{cast a special spell}")
defjanken(a, b): return (a - b + 3) % 3
signal.alarm(1000) print("kurenaif: Hi, I'm a crypto witch. Let's a spell battle with me.")
witch_spell = secrets.token_hex(16) witch_rand = random.Random() witch_rand.seed(int(witch_spell, 16)) print(f"kurenaif: My spell is {witch_spell}. What about your spell?")
defrandbelow(r, n): k = n.bit_length() v = r.getrandbits(k) while v >= n: v = r.getrandbits(k) return v
defjanken(a, b): return (a - b + 3) % 3
r1 = random.Random(THE_SEED) vals = [] for _ inrange(666): b = r1.randint(0, 2) a = (1 - 3 + b) % 3 assert janken(a, b) == 1 vals.append(a)
classUntwister: def__init__(self): self.ctr = count() name = next(self.ctr) self.init_MT = [BitVec(f"MT_{i}_{name}", 32) for i inrange(624)] self.MT = self.symbolic_twist(self.init_MT) self.index = 0 self.solver = Solver() self.subs = 0
# This particular method was adapted from https://www.schutzwerk.com/en/43/posts/attacking_a_random_number_generator/ defsymbolic_untamper(self, solver, y): name = next(self.ctr)
defsymbolic_twist( self, MT, n=624, upper_mask=0x80000000, lower_mask=0x7FFFFFFF, a=0x9908B0DF, m=397, ): """ This method models MT19937 function as a Z3 program """ MT = [i for i in MT] # Just a shallow copy of the state
for i inrange(n): x = (MT[i] & upper_mask) + (MT[(i + 1) % n] & lower_mask) xA = LShR(x, 1) xB = If( x & 1 == 0, xA, xA ^ a ) # Possible Z3 optimization here by declaring auxiliary symbolic variables MT[i] = MT[(i + m) % n] ^ xB
return MT
defget_symbolic(self, guess): name = next(self.ctr) ERROR = 'Must pass a string like "?1100???1001000??0?100?10??10010" where ? represents an unknown bit'
asserttype(guess) == str, ERROR assertall(map(lambda x: x in"01?", guess)), ERROR assertlen(guess) <= 32, "One 32-bit number at a time please" guess = guess.zfill(32)
for i, bit inenumerate(guess): if bit != "?": self.solver.add(Extract(i, i, self.symbolic_guess) == bit)
return self.symbolic_guess
defsubmit(self, guess): """ You need 624 numbers to completely clone the state. You can input less than that though and this will give you the best guess for the state """ self.subs += 1 if self.index >= 624: name = next(self.ctr) next_mt = self.symbolic_twist(self.MT) self.MT = [BitVec(f"MT_{i}_{name}", 32) for i inrange(624)] for i inrange(624): self.solver.add(self.MT[i] == next_mt[i]) self.index = 0
defget_random(self, skip_states=True): """ This will give you a random.Random() instance with the cloned state. """ print("Solving...") self.solver.check() model = self.solver.model()
# Compute best guess for state state = list(map(lambda x: model[x].as_long(), self.init_MT)) result_state = (3, tuple(state + [624]), None) r = random.Random() r.setstate(result_state) if skip_states: for _ inrange(self.subs): r.getrandbits(32) return r
from RNGeesus.src.code_mersenne.mersenne import MT19937
num_seeds = 624 MT = [BitVecVal(i, 32) for i in MT19937(19650218).MT] SEEDS = [BitVec(f"seed[{i}]", 32) for i inrange(num_seeds)] i, j = 1, 0 n = 624 for k inrange(n): MT[i] = (MT[i] ^ ((MT[i - 1] ^ LShR(MT[i - 1], 30)) * 1664525)) + SEEDS[j] + j i += 1 j += 1 if i >= n: MT[0] = MT[n - 1] i = 1 if j == num_seeds: j = 0 for k inrange(n - 1): MT[i] = (MT[i] ^ ((MT[i - 1] ^ LShR(MT[i - 1], 30)) * 1566083941)) - i i += 1 if i >= n: MT[0] = MT[n - 1] i = 1 MT[0] = BitVecVal(0x80000000, 32)
r1 = random.Random(THE_SEED) ut = Untwister() for i inrange(624): ut.solver.add(ut.init_MT[i] == MT[i]) for v in vals: ut.submit(f"{v:02b}" + "?" * 30) r2 = ut.get_random(skip_states=False) for v in vals: assert janken(r2.randint(0, 2), r1.randint(0, 2)) == 1 m = ut.solver.model() seeds = [m[s].as_long() for s in SEEDS] print(seeds)
defarray_to_int(arr): returnint.from_bytes( b"".join([int.to_bytes(i, 4, "little") for i in arr]), "little" )
seed = array_to_int(seeds) r1 = random.Random(THE_SEED) r3 = random.Random(seed) for v in vals: assert janken(r3.randint(0, 2), r1.randint(0, 2)) == 1
app.get("/", (req, res) => { console.log(req.query) req.query.proxy.includes("nginx") ? res.status(400).send("Access here directly, not via nginx :(") : res.send(`Congratz! You got a flag: ${FLAG}`); });
payload = "" method = req.method path = req.path_info if req.query_string: path += "?" + req.query_string payload += f"{method}{path} HTTP/1.1\r\n" for k, v in req.headers.items(): payload += f"{k}: {v}\r\n" payload += "\r\n" print(payload.encode())
sock.send(payload.encode()) time.sleep(.3) try: data = sock.recv(4096) body = data.split(b"\r\n\r\n", 1)[1].decode() except (IndexError, TimeoutError) as e: print(e) body = str(e) return body