Rating:

Homecooked was in the Cryptography category, but it was actually an optimization challenge.

The file attached with the challenge is a python file known as decrypt.py.
After running the decrypt file, it outputs the following character by character and then executes indefinitely

flag{pR1m3s_4

which is part of the flag.
This indicates that this file itself is the decrypt file and it will output our flag, but in indefinite amount of time.

The code of the decrypt.py file was

import base64
num = 0
count = 0
cipher_b64 = b"MTAwLDExMSwxMDAsOTYsMTEyLDIxLDIwOSwxNjYsMjE2LDE0MCwzMzAsMzE4LDMyMSw3MDIyMSw3MDQxNCw3MDU0NCw3MTQxNCw3MTgxMCw3MjIxMSw3MjgyNyw3MzAwMCw3MzMxOSw3MzcyMiw3NDA4OCw3NDY0Myw3NTU0MiwxMDAyOTAzLDEwMDgwOTQsMTAyMjA4OSwxMDI4MTA0LDEwMzUzMzcsMTA0MzQ0OCwxMDU1NTg3LDEwNjI1NDEsMTA2NTcxNSwxMDc0NzQ5LDEwODI4NDQsMTA4NTY5NiwxMDkyOTY2LDEwOTQwMDA="

def a(num):
if (num > 1):
for i in range(2,num):
if (num % i) == 0:
return False
break
return True
else:
return False

def b(num):
my_str = str(num)
rev_str = reversed(my_str)
if list(my_str) == list(rev_str):
return True
else:
return False

cipher = base64.b64decode(cipher_b64).decode().split(",")

while(count < len(cipher)):
if (a(num)):
if (b(num)):
print(chr(int(cipher[count]) ^ num), end='', flush=True)
count += 1
if (count == 13):
num = 50000
if (count == 26):
num = 500000
else:
pass
num+=1

print()


Reverse Engineering the challenge and reimplementing the program in an optimized way was, I guess, was the intended solution.

The way i solved the challenge is by looking at the code a bit and optimizing that code itself.

When we open the file in a text editor, we see that it is not much code just 40 lines of code.

Lets start to understand the code:

It first base64 decodes the cipher_b64 and creates the list out of it which is

['100', '111', '100', '96', '112', '21', '209', '166', '216', '140', '330', '318', '321', '70221', '70414', '70544', '71414', '71810', '72211', '72827', '73000', '73319', '73722', '74088', '74643', '75542', '1002903', '1008094', '1022089', '1028104', '1035337', '1043448', '1055587', '1062541', '1065715', '1074749', '1082844', '1085696', '1092966', '1094000']


Starting from the bottom, we see a while loop which runs for 40 times so we can assume that the length of the flag is 40 characters.


while(count < len(cipher)):
if (a(num)):
if (b(num)):


Inside the while loop there is are two nested if statements which calls function **a** and **b** and expects a true value to execute it, if any of the value returned, either from function a or b, is false then it will not go inside if and jump to else which is just pass and then increments the num variable

else:
pass
num+=1

The function **a** takes a number as an argument and checks if it is a prime

for i in range(2,num):
if (num % i) == 0: # checks for prime numbers numbers returns false if not a prime number
return False
break
return True



Function **b** takes a number (which is prime) and checks if its a palindrome (i.e. if the number is same if read in reverse, example 111, 10101).

my_str = str(num)
rev_str = reversed(my_str)
if list(my_str) == list(rev_str): # checks if its a palindrome
return True


The *slowness* of the program is due to the increment of the **num** variable on 13th and 26th iteration.

So, in a nutshell, the program takes a number, first checks if its prime and then checks if its a palindrome.

The weakness lies here that it first checks for prime and then for palindrome.

To optimize it we can first check for palindrome and then for prime because it has to check for palindromic primes, and the prime checking function **a** takes a lot of time.

**The Solution** is just to swap the functions in the if clause, i.e. change a to b and b to a in the while loop's if clause.

After changing it, it takes around 5 seconds to output the flag.

The solution script is

import base64
num = 0
count = 0
cipher_b64 = b"MTAwLDExMSwxMDAsOTYsMTEyLDIxLDIwOSwxNjYsMjE2LDE0MCwzMzAsMzE4LDMyMSw3MDIyMSw3MDQxNCw3MDU0NCw3MTQxNCw3MTgxMCw3MjIxMSw3MjgyNyw3MzAwMCw3MzMxOSw3MzcyMiw3NDA4OCw3NDY0Myw3NTU0MiwxMDAyOTAzLDEwMDgwOTQsMTAyMjA4OSwxMDI4MTA0LDEwMzUzMzcsMTA0MzQ0OCwxMDU1NTg3LDEwNjI1NDEsMTA2NTcxNSwxMDc0NzQ5LDEwODI4NDQsMTA4NTY5NiwxMDkyOTY2LDEwOTQwMDA="

def a(num):
if (num > 1):
for i in range(2,num):
if (num % i) == 0: # checks for prime numbers nos returns false if not a prime number
return False
break
return True
else:
return False

def b(num):
my_str = str(num)
rev_str = reversed(my_str)
if list(my_str) == list(rev_str): # checks if its a palindrome
return True
else:
return False

cipher = base64.b64decode(cipher_b64).decode().split(",")

while(count < len(cipher)):
if (b(num)): # if number is palindrome, the only change from the original script
if (a(num)): # if number is prime
print(chr(int(cipher[count]) ^ num), end='', flush=True)
count += 1
if (count == 13):
num = 50000
if (count == 26):
num = 500000
else:
pass
num+=1

print()

And it happily gives us the flag

flag{pR1m3s_4re_co0ler_Wh3n_pal1nDr0miC}