77. Brute Force Decryption

Brute Force Decryption

Introduction

In the beginner modules, payload encryption and decryption were demonstrated and a warning was mentioned about saving the encryption key within the binary. Recall that if the encryption key is saved in plaintext within the binary it can be trivially retrieved. One solution is to encrypt the key with another key and decrypt it at runtime. To avoid hardcoding the key inside the binary, the key is brute-forced.

This module will demonstrate an XOR decryption algorithm where the program has to guess the key through brute forcing.

Key Encryption Process

To perform a key brute force, the encryption and decryption functions require a hint byte. Knowing one byte's value before and after the encryption process makes the decryption process possible. In this case, the first byte has been selected as the hint byte.

For example, if the hint byte is BA and when encrypted it becomes 71, then the decryption process will brute force that value until it is reverted to BA, indicating the correct key was used.

Key Encryption Function

The GenerateProtectedKey function takes a hint byte and prepends it as the first byte of the plaintext key. It then uses an XOR encryption algorithm to encrypt the key using a randomly generated key at runtime.

Note that the PrintHex is a function that prints the input buffer as a hex array, and it is being used to print the plaintext generated key.

/*
  - HintByte: is the hint byte that will be saved as the key's first byte
  - sKey: the size of the key to generate
  - ppProtectedKey: pointer to a PBYTE buffer that will recieve the encrypted key
*/

VOID GenerateProtectedKey(IN BYTE HintByte, IN SIZE_T sKey, OUT PBYTE* ppProtectedKey) {

	// Genereting a seed
	srand(time(NULL));

	// 'b' is used as the key of the key encryption algorithm
	BYTE        b                = rand() % 0xFF;

	// 'pKey' is where the original key will be generated to
	PBYTE       pKey             = (PBYTE)malloc(sKey);

	// 'pProtectedKey' is the encrypted version of 'pKey' using 'b'
	PBYTE       pProtectedKey    = (PBYTE)malloc(sKey);

	if (!pKey || !pProtectedKey)
		return;

	// Genereting another seed
	srand(time(NULL) * 2);

	// The key starts with the hint byte
	pKey[0] = HintByte;
	// generating the rest of the key
	for (int i = 1; i < sKey; i++){
		pKey[i] = (BYTE)rand() % 0xFF;
	}


	printf("[+] Generated Key Byte : 0x%0.2X \n\n", b);
	printf("[+] Original Key : ");
	PrintHex(pKey, sKey);

	// Encrypting the key using a xor encryption algorithm
	// Using 'b' as the key
	for (int i = 0; i < sKey; i++){
		pProtectedKey[i] = (BYTE)((pKey[i] + i) ^ b);
	}

	// Saving the encrypted key by pointer
	*ppProtectedKey = pProtectedKey;

	// Freeing the raw key buffer
	free(pKey);
}

Key Decryption Process

Since the encryption key used to encrypt the key was not stored anywhere, the decryption function must be able to guess the value of b shown in the GenerateProtectedKey function. To do so, the decryption function will XOR the first byte of the key, which is the hint byte, with different keys until the resulting byte is the original key's hint byte. When that happens, the function will know that the correct b value was selected. The code snippet below shows this logic.

if (((EncryptedKey[0] ^ b) - 0) == HintByte)
  // Then b's value is the xor encryption key
else
  // Then b's value is not the xor encryption key, try with a different b value

Continuing from the previous example, when 71 becomes BA then the correct b value has been guessed.

Key Decryption Function

The BruteForceDecryption function needs the same hint byte that was passed to the encryption function.

/*
	- HintByte : is the same hint byte that was used in the key generating function
	- pProtectedKey : the encrypted key
	- sKey : the key size
	- ppRealKey : pointer to a PBYTE buffer that will recieve the decrypted key
*/

BYTE BruteForceDecryption(IN BYTE HintByte, IN PBYTE pProtectedKey, IN SIZE_T sKey, OUT PBYTE* ppRealKey) {

	BYTE      b         = 0;
	PBYTE     pRealKey  = (PBYTE)malloc(sKey);

	if (!pRealKey)
		return NULL;

	while (1){

		// Using the hint byte, if this is equal, then we found the 'b' value needed to decrypt the key
		if (((pProtectedKey[0] ^ b) - 0) == HintByte)
			break;
		// else, increment 'b' and try again
		else
			b++;
	}

        // The reverse algorithm of the xor encryption, since 'b' now is known
	for (int i = 0; i < sKey; i++){
		pRealKey[i] = (BYTE)((pProtectedKey[i] ^ b) - i);
	}

        // Saving the decrypted key by pointer
	*ppRealKey = pRealKey;

	return b;
}

Demo

The image below shows the generation of the XOR-encrypted key. The arrows point to the code that generates the respective console output.

The image below shows the successful brute force and decryption.

Conclusion

Although this brute-forcing approach is simple, it can be used to prevent malware analysts and researchers from dumping the key from the binary file. This forces them to debug the binary to understand how the key is generated which is where the anti-analysis techniques come in handy.