# WU Crypto ## Blocky 0 Sources (interesting part): ```python def main(): signal.signal(signal.SIGALRM, handler) signal.alarm(60) key = get_random_block() cipher = BlockCipher(key, 20) while True: inp = input("> ") if inp == 'E': inp = input("Input (in hex): ") inp = bytes.fromhex(inp) assert len(inp) < 90 assert all(b < 243 for b in inp) if inp == 'gimmeflag': print("Result: None") continue pt = pad(inp) iv = get_random_block() enc = iv for i in range(0, len(pt), 9): t = add(pt[i:i+9], iv) iv = cipher.encrypt(t) enc += iv print(f"Result: {enc.hex()}") elif inp == 'D': inp = input("Input (in hex): ") inp = bytes.fromhex(inp) assert len(inp) < 108 assert all(b < 243 for b in inp) iv, ct = inp[:9], inp[9:] dec = b'' for i in range(0, len(ct), 9): t = cipher.decrypt(ct[i:i+9]) dec += sub(t, iv) iv = ct[i:i+9] pt = unpad(dec) if pt == b'gimmeflag': with open('flag', 'r') as f: flag = f.read() print(flag) exit(0) elif pt: print(f"Result: {pt.hex()}") else: print("Result: None") ``` So we need to encrypt b'gimmeflag' but the program is supposed to not let us encrypt it. Somehow we can so I just sent ``b'gimmeflag'.hex() => 67696d6d65666c6167`` to be encrypted and then decrypted ## My ECC Service Sources : ```python from Crypto.Util.number import inverse from hashlib import sha256 import os import signal class NonceGenerator: def __init__(self): self.state = os.urandom(10) self.db = {} def gen(self): self.state = sha256(self.state + b'wow').digest()[:10] key = sha256(self.state).digest()[:8] self.db[key] = self.state return int.from_bytes(self.state, 'big'), key def get(self, key: str): if key not in self.db: print("Wrong key :(") exit(0) return int.from_bytes(self.db[key], 'big') class ECPoint: # Classic implem class MyECCService: BASE_POINT = (2, 3) MODS = [ 942340315817634793955564145941, 743407728032531787171577862237, 738544131228408810877899501401, 1259364878519558726929217176601, 1008010020840510185943345843979, 1091751292145929362278703826843, 793740294757729426365912710779, 1150777367270126864511515229247, 763179896322263629934390422709, 636578605918784948191113787037, 1026431693628541431558922383259, 1017462942498845298161486906117, 734931478529974629373494426499, 934230128883556339260430101091, 960517171253207745834255748181, 746815232752302425332893938923, ] def __init__(self): self.nonce_gen = NonceGenerator() def get_x(self, nonce: int) -> bytes: ret = b"" for mod in self.MODS: p = ECPoint(self.BASE_POINT, mod) x = (nonce * p).x ret += x.to_bytes(13, "big") return ret def gen(self) -> bytes: nonce, key = self.nonce_gen.gen() x = self.get_x(nonce) return b"\x02\x03" + key + x def verify(self, inp: bytes) -> bool: assert len(inp) == 218 nonce = self.nonce_gen.get(inp[2:10]) self.BASE_POINT = (inp[0], inp[1]) x = self.get_x(nonce) return inp[10:] == x def handler(_signum, _frame): print("Time out!") exit(0) def main(): signal.signal(signal.SIGALRM, handler) signal.alarm(300) service = MyECCService() for _ in range(100): service.gen() while True: inp = input("> ") if inp == "G": payload = service.gen() print(f"Payload: {payload.hex()}") elif inp == "V": payload = bytes.fromhex(input("Payload: ")) result = service.verify(payload) print(f"Result: {result}") elif inp == "P": payload = bytes.fromhex(input("Payload: ")) answer = service.gen() if payload == answer: with open("flag.txt", "r") as f: print(f.read()) else: print("Wrong :(") exit(0) if __name__ == "__main__": main() ``` The server is computing payloads composed of : -The coordinates of the base point -The hash of the current state (I don't know what's this doing here) -x-coordinate of multiple points of the base point on 16 differents curves Each time it generates a payload, the state is updated with the hash of itself with the string "wow" appendend. Our goal is to find the next payload so we have to recover the state. To do that we will use the x-coordinates that are given to us. They represents points that are equal to ``nonce*base_point`` and the nonce is the current state, so we just have to recover the nonce with some discrete log. Because the order of the base point on some curves has big factors we're gonna do the discrete log on multiple curves with the small factors and then a classic crt. Solve code (works 1/10 approx, sorry for the server but it flags): ```python from Crypto.Util.number import inverse from hashlib import sha256 import os import signal from pwn import remote r = remote("my-ecc-service.chal.perfect.blue",int(1337)) class NonceGenerator: def __init__(self,state = 0): self.state = state self.db = {} def gen(self): self.state = sha256(self.state + b'wow').digest()[:10] key = sha256(self.state).digest()[:8] self.db[key] = self.state return int.from_bytes(self.state, 'big'), key def get(self, key: str): if key not in self.db: print("Wrong key :(") exit(0) return int.from_bytes(self.db[key], 'big') class ECPoint: def __init__(self, point, mod): self.x = point[0] self.y = point[1] self.mod = mod def inf(self): return ECPoint((0, 0), self.mod) def _is_inf(self): return self.x == 0 and self.y == 0 def __eq__(self, other): assert self.mod == other.mod return self.x == other.x and self.y == other.y def __add__(self, other): assert self.mod == other.mod P, Q = self, other if P._is_inf() and Q._is_inf(): return self.inf() elif P._is_inf(): return Q elif Q._is_inf(): return P if P == Q: lam = (3 * P.x**2 - 3) * inverse(2 * P.y, self.mod) % self.mod elif P.x == Q.x: return self.inf() else: lam = (Q.y - P.y) * inverse(Q.x - P.x, self.mod) % self.mod x = (lam**2 - P.x - Q.x) % self.mod y = (lam * (P.x - x) - P.y) % self.mod return ECPoint((x, y), self.mod) def __rmul__(self, other: int): base, ret = self, self.inf() while other > 0: if other & 1: ret = ret + base other >>= 1 base = base + base return ret class MyECCService: BASE_POINT = (2, 3) MODS = [ 942340315817634793955564145941, 743407728032531787171577862237, 738544131228408810877899501401, 1259364878519558726929217176601, 1008010020840510185943345843979, 1091751292145929362278703826843, 793740294757729426365912710779, 1150777367270126864511515229247, 763179896322263629934390422709, 636578605918784948191113787037, 1026431693628541431558922383259, 1017462942498845298161486906117, 734931478529974629373494426499, 934230128883556339260430101091, 960517171253207745834255748181, 746815232752302425332893938923, ] def __init__(self,state=0): self.nonce_gen = NonceGenerator(state) def get_x(self, nonce: int) -> bytes: ret = b"" for mod in self.MODS: p = ECPoint(self.BASE_POINT, mod) x = (nonce * p).x ret += int(x).to_bytes(13, "big") return ret def gen(self) -> bytes: nonce, key = self.nonce_gen.gen() x = self.get_x(nonce) return b"\x02\x03" + key + x def verify(self, inp: bytes) -> bool: assert len(inp) == 218 nonce = self.nonce_gen.get(inp[2:10]) self.BASE_POINT = (inp[0], inp[1]) x = self.get_x(nonce) return inp[10:] == x MODS = [ 942340315817634793955564145941, 743407728032531787171577862237, 738544131228408810877899501401, 1259364878519558726929217176601, 1008010020840510185943345843979, 1091751292145929362278703826843, 793740294757729426365912710779, 1150777367270126864511515229247, 763179896322263629934390422709, 636578605918784948191113787037, 1026431693628541431558922383259, 1017462942498845298161486906117, 734931478529974629373494426499, 934230128883556339260430101091, 960517171253207745834255748181, 746815232752302425332893938923, ] r.recvuntil(b"> ") r.sendline(b"G") r.recvuntil(b": ") out = bytes.fromhex(r.recvline().rstrip(b"\n").decode()) xs = [Integer(int.from_bytes(out[i:i+13], 'big')) for i in range(10,len(out),13)] a = -3 x1,y1 = 2,3 subresults = [] factors = [] modulus = Integer(1) for mod,x in zip(MODS,xs): print("mod : ",mod) p = mod b = (pow(y1, 2, p) - pow(x1, 3, p) - a * x1) % p E = EllipticCurve(GF(p),[a,b]) G = E(2,3) P = E.lift_x(Integer(x)) print(P) order = G.order() for prime, exponent in factor(order): _factor = prime ** exponent if int(_factor).bit_length() >= 30 or int(_factor).bit_length() < 5: continue print(int(_factor).bit_length()) factors.append(Integer(_factor)) G2 = G*(order//_factor) A2 = P*(order//_factor) subresults.append(Integer(discrete_log(A2, G2, operation='+'))) modulus *= _factor if modulus > 2**81: break print("size : ",len(bin(modulus))) nonce = crt(subresults,factors) print(nonce) ngen = MyECCService(int(nonce).to_bytes(10, "big")) r.recvuntil(b"> ") r.sendline(b"P") r.recvuntil(b": ") r.sendline(ngen.gen().hex()) print(r.recvline()) ```