Boston Key Party CTF 2014 | Decrypt Img [Write Up]

We encrypted an image that we drew in paint, but lost the original! Can you recover it for us?
http://bostonkeyparty.net/challenges/decryptimg-a921005aad6a6b6b445d0d754d54a311.zip

In this reversing task, we were given three files: con.exe, cryptdll.dll and decryptme.bmp.bkenc. The program let us encrypt a file with a 54-byte key. Obviously, our mission is to decrypt decryptme.bmp.bkenc. Firstly, we notice that the file we have to decrypt is a BMP file, and that the header of BMP files has a lenght of 54 bytes.

con.exe is a .NET executable, so we are going to decompile it.

private static void Main(string[] args)
{
	Console.WriteLine("welcome to the boston key party encryption");
	Console.WriteLine("we encrypted this picture, but forgot the password");
	Console.WriteLine("can you recover it for us?");
	Console.Write("give me a file: ");
	string str = Console.ReadLine();
	string str1 = str;
	if (!File.Exists(str))
	{
		Console.WriteLine("that's not even a file, bye!");
		return;
	}
	Console.WriteLine("Got it, you want to encrypt {0}", str);
	byte[] numArray = File.ReadAllBytes(str);
	Console.WriteLine("now give your encryption key in a file: ");
	str = Console.ReadLine();
	if (!File.Exists(str))
	{
		Console.WriteLine("that's not even a file, bye!");
		return;
	}
	Console.WriteLine("Got it, your key is", str);
	byte[] numArray1 = File.ReadAllBytes(str);
	if ((int)numArray1.Length != 54)
	{
		Console.WriteLine("sorry, but the bkpe standard mandates a key length of 54 bytes");
	}
	Encryption.encrypt(numArray, (int)numArray.Length, numArray1, (int)numArray1.Length);
	FileStream fileStream = new FileStream(string.Concat(str1, ".bkenc"), FileMode.Create, FileAccess.Write);
	fileStream.Write(numArray, 0, (int)numArray.Length);
	fileStream.Close();
}

The program takes two files: the file to encrypt and a file that contains the 54-byte key. Afterwards, the interesting encrypt() function is called, which we can find in cryptdll.dll. We load the dll under IDA. Here is the interesting part (a1 = plaintext, a2 = len(plaintext), a3 = key and a4 = len(key)):

char __stdcall encrypt(int a1, int a2, int a3, signed int a4)
{
  signed int v4; // edx@1
  int v5; // edi@1
  int v6; // ecx@2
  char result; // al@3
  char v8; // al@3
  int v9; // esi@4
  int v10; // edx@5
  int v11; // ecx@5

  v5 = 0;
  v4 = 1;
  if ( a4 > 1 )
  {
    v6 = a3 + 1;
    do
    {
      v8 = *(_BYTE *)v6 ^ *(_BYTE *)(v6 - 1);
      ++v6;
      result = v4++ ^ v8;
      *(_BYTE *)(v6 - 1) = result;
    }
    while ( v4 < a4 );
  }
  v9 = 0;
  if ( a2 > 0 )
  {
    v11 = a3;
    v10 = a1;
    do
    {
      result = *(_BYTE *)(v5 + v11);
      *(_BYTE *)(v9 + v10) ^= result;
      ++v5;
      if ( v5 == a4 )
      {
        result = sub_10001270(v11, a4);
        v11 = a3;
        v10 = a1;
        v5 = 0;
      }
      ++v9;
    }
    while ( v9 < a2 );
  }
  return result;
}

Firstly, our 54-byte key is encrypted. The encryption can be basically written like this (in Python):

key = [...]
for i in range(1, len(key)):
	key[i] = key[i] ^ key[i - 1] ^ i

We can decrypt an encrypted key like this:

encrypted_key = [...]
key_plain = [encrypted_key[0]]
for i in range(1, len(encrypted_key)):
	key_plain.append(encrypted_key[i] ^ encrypted_key[i - 1] ^ i)

Good. After that, our file is simply XOR’d with the encrypted key. When the end of the key is reached, a new key is generated using rand(), with a srand() seeded by the previous one. That means if we make even a little error of one bit in the key, all the next keys will be wrong, and the BMP won’t be decrypted successfully.

Like we noticed earlier, the key length is 54, and that’s a good thing because the BMP header is 54-byte long as well 😀 Thus we may be able to rebuild the entire BMP header, and then find the key. Here is a « classic » hex-encoded BMP header.

42 4D ?? ?? ?? ?? 00 00 00 00 36 00 00 00 28 00
00 00 ?? ?? ?? ?? ?? ?? ?? ?? 01 00 18 00 00 00
00 00 ?? ?? ?? ?? 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00

File size
Image width
Image height
Image size

As the encryption works byte by byte, the file size hasn’t changed : it’s the encrypted file size, i.e. 1,440,054 bytes. The image size is the file size – header size, i.e. 1,440,000. We convert them in 32-bit little endian numbers.

42 4D 36 F9 15 00 00 00 00 00 36 00 00 00 28 00
00 00 ?? ?? ?? ?? ?? ?? ?? ?? 01 00 18 00 00 00
00 00 00 F9 15 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00

It remains for us to find the width and the height of the image. The image size is 1,440,000 and there are 3 bytes per pixel, so there should be 480,000 pixels. Some values, like 768px * 625px or 800px * 600px seem good.

x = 768
y = 625

s = lambda _: __import__('struct').pack('<L', _)
crypted_header = "264250FA730B3574142F202510773A6E1A633754075A525504512C0E33027407461452EE4E1F55014D021F0C195C46235828493E4231".decode('hex')
h = 'BM6\xf9\x15\x00\x00\x00\x00\x006\x00\x00\x00(\x00\x00\x00' + s(x) + s(y) + '\x01\x00\x18\x00\x00\x00\x00\x00\x00\xf9\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
xored_key = [ord(b) ^ ord(d) for b, d in zip(h, crypted_header)]
key_plain = [xored_key[0]]
for i in range(1, len(xored_key)):
	key_plain.append(xored_key[i] ^ xored_key[i - 1] ^ i)
print "Key: %s" % (x, y, ''.join(map(chr, key_plain)))

For 768*625, the possible key is: djkfah8Fh2389jksdhFsDHocKLf894hlasdfhalsdf789h4JKASDHF.
For 800*600, the possible key is: djkfah8Fh2389jksdhfSDHFJKLf894hlasdfhalsdf789h4JKASDHF.
They both seem like good keys, so we are going to try them. We use con.exe with decryptme.bmp.bkenc and we give the program a file containing one of the two keys.

It turns out that the key of 800px * 600px decrypts the BMP correctly. Enjoy 😀

decryptme.bmp.bkenc.bkenc

Publicités

Une réflexion au sujet de « Boston Key Party CTF 2014 | Decrypt Img [Write Up] »

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s