CHCCTF Random Writeup
Given:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/usr/local/bin/python
import random
import os
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
print("Welcome to my maze that changes everytime")
flag = open('./flag.txt','rb').read().strip()
def menu():
print("""
Choose an option:
1. Get Random Data
2. Get Flag
3. Exit """)
def second_exists(num):
random.seed(int(os.urandom(64).hex(),16))
key = b''
intermediate = random.randbytes(1)
for i in range(16):
key += intermediate
random.seed(int(intermediate.hex(),16))
intermediate = random.randbytes(1)
random.seed(num)
newkey = int.from_bytes(random.randbytes(16)) ^ int.from_bytes(key)
newkey = newkey.to_bytes(len(key) , 'big')
enc_flag = encrypt_flag(flag,newkey)
print(f"Encrypted Flag in hex: {enc_flag.hex()}")
def encrypt_flag(message,key):
cipher = AES.new(key, AES.MODE_ECB)
ciphertext = cipher.encrypt(pad(message, AES.block_size))
return ciphertext
while True:
menu()
try:
data = int(input("Enter your choice: "))
except ValueError:
print("Invalid input. Please enter a number.")
continue
if data == 1:
total = []
try:
i = int(input("How many random numbers do you want: "))
except ValueError:
continue
for x in range(i):
total.append(random.getrandbits(32))
print(total)
elif data == 2:
print("Not so fast bro, just answer a simple question")
print("Guess a number which I am thinking from 1-10000000000")
num = random.randint(1,10000000000)
try:
guess = int(input("Your guess: "))
except ValueError:
print("Invalid input. Please enter a number.")
continue
if guess == num:
print("You guessed right")
second_exists(num)
exit()
else:
print("You guessed wrong")
exit()
else:
exit()
The particular vulnerability exists in the code ability to give us as many random numbers we want, due to pseudo randomness we can predicted the next generated random number huh not so random after all
for this will be using the randcrack
to predict the next number to break the pseudo randomness we need to generate 624
random numbers
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import random
from randcrack import RandCrack
from Crypto.Cipher import AES
from pwn import *
con = remote('ctf.cyberhacktivators.club', 32952)
con.sendlineafter(b"Enter your choice: ", "1")
con.sendlineafter(b"How many random numbers do you want: ", b"624")
random_numbers = con.recvuntil(b']').decode()[1:-1].split(", ")
#print(random_numbers)
random_numbers = [int(i) for i in random_numbers]
print(len(random_numbers))
rc = RandCrack()
for i in range(624):
rc.submit(random_numbers[i])
predicted = rc.predict_randint(1, 10000000000)
this is successfully predict the next number
1
2
3
4
5
6
7
8
9
10
11
12
13
14
elif data == 2:
print("Not so fast bro, just answer a simple question")
print("Guess a number which I am thinking from 1-10000000000")
num = random.randint(1,10000000000)
try:
guess = int(input("Your guess: "))
except ValueError:
print("Invalid input. Please enter a number.")
continue
if guess == num:
print("You guessed right")
second_exists(num)
exit()
when we press 2 this will generate a new random number which will be equal to the predicted
we predicted using the randcrack
predict_randint
this passes the first check
now on to the second function that encrypts
1
2
3
4
5
6
7
8
9
10
11
12
13
def second_exists(num):
random.seed(int(os.urandom(64).hex(),16))
key = b''
intermediate = random.randbytes(1)
for i in range(16):
key += intermediate
random.seed(int(intermediate.hex(),16))
intermediate = random.randbytes(1)
random.seed(num)
newkey = int.from_bytes(random.randbytes(16)) ^ int.from_bytes(key)
newkey = newkey.to_bytes(len(key) , 'big')
enc_flag = encrypt_flag(flag,newkey)
print(f"Encrypted Flag in hex: {enc_flag.hex()}")
intermediate = random.randbytes(1)
this only generates one byte and in the loop AES key is constructed using this intermediate
we can easily create a bruteforce script to create the correct key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
for i in range(0xff):
key = b''
for j in range(16):
key += i.to_bytes(1, 'big')
random.seed(int.from_bytes(key, 'big'))
intermediate = random.randbytes(1)
random.seed(predicted)
newkey = int.from_bytes(random.randbytes(16), 'big') ^ int.from_bytes(key, 'big')
newkey = newkey.to_bytes(len(key) , 'big')
cipher = AES.new(newkey, AES.MODE_ECB)
plaintext = cipher.decrypt(enc_flag)
if b'CHC' in plaintext:
print(plaintext)
break
solve script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import random
from randcrack import RandCrack
from Crypto.Cipher import AES
from pwn import *
con = remote('ctf.cyberhacktivators.club', 32952)
con.sendlineafter(b"Enter your choice: ", "1")
con.sendlineafter(b"How many random numbers do you want: ", b"624")
random_numbers = con.recvuntil(b']').decode()[1:-1].split(", ")
#print(random_numbers)
random_numbers = [int(i) for i in random_numbers]
print(len(random_numbers))
rc = RandCrack()
for i in range(624):
rc.submit(random_numbers[i])
predicted = rc.predict_randint(1, 10000000000)
con.sendlineafter(b"Enter your choice: ", "2")
con.sendlineafter(b"Your guess: ", str(predicted))
print(predicted)
enc_flag = con.recvall().strip().split(b": ")[1]
print(enc_flag)
enc_flag = bytes.fromhex(enc_flag.decode())
print(enc_flag)
key = b''
for i in range(0xff):
key = b''
for j in range(16):
key += i.to_bytes(1, 'big')
random.seed(int.from_bytes(key, 'big'))
intermediate = random.randbytes(1)
random.seed(predicted)
newkey = int.from_bytes(random.randbytes(16), 'big') ^ int.from_bytes(key, 'big')
newkey = newkey.to_bytes(len(key) , 'big')
cipher = AES.new(newkey, AES.MODE_ECB)
plaintext = cipher.decrypt(enc_flag)
if b'CHC' in plaintext:
print(plaintext)
break
flag:
1
CHC{rand0m5_ar3_fun_hT6eZ0lXBrfK_unl3ss_in_0wn_c0d34DgzKXmm}