# DamCTF2024 - Writeups ## aedes ```python #!/usr/local/bin/python3 from functools import reduce, partial import operator import secrets from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes def xor(*bs): return bytes(reduce(operator.xor, bb) for bb in zip(*bs, strict=True)) # def expand_k(k): # sched = [] # for i in range(7): # subkey = F(k, i.to_bytes(16)) # sched[i:i] = [subkey, subkey] # sched.insert(7, F(k, (7).to_bytes(16))) # return sched def expand_k(k): sched = [] for i in range(7): subkey = F(k, i.to_bytes(16, byteorder='big')) # Specify byteorder='big' sched[i:i] = [subkey, subkey] sched.insert(7, F(k, (7).to_bytes(16, byteorder='big'))) # Specify byteorder='big' return sched def F(k, x): f = Cipher(algorithms.AES(k), modes.ECB()).encryptor() return f.update(x) + f.finalize() def enc(k, m): subkeys = expand_k(k) left, right = m[:16], m[16:] for i in range(15): old_right = right right = xor(left, F(subkeys[i], right)) left = old_right return left + right print("Introducing AEDES, the result of taking all of the best parts of AES and DES and shoving them together! It should be a secure block cipher but proofs are hard so I figured I'd enlist the help of random strangers on the internet! If you're able to prove me wrong you might get a little prize :)") key = secrets.token_bytes(16) with open("flag", "rb") as f: flag = f.read(32) assert len(flag) == 32 flag_enc = enc(key, flag) print("Since I'm so confident in the security of AEDES, here's the encrypted flag. Good luck decrypting it without the key :)") print(flag_enc.hex()) for _ in range(5): pt_hex = input("Your encryption query (hex): ") try: pt = bytes.fromhex(pt_hex) assert len(pt) == 32 except (ValueError, AssertionError): print("Make sure your queries are 32 bytes of valid hex please") continue ct = enc(key, pt) print("Result (hex):", ct.hex()) ``` Bài này khá giống **Feistel Cipher** nhưng có điều đặc biệt là có 15 key và các key[i] = key[15-i]. ![image](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Feistel_cipher_diagram_en.svg/450px-Feistel_cipher_diagram_en.svg.png) Từ hình trên, ta sẽ đổi ngược lại thứ tự L và R, sau đó sẽ gửi lại cho server, vì key[i] = key[15-i] nên ta không phải florentino nha. Chỉ có điều là phải đổi lại thứ tự sau khi lấy lại vì số key là lẻ. ```python from pwn import* io = process(["python3","aedes.py"]) io.recvuntil(b'without the key :)\n') enc_flag = io.recvuntil(b'\n',drop=True).decode() print(enc_flag) left = enc_flag[:32] right = enc_flag[32:] io.recvuntil(b'Your encryption query (hex): ') io.sendline((right+left).encode()) io.recvuntil(b'Result (hex): ') flag = io.recvuntil(b'\n',drop=True).decode() left = flag[32:] right = flag[:32] print(bytes.fromhex(left+right)) ``` **Flag: dam{k3Y_sch3dul35_4r3_H4rd_0k4y}** ## Fundamental ```python #!/usr/local/bin/python3 from Crypto.Util.number import* import secrets import sys p = 129887472129814551050345803501316649949 def poly_eval(coeffs, x): res = 0 for c in coeffs: res = (c + x * res) % p return res def pad(m): padlen = 16 - (len(m) % 16) padding = bytes([padlen] * padlen) return m + padding def to_coeffs(m): coeffs = [] for i in range(0, len(m), 16): chunk = m[i:i+16] coeffs.append(bytes_to_long(chunk)) return coeffs def auth(s, k, m): coeffs = to_coeffs(m) mac_int = (poly_eval(coeffs, k) + s) % p return mac_int.to_bytes(16, byteorder='big') def ver(s, k, m, t): return secrets.compare_digest(t, auth(s, k, m)) def hexinput(msg): try: return bytes.fromhex(input(msg)) except ValueError: print("please enter a valid hex string") return None k = secrets.randbelow(p) kp = secrets.randbelow(p) s = secrets.randbelow(p) target = b"pleasepleasepleasepleasepleasepl" print("Poly1305 is hard to understand so I decided to try simplifying it myself! Its parameters and stuff were probably just fluff right?") print("Anyways it should be secure but if you can prove me wrong I'll give you a little something special :)") print() print("First I'll let you sign an arbitrary message") while (msg1 := hexinput("message to sign (hex): ")) is None: pass if len(msg1) == 0: print("message must be nonempty") sys.exit(1) elif msg1 == target: print("nice try :)") sys.exit(1) if len(msg1) % 16 != 0: mac = auth(s, kp, pad(msg1)) else: mac = auth(s, k, msg1) print("authentication tag:", mac.hex()) print() print("Now I just *might* give you the flag if you convince me my authenticator is insecure. Give me a valid tag and I'll give you the flag.") while (tag := hexinput("enter verification tag (hex): ")) is None: pass if ver(s, k, target, tag): print("oh you did it...here's your flag I guess") with open("flag") as f: print(f.read()) else: print("invalid authentication tag") ``` Bài này sign dưới thuật toán như sau: $$b_0*k^3 + b_1*k^2 + b_3*k + b_4 +s\mod p$$ Và sign target sẽ có dạng như sau: $$target_1 * k + target_2 + s \mod p$$ Bài này có hai cách, cách thứ nhất là mình sẽ gửi 3 block, với block thứ nhất là "00"*16, khi đó sẽ thu được sign của target mà không làm ảnh hưởng tới sign. Cách hai, ta sẽ lấy block 1 của target + p, khi đó mod p sẽ trở thành target luôn. ```python from Crypto.Util.number import* from pwn import* io = process(["python3","fundamental.py"]) target = b"pleasepleasepleasepleasepleasepl" io.recvuntil(b'(hex): ') io.sendline(b"00"*16 + target.hex().encode()) io.recvuntil(b'authentication tag: ') tag = io.recvuntil(b'\n',drop=True) io.recvuntil(b'(hex): ') io.sendline(tag) io.interactive() ``` ```python from Crypto.Util.number import* from pwn import* io = process(["python3","fundamental.py"]) target = b"pleasepleasepleasepleasepleasepl" p = 129887472129814551050345803501316649949 b1 = target[:16] b2 = target[16:] b1 = long_to_bytes(bytes_to_long(b1) + p).hex().encode() io.recvuntil(b'(hex): ') io.sendline(b1 + b2.hex().encode()) io.recvuntil(b'authentication tag: ') tag = io.recvuntil(b'\n',drop=True) io.recvuntil(b'(hex): ') io.sendline(tag) io.interactive() ``` **Flag: dam{17'5_4lW4Y5_7h3_z3r0s_15n'7_17}** ## fundamental-revenge ```python #!/usr/local/bin/python3 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes import secrets import sys from Crypto.Util.number import* p = 168344944319507532329116333970287616503 def F(k, x): f = Cipher(algorithms.AES(k), modes.ECB()).encryptor() return f.update(x) + f.finalize() def poly_eval(coeffs, x): res = 0 for c in coeffs: res = (c + x * res) % p return res def to_coeffs(m): coeffs = [1] for i in range(0, len(m), 16): chunk = m[i : i + 16] coeffs.append(bytes_to_long(chunk)) return coeffs def auth(k, s, m): coeffs = to_coeffs(m) poly_k = bytes_to_long(k[16:]) mac_int = (poly_eval(coeffs, poly_k) + s) % p return F(k[:16], mac_int.to_bytes(16, byteorder='big')) def ver(k, s, m, t): return secrets.compare_digest(auth(k, s, m), t) def hexinput(msg): try: b = bytes.fromhex(input(msg)) assert len(b) % 16 == 0 and len(b) > 0 return b except (ValueError, AssertionError): print("please enter a valid hex string") return None k = secrets.token_bytes(32) s = secrets.randbelow(p) target = b"gonna ace my crypto final with all this studying" print("Let's try this again, shall we?") print() while (msg1 := hexinput("first message to sign (hex): ")) is None: pass if msg1 == target: print("nice try :)") sys.exit(1) mac = auth(k, s, msg1) print("authentication tag:", mac.hex()) print() print("now sign this:", target.decode()) while (tag := hexinput("enter verification tag (hex): ")) is None: pass if ver(k, s, target, tag): print("oh you did it again...I guess I'm just bad at cryptography :((") with open("flag") as f: print("here's your flag again..", f.read()) else: print("invalid authentication tag") ``` Bài này có khè mình hơi rén, nhưng mà dùng cách hai như bài trên thì ra, không phải flo. ```python from Crypto.Util.number import* from pwn import* io = process(["python3","fundamental-revenge.py"]) target = b"gonna ace my crypto final with all this studying" p = 168344944319507532329116333970287616503 b1 = target[:16] b2 = target[16:] b1 = long_to_bytes(bytes_to_long(b1) + p).hex().encode() io.recvuntil(b'(hex): ') io.sendline(b1 + b2.hex().encode()) io.recvuntil(b'authentication tag: ') tag = io.recvuntil(b'\n',drop=True) io.recvuntil(b'(hex): ') io.sendline(tag) io.interactive() ``` **Flag: dam{50m371m35_1_m155_g00d_0lD_0n3_71m3_P4d}**