Problem

Solution

This is one of the lowest-rated problems I have encountered thus far.. Let’s see if it lives up to its reputation!

As stated in the description, we are given two files: the ciphertext and the encoder that produced it. The ciphertext is as follows:

DLSeGAGDgBNJDQJDCFSFnRBIDjgHoDFCFtHDgJpiHtGDmMAQFnRBJKkBAsTMrsPSDDnEFCFtIbEDtDCIbFCFtHTJDKerFldbFObFCFtLBFkBAAAPFnRBJGEkerFlcPgKkImHnIlATJDKbTbFOkdNnsgbnJRMFnRBNAFkBAAAbrcbTKAkOgFpOgFpOpkBAAAAAAAiClFGIPFnRBaKliCgClFGtIBAAAAAAAOgGEkImHnIl

And the encoder, convert.py:

import sys  
chars = ""  
from fileinput import input  
for line in input():  
	chars += line  
 
lookup1 = "\n \"#()*+/1:=[]abcdefghijklmnopqrstuvwxyz"  
lookup2 = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst"  
 
out = ""  
 
prev = 0  
for char in chars:  
	cur = lookup1.index(char)  
	out += lookup2[(cur - prev) % 40]  
	prev = cur  
 
sys.stdout.write(out)

This is just a simple cyclical substitution cipher (and we’re given the lookup tables already) so reversing this is quite trivial: simply repeat the steps but flip the lookup and arithmetic operations. My script is as follows:

lookup1 = "\n \"#()*+/1:=[]abcdefghijklmnopqrstuvwxyz"  
lookup2 = "ABCDEFGHIJKLMNOPQRSTabcdefghijklmnopqrst"  
 
with open("ciphertext", "r") as f:  
	c = f.read()  
 
flag = ""  
 
prev = 0  
for i in c:  
	cur = (lookup2.index(i) + prev) % 40  
	flag += lookup1[cur]  
	prev = cur  
 
print(flag)

After running this, I expected the flag, but instead, received the following output in my terminal:

#asciiorder  
#fortychars  
#selfinput  
#pythontwo  
  
chars = ""  
from fileinput import input  
for line in input():  
   chars += line  
b = 1 / 1  
  
for i in range(len(chars)):  
   if i == b * b * b:  
       print chars[i] #prints  
       b += 1 / 1

So it seems that the plaintext is actually a Python2 script! I piped this into a file named convert2.py, then reproduced and modified the logic in a separate (Python3) script which I then ran on the ciphertext.

with open("ciphertext", "r") as f:  
	c = f.read()
 
b = 1    
flag = ""  
for i in range(len(c)):  
	if i == b * b * b:  
		flag += c[i]  
		b += 1  
 
print(f"picoCTF{{{flag}}}")

This yielded some gibberish that was clearly not the flag. Weird. We were only given one ciphertext, so what now? I pondered for a moment before remembering the peculiar comments at the top of convert2.py:

#asciiorder  
#fortychars  
#selfinput  
#pythontwo 

In particular, the #selfinput comment gave me the idea to use the Python2 script itself as input. So I did exactly that:

with open("convert2.py", "r") as f:  
	c = f.read()
 
b = 1    
flag = ""  
for i in range(len(c)):  
	if i == b * b * b:  
		flag += c[i]  
		b += 1  
 
print(f"picoCTF{{{flag}}}")

Finally, the output was a coherent word, and according to picoCTF, the correct flag.

Reflection

I don’t think this challenge deserves the 32% rating it has currently. It wasn’t particularly difficult, frustrating, or time consuming (it took me maybe 10 minutes?).