PicoCTF – Crudecrypt

Hi guys 🙂

In this post I will try to explain how I solved the challenge CrudeCrypt.
If you haven’t done yet, I invite you to read my introduction post which explains what picoCTF is.

Such as the previous challenges (NeverNote, BestShell and ExecuteMe) the binary has the flag setgid on, and is vulnerable to a buffer overflow attack which allows arbitrary code execution. ASLR (Address Space Layout Randomization) is disabled and the stack is executable. Even though this challenge should be harder to solve than NeverNote (it’s the following one in the list), I found it easier.

The source code of the binary crude_crypt is composed of 210 lines and allows to cipher / decipher any files by using the symmetric encryption algorithm AES in CBC (Cipher Block Chaining) mode with a 128 bit key. The ciphering / deciphering functions are provided by the libmcrypt library.

Let’s play with the binary !

pico4180@shell:/home/crudecrypt$ file crude_crypt
crude_crypt: setgid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=4f078a5985107cf42c7f527e82a2fbcfb201948e, not stripped

Let’s create a small text file and test the encryption process (encrypt mode):

pico4180@shell:/home/crudecrypt$ echo "Hello World CrudeCrypt" > /tmp/test_clear
pico4180@shell:/home/crudecrypt$ ./crude_crypt encrypt /tmp/test_clear /tmp/test_ciphered
-=- Welcome to CrudeCrypt 0.1 Beta -=-
-> File password: toto
=> Encrypted file successfully
pico4180@shell:/home/crudecrypt$ hexdump -C /tmp/test_ciphered
00000000 55 ae 31 0f df 8a 42 8c ac a3 6a 47 2f 29 87 e5 |U.1...B...jG/)..|
00000010 c1 2c c9 a5 53 ea 84 a9 65 83 e8 4a 27 96 34 6e |.,..S...e..J'.4n|
00000020 36 ca 5f c4 38 ba b0 45 43 e1 11 8d d2 33 46 f6 |6._.8..EC....3F.|
00000030 3d f7 50 da c6 fc c9 98 27 83 78 37 16 bc e9 56 |=.P.....'.x7...V|

Let’s try to recover our original file by using the decryption process (decrypt mode):

pico4180@shell:/home/crudecrypt$ ./crude_crypt decrypt /tmp/test_cipher /tmp/test_clear_2
-=- Welcome to CrudeCrypt 0.1 Beta -=-
-> File password: toto
[#] Warning: File not encrypted by current machine.
=> Decrypted file successfully
pico4180@shell:/home/crudecrypt$ diff /tmp/test_clear /tmp/test_clear_2

You can notice that after the encryption process, the file size has been increased of 41 bytes. Originally, the file size was 23 bytes but a file header (40 bytes) is added before the encryption, as well as 1 additional padding byte in order to align the file size on 16 bytes (block size used for the encryption). Indeed, “Block cipher modes operate on whole blocks and require that the last part of the data be padded to a full block if it is smaller than the current block” (wikipedia).

The file header (40 bytes) is composed of 3 fields:

  • magic_number: set to 0xc0dec0de (stored as 0xdec0dec0 -> little-endian)
  • file_size: length of the effective payload to encrypt (it doesn’t include the header size neither the padding bytes)
  • host: result of gethostname()

The first step of the decryption process is to decrypt the file header, and to check whether the magic number is valid. If so, it then decrypt the whole file.
A verbose message is printed on the standard output if the host name on which the file has been encrypted is not the same as the current host name. The function check_hostname() perform this check and is vulnerable:

The function strncpy() will copy up to strlen(header->host) bytes into the saved_host buffer which has a 32 bytes fixed length. So if we craft a special file with a host field greater than 32 bytes, we can exploit a buffer overflow vulnerability.

Let’s build the payload with a simple python script (you might need to install the package python-crypto):

The shellcode used for spawning the shell is described in depth in this post. There is not effective payload since it is not needed to exploit the buffer overflow.

Once our python script is written, we can run it to generate the encrypted payload:

pico4180@shell:/home/crudecrypt$ python ~/crudecrypt/crudecrypt.py
Before alignment payload len is 57
After alignment payload len is 64
Payload is written in /tmp/encrypted_payload

Now, let’s use our payload to solve the challenge !

pico4180@shell:/home/crudecrypt$ ./crude_crypt decrypt /tmp/encrypted_payload /tmp/foo
-=- Welcome to CrudeCrypt 0.1 Beta -=-
-> File password: toto

$ ls
Makefile crude_crypt crude_crypt.c flag.txt
$ cat flag.txt
$ exit

And this is our flag: writing_software_is_hard 🙂

Hopefully you enjoyed this write-up, if you want the source code of this challenge, it can be downloaded here.


Security Engineer / Malware Analyst, interested in reverse engineering, vulnerability exploitation, OS architecture & software developpement.

Leave a Reply

Your email address will not be published. Required fields are marked *