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?).