Write up for UTCTF 2024 - Crypto/numbers go brrr

Challenge

I wrote an amazing encryption service. It is definitely flawless, so I’ll encrypt the flag and give it to you.

#!/usr/bin/env python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import time

seed = int(time.time() * 1000) % (10 ** 6)
def get_random_number():
    global seed 
    seed = int(str(seed * seed).zfill(12)[3:9])
    return seed

def encrypt(message):
    key = b''
    for i in range(8):
        key += (get_random_number() % (2 ** 16)).to_bytes(2, 'big')
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = cipher.encrypt(pad(message, AES.block_size))
    return ciphertext.hex()

print("Thanks for using our encryption service! To get the encrypted flag, type 1. To encrypt a message, type 2.")
while True:
    print("What would you like to do (1 - get encrypted flag, 2 - encrypt a message)?")
    user_input = int(input())
    if(user_input == 1):
        break

    print("What is your message?")
    message = input()
    print("Here is your encrypted message:", encrypt(message.encode()))


flag = open('./src/flag.txt', 'r').read()
print("Here is the encrypted flag:", encrypt(flag.encode()))

Explain

The server generates an AES key with time.time(), so I can find it. After that, I can decrypt the encrypted flag using the key.

Solve

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
import time
from pwn import *

t = time.time()
seed = int(t * 1000) % (10 ** 6)
def get_random_number():
    global seed 
    seed = int(str(seed * seed).zfill(12)[3:9])
    return seed

def encrypt(message):
    key = b''
    for i in range(8):
        key += (get_random_number() % (2 ** 16)).to_bytes(2, 'big')
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = cipher.encrypt(pad(message, AES.block_size))
    return ciphertext.hex()

def decrypt(ciphertext_hex):
    key = b''
    for i in range(8):
        key += (get_random_number() % (2 ** 16)).to_bytes(2, 'big')
    cipher = AES.new(key, AES.MODE_ECB)
    ciphertext = bytes.fromhex(ciphertext_hex)
    decrypted = cipher.decrypt(ciphertext)
    unpadded = unpad(decrypted, AES.block_size)

    return unpadded.decode('utf-8')

r = remote("betta.utctf.live", 7356)

r.recvuntil(b"What would you like to do (1 - get encrypted flag, 2 - encrypt a message)?")
r.sendline(b"2")

r.recvuntil(b"What is your message?")
msg = b"Hello world"
r.sendline(msg)

r.recvuntil(b"Here is your encrypted message: ")
enc_msg = str(r.recv()[:32], "utf-8")
exp_enc_msg = encrypt(msg)
i = 0

while exp_enc_msg != enc_msg:
    seed = int(t * 1000) % (10 ** 6) + i
    i = i + 1
    exp_enc_msg = encrypt(msg)

r.sendline(b"1")
r.recvuntil(b"Here is the encrypted flag: ")
enc_flag = str(r.recv()[:-1], "utf-8")

print(decrypt(enc_flag))
❯ python3 utctf_numbers-go-brrr.py
[+] Opening connection to betta.utctf.live on port 7356: Done
utflag{deep_seated_and_recurring_self-doubts}
[*] Closed connection to betta.utctf.live port 7356