Categories
Challenge

DEF CON 32 – Cloud Village CTF – Azurite’s Spell

Azurite’s Spell was worth a relatively small amount of points, so it was the first I looked at, as a bit of a warm-up. That said, a bit of confusion on my end meant it took way too long to complete.

Oh well. That’s how life goes.

Image of the Azurite's spell intro card

Azurite’s Spell Intro Card

From the intro card it’s clear to see that we’ll have to decrypt a ciphertext. There’s also wording suggesting we’ll have to do something with an Azure function and look around for bits and pieces of the function, but that didn’t end up being the case. Clicking the “Enter Here” link, shows us this page:

From this we can confirm that we’re going to be decrypting some ciphertext. There’s also a reverence to some initialization vectors (IVs) and the fact that the key is empty (b’\x00′ * 32) and 256 bits (32 bytes, from the size of the key).

The “base six and four” portion point out base64 encoding, though that is usually easily recognizable. Still, nice to have it confirmed, especially because I’m about to trip up on that fact.

Armed with this information, we click the phone on the right, and get some JSON (which is generated fresh every time we load this URL).

We see an encrypted message and a list of initialization vectors. Easy enough – Lets iterate through them with a zeroed-out key and try to decrypt the message. We can assume that it’s using the CBC mode, but to be sure, let’s iterate through all the modes as well.

Python Code (run.py)

from Crypto.Cipher import AES
import hashlib
import base64

data = {"encrypted_message": "6TBhMq7FVvVAYVDBi9Bq1xg9jcLCwAl0vOEb5hJR16wm0Zhuft5Jb490tU2F86aIey4gZwUUBU4uP89xOQVBZQ==", "iv_list": ["A4/pZ6PQ1KlMvtegS9363w==", "weO/KTzVF+cveXhnGiDyjQ==", "D24UM9a0KX4XWLvdyUSzkQ==", "gsrwi/xnEEB7PG/mCr+5KQ==", "0iQr4/ZoO/PgmuodFz4mKg==", "wlWb0O13fWDtBN92A6RhoQ==", "gvG/YiCX7oVWftr431Npug==", "z+imkHbSIDBncNzS4cVBeA==", "2t+0FMblxj/PdnX9fq9aVQ==", "6TBhMq7FVvVAYVDBi9Bq1w==", "zYItkHZZHBlt8+2TOGinWQ==", "cVJzJWdPyr6z4kfQwfUqZg==", "+XABmOijKlDdHI16cpo89A==", "gCFGXlBjkBlAOtYMbePK0Q==", "S5LZDF+7SYuCcNiVKe5hFA==", "YdaGwhqvMlu8vCAQuX67sA=="]}




def decrypt(encrypted,key,iv, mode):   #Stolen from somewhere on stackoverflow
  cipher = AES.new(key, mode, iv)
  decryptedtext = cipher.decrypt(encrypted)   # Base64 decode the ciphertext
  return (decryptedtext)
k = bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000000')
for m in range(1, 12):
	print("MODE ", m)
	for i in data["iv_list"]:

		unb64_enc = base64.b64decode(data["encrypted_message"])
		unb64_i = base64.b64decode(i)
		#print("\tTrying IV: ", base64.b64decode(i).hex())
		try:
			decrypted = decrypt(
				unb64_enc,
				k,
				unb64_i, 
				m)
			print("\t**********FOUND:", decrypted)
		except Exception as error:
			#print("\tFailed", error) 
			continue

Running this produces a lot of output, which we can scroll through and look for anything looking like a decrypted flag, which we find in the mode 2 section (which, surprise surprise, is the CBC mode):

Screenshot showing output with found flag

Screenshot of Output

Et voila, the flag is

FLAG-{7h3EnCh4nT3dK3y0f4zur1te92847n11}

Now it’s at this point that I should point out an error on my part. You’ll notice in the original JSON screenshot, the encrypted message appears to be base64-like, but oddly enough, without any lowercase characters. I’ll forgive you for having missed that, if you’ll reciprocate and forgive me for the same. I treated it like base64 for way too long (and also disabled error printing in my try-except block…) which caused this to take way longer than it should have. In the Python code, you’ll see it looks far more like normal base64, with lowercase letters and everything. That’s because it is initially given to you as base32-encoded base64-encoded ciphertext, and a good amount of repeated head-wall contact was solved by my first base32-decoding that string and replacing it in the JSON. Suddenly everything worked and the world wasn’t terrible.

Leave a Reply