Home Projects Blog Contact

EXORcism - Praygan CTF 2019


Files avaiable at https://github.com/NullPxl/CTF-writeups/tree/master/prayganctf2019/exorcism

My friend Alex needs your help very fast. He has been possessed by a ghost and the only way to save him is if you tell the flag to the ghost. Hurry up, time is running out!

HINT: We need a very quick response from you so that you can save him from the ghost by exorcizing it with the flag.
<encoded.txt file>

On downloading the encoded.txt file you might realize there's a lot of unnecessary newlines, so to get rid of that you can just make a python script (or use find and replace)

with open('encoded.txt', 'r') as h:
    f = h.readlines()

c = ''.join([n.strip() for n in f])

The hardest part of this challenge was figuring out what this garbled mess of 1s and 0s actually was, and the hint was very helpful here.
We need a very *quick response* aka, it's a qr code. After playing with your terminal size for a bit, you should get something that looks like this: qrs(the 2 codes are the same, to get only the qr code 1s and 0s I just copy and pasted.)

I've seen these kinds of challenges before, so I had a script in one of my other directories just for this.
NOTE: this is most likely a modified script that I found somewhere but because it was just sitting on my hard drive in a random directory, I don't know where :(

from PIL import Image

with open('qr.txt', 'r') as h:
    f = h.readlines()

qr = ''.join([n.strip() for n in f])

outimg = Image.new("RGB",(74,37),"black") # 74 37 are the qr code dimensions
pixels_out = outimg.load()

for y in range(0,37):
    for x in range (0,74):
        if qr[val]=="1":
            pixels_out[(x,y)]=(255,255,255) # white


After scanning this code, you'll get 160f15011d1b095339595138535f135613595e1a Looking back at the challenge title and description, it's probably a good guess that you need to xor it with something, because we know the flag format this is simple.

# script modified from https://medium.com/@thereallulz/writeup-codebattle-2018-all-challenges-by-thereallulz-e7a99ade6305
ciphertext = '160f15011d1b095339595138535f135613595e1a'.decode('hex')

def xor(s1,s2):
    return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2))

def repeat(s, l):
    return (s*(int(l/len(s))+1))[:l]

print('Byte length of the ciphertext: ' + str(len(ciphertext)))

xor_5_bytes = xor('pctf{', ciphertext)
print ('First 5 bytes after XOR with the flag format: ' + xor_5_bytes)

this returns


for the first 5 bytes, which is just "flag" repeating, so after xoring the hex string with "flag", we get the flag

# Appended to bottom of previous script
key = repeat("flag", len(ciphertext))
print xor(ciphertext, key)