This was a nice little challenge from sCTF 2016. I’m trying to work on more tricky stuff, but this one looked like fun and I couldn’t resist.
Challenge:
Welcome to Verticode, the new method of translating text into vertical codes.
Each verticode has two parts: the color shift and the code.
The code takes the inputted character and translates it into an ASCII code, and then into binary, then puts that into an image in which each black pixel represents a 1 and each white pixel represents a 0.
For example, A is 65 which is 1000001 in binary, B is 66 which is 1000010, and C is 67 which is 1000011, so the corresponding verticode would look like this.
Except, it isn’t that simple.
A color shift is also integrated, which means that the color before each verticode shifts the ASCII code, by adding the number that the color corresponds to, before translating it into binary. In that case, the previous verticode could also look like this.
The table for the color codes is:
0 = Red 1 = Purple 2 = Blue 3 = Green 4 = Yellow 5 = Orange
This means that a red color shift for the letter A, which is 65 + 0 = 65, would translate into 1000001 in binary; however, a green color shift for the letter A, which is 65 + 3 = 68, would translate into 1000100 in binary.
Given this verticode, read the verticode into text and find the flag.
Note that the flag will not be in the typical sctf{flag} format, but will be painfully obvious text. Once you find this text, you will submit it in the sctf{text} format. So, if the text you find is adunnaisawesome, you will submit it as sctf{adunnaisawesome}.
Files:
Solution:
The very comprehensive intro text gives us all the information we need to solve the supplied code.
Here’s a close up of the start of the image:
The squares are are 12 x 12 pixels, the colour band is 84 pixels wide and the binary data is only 7 bits wide.
It was easy to knock up some Python with PIL to read the data and dump the resulting text.
Note that the shift specified in the intro text is actually just a subtract operation.
I used an offset of 2 pixels into the blocks to avoid any errors from hitting block boundaries.
import PIL.Image, sys
puzData = PIL.Image.open("code1.png").load()
px = 2
py = 2
offset = 0
byteBits = 0
for ln in range(0, 12900/12): # Image height / block size
px = 2
# Get colour value
colRGB = puzData[px,py]
if colRGB == (128, 0, 128): # Purple (1)
offset = 1
elif colRGB == (0, 0, 255): # Blue (2)
offset = 2
elif colRGB == (0, 128, 0): # Green (3)
offset = 3
elif colRGB == (255, 255, 0): # Yellow (4)
offset = 4
elif colRGB == (255, 165, 0): # Orange (5)
offset = 5
else:
offset = 0 # red (0)
# Jump out to bit data
px = 85
byteBits = 0
for b in range(0,7):
byteBits <<= 1
if puzData[px,py][0] == 0:
byteBits +=1
px += 12
sys.stdout.write(chr(int(byteBits - offset)))
# Next row
py += 12
The output from the code is a mess of plaintext:
JoeLopowasamanofmildtemperamentshortstatureandhadthegoaltobecometheworldsfastesttelephoneeater ThoughLoponeverknewevenbasicphysicshecreatedatelescopecapableofsightingthesmallesthaironanalienwholivedquiteafewlightyearsaway JoeLopoquicklydestroyedalargeboulderandusedtheshatteredremainstoformeightsmallstatuesthatstronglyresembledtinycreaturesbeingorrelatedtothewaterflea Heplacedtheminacircularpatterntoformasortofshrineandplacedthetelescopeinthemiddleofit Hethenchanneledthepowerofthestonewaterfleasintothetelescopetoviewthepoweroftheheavens Hewasinatrancewiththebeautyofthemysteriousdimensionanddidntevennoticetheverylargetornadoheadingtowardhim TheshrinewasquicklydemolishedandtheimmediatewithdrawlofpowersentJoeLobointoalairofpitchblacknessfoundtobeaparalleldimensionthatcaus ABCiamtheflagalllowercasenojokeDEFanyonewhosefirstnamebeganwithJalongwithMLandQtobecomeratheruncomfortable Joewasalsosuddenlyintroducedtoundroclamaticolomphasisciousytheeccentrictapewormwithastrongmorrocanaccent ImundroclamaticolomphasisciousytheeccentrictapewormIlikepizzasohowareyadoinIhavenoideasaidJoe
However, there is a small bit that is useful:
ABCiamtheflagalllowercasenojokeDEF