Write up for UTCTF 2024 - Crypto/numbers go brrr 2

Challenge

A spiritual successor the first.

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

seed = random.randint(0, 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 key.hex(), ciphertext.hex()


print("Thanks for using our encryption service! To get the start guessing, type 1. To encrypt a message, type 2.")
print("You will need to guess the key (you get 250 guesses for one key). You will do this 3 times!")

for i in range(3):
    seed = random.randint(0, 10 ** 6)
    print("Find the key " + str(i + 1) + " of 3!")
    key = encrypt(b"random text to initalize key")[0]
    while True:
        print("What would you like to do (1 - guess the key, 2 - encrypt a message)?")
        user_input = int(input())
        if(user_input == 1):
            break

        print("What is your message?")
        message = input()
        key, ciphertext = encrypt(message.encode())
        print("Here is your encrypted message:", ciphertext)
    print("You have 250 guesses to find the key!")
    
    found = False
    for j in range(250):
        print("What is your guess (in hex)?")
        guess = str(input()).lower()
        if guess == key:
            print("You found the key!")
            found = True
            break
        else:
            print("That is not the key!")

    if not found:
        print("You did not find the key!")
        exit(0)


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

Explain

Because the range of seed is not large (10^6), I was able to infer the seed value similar to the previous challenge.

Solve

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Random import random
from pwn import *

seed = random.randint(0, 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 key.hex(), ciphertext.hex()

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

key_dict = {}

for i in range(10 ** 6):
    msg = b"Hello world"
    seed = i % (10 ** 6)
    encrypt(msg)
    i = i + 1
    key, enc_msg = encrypt(msg)
    key_dict[enc_msg] = key

for i in range(3):
    r.recvuntil(b"What would you like to do (1 - guess the key, 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")    
    key = key_dict[enc_msg]   

    r.sendline(b"1")
    r.recvuntil(b"What is your guess (in hex)?")
    r.sendline(bytes(key, "utf-8"))

r.recvuntil(b"Here is the flag: ")
print(str(r.recvline(), "utf-8")[:-1])
❯ python3 utctf_numbers-go-brrr-2.py
[+] Opening connection to betta.utctf.live on port 2435: Done
utflag{ok_you_are_either_really_lucky_or_you_solved_it_as_intended_yay}
[*] Closed connection to betta.utctf.live port 2435