TCTT2023: PRINT

Gym
5 min readSep 17, 2023

--

Welcome to my writeup of the TH Cyber Top Talent 2023 Capture The Flag event! This is one of the challenges in Reverse Engineering/Pwnable category worth 300 points, but it is not as difficult as other Reverse Engineering/Pwnable challenges in my opinion.

We get a python file from the challenge and a description saying that the python code has bugs or mistakes that we need to fix. If we fix it, we should get an MD5 hex. Here is the original code.

def sixfour(aha):
#aha.insert(7, '4')
#aha.insert(15, '2')
#aha.insert(23, '0')
#aha.insert('31', '5')
#aha = ' '.join(aha)

aha_sixfour = aha.encode('asci')
naja_boy = base64.b32decode(aha_sixfour)
naja = naja_b.decode('asci')

printf("flag{"+str(naja)+"}")

def rolling(realText, step):
out = []
cryptText = []

uppercase = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
lowercase = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

for eachLetter in realText:
if eachLetter in uppercase:
index = uppercase.index(eachLetter)
crypting = (index + step) % 25
cryptText.append(crypting)
newLetter = uppercase[crypting]
out.append(newLetter)
elif eachLetter in lowercase:
index = lowercase.index(eachLetter)
crypting = (index + step) % 26
cryptText.append(crypting)
newLetter = lowercase[crypting]
out.append(newLetter)
out.append("=")
sixfour(out)

print("========== Welcome to PRINT ==========")
msg = "UNMtFqHTgY"
+"rGATTAMqGDFUAH"
+"eFATUqXfVKUsFgL"
n = '0'
rolling(msg, num)

There are many simple mistakes related to the python syntax, indentation, or misspelling of words, so let’s fix that first.

import base64 # this program used base64 library, but didn't imported it

def sixfour(aha):
#aha.insert(7, '4')
#aha.insert(15, '2')
#aha.insert(23, '0')
#aha.insert('31', '5')
#aha = ' '.join(aha)

aha_sixfour = aha.encode('ascii') # change asci to ascii
naja_boy = base64.b32decode(aha_sixfour)
naja = naja_boy.decode('ascii') # change asci to ascii, change naja_b to naja_boy

print("flag{"+str(naja)+"}") # there is no printf in python, only print

def rolling(realText, step):
out = []
cryptText = []

uppercase = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
lowercase = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']

for eachLetter in realText:
if eachLetter in uppercase:
index = uppercase.index(eachLetter)
crypting = (index + step) % 25
cryptText.append(crypting)
newLetter = uppercase[crypting]
out.append(newLetter)
elif eachLetter in lowercase:
index = lowercase.index(eachLetter)
crypting = (index + step) % 26
cryptText.append(crypting) # fix the indentation of this line
newLetter = lowercase[crypting]
out.append(newLetter)
out.append("=")
sixfour(out)

print("========== Welcome to PRINT ==========")
msg = "UNMtFqHTgY"+"rGATTAMqGDFUAH"+"eFATUqXfVKUsFgL" # concanated string should be on the same line
n = '0'
rolling(msg, n) # change num to n as that is what it should be

Now we should try to understand what this code does to know if we should change anything.

We can see that a function “rolling” is called with msg, which look like a base64 encoded string, and n as parameters. Looking into rolling, the function get index of each letter from uppercase and lowercase array, which would be the number order in the Alphabet. A is 0, B is 1, …, Z is 25.

Then this index is added by the step parameter which is n and modded by 25 if uppercase or 26 if lowercase. After that a new letter with the index is added to the output. Then sixfour function is called with the output array as a parameter.

TLDR; After analyzing the rolling function, we now know that what it does is rotating alphabetical character by the number of step just like Caesar Cipher.

Since rolling is basically Caesar Cipher, we noticed that when index is added by step, the mod value should be 26 since there are 26 characters in the Alphabet.

 for eachLetter in realText:
if eachLetter in uppercase:
index = uppercase.index(eachLetter)
crypting = (index + step) % 26 # change from 25 to 26
cryptText.append(crypting)
newLetter = uppercase[crypting]
out.append(newLetter)
elif eachLetter in lowercase:
index = lowercase.index(eachLetter)
crypting = (index + step) % 26
cryptText.append(crypting) # fix the indentation of this line
newLetter = lowercase[crypting]
out.append(newLetter)

Next, let’s take a look at the sixfour function. We know that the output from rolling is an array, but we see that the function tried to encode its input with Ascii, which it can’t do with an array.

We also see that there is a commented code that would fix this issue by turning array containing characters into a string. What we don’t know is whether aha.insert() comments is necessary, but for now let’s use them as well.

def sixfour(aha):
# uncommented these line
aha.insert(7, '4')
aha.insert(15, '2')
aha.insert(23, '0')
aha.insert(31, '5') # change '31' to 31
aha = ' '.join(aha)

aha_sixfour = aha.encode('ascii') # change asci to ascii
naja_boy = base64.b32decode(aha_sixfour)
naja = naja_boy.decode('ascii') # change asci to ascii, change naja_b to naja_boy

print("flag{"+str(naja)+"}") # there is no printf in python, only print

Trying to run this code will cause an issue. Because we are trying to decode aha_sixfour with base32, and aha contain a ‘0’ which is not a base32 character.

‘0’ is not a base32 character

Since the function is called “sixfour”, and the variable is called aha_sixfour, it’s more likely that we should use b64decode instead of removing the ‘0’.

def sixfour(aha):
# uncommented these line
aha.insert(7, '4')
aha.insert(15, '2')
aha.insert(23, '0')
aha.insert(31, '5') # change '31' to 31
aha = ' '.join(aha)

aha_sixfour = aha.encode('ascii') # change asci to ascii
# naja_boy = base64.b32decode(aha_sixfour)
naja_boy = base64.b64decode(aha_sixfour)
naja = naja_boy.decode('ascii') # change asci to ascii, change naja_b to naja_boy

print("flag{"+str(naja)+"}") # there is no printf in python, only print

We are really close to getting the flag, but now we have one last problem.

print("========== Welcome to PRINT ==========")
msg = "UNMtFqHTgY"+"rGATTAMqGDFUAH"+"eFATUqXfVKUsFgL" # concanated string should be on the same line
n = '0'
rolling(msg, n) # change num to n as that is what it should be

Remember that rolling will shift each letter in msg by n? Well, we have no idea what n is. We can just brute force n until we get an MD5 hex.

You can continue to find the flag using only the python code, but since the code only shift characters, add some number with aha.insert(), and then decode it with base64, I just print the value of aha then use CyberChef to decode it.

def sixfour(aha):
aha.insert(7, '4')
aha.insert(15, '2')
aha.insert(23, '0')
aha.insert(31, '5')
aha = ''.join(aha)
print(aha) # add this

aha_sixfour = aha.encode('ascii')
naja_boy = base64.b64decode(aha_sixfour)
# naja_boy = base64.b32decode(aha_sixfour)
naja = naja_boy.decode('ascii')
print("flag{"+str(naja)+"}")

aha = UNMtFqH4TgYrGAT2TAMqGDF0UAHeFAT5UqXfVKUsFgL=

Wrap the hex with CTT23{ and } and you get the flag! Thank you for reading.

--

--